feat(ecs): per-component enable/disable + inspector rework
Add `active` flag to the 8 behavior components (Unity enabled / UE SetActive semantics; structural Id/Name/Transform excluded). Consumers skip disabled components: renderer (mesh/light/anim), primary camera election, physics reconciliation (body/constraint/character destroyed and rebuilt on toggle), scripts keep instance but stop ticking. Inspector: component headers now carry an enable checkbox and a remove button, Add-Component menu greys out attached entries, and conflict hints added (RigidBody <-> CharacterController mutual exclusion, Joint requires RigidBody, Animation requires MeshRenderer, unregistered script name). Scenes persist Active per component (default true).
This commit is contained in:
@@ -356,6 +356,19 @@ namespace Engine {
|
||||
for (auto [e, rb, tr]: view.each()) {
|
||||
const u32 bits = static_cast<u32>(entt::to_integral(e));
|
||||
const auto found = m_Bodies.find(bits);
|
||||
|
||||
// 禁用 = 视为组件不存在:销毁 body(对账自然消化),重启用即重建
|
||||
if (!rb.active) {
|
||||
if (found != m_Bodies.end()) {
|
||||
bi.RemoveBody(found->second.id);
|
||||
bi.DestroyBody(found->second.id);
|
||||
m_Bodies.erase(found);
|
||||
invalidated.push_back(bits);
|
||||
}
|
||||
rb.bodyValid = false;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (found == m_Bodies.end() || !rb.bodyValid) {
|
||||
if (found != m_Bodies.end()) {
|
||||
bi.RemoveBody(found->second.id);
|
||||
@@ -437,12 +450,14 @@ namespace Engine {
|
||||
!= invalidated.end();
|
||||
};
|
||||
|
||||
// 1. owner 实体/组件没了,或任一端 body 本帧被销毁/重建 → 移除
|
||||
// 1. owner 实体/组件没了(或被禁用),或任一端 body 本帧被销毁/重建 → 移除
|
||||
for (auto it = m_Constraints.begin(); it != m_Constraints.end();) {
|
||||
const u32 owner = it->first;
|
||||
const auto ownerEnt = static_cast<entt::entity>(owner);
|
||||
const bool ownerGone = !reg.valid(ownerEnt) ||
|
||||
!reg.all_of<ECS::JointComponent>(ownerEnt);
|
||||
const ECS::JointComponent* jc =
|
||||
reg.valid(ownerEnt) ? reg.try_get<ECS::JointComponent>(ownerEnt)
|
||||
: nullptr;
|
||||
const bool ownerGone = jc == nullptr || !jc->active;
|
||||
if (ownerGone || tainted(owner) || tainted(it->second.targetBits)) {
|
||||
m_System->RemoveConstraint(it->second.constraint.GetPtr());
|
||||
it = m_Constraints.erase(it);
|
||||
@@ -451,10 +466,13 @@ namespace Engine {
|
||||
}
|
||||
}
|
||||
|
||||
// 2. 遍历组件:快照变更 → 重建;缺失 → 创建。
|
||||
// 2. 遍历组件:快照变更 → 重建;缺失 → 创建;禁用 → 跳过(上面已清)。
|
||||
// target 惰性按 UUID 反查(UUID 失联/body 未就绪 → 下帧再试)
|
||||
auto view = reg.view<ECS::JointComponent>();
|
||||
for (auto [e, jc]: view.each()) {
|
||||
if (!jc.active) {
|
||||
continue;
|
||||
}
|
||||
const u32 bits = static_cast<u32>(entt::to_integral(e));
|
||||
const auto found = m_Constraints.find(bits);
|
||||
if (found != m_Constraints.end()) {
|
||||
@@ -584,22 +602,26 @@ namespace Engine {
|
||||
void PhysicsWorld::SyncCharacters() {
|
||||
auto ® = m_Scene.GetRegistry();
|
||||
|
||||
// 1. 孤儿清理:实体销毁 / 组件被移除(Ref 析构即清理,无 body 负担)
|
||||
// 1. 孤儿清理:实体销毁 / 组件被移除或禁用(Ref 析构即清理,无 body 负担)
|
||||
for (auto it = m_Characters.begin(); it != m_Characters.end();) {
|
||||
const auto e = static_cast<entt::entity>(it->first);
|
||||
if (!reg.valid(e) ||
|
||||
!reg.all_of<ECS::CharacterControllerComponent,
|
||||
ECS::TransformComponent>(e)) {
|
||||
const auto* cc = reg.valid(e)
|
||||
? reg.try_get<ECS::CharacterControllerComponent>(e) : nullptr;
|
||||
if (cc == nullptr || !cc->active ||
|
||||
!reg.all_of<ECS::TransformComponent>(e)) {
|
||||
it = m_Characters.erase(it);
|
||||
} else {
|
||||
++it;
|
||||
}
|
||||
}
|
||||
|
||||
// 2. 遍历:缺失 → 创建;参数变更 → 重建;编辑器拖动 → 传送
|
||||
// 2. 遍历:缺失 → 创建;参数变更 → 重建;编辑器拖动 → 传送;禁用 → 跳过
|
||||
auto view = reg.view<ECS::CharacterControllerComponent,
|
||||
ECS::TransformComponent>();
|
||||
for (auto [e, cc, tr]: view.each()) {
|
||||
if (!cc.active) {
|
||||
continue;
|
||||
}
|
||||
const u32 bits = static_cast<u32>(entt::to_integral(e));
|
||||
CharacterController::Params p;
|
||||
p.radius = cc.radius;
|
||||
|
||||
Reference in New Issue
Block a user