Files
HelloVulkan/Engine/include/Engine/Scripting/Script.h
T
atdunbg 3403822a52 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.
2026-10-03 18:02:49 +08:00

116 lines
4.2 KiB
C++
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
//
// Script.h:C++ 原生脚本设施(行为基类 + 按名注册表)。
//
// 设计参照 UE:脚本 = 挂在实体上的行为对象 + 统一基类生命周期
//(OnCreate/OnUpdate/OnDestroy,BeginPlay/Tick 语义)。引擎帧循环只认
// 基类接口——将来接 Lua/C# 时各实现一种 foreign behavior(内部持
// VM 状态),引擎零改动。
//
// 组件只存 scriptName 字符串(POD,序列化天然支持:Lua/C# 同样按名
// 解析);实例由 ScriptRuntime 对账式管理(照 PhysicsWorld 模式),
// 不进组件——组件可拷贝/可比较。
//
// 脚本作者注意:
// OnCreate/OnUpdate 阶段实体与组件已就位(假定所需组件存在);
// OnDestroy 只做自身清理——可能因实体销毁触发,句柄已失效。
//
#ifndef HELLOVULKAN_SCRIPT_H
#define HELLOVULKAN_SCRIPT_H
#include <Engine/Core/Types.h>
#include <Engine/ECS/Components.h>
#include <Engine/ECS/Scene.h>
#include <string>
#include <unordered_map>
#include <vector>
namespace Engine {
namespace Scripting {
class ScriptBehavior {
public:
virtual ~ScriptBehavior() = default;
// 注册表键(= 组件 scriptName),须与 ENGINE_REGISTER_SCRIPT 名一致
virtual const char* Name() const = 0;
// 生命周期(吃 scaled dt:编辑器暂停 timeScale=0 时自然停摆)
virtual void OnCreate() {}
virtual void OnUpdate(f32 dt) {}
virtual void OnDestroy() {}
protected:
// 便捷访问:绑定实体的 TransformComponent
ECS::TransformComponent& Transform() {
return m_Scene->GetRegistry().get<ECS::TransformComponent>(m_Entity);
}
template<typename T>
T& Get() {
return m_Scene->GetRegistry().get<T>(m_Entity);
}
bool Valid() const { return m_Scene != nullptr && m_Entity != entt::null; }
private:
friend class ScriptRuntime;
void Bind(ECS::Scene& scene, entt::entity e) {
m_Scene = &scene;
m_Entity = e;
}
ECS::Scene* m_Scene = nullptr;
entt::entity m_Entity = entt::null;
};
// ---- 按名注册表:脚本类文件级自注册,运行时按名实例化 ----
class ScriptRegistry {
public:
using Factory = Scope<ScriptBehavior> (*)();
static void Register(const std::string& name, Factory f) {
Map()[name] = f;
Names().push_back(name); // Inspector 下拉用(保持注册序)
}
static Scope<ScriptBehavior> Create(const std::string& name) {
auto& m = Map();
const auto it = m.find(name);
return it != m.end() ? it->second() : nullptr;
}
static bool Contains(const std::string& name) {
return Map().count(name) != 0;
}
static std::vector<std::string>& Names() {
static std::vector<std::string> v;
return v;
}
private:
static std::unordered_map<std::string, Factory>& Map() {
static std::unordered_map<std::string, Factory> m;
return m;
}
};
} // namespace Scripting
} // namespace Engine
// 文件级自注册:脚本 cpp 里写一行即可,Inspector 下拉与序列化按
// 类名(字符串)解析。T 须默认可构造并继承 ScriptBehavior
#define ENGINE_REGISTER_SCRIPT(T) \
static const bool s_##T##_registered = [] { \
::Engine::Scripting::ScriptRegistry::Register( \
#T, []() -> ::Engine::Scope< \
::Engine::Scripting::ScriptBehavior> { \
return ::Engine::CreateScope<T>(); \
}); \
return true; \
}();
#endif //HELLOVULKAN_SCRIPT_H