Fix editor selection system: SelectionManager ID types and Scene lookup

- SelectionManager now implements ISelectionManager interface with uint64_t IDs
- Remove SelectionManager/SceneManager circular dependency via EventBus
- Add Scene::FindByID() for proper ID-based entity lookup
- SceneManager::GetEntity() now uses FindByID instead of name-based Find
- Fix editor CMakeLists.txt XCEngine.lib path
- EventBus now thread-safe with shared_mutex
This commit is contained in:
2026-03-25 17:51:15 +08:00
parent 6612330347
commit b0d0576763
8 changed files with 77 additions and 39 deletions

View File

@@ -62,7 +62,7 @@ target_link_libraries(${PROJECT_NAME} PRIVATE
d3d12.lib d3d12.lib
dxgi.lib dxgi.lib
d3dcompiler.lib d3dcompiler.lib
${CMAKE_CURRENT_SOURCE_DIR}/../engine/build/Release/XCEngine.lib ${CMAKE_CURRENT_SOURCE_DIR}/../build/engine/Release/XCEngine.lib
) )
set_target_properties(${PROJECT_NAME} PROPERTIES set_target_properties(${PROJECT_NAME} PROPERTIES

View File

@@ -7,6 +7,7 @@
#include "ISceneManager.h" #include "ISceneManager.h"
#include "Managers/SceneManager.h" #include "Managers/SceneManager.h"
#include "Managers/ProjectManager.h" #include "Managers/ProjectManager.h"
#include "EditorEvents.h"
#include <string> #include <string>
#include <memory> #include <memory>
@@ -20,7 +21,16 @@ public:
, m_selectionManager(std::make_unique<SelectionManager>(*m_eventBus)) , m_selectionManager(std::make_unique<SelectionManager>(*m_eventBus))
, m_sceneManager(std::make_unique<SceneManager>()) , m_sceneManager(std::make_unique<SceneManager>())
, m_projectManager(std::make_unique<ProjectManager>()) { , m_projectManager(std::make_unique<ProjectManager>()) {
m_sceneManager->SetSelectionManager(m_selectionManager.get());
m_entityDeletedHandlerId = m_eventBus->Subscribe<EntityDeletedEvent>([this](const EntityDeletedEvent& event) {
if (m_selectionManager->GetSelectedEntity() == event.entityId) {
m_selectionManager->ClearSelection();
}
});
}
~EditorContext() {
m_eventBus->Unsubscribe<EntityDeletedEvent>(m_entityDeletedHandlerId);
} }
EventBus& GetEventBus() override { EventBus& GetEventBus() override {
@@ -53,6 +63,7 @@ private:
std::unique_ptr<SceneManager> m_sceneManager; std::unique_ptr<SceneManager> m_sceneManager;
std::unique_ptr<ProjectManager> m_projectManager; std::unique_ptr<ProjectManager> m_projectManager;
std::string m_projectPath; std::string m_projectPath;
uint64_t m_entityDeletedHandlerId;
}; };
} }

View File

@@ -6,11 +6,25 @@
#include <algorithm> #include <algorithm>
#include <cassert> #include <cassert>
#include <cstdint> #include <cstdint>
#include <typeinfo> #include <mutex>
#include <shared_mutex>
namespace XCEngine { namespace XCEngine {
namespace Editor { namespace Editor {
template<typename T>
struct EventTypeId {
static uint32_t Get() {
static const uint32_t id = s_nextId++;
return id;
}
private:
static uint32_t s_nextId;
};
template<typename T>
uint32_t EventTypeId<T>::s_nextId = 0;
class EventBus { class EventBus {
public: public:
using EventHandler = std::function<void()>; using EventHandler = std::function<void()>;
@@ -18,8 +32,12 @@ public:
template<typename T> template<typename T>
uint64_t Subscribe(std::function<void(const T&)> handler) { uint64_t Subscribe(std::function<void(const T&)> handler) {
static_assert(sizeof(T) > 0, "Event type must be defined"); static_assert(sizeof(T) > 0, "Event type must be defined");
size_t typeId = typeid(T).hash_code(); uint32_t typeId = EventTypeId<T>::Get();
uint64_t handlerId = m_nextHandlerId++; uint64_t handlerId = 0;
{
std::lock_guard<std::shared_mutex> lock(m_mutex);
handlerId = m_nextHandlerId++;
auto it = m_handlers.find(typeId); auto it = m_handlers.find(typeId);
if (it == m_handlers.end()) { if (it == m_handlers.end()) {
@@ -32,6 +50,7 @@ public:
handler(*static_cast<const T*>(data)); handler(*static_cast<const T*>(data));
}; };
m_handlers[typeId].push_back(entry); m_handlers[typeId].push_back(entry);
}
return handlerId; return handlerId;
} }
@@ -39,8 +58,9 @@ public:
template<typename T> template<typename T>
void Unsubscribe(uint64_t handlerId) { void Unsubscribe(uint64_t handlerId) {
static_assert(sizeof(T) > 0, "Event type must be defined"); static_assert(sizeof(T) > 0, "Event type must be defined");
size_t typeId = typeid(T).hash_code(); uint32_t typeId = EventTypeId<T>::Get();
std::lock_guard<std::shared_mutex> lock(m_mutex);
auto it = m_handlers.find(typeId); auto it = m_handlers.find(typeId);
if (it != m_handlers.end()) { if (it != m_handlers.end()) {
auto& handlers = it->second; auto& handlers = it->second;
@@ -55,8 +75,9 @@ public:
template<typename T> template<typename T>
void Publish(const T& event) { void Publish(const T& event) {
static_assert(sizeof(T) > 0, "Event type must be defined"); static_assert(sizeof(T) > 0, "Event type must be defined");
size_t typeId = typeid(T).hash_code(); uint32_t typeId = EventTypeId<T>::Get();
std::shared_lock<std::shared_mutex> lock(m_mutex);
auto it = m_handlers.find(typeId); auto it = m_handlers.find(typeId);
if (it != m_handlers.end()) { if (it != m_handlers.end()) {
for (const auto& entry : it->second) { for (const auto& entry : it->second) {
@@ -66,6 +87,7 @@ public:
} }
void Clear() { void Clear() {
std::lock_guard<std::shared_mutex> lock(m_mutex);
m_handlers.clear(); m_handlers.clear();
} }
@@ -75,8 +97,9 @@ private:
std::function<void(const void*)> handler; std::function<void(const void*)> handler;
}; };
std::unordered_map<size_t, std::vector<HandlerEntry>> m_handlers; std::unordered_map<uint32_t, std::vector<HandlerEntry>> m_handlers;
uint64_t m_nextHandlerId = 0; uint64_t m_nextHandlerId = 0;
std::shared_mutex m_mutex;
}; };
} }

View File

@@ -1,5 +1,4 @@
#include "SceneManager.h" #include "SceneManager.h"
#include "SelectionManager.h"
#include <algorithm> #include <algorithm>
namespace XCEngine { namespace XCEngine {
@@ -7,10 +6,6 @@ namespace Editor {
SceneManager::SceneManager() = default; SceneManager::SceneManager() = default;
void SceneManager::SetSelectionManager(ISelectionManager* selectionManager) {
m_selectionManager = selectionManager;
}
::XCEngine::Components::GameObject* SceneManager::CreateEntity(const std::string& name, ::XCEngine::Components::GameObject* parent) { ::XCEngine::Components::GameObject* SceneManager::CreateEntity(const std::string& name, ::XCEngine::Components::GameObject* parent) {
if (!m_scene) { if (!m_scene) {
m_scene = new ::XCEngine::Components::Scene("EditorScene"); m_scene = new ::XCEngine::Components::Scene("EditorScene");
@@ -41,12 +36,8 @@ void SceneManager::DeleteEntity(::XCEngine::Components::GameObject::ID id) {
m_rootEntities.erase(std::remove(m_rootEntities.begin(), m_rootEntities.end(), entity), m_rootEntities.end()); m_rootEntities.erase(std::remove(m_rootEntities.begin(), m_rootEntities.end(), entity), m_rootEntities.end());
} }
if (m_selectionManager && m_selectionManager->GetSelectedEntity() == entity->GetID()) {
m_selectionManager->ClearSelection();
}
m_scene->DestroyGameObject(entity); m_scene->DestroyGameObject(entity);
OnEntityDeleted.Invoke(id); OnEntityDeleted.Invoke(entity->GetID());
} }
SceneManager::ClipboardData SceneManager::CopyEntityRecursive(const ::XCEngine::Components::GameObject* entity) { SceneManager::ClipboardData SceneManager::CopyEntityRecursive(const ::XCEngine::Components::GameObject* entity) {

View File

@@ -13,14 +13,11 @@
#include <XCEngine/Components/GameObject.h> #include <XCEngine/Components/GameObject.h>
#include <XCEngine/Scene/Scene.h> #include <XCEngine/Scene/Scene.h>
#include "Core/ISelectionManager.h"
#include "Core/ISceneManager.h" #include "Core/ISceneManager.h"
namespace XCEngine { namespace XCEngine {
namespace Editor { namespace Editor {
class ISelectionManager;
class SceneManager : public ISceneManager { class SceneManager : public ISceneManager {
public: public:
SceneManager(); SceneManager();
@@ -28,11 +25,11 @@ public:
::XCEngine::Components::GameObject* CreateEntity(const std::string& name, ::XCEngine::Components::GameObject* parent = nullptr); ::XCEngine::Components::GameObject* CreateEntity(const std::string& name, ::XCEngine::Components::GameObject* parent = nullptr);
::XCEngine::Components::GameObject* GetEntity(::XCEngine::Components::GameObject::ID id) { ::XCEngine::Components::GameObject* GetEntity(::XCEngine::Components::GameObject::ID id) {
return m_scene ? m_scene->Find(std::to_string(id)) : nullptr; return m_scene ? m_scene->FindByID(id) : nullptr;
} }
const ::XCEngine::Components::GameObject* GetEntity(::XCEngine::Components::GameObject::ID id) const { const ::XCEngine::Components::GameObject* GetEntity(::XCEngine::Components::GameObject::ID id) const {
return m_scene ? m_scene->Find(std::to_string(id)) : nullptr; return m_scene ? m_scene->FindByID(id) : nullptr;
} }
const std::vector<::XCEngine::Components::GameObject*>& GetRootEntities() const { const std::vector<::XCEngine::Components::GameObject*>& GetRootEntities() const {
@@ -52,8 +49,6 @@ public:
bool HasClipboardData() const { return m_clipboard.has_value(); } bool HasClipboardData() const { return m_clipboard.has_value(); }
void SetSelectionManager(ISelectionManager* selectionManager);
::XCEngine::Core::Event<::XCEngine::Components::GameObject::ID> OnEntityCreated; ::XCEngine::Core::Event<::XCEngine::Components::GameObject::ID> OnEntityCreated;
::XCEngine::Core::Event<::XCEngine::Components::GameObject::ID> OnEntityDeleted; ::XCEngine::Core::Event<::XCEngine::Components::GameObject::ID> OnEntityDeleted;
::XCEngine::Core::Event<::XCEngine::Components::GameObject::ID> OnEntityChanged; ::XCEngine::Core::Event<::XCEngine::Components::GameObject::ID> OnEntityChanged;
@@ -74,7 +69,6 @@ private:
::XCEngine::Components::Scene* m_scene = nullptr; ::XCEngine::Components::Scene* m_scene = nullptr;
std::vector<::XCEngine::Components::GameObject*> m_rootEntities; std::vector<::XCEngine::Components::GameObject*> m_rootEntities;
std::optional<ClipboardData> m_clipboard; std::optional<ClipboardData> m_clipboard;
ISelectionManager* m_selectionManager = nullptr;
}; };
} }

View File

@@ -1,6 +1,8 @@
#pragma once #pragma once
#include <string> #include <string>
#include <memory>
#include <cassert>
namespace XCEngine { namespace XCEngine {
namespace Editor { namespace Editor {
@@ -23,8 +25,16 @@ public:
void SetOpen(bool open) { m_isOpen = open; } void SetOpen(bool open) { m_isOpen = open; }
void Toggle() { m_isOpen = !m_isOpen; } void Toggle() { m_isOpen = !m_isOpen; }
void SetContext(IEditorContext* context) { m_context = context; } void SetContext(IEditorContext* context) {
IEditorContext* GetContext() const { return m_context; } assert(!m_context && "Context already set!");
m_context = context;
}
IEditorContext* GetContext() const {
return m_context;
}
bool HasContext() const { return m_context != nullptr; }
protected: protected:
std::string m_name; std::string m_name;

View File

@@ -30,6 +30,7 @@ public:
void DestroyGameObject(GameObject* gameObject); void DestroyGameObject(GameObject* gameObject);
GameObject* Find(const std::string& name) const; GameObject* Find(const std::string& name) const;
GameObject* FindByID(GameObjectID id) const;
GameObject* FindGameObjectWithTag(const std::string& tag) const; GameObject* FindGameObjectWithTag(const std::string& tag) const;
template<typename T> template<typename T>

View File

@@ -79,6 +79,14 @@ GameObject* Scene::Find(const std::string& name) const {
return nullptr; return nullptr;
} }
GameObject* Scene::FindByID(GameObjectID id) const {
auto it = m_gameObjects.find(id);
if (it != m_gameObjects.end()) {
return it->second.get();
}
return nullptr;
}
GameObject* Scene::FindInChildren(GameObject* parent, const std::string& name) const { GameObject* Scene::FindInChildren(GameObject* parent, const std::string& name) const {
for (size_t i = 0; i < parent->GetChildCount(); ++i) { for (size_t i = 0; i < parent->GetChildCount(); ++i) {
GameObject* child = parent->GetChild(i); GameObject* child = parent->GetChild(i);