feat(engine): C++ native script system (ScriptBehavior + registry)

ScriptComponent stores only a script name string (POD, serializable);
instances are reconciled by ScriptRuntime each frame (PhysicsWorld
pattern): orphan/rename cleanup with OnDestroy, per-name instantiation
from a file-level self-registering registry (ENGINE_REGISTER_SCRIPT),
OnCreate on bind, OnUpdate after physics step on scaled dt. Engine
framerate loop only sees the base interface, so future Lua/C# behaviors
plug in as foreign ScriptBehavior implementations with zero engine
changes. Ships a Spinner sample script and Inspector script picker.
This commit is contained in:
2026-10-03 18:02:49 +08:00
parent ea6beee593
commit 3403822a52
10 changed files with 348 additions and 2 deletions
@@ -343,6 +343,14 @@ namespace Engine::Serialization {
out << YAML::EndMap;
}
// 脚本只存类名(注册表键);未注册名字加载后运行时警告跳过
if (const auto* sc = reg.try_get<ECS::ScriptComponent>(e)) {
out << YAML::Key << "Script" << YAML::Value << YAML::BeginMap;
out << YAML::Key << "ScriptName" << YAML::Value
<< sc->scriptName;
out << YAML::EndMap;
}
out << YAML::EndMap; // Components
out << YAML::EndMap; // Entity
}
@@ -628,6 +636,12 @@ namespace Engine::Serialization {
cc.stepHeight = n["StepHeight"].as<f32>(cc.stepHeight);
// 状态(速度/着地)由 PhysicsWorld 对账重建,不存
}
// 脚本:只存类名;未注册名字由 ScriptRuntime 运行时警告跳过
if (const YAML::Node n = comps["Script"]) {
auto& sc = handle.AddComponent<ECS::ScriptComponent>();
sc.scriptName = n["ScriptName"].as<std::string>("");
}
}
// 加载在 OnImGuiRender 段,错过本帧 UpdateTransforms: