新增脚本参数系统:声明式参数 + Inspector 自动控件 + 场景序列化
DeclareParams/ParamBuilder 声明字段,运行时反射读写; Inspector 按类型生成控件,SceneSerializer 直通存取; 动画系统接入参数化裁剪;新增 OrbitMove 示例脚本
This commit is contained in:
@@ -12,6 +12,7 @@
|
||||
#include <Engine/Core/UUID.h>
|
||||
#include <Engine/Core/AssetRef.h>
|
||||
#include <Engine/Renderer/Camera.h>
|
||||
#include <Engine/Scripting/ScriptParam.h>
|
||||
|
||||
#include <entt/entt.hpp>
|
||||
|
||||
@@ -233,6 +234,10 @@ namespace Engine::ECS {
|
||||
f32 fadeElapsed = 0.0f; // 已过渡时长
|
||||
f32 fadeDuration = 0.3f; // 过渡总时长(秒)
|
||||
|
||||
// 按名播放的运行时反指(瞬态,不落盘不序列化):AnimationSystem
|
||||
// 每帧回填(同 RigidBody::bodyValid 模式)。空 = 模型未就绪
|
||||
const Model* runtimeModel = nullptr;
|
||||
|
||||
// ---- 资产引用(序列化落盘)----
|
||||
// 外部动画资产(编辑器 attach 的动画容器文件,随场景持久化)。
|
||||
// clips 本体运行时并入 Model 资产(共享);重启后由 Inspector
|
||||
@@ -253,6 +258,16 @@ namespace Engine::ECS {
|
||||
std::unordered_map<std::string, bool> boolParams;
|
||||
std::vector<std::string> pendingTriggers; // 消费即清(瞬态)
|
||||
|
||||
// 阻尼参数推进状态(瞬态,不落盘——重启从 floatParams 当前值
|
||||
// 重新起步):current 逐帧指数逼近 target,写回 floatParams 供
|
||||
// 状态机条件统一读取
|
||||
struct DampedParam {
|
||||
float current = 0.0f;
|
||||
float target = 0.0f;
|
||||
float response = 8.0f; // 逼近速率(1/s,越大越快)
|
||||
};
|
||||
std::unordered_map<std::string, DampedParam> dampedParams;
|
||||
|
||||
// 状态机瞬态(不落盘,重启从 entry state 开始)
|
||||
std::string currentState; // 空 = 未初始化(System 首帧置 entry)
|
||||
f32 stateTime = 0.0f; // 当前状态播放头(秒)
|
||||
@@ -266,6 +281,22 @@ namespace Engine::ECS {
|
||||
{
|
||||
floatParams[name] = value;
|
||||
}
|
||||
// 阻尼变体(Unity SetFloat(name, target, dampTime) 同构):登记
|
||||
// 目标值,AnimationSystem Controller 驱动期逐帧指数逼近(帧率
|
||||
// 无关)。首次登记从 floatParams 当前值起步(无则取目标),
|
||||
// 避免跳变。与 SetFloat 同名混用 = 直接 set 会被下一帧阻尼值
|
||||
// 覆盖,别混
|
||||
void SetFloatDamped(const std::string& name, float target, float response)
|
||||
{
|
||||
auto [it, inserted] = dampedParams.try_emplace(name);
|
||||
it->second.target = target;
|
||||
it->second.response = glm::max(response, 0.01f);
|
||||
if (inserted) {
|
||||
const auto fp = floatParams.find(name);
|
||||
it->second.current =
|
||||
fp != floatParams.end() ? fp->second : target;
|
||||
}
|
||||
}
|
||||
void SetBool(const std::string& name, bool value)
|
||||
{
|
||||
boolParams[name] = value;
|
||||
@@ -309,6 +340,13 @@ namespace Engine::ECS {
|
||||
time = 0.0f;
|
||||
playing = true;
|
||||
}
|
||||
|
||||
// 按名播放(Unity Animator.Play("Walk") 同形,推荐):clip 名经
|
||||
// runtimeModel 的 clip 表解析,消掉下标硬耦合(attach 顺序变了
|
||||
// 脚本不改)。未命中/模型未就绪返回 false。实现住
|
||||
// AnimationSystem.cpp——需要 Model 完整类型,组件头保持零资产
|
||||
// 依赖(Model.h 拖 vulkan/assimp,进本头会污染全部包含者)
|
||||
bool Play(const std::string& clipName, f32 fadeSeconds = 0.3f);
|
||||
};
|
||||
|
||||
// 相机。一个场景中 primary=true 的实体是当前渲染视角。
|
||||
@@ -430,6 +468,9 @@ namespace Engine::ECS {
|
||||
f32 mass = 70.0f;
|
||||
f32 maxSlopeAngle = 45.0f; // 度:可站立的最大坡角
|
||||
f32 stepHeight = 0.4f; // 台阶自动攀爬高(WalkStairs)
|
||||
// 重力倍率(UE CharacterMovement.GravityScale 同位):真实 9.81
|
||||
// 对人形角色偏"棉花糖"(滞空 1.2s+),游戏手感普遍 2~3 倍
|
||||
f32 gravityScale = 2.0f;
|
||||
|
||||
bool operator==(const CharacterControllerComponent&) const = default;
|
||||
// 实体原点 = 脚底(胶囊底端),符合角色 pivot 习惯
|
||||
@@ -444,6 +485,11 @@ namespace Engine::ECS {
|
||||
struct Item {
|
||||
std::string name;
|
||||
bool active = true; // 禁用 = 实例保留但停 OnUpdate
|
||||
// 参数(权威数据,随场景序列化)。脚本成员 = 运行时真值,
|
||||
// 实例化期由 ScriptRuntime 按 schema 灌入:键缺失 → 声明
|
||||
// 默认,多余键忽略;运行时 SetParam 写穿回这里(PIE 实时
|
||||
// 反映,Stop 由快照天然回滚)
|
||||
std::vector<Engine::Scripting::ScriptParam> params;
|
||||
|
||||
bool operator==(const Item&) const = default;
|
||||
};
|
||||
|
||||
@@ -6,6 +6,13 @@
|
||||
// 基类接口——将来接 Lua/C# 时各实现一种 foreign behavior(内部持
|
||||
// VM 状态),引擎零改动。
|
||||
//
|
||||
// 瘦基类(对齐 Unity6/UE5 现役形态):基类只供生命周期 + 参数 +
|
||||
// 实体访问器,零领域 API——物理住 PhysicsWorld(App().GetPhysics()
|
||||
// 直调),动画住 AnimationComponent(GetComponent 直调,Animator
|
||||
// 同形)。DeclareParams = C# 反射缺失下 UPROPERTY 的等价物:Unity
|
||||
// 里你从不写声明是因为语言替你做了,C++ 里每个引擎都得补这一环
|
||||
//(UE 让你标 UPROPERTY,这里让你写一行 Declare)。
|
||||
//
|
||||
// 组件只存 scriptName 字符串(POD,序列化天然支持:Lua/C# 同样按名
|
||||
// 解析);实例由 ScriptRuntime 对账式管理(照 PhysicsWorld 模式),
|
||||
// 不进组件——组件可拷贝/可比较。
|
||||
@@ -21,10 +28,12 @@
|
||||
#include <Engine/Core/Types.h>
|
||||
#include <Engine/ECS/Components.h>
|
||||
#include <Engine/ECS/Scene.h>
|
||||
#include <Engine/Scripting/ScriptParam.h>
|
||||
|
||||
#include <string>
|
||||
#include <unordered_map>
|
||||
#include <vector>
|
||||
#include <algorithm>
|
||||
|
||||
namespace Engine {
|
||||
|
||||
@@ -39,11 +48,27 @@ namespace Engine {
|
||||
// 注册表键(= 组件 scriptName),须与 ENGINE_REGISTER_SCRIPT 名一致
|
||||
virtual const char* Name() const = 0;
|
||||
|
||||
// 生命周期(吃 scaled dt:编辑器暂停 timeScale=0 时自然停摆)
|
||||
// 生命周期(吃 scaled dt:编辑器暂停 timeScale=0 时自然停摆)。
|
||||
// 顺序对齐 Unity:OnCreate(= Awake,实例化即调)→ OnEnable
|
||||
// → OnStart(首次 OnUpdate 前一次)→ OnUpdate* → OnDisable →
|
||||
// OnDestroy(可能因实体销毁触发,句柄已失效只做自身清理)。
|
||||
// 跨实体 OnEnable 顺序不保证(实例表无序),与 Unity 一致
|
||||
virtual void OnCreate() {}
|
||||
virtual void OnEnable() {}
|
||||
virtual void OnStart() {}
|
||||
virtual void OnUpdate(f32 dt) {}
|
||||
virtual void OnDisable() {}
|
||||
virtual void OnDestroy() {}
|
||||
|
||||
// 参数声明(实例化灌值与注册表 schema 缓存共用入口;外语绑定
|
||||
// 同键接入)。契约:只写成员指针与常量,禁触场景/应用——
|
||||
// schema 缓存路径用未 Bind 的临时实例调用
|
||||
virtual void DeclareParams(ParamBuilder& pb) { (void)pb; }
|
||||
|
||||
// 参数灌值后回调(外语侧收值接缝:C++ 成员已直写,VM 绑定
|
||||
// 在此按名取值)
|
||||
virtual void OnParamsApplied() {}
|
||||
|
||||
protected:
|
||||
// 便捷访问:绑定实体的 TransformComponent
|
||||
ECS::TransformComponent& Transform() {
|
||||
@@ -60,7 +85,8 @@ namespace Engine {
|
||||
return m_Scene->GetRegistry().try_get<T>(m_Entity);
|
||||
}
|
||||
|
||||
// 绑定实体的 entityBits(物理门面按键值寻址)
|
||||
// 绑定实体的 entityBits(PhysicsWorld 按此键寻址:直调
|
||||
// App().GetPhysics() 的各 API 时作第一个参数)
|
||||
u32 EntityBits() const {
|
||||
return static_cast<u32>(entt::to_integral(m_Entity));
|
||||
}
|
||||
@@ -70,6 +96,66 @@ namespace Engine {
|
||||
|
||||
bool Valid() const { return m_Scene != nullptr && m_Entity != entt::null; }
|
||||
|
||||
// ---- 组件启停(Unity SetActive / Behaviour.enabled 语义)----
|
||||
// 自身组件总开关(整个脚本组件停 tick,实例保留)
|
||||
void SetActive(bool on) {
|
||||
if (auto* sc = m_Scene->GetRegistry().try_get<ECS::ScriptComponent>(m_Entity)) {
|
||||
sc->active = on;
|
||||
}
|
||||
}
|
||||
bool IsActive() const {
|
||||
const auto* sc =
|
||||
m_Scene->GetRegistry().try_get<ECS::ScriptComponent>(m_Entity);
|
||||
return sc != nullptr && sc->active;
|
||||
}
|
||||
|
||||
// ---- 自身参数(写成员 + 写穿 Item.params:PIE Inspector
|
||||
// 实时反映,Stop 由快照天然回滚;首次写新名一次分配)----
|
||||
// 返回成员直写是否命中(未声明参数 false,存档仍记录——前向
|
||||
// 兼容)。跨脚本/跨实体访问走 App().GetScripts().SetParam/GetParam
|
||||
bool SetParam(const std::string& name, const ParamValue& v) {
|
||||
const bool hit = WriteParam(name, v);
|
||||
if (auto* sc = Valid()
|
||||
? m_Scene->GetRegistry().try_get<ECS::ScriptComponent>(m_Entity)
|
||||
: nullptr) {
|
||||
const auto item = std::find_if(
|
||||
sc->items.begin(), sc->items.end(),
|
||||
[&](const ECS::ScriptComponent::Item& it) {
|
||||
return it.name == Name();
|
||||
});
|
||||
if (item != sc->items.end()) {
|
||||
const auto pit = std::find_if(
|
||||
item->params.begin(), item->params.end(),
|
||||
[&](const ScriptParam& p) { return p.name == name; });
|
||||
if (pit != item->params.end()) {
|
||||
pit->value = v;
|
||||
} else {
|
||||
item->params.push_back({name, v});
|
||||
}
|
||||
}
|
||||
}
|
||||
return hit;
|
||||
}
|
||||
template<typename T>
|
||||
bool SetParam(const std::string& name, const T& v) {
|
||||
return SetParam(name, ParamValue{v});
|
||||
}
|
||||
bool GetParam(const std::string& name, ParamValue& out) const {
|
||||
return ReadParam(name, out);
|
||||
}
|
||||
template<typename T>
|
||||
bool GetParam(const std::string& name, T& out) const {
|
||||
ParamValue v;
|
||||
if (!ReadParam(name, v)) {
|
||||
return false;
|
||||
}
|
||||
if (auto* p = std::get_if<T>(&v.v)) {
|
||||
out = *p;
|
||||
return true;
|
||||
}
|
||||
return false; // 类型不符
|
||||
}
|
||||
|
||||
private:
|
||||
friend class ScriptRuntime;
|
||||
void Bind(ECS::Scene& scene, entt::entity e, Application* app) {
|
||||
@@ -77,10 +163,19 @@ namespace Engine {
|
||||
m_Entity = e;
|
||||
m_App = app;
|
||||
}
|
||||
// 参数绑定表交接(实例化/RefreshParams 时由运行时填充)
|
||||
void SetParamBinds(std::vector<ParamBuilder::Bind> binds) {
|
||||
m_ParamBinds = std::move(binds);
|
||||
}
|
||||
// 按名写/读成员(经绑定表;未声明或类型不符返回 false)。
|
||||
// 写穿 Item.params 由运行时侧完成(需查组件项)
|
||||
bool WriteParam(const std::string& name, const ParamValue& v);
|
||||
bool ReadParam(const std::string& name, ParamValue& out) const;
|
||||
|
||||
ECS::Scene* m_Scene = nullptr;
|
||||
entt::entity m_Entity = entt::null;
|
||||
Application* m_App = nullptr;
|
||||
std::vector<ParamBuilder::Bind> m_ParamBinds;
|
||||
};
|
||||
|
||||
// ---- 按名注册表:脚本类文件级自注册,运行时按名实例化 ----
|
||||
@@ -108,6 +203,28 @@ namespace Engine {
|
||||
return v;
|
||||
}
|
||||
|
||||
// 参数 schema(惰性缓存):临时实例调 DeclareParams 取声明
|
||||
// ——每脚本类一次性(注册先于 main,加载期必就绪)。未注册
|
||||
// 脚本 = 空 schema(读端未知键全忽略,弱前向兼容)
|
||||
static const std::vector<ParamBuilder::Desc>&
|
||||
Schema(const std::string& name) {
|
||||
static const std::vector<ParamBuilder::Desc> kEmpty;
|
||||
static std::unordered_map<std::string,
|
||||
std::vector<ParamBuilder::Desc>>
|
||||
cache;
|
||||
const auto it = cache.find(name);
|
||||
if (it != cache.end()) {
|
||||
return it->second;
|
||||
}
|
||||
std::vector<ParamBuilder::Desc> descs;
|
||||
if (auto behavior = Create(name)) {
|
||||
ParamBuilder pb;
|
||||
behavior->DeclareParams(pb);
|
||||
descs = pb.Descs();
|
||||
}
|
||||
return cache.emplace(name, std::move(descs)).first->second;
|
||||
}
|
||||
|
||||
private:
|
||||
static std::unordered_map<std::string, Factory>& Map() {
|
||||
static std::unordered_map<std::string, Factory> m;
|
||||
|
||||
@@ -0,0 +1,215 @@
|
||||
//
|
||||
// ScriptParam.h:脚本参数——按名变体值 + 声明式 schema 构建器。
|
||||
//
|
||||
// 设计本质(UE/Unity 共识):参数数据按名存在序列化层(组件
|
||||
// ScriptComponent::Item.params,随场景持久化),C++ 脚本成员只是运行时
|
||||
// 真值缓存——这条边界正是将来 Lua/C# 外语绑定的接缝:
|
||||
// - C++ 脚本:DeclareParams 里 Field(name, &成员, 默认值) 双写声明
|
||||
// (灌值直写成员,零回收代码);
|
||||
// - 外语脚本:Declare(name, 类型, 默认值) 无指针声明,值经
|
||||
// OnParamsApplied 回收(VM 侧按名取)。
|
||||
//
|
||||
// schema 由注册表按脚本类名惰性缓存(ScriptRegistry::Schema)——描述
|
||||
// 里不含指针,缓存无悬垂;成员指针(Bind)只随实例存亡,不进缓存。
|
||||
//
|
||||
|
||||
#ifndef HELLOVULKAN_SCRIPT_PARAM_H
|
||||
#define HELLOVULKAN_SCRIPT_PARAM_H
|
||||
|
||||
#include <Engine/Core/Types.h>
|
||||
|
||||
#include <glm/glm.hpp>
|
||||
|
||||
#include <algorithm>
|
||||
#include <cassert>
|
||||
#include <string>
|
||||
#include <string_view>
|
||||
#include <variant>
|
||||
#include <vector>
|
||||
|
||||
namespace Engine {
|
||||
namespace Scripting {
|
||||
|
||||
// 参数类型(序列化分支 + Inspector 控件选择)。枚举序 = variant
|
||||
// 臂序(ParamValue::Type() 直转);新类型 = 枚举尾插 + 追加臂,
|
||||
// 旧档位前向兼容
|
||||
enum class ParamType : u8 { F32, I32, Bool, String, Vec2, Vec3 };
|
||||
|
||||
// 类型 → 枚举映射(Field 重载与消费端共用)
|
||||
template<typename T> struct ParamTypeOf;
|
||||
template<> struct ParamTypeOf<f32> { static constexpr ParamType value = ParamType::F32; };
|
||||
template<> struct ParamTypeOf<i32> { static constexpr ParamType value = ParamType::I32; };
|
||||
template<> struct ParamTypeOf<bool> { static constexpr ParamType value = ParamType::Bool; };
|
||||
template<> struct ParamTypeOf<std::string> { static constexpr ParamType value = ParamType::String; };
|
||||
template<> struct ParamTypeOf<glm::vec2> { static constexpr ParamType value = ParamType::Vec2; };
|
||||
template<> struct ParamTypeOf<glm::vec3> { static constexpr ParamType value = ParamType::Vec3; };
|
||||
|
||||
// 单参数的存储形态(类型标签由 variant 臂序自持)
|
||||
struct ParamValue {
|
||||
std::variant<f32, i32, bool, std::string, glm::vec2, glm::vec3>
|
||||
v{f32{0.0f}};
|
||||
|
||||
ParamType Type() const { return static_cast<ParamType>(v.index()); }
|
||||
|
||||
template<typename T>
|
||||
static constexpr ParamType TypeOf() { return ParamTypeOf<T>::value; }
|
||||
|
||||
bool operator==(const ParamValue&) const = default;
|
||||
};
|
||||
|
||||
// 单参数(组件 Item.params 的元素,随场景序列化)
|
||||
struct ScriptParam {
|
||||
std::string name;
|
||||
ParamValue value;
|
||||
|
||||
bool operator==(const ScriptParam&) const = default;
|
||||
};
|
||||
|
||||
// 参数声明构建器:DeclareParams(pb) 里逐项 Field/Declare,产出
|
||||
// 描述列表(schema,持久)+ 绑定表(成员指针,瞬态)。
|
||||
// Field 即把声明默认写进成员——未灌值前成员就是默认值
|
||||
class ParamBuilder {
|
||||
public:
|
||||
// ---- C++ 侧:声明 + 绑定成员(灌值直写)----
|
||||
void Field(std::string_view name, f32* dst, f32 def) {
|
||||
*dst = def;
|
||||
AddDesc(name, ParamType::F32, ParamValue{def});
|
||||
m_Binds.push_back({std::string(name), ParamType::F32, dst});
|
||||
}
|
||||
void Field(std::string_view name, i32* dst, i32 def) {
|
||||
*dst = def;
|
||||
AddDesc(name, ParamType::I32, ParamValue{def});
|
||||
m_Binds.push_back({std::string(name), ParamType::I32, dst});
|
||||
}
|
||||
void Field(std::string_view name, bool* dst, bool def) {
|
||||
*dst = def;
|
||||
AddDesc(name, ParamType::Bool, ParamValue{def});
|
||||
m_Binds.push_back({std::string(name), ParamType::Bool, dst});
|
||||
}
|
||||
void Field(std::string_view name, std::string* dst,
|
||||
const std::string& def) {
|
||||
*dst = def;
|
||||
AddDesc(name, ParamType::String, ParamValue{def});
|
||||
m_Binds.push_back({std::string(name), ParamType::String, dst});
|
||||
}
|
||||
void Field(std::string_view name, glm::vec2* dst,
|
||||
const glm::vec2& def) {
|
||||
*dst = def;
|
||||
AddDesc(name, ParamType::Vec2, ParamValue{def});
|
||||
m_Binds.push_back({std::string(name), ParamType::Vec2, dst});
|
||||
}
|
||||
void Field(std::string_view name, glm::vec3* dst,
|
||||
const glm::vec3& def) {
|
||||
*dst = def;
|
||||
AddDesc(name, ParamType::Vec3, ParamValue{def});
|
||||
m_Binds.push_back({std::string(name), ParamType::Vec3, dst});
|
||||
}
|
||||
|
||||
// ---- 外语侧:仅声明不绑定(值经 OnParamsApplied 回收)----
|
||||
void Declare(std::string_view name, ParamType t, ParamValue def) {
|
||||
AddDesc(name, t, std::move(def));
|
||||
}
|
||||
|
||||
// 声明描述(持久——进注册表 schema 缓存,无指针无悬垂)
|
||||
struct Desc {
|
||||
std::string name;
|
||||
ParamType type;
|
||||
ParamValue def;
|
||||
};
|
||||
// 成员绑定(瞬态——指针随声明实例存亡,只进 ScriptBehavior)
|
||||
struct Bind {
|
||||
std::string name;
|
||||
ParamType type;
|
||||
void* ptr;
|
||||
};
|
||||
|
||||
const std::vector<Desc>& Descs() const { return m_Descs; }
|
||||
std::vector<Bind> TakeBinds() { return std::move(m_Binds); }
|
||||
|
||||
// 单绑定写值:类型不符返回 false(SetParam/跨脚本写共用)
|
||||
static bool WriteBind(const Bind& b, const ParamValue& v) {
|
||||
if (b.type != v.Type()) {
|
||||
return false;
|
||||
}
|
||||
switch (b.type) {
|
||||
case ParamType::F32:
|
||||
*static_cast<f32*>(b.ptr) = std::get<f32>(v.v);
|
||||
return true;
|
||||
case ParamType::I32:
|
||||
*static_cast<i32*>(b.ptr) = std::get<i32>(v.v);
|
||||
return true;
|
||||
case ParamType::Bool:
|
||||
*static_cast<bool*>(b.ptr) = std::get<bool>(v.v);
|
||||
return true;
|
||||
case ParamType::String:
|
||||
*static_cast<std::string*>(b.ptr) =
|
||||
std::get<std::string>(v.v);
|
||||
return true;
|
||||
case ParamType::Vec2:
|
||||
*static_cast<glm::vec2*>(b.ptr) = std::get<glm::vec2>(v.v);
|
||||
return true;
|
||||
case ParamType::Vec3:
|
||||
*static_cast<glm::vec3*>(b.ptr) = std::get<glm::vec3>(v.v);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
// 单绑定读值(按绑定类型出值;GetParam/跨脚本读共用)
|
||||
static bool ReadBind(const Bind& b, ParamValue& out) {
|
||||
switch (b.type) {
|
||||
case ParamType::F32:
|
||||
out = ParamValue{*static_cast<f32*>(b.ptr)};
|
||||
return true;
|
||||
case ParamType::I32:
|
||||
out = ParamValue{*static_cast<i32*>(b.ptr)};
|
||||
return true;
|
||||
case ParamType::Bool:
|
||||
out = ParamValue{*static_cast<bool*>(b.ptr)};
|
||||
return true;
|
||||
case ParamType::String:
|
||||
out = ParamValue{*static_cast<std::string*>(b.ptr)};
|
||||
return true;
|
||||
case ParamType::Vec2:
|
||||
out = ParamValue{*static_cast<glm::vec2*>(b.ptr)};
|
||||
return true;
|
||||
case ParamType::Vec3:
|
||||
out = ParamValue{*static_cast<glm::vec3*>(b.ptr)};
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
// 按名灌值进绑定成员:命中→直写;未绑定(外语声明)→跳过;
|
||||
// 类型不符/多余键→忽略(读端已校正,这里兜底)。
|
||||
// 缺失键不写——Field 已把声明默认写进成员
|
||||
void Apply(const std::vector<ScriptParam>& params) const {
|
||||
for (const auto& p : params) {
|
||||
const auto b = std::find_if(
|
||||
m_Binds.begin(), m_Binds.end(),
|
||||
[&](const Bind& it) { return it.name == p.name; });
|
||||
if (b != m_Binds.end()) {
|
||||
WriteBind(*b, p.value);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
void AddDesc(std::string_view name, ParamType t, ParamValue def) {
|
||||
assert(!name.empty() && "参数名不能为空");
|
||||
assert(std::none_of(m_Descs.begin(), m_Descs.end(),
|
||||
[&](const Desc& d) {
|
||||
return d.name == name;
|
||||
}) &&
|
||||
"参数重名");
|
||||
m_Descs.push_back({std::string(name), t, std::move(def)});
|
||||
}
|
||||
|
||||
std::vector<Desc> m_Descs;
|
||||
std::vector<Bind> m_Binds;
|
||||
};
|
||||
|
||||
} // namespace Scripting
|
||||
} // namespace Engine
|
||||
|
||||
#endif //HELLOVULKAN_SCRIPT_PARAM_H
|
||||
@@ -1,7 +1,11 @@
|
||||
//
|
||||
// ScriptRuntime.h:脚本生命周期驱动。对账式管理(组件项加/删/改名 ↔
|
||||
// 实例建/毁),照 PhysicsWorld.SyncScene 模式——每帧对账,编辑器操作
|
||||
// 即时生效,场景加载/实体销毁自动消化。
|
||||
// 实例建/毁),照 PhysicsWorld.SyncScene 模式——Running 中每帧对账,
|
||||
// PIE 中组件增删/改名即时生效,场景加载/实体销毁自动消化。
|
||||
//
|
||||
// Play Mode 语义(Unity/UE PIE 对齐):编辑态不实例化任何脚本——
|
||||
// 实例生命周期 = 一次 PIE 局,Play 后首次 Update 对账重建,Stop 时
|
||||
// EndPlay 统一终局,成员状态不跨局泄漏。
|
||||
//
|
||||
// 多脚本:一个实体可挂任意多个脚本项(ScriptComponent::items),实例
|
||||
// 按名字与 items 对账——重命名/删除走 OnDestroy 清理,未动的实例不重建。
|
||||
@@ -23,10 +27,13 @@ namespace Engine {
|
||||
|
||||
namespace Scripting {
|
||||
|
||||
// 实例簿记:行为对象 + OnCreate 是否已调
|
||||
// 实例簿记:行为对象 + 生命周期位(created = OnCreate 已调;
|
||||
// enabled = 当前启停态,翻转检测基准;started = OnStart 已发)
|
||||
struct ScriptInstance {
|
||||
Scope<ScriptBehavior> behavior;
|
||||
bool created = false;
|
||||
bool enabled = false;
|
||||
bool started = false;
|
||||
};
|
||||
|
||||
class ScriptRuntime {
|
||||
@@ -34,9 +41,29 @@ namespace Engine {
|
||||
explicit ScriptRuntime(ECS::Scene& scene, Application* app = nullptr)
|
||||
: m_Scene(scene), m_App(app) {}
|
||||
|
||||
// 每帧调用(吃 scaled dt):对账 → OnCreate(新实例)→ OnUpdate
|
||||
// 每帧调用(吃 scaled dt):对账 → OnCreate(新实例)→
|
||||
// 启停翻转(OnEnable/OnDisable)→ OnStart(首次)→ OnUpdate。
|
||||
// 仅 Running 态被调(Application 门控)
|
||||
void Update(f32 dt);
|
||||
|
||||
// 终局(GameFlow →Stopped 广播):全部实例 OnDisable →
|
||||
// OnDestroy 并清空簿记。Unity 退出 Play 语义——成员状态
|
||||
// (动量/边沿/当前 clip)不跨局泄漏,下次 Play 对账重建
|
||||
void EndPlay();
|
||||
|
||||
// 参数热刷新(编辑期一次性):实体全部实例重新 DeclareParams
|
||||
// + 按组件存档灌值(Inspector 编辑参数后调)
|
||||
void RefreshParams(u32 entityBits);
|
||||
|
||||
// 跨脚本按名参数访问(本实体兄弟项或跨实体):SetParam 写
|
||||
// 目标实例成员 + 写穿 Item.params(PIE Inspector 实时反映);
|
||||
// GetParam 读目标实例成员。实体无组件/项不存在/实例未建
|
||||
// 返回 false
|
||||
bool SetParam(u32 entityBits, const std::string& scriptName,
|
||||
const std::string& param, const ParamValue& value);
|
||||
bool GetParam(u32 entityBits, const std::string& scriptName,
|
||||
const std::string& param, ParamValue& out) const;
|
||||
|
||||
private:
|
||||
void Sync();
|
||||
|
||||
|
||||
Reference in New Issue
Block a user