添加场景的组件动态添加删除功能

This commit is contained in:
2025-05-31 13:30:54 +08:00
parent 31c77dcb5e
commit fdd13a8726
20 changed files with 340 additions and 59 deletions

View File

@ -1,6 +1,8 @@
set(PROJECT_NAME Hazel)
project(${PROJECT_NAME})
add_subdirectory(vendor/SDL)
set(SDL3_SHARED ON CACHE BOOL "Build SDL as shared library" FORCE)
add_subdirectory(vendor/spdlog)
add_subdirectory(vendor/GLAD)
@ -25,21 +27,13 @@ file(GLOB IMGUI ${IMGUI_INCLUDE_DIR}/*.cpp
${IMGUI_INCLUDE_DIR}/backends/imgui_impl_opengl3.cpp
${IMGUI_INCLUDE_DIR}/backends/imgui_impl_sdl3.cpp)
add_library(Hazel SHARED
add_library(${PROJECT_NAME} SHARED
${SOURCES}
${IMGUI}
${STB}
${ENTT}
${IMGUI}
)
set(BUILD_SHARED_LIBS ON CACHE BOOL "Build shared libraries" FORCE)
add_subdirectory(vendor/SDL)
set(SDL3_SHARED ON CACHE BOOL "Build SDL as shared library" FORCE)
target_include_directories(${PROJECT_NAME}
PUBLIC
${CMAKE_CURRENT_SOURCE_DIR}/src # 暴露头文件给其他项目

View File

@ -29,6 +29,7 @@ namespace Hazel
ImGui::StyleColorsDark();
ImGuiIO& io = ImGui::GetIO();
ImGuiStyle& style = ImGui::GetStyle();
io.ConfigFlags |= ImGuiBackendFlags_HasMouseCursors;
io.ConfigFlags |= ImGuiBackendFlags_HasSetMousePos;
@ -37,6 +38,17 @@ namespace Hazel
io.ConfigFlags |= ImGuiConfigFlags_DockingEnable; // IF using Docking Branch
io.ConfigFlags |= ImGuiConfigFlags_ViewportsEnable;
io.Fonts->AddFontFromFileTTF("assets/fonts/OpenSans/OpenSans-Bold.ttf", 18.0f);
io.FontDefault = io.Fonts->AddFontFromFileTTF("assets/fonts/OpenSans/OpenSans-Regular.ttf", 18.0f);
if (io.ConfigFlags & ImGuiConfigFlags_ViewportsEnable)
{
style.WindowRounding = 0.0f;
style.Colors[ImGuiCol_WindowBg].w = 1.0f;
}
SetDarkThemeColors();
Application& app = Application::Get();
auto window = static_cast<SDL_Window*>(app.GetWindow().GetNativeWindow());
auto GL_Context = static_cast<SDL_GLContext>(app.GetWindow().GetNativeGLContext());
@ -99,4 +111,38 @@ namespace Hazel
SDL_GL_MakeCurrent(backup_current_window, backup_current_context);
}
}
void ImGuiLayer::SetDarkThemeColors()
{
auto& colors = ImGui::GetStyle().Colors;
colors[ImGuiCol_WindowBg] = ImVec4{ 0.1f, 0.105f, 0.11f, 1.0f };
// Headers
colors[ImGuiCol_Header] = ImVec4{ 0.2f, 0.205f, 0.21f, 1.0f };
colors[ImGuiCol_HeaderHovered] = ImVec4{ 0.3f, 0.305f, 0.31f, 1.0f };
colors[ImGuiCol_HeaderActive] = ImVec4{ 0.15f, 0.1505f, 0.151f, 1.0f };
// Buttons
colors[ImGuiCol_Button] = ImVec4{ 0.2f, 0.205f, 0.21f, 1.0f };
colors[ImGuiCol_ButtonHovered] = ImVec4{ 0.3f, 0.305f, 0.31f, 1.0f };
colors[ImGuiCol_ButtonActive] = ImVec4{ 0.15f, 0.1505f, 0.151f, 1.0f };
// Frame BG
colors[ImGuiCol_FrameBg] = ImVec4{ 0.2f, 0.205f, 0.21f, 1.0f };
colors[ImGuiCol_FrameBgHovered] = ImVec4{ 0.3f, 0.305f, 0.31f, 1.0f };
colors[ImGuiCol_FrameBgActive] = ImVec4{ 0.15f, 0.1505f, 0.151f, 1.0f };
// Tabs
colors[ImGuiCol_Tab] = ImVec4{ 0.15f, 0.1505f, 0.151f, 1.0f };
colors[ImGuiCol_TabHovered] = ImVec4{ 0.38f, 0.3805f, 0.381f, 1.0f };
colors[ImGuiCol_TabActive] = ImVec4{ 0.28f, 0.2805f, 0.281f, 1.0f };
colors[ImGuiCol_TabUnfocused] = ImVec4{ 0.15f, 0.1505f, 0.151f, 1.0f };
colors[ImGuiCol_TabUnfocusedActive] = ImVec4{ 0.2f, 0.205f, 0.21f, 1.0f };
// Title
colors[ImGuiCol_TitleBg] = ImVec4{ 0.15f, 0.1505f, 0.151f, 1.0f };
colors[ImGuiCol_TitleBgActive] = ImVec4{ 0.15f, 0.1505f, 0.151f, 1.0f };
colors[ImGuiCol_TitleBgCollapsed] = ImVec4{ 0.15f, 0.1505f, 0.151f, 1.0f };
}
}

View File

@ -24,6 +24,9 @@ namespace Hazel {
void BlockEvents(const bool block) {m_BlockEvent = block;}
private:
void SetDarkThemeColors();
private:
bool m_BlockEvent = true;
float m_Time = 0.0f;

View File

@ -4,6 +4,7 @@
#ifndef COMPONENTS_H
#define COMPONENTS_H
#include <glm/ext/matrix_transform.hpp>
#include <Hazel/Renderer/Camera.h>
#include <Hazel/Renderer/ScenceCamera.h>
@ -14,16 +15,26 @@ namespace Hazel
{
struct TransformComponent
{
glm::mat4 Transform{1.0f};
glm::vec3 Translation{0.0f};
glm::vec3 Rotation{0.0f};
glm::vec3 Scale{1.0f};
TransformComponent() = default;
TransformComponent(const TransformComponent&) = default;
TransformComponent(const glm::mat4& transform) : Transform(transform)
TransformComponent(const glm::vec3& translation) : Translation(translation)
{
}
operator glm::mat4&() { return Transform; }
operator const glm::mat4&() const { return Transform; }
glm::mat4 GetTransform() const
{
glm::mat4 rotation = glm::rotate(glm::mat4(1.0f), Rotation.x, glm::vec3(1.0f, 0.0f, 0.0f))
* glm::rotate(glm::mat4(1.0f), Rotation.y, glm::vec3(0.0f, 1.0f, 0.0f))
* glm::rotate(glm::mat4(1.0f), Rotation.z, glm::vec3(0.0f, 0.0f, 1.0f));
return glm::translate(glm::mat4(1.0f), Translation) * rotation * glm::scale(glm::mat4(1.0f), Scale);
}
};
struct SpriteRendererComponent

View File

@ -26,10 +26,11 @@ namespace Hazel
if (HasComponent<T>())
{
HZ_CORE_WARN("Entity already has this Component!");
return GetComponent<T>();
assert(false);
}
return m_Scene->m_Registry.emplace<T>(m_EntityHandle, std::forward<Args>(args)...);
T& component = m_Scene->m_Registry.emplace<T>(m_EntityHandle, std::forward<Args>(args)...);
m_Scene->OnComponentAdded<T>(*this, component);
return component;
}
template<typename T>
@ -66,7 +67,8 @@ namespace Hazel
}
explicit operator bool() const { return m_EntityHandle != entt::null; }
operator uint32_t() const { return static_cast<uint32_t>(m_EntityHandle); }
explicit operator uint32_t() const { return static_cast<uint32_t>(m_EntityHandle); }
operator entt::entity() const { return m_EntityHandle; }
bool operator==(const Entity& other) const { return m_EntityHandle == other.m_EntityHandle && m_Scene == other.m_Scene; }
bool operator!=(const Entity& other) const { return !(*this==other); }

View File

@ -29,6 +29,11 @@ namespace Hazel
return entity;
}
void Scene::DestoryEntity(const Entity entity)
{
m_Registry.destroy(entity);
}
void Scene::OnUpdate(TimeStep& ts)
{
@ -51,7 +56,7 @@ namespace Hazel
// Renderer 2D
Camera* mainCamera = nullptr;
glm::mat4* cameraTranform = nullptr;
glm::mat4 cameraTranform;
{
for (auto view = m_Registry.view<TransformComponent, CameraComponent>(); const auto entity : view)
{
@ -60,7 +65,7 @@ namespace Hazel
if (camera.Primary)
{
mainCamera = &camera.Camera;
cameraTranform = &transform.Transform;
cameraTranform = transform.GetTransform();
break;
}
}
@ -70,14 +75,14 @@ namespace Hazel
if (mainCamera != nullptr)
{
Renderer2D::BeginScene(mainCamera->GetProjection(), *cameraTranform);
Renderer2D::BeginScene(mainCamera->GetProjection(), cameraTranform);
auto group = m_Registry.group<TransformComponent>(entt::get<SpriteRendererComponent>);
for (auto entity : group)
{
auto [transform, sprite] = group.get<TransformComponent, SpriteRendererComponent>(entity);
Renderer2D::DrawQuad(transform, sprite.Color);
Renderer2D::DrawQuad(transform.GetTransform(), sprite.Color);
}
Renderer2D::EndScene();
@ -102,4 +107,43 @@ namespace Hazel
}
}
template <typename T>
void Scene::OnComponentAdded(Entity entity, T& component)
{
static_assert(false);
}
template<>
void Scene::OnComponentAdded<TransformComponent>(Entity entity, TransformComponent& component)
{
}
template<>
void Scene::OnComponentAdded<CameraComponent>(Entity entity, CameraComponent& component)
{
component.Camera.SetViewPortSize(m_ViewportWidth, m_ViewportHeight);
}
template<>
void Scene::OnComponentAdded<SpriteRendererComponent>(Entity entity, SpriteRendererComponent& component)
{
}
template<>
void Scene::OnComponentAdded<TagComponent>(Entity entity, TagComponent& component)
{
}
template<>
void Scene::OnComponentAdded<NativeScriptComponent>(Entity entity, NativeScriptComponent& component)
{
}
template HAZEL_API void Scene::OnComponentAdded<TransformComponent>(Entity, TransformComponent&);
template HAZEL_API void Scene::OnComponentAdded<CameraComponent>(Entity, CameraComponent&);
template HAZEL_API void Scene::OnComponentAdded<TagComponent>(Entity, TagComponent&);
template HAZEL_API void Scene::OnComponentAdded<SpriteRendererComponent>(Entity, SpriteRendererComponent&);
template HAZEL_API void Scene::OnComponentAdded<NativeScriptComponent>(Entity, NativeScriptComponent&);
}

View File

@ -24,9 +24,18 @@ namespace Hazel
Entity CreateEntity(const std::string& name = "");
void DestoryEntity(Entity entity);
void OnUpdate(TimeStep& ts);
void OnViewportResize(uint32_t width, uint32_t height);
private:
template<typename T>
void OnComponentAdded(Entity entity, T& component);
private:
entt::registry m_Registry;

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

View File

@ -57,16 +57,16 @@ namespace Hazel
public:
void OnUpdate(const TimeStep ts)
{
auto& transform = GetComponent<TransformComponent>().Transform;
auto& translation = GetComponent<TransformComponent>().Translation;
static const auto state = SDL_GetKeyboardState(nullptr);
if (state[SDL_SCANCODE_A])
transform[3][0] -= ts * speed;
translation.x -= ts * speed;
if (state[SDL_SCANCODE_D])
transform[3][0] += ts * speed;
translation.x += ts * speed;
if (state[SDL_SCANCODE_W])
transform[3][1] += ts * speed;
translation.y += ts * speed;
if (state[SDL_SCANCODE_S])
transform[3][1] -= ts * speed;
translation.y -= ts * speed;
}
private:
float speed = 2.0f;
@ -175,6 +175,8 @@ namespace Hazel
// Submit the DockSpace
ImGuiIO& io = ImGui::GetIO();
ImGuiStyle& style = ImGui::GetStyle();
style.WindowMinSize.x = 350.0f;
if (io.ConfigFlags & ImGuiConfigFlags_DockingEnable)
{
ImGuiID dockspace_id = ImGui::GetID("MyDockSpace");

View File

@ -3,8 +3,8 @@
//
#include "SceneHierachyPanel.h"
#include <imgui.h>
#include <imgui_internal.h>
#include <glm/gtc/type_ptr.hpp>
#include <Hazel/Scene/Components.h>
@ -34,6 +34,13 @@ namespace Hazel
m_SelectionContext = { };
}
if (ImGui::BeginPopupContextWindow(0, ImGuiPopupFlags_MouseButtonRight | ImGuiPopupFlags_NoOpenOverItems))
{
if (ImGui::MenuItem("Create Empty Entity"))
m_Context->CreateEntity("Empty Entity");
ImGui::EndPopup();
}
ImGui::End();
ImGui::Begin("Properties");
@ -48,18 +55,172 @@ namespace Hazel
void SceneHierachyPanel::DrawEntityNode(Entity entity)
{
auto& tag = entity.GetComponent<TagComponent>().Tag;
bool entityDeleted = false;
ImGuiTreeNodeFlags flags = ((m_SelectionContext == entity) ? ImGuiTreeNodeFlags_Selected : 0) | ImGuiTreeNodeFlags_OpenOnArrow;
flags |= ImGuiTreeNodeFlags_SpanAvailWidth;
const bool isopened = ImGui::TreeNodeEx((void*)(uint64_t)(uint32_t)entity, flags, tag.c_str());
if (ImGui::IsItemClicked())
{
m_SelectionContext = entity;
}
if (ImGui::BeginPopupContextItem())
{
if (ImGui::MenuItem("Delete Entity"))
entityDeleted = true;
ImGui::EndPopup();
}
if (isopened)
{
ImGui::TreePop();
}
if (entityDeleted)
{
m_Context->DestoryEntity(entity);
if (m_SelectionContext == entity)
{
m_SelectionContext = {};
}
}
}
static bool DrawVec3Control(const std::string& label, glm::vec3& values, float resetValue = 0.0f, float columnWidth = 100.0f)
{
ImGuiIO& io = ImGui::GetIO();
auto boldFont = io.Fonts->Fonts[0];
bool isChanged = false;
ImGui::PushID(label.c_str());
const ImGuiContext* context = ImGui::GetCurrentContext();
ImGui::Columns(2);
ImGui::SetColumnWidth(0, columnWidth);
ImGui::Text(label.c_str());
ImGui::NextColumn();
ImGui::PushMultiItemsWidths(3, ImGui::CalcItemWidth());
ImGui::PushStyleVar(ImGuiStyleVar_ItemSpacing, {0.0f, 0.0f});
float lineHeight = context->Font->FontSize + context->Style.FramePadding.y * 2.0f;
// float lineHeight = GImGui->Font->FontSize + GImGui->Style.FramePadding.y * 2.0f;
ImVec2 buttonSize = {lineHeight + 3.0f, lineHeight};
ImGui::PushStyleColor(ImGuiCol_Button, ImVec4{0.8f, 0.1f, 0.15f, 1.0f});
ImGui::PushStyleColor(ImGuiCol_ButtonHovered, ImVec4{0.9f, 0.2f, 0.2f, 1.0f});
ImGui::PushStyleColor(ImGuiCol_ButtonActive, ImVec4{0.8f, 0.1f, 0.15f, 1.0f});
ImGui::PushFont(boldFont);
if (ImGui::Button("X", buttonSize))
{
values.x = resetValue;
isChanged = true;
}
ImGui::PopFont();
ImGui::PopStyleColor(3);
ImGui::SameLine();
if (ImGui::DragFloat("##X", &values.x, 0.1f))
{
isChanged = true;
}
ImGui::PopItemWidth();
ImGui::SameLine();
ImGui::PushStyleColor(ImGuiCol_Button, ImVec4{0.2f, 0.7f, 0.2f, 1.0f});
ImGui::PushStyleColor(ImGuiCol_ButtonHovered, ImVec4{0.3f, 0.8f, 0.3f, 1.0f});
ImGui::PushStyleColor(ImGuiCol_ButtonActive, ImVec4{0.2f, 0.7f, 0.2f, 1.0f});
ImGui::PushFont(boldFont);
if (ImGui::Button("Y", buttonSize))
{
values.y = resetValue;
isChanged = true;
}
ImGui::PopFont();
ImGui::PopStyleColor(3);
ImGui::SameLine();
if (ImGui::DragFloat("##Y", &values.y, 0.1f))
{
isChanged = true;
}
ImGui::PopItemWidth();
ImGui::SameLine();
ImGui::PushStyleColor(ImGuiCol_Button, ImVec4{0.1f, 0.25f, 0.8f, 1.0f});
ImGui::PushStyleColor(ImGuiCol_ButtonHovered, ImVec4{0.2f, 0.35f, 0.9f, 1.0f});
ImGui::PushStyleColor(ImGuiCol_ButtonActive, ImVec4{0.1f, 0.25f, 0.8f, 1.0f});
ImGui::PushFont(boldFont);
if (ImGui::Button("Z", buttonSize))
{
values.z = resetValue;
isChanged = true;
}
ImGui::PopFont();
ImGui::PopStyleColor(3);
ImGui::SameLine();
if (ImGui::DragFloat("##Z", &values.z, 0.1f))
{
isChanged = true;
}
ImGui::PopItemWidth();
ImGui::PopStyleVar();
ImGui::Columns(1);
ImGui::PopID();
return isChanged;
}
template<typename T, typename UIfunction>
void DrawComponent(const std::string& name, Entity entity, UIfunction uiFunction)
{
constexpr ImGuiTreeNodeFlags treeNodeFlags = ImGuiTreeNodeFlags_DefaultOpen | ImGuiTreeNodeFlags_Framed | ImGuiTreeNodeFlags_SpanAvailWidth | ImGuiTreeNodeFlags_AllowItemOverlap | ImGuiTreeNodeFlags_FramePadding;;
if (entity.HasComponent<T>())
{
auto& component = entity.GetComponent<T>();
ImVec2 contextReginAvail = ImGui::GetContentRegionAvail();
ImGui::PushStyleVar(ImGuiStyleVar_FramePadding, {4.0f, 4.0f});
auto context = ImGui::GetCurrentContext();
float lineHeight = context->Font->FontSize + context->Style.FramePadding.y * 2.0f;
ImGui::Separator();
bool open = ImGui::TreeNodeEx((void*)(uint64_t)typeid(T).hash_code(), treeNodeFlags, name.c_str());
ImGui::PopStyleVar();
ImGui::SameLine(contextReginAvail.x - lineHeight * 0.5f);
if (ImGui::Button("+", ImVec2(lineHeight, lineHeight)))
{
ImGui::OpenPopup("ComponentSettings");
}
bool removeComponent = false;
if (ImGui::BeginPopup("ComponentSettings"))
{
if (ImGui::MenuItem("Remove Component"))
removeComponent = true;
ImGui::EndPopup();
}
if (open)
{
uiFunction(component);
ImGui::TreePop();
}
if (removeComponent)
entity.RemoveComponent<SpriteRendererComponent>();
}
}
void SceneHierachyPanel::DrawComponents(Entity entity)
@ -77,25 +238,44 @@ namespace Hazel
}
}
if (entity.HasComponent<TransformComponent>())
ImGui::SameLine();
ImGui::PushItemWidth(-1);
if (ImGui::Button("Add"))
ImGui::OpenPopup("Add");
if (ImGui::BeginPopup("Add"))
{
if (ImGui::TreeNodeEx((void*)(uint64_t)typeid(TransformComponent).hash_code(), ImGuiTreeNodeFlags_DefaultOpen, "Transform"))
if (ImGui::MenuItem("Camera"))
{
auto& transform = entity.GetComponent<TransformComponent>().Transform;
ImGui::DragFloat3("Position", glm::value_ptr(transform[3]), 0.01f);
ImGui::TreePop();
m_SelectionContext.AddComponent<CameraComponent>();
ImGui::CloseCurrentPopup();
}
if (ImGui::MenuItem("Sprite"))
{
m_SelectionContext.AddComponent<SpriteRendererComponent>();
ImGui::CloseCurrentPopup();
}
if (entity.HasComponent<CameraComponent>())
{
if (ImGui::TreeNodeEx((void*)(uint64_t)typeid(CameraComponent).hash_code(), ImGuiTreeNodeFlags_DefaultOpen, "Transform"))
{
auto& cameraComponent = entity.GetComponent<CameraComponent>();
auto& camera = cameraComponent.Camera;
ImGui::EndPopup();
}
ImGui::PopItemWidth();
ImGui::Checkbox("isPrimary", &cameraComponent.Primary);
DrawComponent<TransformComponent>("Transform", entity, [](auto& component)
{
DrawVec3Control("Translation", component.Translation);
glm::vec3 rotation = glm::degrees(component.Rotation);
if (DrawVec3Control("Rotation", rotation))
{
component.Rotation = glm::radians(rotation);
}
DrawVec3Control("Scale", component.Scale, 1.0f);
});
DrawComponent<CameraComponent>("Camera", entity, [](auto& component)
{
auto& camera = component.Camera;
ImGui::Checkbox("isPrimary", &component.Primary);
static const char* projectionTypeStrings[] = { "Perspective", "Orthographic" };
const char* currentProjectionTypeString = projectionTypeStrings[(int)camera.GetProjectionType()];
@ -146,23 +326,13 @@ namespace Hazel
if (ImGui::DragFloat("Far", &far))
camera.SetOrthographicFarClip(far);
ImGui::Checkbox("Fixed Aspect Ratio", &cameraComponent.FixedAspectRatio);
ImGui::Checkbox("Fixed Aspect Ratio", &component.FixedAspectRatio);
}
});
ImGui::TreePop();
}
}
if (entity.HasComponent<SpriteRendererComponent>())
DrawComponent<SpriteRendererComponent>("Sprite Renderer", entity, [](auto& component)
{
if (ImGui::TreeNodeEx((void*)(uint64_t)typeid(SpriteRendererComponent).hash_code(), ImGuiTreeNodeFlags_DefaultOpen, "Sprite Renderer"))
{
auto& spriteRendererComponent = entity.GetComponent<SpriteRendererComponent>();
ImGui::ColorEdit4("Color", glm::value_ptr(spriteRendererComponent.Color));
ImGui::TreePop();
}
}
ImGui::ColorEdit4("Color", glm::value_ptr(component.Color));
});
}
}

View File

@ -10,7 +10,7 @@ namespace Hazel
{
public:
HazelEditor()
: Application("Hazel Editor", 1280, 720)
: Application("Hazel Editor", 1600, 900)
{
PushLayer(new EditorLayer());
}