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:
2026-10-03 18:45:03 +08:00
parent cad0481bc0
commit b6d0868e15
7 changed files with 280 additions and 65 deletions
+8
View File
@@ -123,12 +123,14 @@ namespace Engine::ECS {
// ECS 不感知资产系统内部;解析归 AssetManager,悬空由 generation 拦截。
struct MeshRendererComponent {
ModelRef model;
bool active = true; // 禁用 = 渲染/拾取跳过(Unity enabled / UE SetActive)
};
// 骨骼动画播放状态(纯数据 + 便捷控制)。
// 采样/局部层混合逻辑在 SceneRenderer::BuildFrame(渲染侧唯一消费者);
// ImGui 面板经 Play()/字段直接驱动。无此组件 = 按绑定姿势渲染。
struct AnimationComponent {
bool active = true; // 禁用 = 暂停采样(按当前帧姿态定格)
int clipIndex = 0; // 当前 clip(Model::GetClips 下标)
bool playing = true;
bool loop = true;
@@ -158,6 +160,7 @@ namespace Engine::ECS {
// 相机。一个场景中 primary=true 的实体是当前渲染视角。
struct CameraComponent {
bool active = true; // 禁用 = 退出 primary 候选
Camera camera;
bool primary = false;
};
@@ -166,6 +169,7 @@ namespace Engine::ECS {
// Directional——方向即照射方向(无位置,阴影光源用第 0 盏方向光)
// Point——位置取实体 Transform(固有的),向四周发光 + 距离衰减
struct LightComponent {
bool active = true; // 禁用 = 不进光照
enum class Type : u8 { Directional = 0, Point = 1 };
Type type = Type::Directional;
glm::vec3 direction{0.0f, -1.0f, 0.0f}; // 仅方向光
@@ -183,6 +187,7 @@ namespace Engine::ECS {
// 角色 = CharacterControllerComponent(CharacterVirtual),载具 = VehicleComponent
// (VehicleConstraint)——都走 PhysicsWorld 门面,不复用本组件。
struct RigidBodyComponent {
bool active = true; // 禁用 = body 销毁/不建(对账自然消化)
enum class BodyType : u8 { Static = 0, Dynamic = 1, Kinematic = 2 };
// Mesh:用实体 MeshRenderer 的视觉网格建碰撞(Unity MeshCollider
// 语义)——Static → Jolt MeshShape(精确三角网格,不可移动);
@@ -241,6 +246,7 @@ namespace Engine::ECS {
// target 存 UUID(跨存档稳定),惰性解析——SyncScene 每帧按 UUID 反查
// 实体:序列化无需两遍解析,UUID 再生后自然失联,加载顺序无关
struct JointComponent {
bool active = true; // 禁用 = 约束销毁/不建(对账自然消化)
enum class Type : u8 { Fixed = 0, Hinge = 1, Ball = 2 };
Type type = Type::Fixed;
UUID target{}; // 目标实体(0 = 未设置,不建约束)
@@ -264,6 +270,7 @@ namespace Engine::ECS {
// 只存参数(序列化:存参数不存状态),速度/着地等运行时状态在
// PhysicsWorld 的 CharacterController 里(对账重建)
struct CharacterControllerComponent {
bool active = true; // 禁用 = 角色移除(重启用按当前 Transform 重建)
f32 radius = 0.3f; // 胶囊半径
f32 capsuleHalfHeight = 0.4f; // 圆柱段半高(总高 = 2*(hh+r))
f32 mass = 70.0f;
@@ -278,6 +285,7 @@ namespace Engine::ECS {
// ENGINE_REGISTER_SCRIPT 自注册;将来 Lua/C# 脚本同键解析。
// 实例生命周期由 ScriptRuntime 对账管理(不进组件,保持可拷贝)
struct ScriptComponent {
bool active = true; // 禁用 = 实例保留但停 OnUpdate(Unity disable)
std::string scriptName;
bool operator==(const ScriptComponent&) const = default;