refactor(editor): 重构 Editor 使用 Engine 的 Component/Scene 系统

- Editor CMakeLists.txt 链接 XCEngine 库
- 删除 editor/src/Core/GameObject.h (简化版)
- SelectionManager 使用 Engine::Components::GameObject*
- SceneManager 使用 Engine::Scene
- HierarchyPanel 使用 Engine GameObject API
- InspectorPanel 使用 Engine TransformComponent

注意: Engine RHI Shader 接口有编译错误需要修复
This commit is contained in:
2026-03-24 18:38:01 +08:00
parent c66ba2feb3
commit 135fe9145b
12 changed files with 346 additions and 560 deletions

View File

@@ -9,7 +9,7 @@ namespace UI {
HierarchyPanel::HierarchyPanel() : Panel("Hierarchy") {
SceneManager::Get().CreateDemoScene();
m_selectionHandlerId = SelectionManager::Get().OnSelectionChanged.Subscribe([this](EntityID) {
m_selectionHandlerId = SelectionManager::Get().OnSelectionChanged.Subscribe([this](uint64_t) {
});
}
@@ -30,8 +30,8 @@ void HierarchyPanel::Render() {
ImGui::BeginChild("EntityList");
for (EntityID id : SceneManager::Get().GetRootEntities()) {
RenderEntity(id, filter);
for (auto* entity : SceneManager::Get().GetRootEntities()) {
RenderEntity(entity, filter);
}
if (ImGui::IsWindowHovered() && ImGui::IsMouseDown(0) && !ImGui::IsAnyItemHovered()) {
@@ -41,19 +41,16 @@ void HierarchyPanel::Render() {
}
if (ImGui::BeginPopupContextWindow("HierarchyContextMenu", ImGuiPopupFlags_MouseButtonRight)) {
RenderCreateMenu(INVALID_ENTITY_ID);
RenderCreateMenu(nullptr);
ImGui::EndPopup();
}
ImGui::InvisibleButton("##DragTarget", ImVec2(-1, -1));
if (ImGui::BeginDragDropTarget()) {
if (const ImGuiPayload* payload = ImGui::AcceptDragDropPayload("ENTITY_ID")) {
EntityID sourceId = *(const EntityID*)payload->Data;
if (sourceId != INVALID_ENTITY_ID) {
const GameObject* sourceEntity = SceneManager::Get().GetEntity(sourceId);
if (sourceEntity && sourceEntity->parent != INVALID_ENTITY_ID) {
SceneManager::Get().MoveEntity(sourceId, INVALID_ENTITY_ID);
}
if (const ImGuiPayload* payload = ImGui::AcceptDragDropPayload("ENTITY_PTR")) {
XCEngine::Components::GameObject* sourceEntity = *(XCEngine::Components::GameObject**)payload->Data;
if (sourceEntity && sourceEntity->GetParent() != nullptr) {
SceneManager::Get().MoveEntity(sourceEntity->GetID(), 0);
}
}
ImGui::EndDragDropTarget();
@@ -69,28 +66,26 @@ void HierarchyPanel::RenderSearchBar() {
ImGui::InputTextWithHint("##Search", "Search...", m_searchBuffer, sizeof(m_searchBuffer));
}
void HierarchyPanel::RenderEntity(EntityID id, const std::string& filter) {
auto& sceneManager = SceneManager::Get();
GameObject* entity = sceneManager.GetEntity(id);
void HierarchyPanel::RenderEntity(XCEngine::Components::GameObject* entity, const std::string& filter) {
if (!entity) return;
if (!filter.empty() && !PassesFilter(id, filter)) {
if (!filter.empty() && !PassesFilter(entity, filter)) {
return;
}
ImGui::PushID(static_cast<int>(id));
ImGui::PushID(static_cast<int>(entity->GetID()));
ImGuiTreeNodeFlags flags = ImGuiTreeNodeFlags_OpenOnArrow | ImGuiTreeNodeFlags_SpanAvailWidth;
if (entity->children.empty()) {
if (entity->GetChildCount() == 0) {
flags |= ImGuiTreeNodeFlags_Leaf;
}
if (SelectionManager::Get().IsSelected(id)) {
if (SelectionManager::Get().IsSelected(entity)) {
flags |= ImGuiTreeNodeFlags_Selected;
}
if (m_renaming && m_renamingEntity == id) {
if (m_renaming && m_renamingEntity == entity) {
if (m_renameJustStarted) {
ImGui::SetKeyboardFocusHere();
m_renameJustStarted = false;
@@ -99,43 +94,43 @@ void HierarchyPanel::RenderEntity(EntityID id, const std::string& filter) {
ImGui::SetNextItemWidth(-1);
if (ImGui::InputText("##Rename", m_renameBuffer, sizeof(m_renameBuffer), ImGuiInputTextFlags_EnterReturnsTrue | ImGuiInputTextFlags_AutoSelectAll)) {
if (strlen(m_renameBuffer) > 0) {
sceneManager.RenameEntity(id, m_renameBuffer);
SceneManager::Get().RenameEntity(entity->GetID(), m_renameBuffer);
}
m_renaming = false;
m_renamingEntity = INVALID_ENTITY_ID;
m_renamingEntity = nullptr;
}
if (!ImGui::IsItemActive() && ImGui::IsMouseClicked(0)) {
if (strlen(m_renameBuffer) > 0) {
sceneManager.RenameEntity(id, m_renameBuffer);
SceneManager::Get().RenameEntity(entity->GetID(), m_renameBuffer);
}
m_renaming = false;
m_renamingEntity = INVALID_ENTITY_ID;
m_renamingEntity = nullptr;
}
} else {
bool isOpen = ImGui::TreeNodeEx(entity->name.c_str(), flags);
bool isOpen = ImGui::TreeNodeEx(entity->GetName().c_str(), flags);
if (ImGui::IsItemClicked() && !ImGui::IsItemToggledOpen()) {
SelectionManager::Get().SetSelectedEntity(id);
SelectionManager::Get().SetSelectedEntity(entity);
}
if (ImGui::IsItemHovered() && ImGui::IsMouseDoubleClicked(0)) {
m_renaming = true;
m_renamingEntity = id;
strcpy_s(m_renameBuffer, entity->name.c_str());
m_renamingEntity = entity;
strcpy_s(m_renameBuffer, entity->GetName().c_str());
m_renameJustStarted = true;
}
HandleDragDrop(id);
HandleDragDrop(entity);
if (ImGui::BeginPopupContextItem("EntityContextMenu")) {
RenderContextMenu(id);
RenderContextMenu(entity);
ImGui::EndPopup();
}
if (isOpen) {
for (EntityID childId : entity->children) {
RenderEntity(childId, filter);
for (size_t i = 0; i < entity->GetChildCount(); i++) {
RenderEntity(entity->GetChild(i), filter);
}
ImGui::TreePop();
}
@@ -144,128 +139,115 @@ void HierarchyPanel::RenderEntity(EntityID id, const std::string& filter) {
ImGui::PopID();
}
void HierarchyPanel::RenderContextMenu(EntityID id) {
void HierarchyPanel::RenderContextMenu(XCEngine::Components::GameObject* entity) {
auto& sceneManager = SceneManager::Get();
auto& selectionManager = SelectionManager::Get();
if (ImGui::BeginMenu("Create")) {
RenderCreateMenu(id);
RenderCreateMenu(entity);
ImGui::EndMenu();
}
ImGui::Separator();
if (ImGui::MenuItem("Rename", "F2")) {
const GameObject* entity = sceneManager.GetEntity(id);
if (entity) {
m_renaming = true;
m_renamingEntity = id;
strcpy_s(m_renameBuffer, entity->name.c_str());
m_renamingEntity = entity;
strcpy_s(m_renameBuffer, entity->GetName().c_str());
m_renameJustStarted = true;
}
}
if (ImGui::MenuItem("Delete", "Delete")) {
sceneManager.DeleteEntity(id);
sceneManager.DeleteEntity(entity->GetID());
}
ImGui::Separator();
if (ImGui::MenuItem("Copy", "Ctrl+C")) {
sceneManager.CopyEntity(id);
sceneManager.CopyEntity(entity->GetID());
}
if (ImGui::MenuItem("Paste", "Ctrl+V", false, sceneManager.HasClipboardData())) {
sceneManager.PasteEntity(id);
sceneManager.PasteEntity(entity->GetID());
}
if (ImGui::MenuItem("Duplicate", "Ctrl+D")) {
EntityID newId = sceneManager.DuplicateEntity(id);
if (newId != INVALID_ENTITY_ID) {
selectionManager.SetSelectedEntity(newId);
uint64_t newId = sceneManager.DuplicateEntity(entity->GetID());
if (newId != 0) {
// Selection handled via callback
}
}
}
void HierarchyPanel::RenderCreateMenu(EntityID parent) {
void HierarchyPanel::RenderCreateMenu(XCEngine::Components::GameObject* parent) {
auto& sceneManager = SceneManager::Get();
auto& selectionManager = SelectionManager::Get();
if (ImGui::MenuItem("Empty Object")) {
EntityID newId = sceneManager.CreateEntity("GameObject", parent);
selectionManager.SetSelectedEntity(newId);
auto* newEntity = sceneManager.CreateEntity("GameObject", parent);
selectionManager.SetSelectedEntity(newEntity);
}
ImGui::Separator();
if (ImGui::MenuItem("Camera")) {
EntityID newId = sceneManager.CreateEntity("Camera", parent);
sceneManager.GetEntity(newId)->AddComponent<TransformComponent>();
selectionManager.SetSelectedEntity(newId);
auto* newEntity = sceneManager.CreateEntity("Camera", parent);
newEntity->AddComponent<XCEngine::Components::TransformComponent>();
selectionManager.SetSelectedEntity(newEntity);
}
if (ImGui::MenuItem("Light")) {
EntityID newId = sceneManager.CreateEntity("Light", parent);
selectionManager.SetSelectedEntity(newId);
auto* newEntity = sceneManager.CreateEntity("Light", parent);
selectionManager.SetSelectedEntity(newEntity);
}
ImGui::Separator();
if (ImGui::MenuItem("Cube")) {
EntityID newId = sceneManager.CreateEntity("Cube", parent);
sceneManager.GetEntity(newId)->AddComponent<TransformComponent>();
sceneManager.GetEntity(newId)->AddComponent<MeshRendererComponent>()->meshName = "Cube";
selectionManager.SetSelectedEntity(newId);
auto* newEntity = sceneManager.CreateEntity("Cube", parent);
newEntity->AddComponent<XCEngine::Components::TransformComponent>();
selectionManager.SetSelectedEntity(newEntity);
}
if (ImGui::MenuItem("Sphere")) {
EntityID newId = sceneManager.CreateEntity("Sphere", parent);
sceneManager.GetEntity(newId)->AddComponent<TransformComponent>();
sceneManager.GetEntity(newId)->AddComponent<MeshRendererComponent>()->meshName = "Sphere";
selectionManager.SetSelectedEntity(newId);
auto* newEntity = sceneManager.CreateEntity("Sphere", parent);
newEntity->AddComponent<XCEngine::Components::TransformComponent>();
selectionManager.SetSelectedEntity(newEntity);
}
if (ImGui::MenuItem("Plane")) {
EntityID newId = sceneManager.CreateEntity("Plane", parent);
sceneManager.GetEntity(newId)->AddComponent<TransformComponent>();
sceneManager.GetEntity(newId)->AddComponent<MeshRendererComponent>()->meshName = "Plane";
selectionManager.SetSelectedEntity(newId);
auto* newEntity = sceneManager.CreateEntity("Plane", parent);
newEntity->AddComponent<XCEngine::Components::TransformComponent>();
selectionManager.SetSelectedEntity(newEntity);
}
}
void HierarchyPanel::HandleDragDrop(EntityID id) {
auto& sceneManager = SceneManager::Get();
void HierarchyPanel::HandleDragDrop(XCEngine::Components::GameObject* entity) {
if (ImGui::BeginDragDropSource(ImGuiDragDropFlags_None)) {
m_dragSource = id;
ImGui::SetDragDropPayload("ENTITY_ID", &id, sizeof(EntityID));
const GameObject* entity = sceneManager.GetEntity(id);
if (entity) {
ImGui::Text("%s", entity->name.c_str());
}
m_dragSource = entity;
ImGui::SetDragDropPayload("ENTITY_PTR", &entity, sizeof(XCEngine::Components::GameObject*));
ImGui::Text("%s", entity->GetName().c_str());
ImGui::EndDragDropSource();
}
if (ImGui::BeginDragDropTarget()) {
if (const ImGuiPayload* payload = ImGui::AcceptDragDropPayload("ENTITY_ID")) {
EntityID sourceId = *(const EntityID*)payload->Data;
if (sourceId != id && sourceId != INVALID_ENTITY_ID) {
const GameObject* targetEntity = sceneManager.GetEntity(id);
const GameObject* sourceEntity = sceneManager.GetEntity(sourceId);
if (const ImGuiPayload* payload = ImGui::AcceptDragDropPayload("ENTITY_PTR")) {
XCEngine::Components::GameObject* sourceEntity = *(XCEngine::Components::GameObject**)payload->Data;
if (sourceEntity != entity && sourceEntity != nullptr) {
bool isValidMove = true;
EntityID checkParent = targetEntity ? targetEntity->parent : INVALID_ENTITY_ID;
while (checkParent != INVALID_ENTITY_ID) {
if (checkParent == sourceId) {
XCEngine::Components::GameObject* checkParent = entity;
while (checkParent != nullptr) {
if (checkParent == sourceEntity) {
isValidMove = false;
break;
}
const GameObject* parentEntity = sceneManager.GetEntity(checkParent);
checkParent = parentEntity ? parentEntity->parent : INVALID_ENTITY_ID;
checkParent = checkParent->GetParent();
}
if (isValidMove && sourceEntity && sourceEntity->parent != id) {
sceneManager.MoveEntity(sourceId, id);
if (isValidMove) {
sceneManager.MoveEntity(sourceEntity->GetID(), entity->GetID());
}
}
}
@@ -277,64 +259,56 @@ void HierarchyPanel::HandleKeyboardShortcuts() {
auto& sceneManager = SceneManager::Get();
auto& selectionManager = SelectionManager::Get();
EntityID selectedId = selectionManager.GetSelectedEntity();
XCEngine::Components::GameObject* selectedEntity = selectionManager.GetSelectedEntity();
if (ImGui::IsWindowFocused()) {
if (ImGui::IsKeyPressed(ImGuiKey_Delete)) {
if (selectedId != INVALID_ENTITY_ID) {
sceneManager.DeleteEntity(selectedId);
if (selectedEntity != nullptr) {
sceneManager.DeleteEntity(selectedEntity->GetID());
}
}
if (ImGui::IsKeyPressed(ImGuiKey_F2)) {
if (selectedId != INVALID_ENTITY_ID) {
const GameObject* entity = sceneManager.GetEntity(selectedId);
if (entity) {
m_renaming = true;
m_renamingEntity = selectedId;
strcpy_s(m_renameBuffer, entity->name.c_str());
m_renameJustStarted = true;
}
if (selectedEntity != nullptr) {
m_renaming = true;
m_renamingEntity = selectedEntity;
strcpy_s(m_renameBuffer, selectedEntity->GetName().c_str());
m_renameJustStarted = true;
}
}
ImGuiIO& io = ImGui::GetIO();
if (io.KeyCtrl) {
if (ImGui::IsKeyPressed(ImGuiKey_C)) {
if (selectedId != INVALID_ENTITY_ID) {
sceneManager.CopyEntity(selectedId);
if (selectedEntity != nullptr) {
sceneManager.CopyEntity(selectedEntity->GetID());
}
}
if (ImGui::IsKeyPressed(ImGuiKey_V)) {
if (sceneManager.HasClipboardData()) {
sceneManager.PasteEntity(selectedId);
sceneManager.PasteEntity(selectedEntity ? selectedEntity->GetID() : 0);
}
}
if (ImGui::IsKeyPressed(ImGuiKey_D)) {
if (selectedId != INVALID_ENTITY_ID) {
EntityID newId = sceneManager.DuplicateEntity(selectedId);
if (newId != INVALID_ENTITY_ID) {
selectionManager.SetSelectedEntity(newId);
}
if (selectedEntity != nullptr) {
sceneManager.DuplicateEntity(selectedEntity->GetID());
}
}
}
}
}
bool HierarchyPanel::PassesFilter(EntityID id, const std::string& filter) {
auto& sceneManager = SceneManager::Get();
const GameObject* entity = sceneManager.GetEntity(id);
bool HierarchyPanel::PassesFilter(XCEngine::Components::GameObject* entity, const std::string& filter) {
if (!entity) return false;
if (entity->name.find(filter) != std::string::npos) {
if (entity->GetName().find(filter) != std::string::npos) {
return true;
}
for (EntityID childId : entity->children) {
if (PassesFilter(childId, filter)) {
for (size_t i = 0; i < entity->GetChildCount(); i++) {
if (PassesFilter(entity->GetChild(i), filter)) {
return true;
}
}
@@ -342,4 +316,4 @@ bool HierarchyPanel::PassesFilter(EntityID id, const std::string& filter) {
return false;
}
}
}