新增脚本参数系统:声明式参数 + Inspector 自动控件 + 场景序列化
DeclareParams/ParamBuilder 声明字段,运行时反射读写; Inspector 按类型生成控件,SceneSerializer 直通存取; 动画系统接入参数化裁剪;新增 OrbitMove 示例脚本
This commit is contained in:
@@ -22,6 +22,7 @@
|
||||
#include <Engine/Assets/AssetManager.h>
|
||||
#include <Engine/Core/Log.h>
|
||||
#include <Engine/Core/UUID.h>
|
||||
#include <Engine/Scripting/Script.h>
|
||||
|
||||
#include <yaml-cpp/yaml.h>
|
||||
|
||||
@@ -50,6 +51,11 @@ namespace Engine::Serialization {
|
||||
<< YAML::EndSeq;
|
||||
}
|
||||
|
||||
void EmitVec2(YAML::Emitter& e, const glm::vec2& v)
|
||||
{
|
||||
e << YAML::Flow << YAML::BeginSeq << v.x << v.y << YAML::EndSeq;
|
||||
}
|
||||
|
||||
void EmitVec4(YAML::Emitter& e, const glm::vec4& v)
|
||||
{
|
||||
e << YAML::Flow << YAML::BeginSeq << v.x << v.y << v.z << v.w
|
||||
@@ -71,6 +77,71 @@ namespace Engine::Serialization {
|
||||
n[2].as<f32>(def.z)};
|
||||
}
|
||||
|
||||
glm::vec2 ReadVec2(const YAML::Node& n, const glm::vec2& def)
|
||||
{
|
||||
if (!n || !n.IsSequence() || n.size() < 2) return def;
|
||||
return {n[0].as<f32>(def.x), n[1].as<f32>(def.y)};
|
||||
}
|
||||
|
||||
// ---- 脚本参数读端:按声明 schema 收值(调用方保证键已知)----
|
||||
// 数值臂互转(YAML 标量无类型:"3" 作 f32/i32 均自然,i32 声明
|
||||
// 遇浮点值截断);形不符回声明默认 + 警告(加载期一次,不刷屏)
|
||||
template<typename T>
|
||||
bool TryAs(const YAML::Node& n, T& out)
|
||||
{
|
||||
try {
|
||||
out = n.as<T>();
|
||||
return true;
|
||||
} catch (const std::exception&) {
|
||||
return false; // 类型不符 / 僵尸节点硬化异常统一兜住
|
||||
}
|
||||
}
|
||||
|
||||
Scripting::ParamValue ReadParamValue(
|
||||
const YAML::Node& n, const Scripting::ParamBuilder::Desc& d)
|
||||
{
|
||||
switch (d.type) {
|
||||
case Scripting::ParamType::F32: {
|
||||
f32 v{};
|
||||
if (TryAs<f32>(n, v)) return {v};
|
||||
i32 iv{};
|
||||
if (TryAs<i32>(n, iv)) return {static_cast<f32>(iv)};
|
||||
break;
|
||||
}
|
||||
case Scripting::ParamType::I32: {
|
||||
i32 v{};
|
||||
if (TryAs<i32>(n, v)) return {v};
|
||||
f32 fv{};
|
||||
if (TryAs<f32>(n, fv)) return {static_cast<i32>(fv)};
|
||||
break;
|
||||
}
|
||||
case Scripting::ParamType::Bool: {
|
||||
bool v{};
|
||||
if (TryAs<bool>(n, v)) return {v};
|
||||
break;
|
||||
}
|
||||
case Scripting::ParamType::String: {
|
||||
if (n.IsScalar()) return {n.Scalar()};
|
||||
break;
|
||||
}
|
||||
case Scripting::ParamType::Vec2: {
|
||||
if (n && n.IsSequence() && n.size() >= 2) {
|
||||
return {ReadVec2(n, glm::vec2{0.0f})};
|
||||
}
|
||||
break;
|
||||
}
|
||||
case Scripting::ParamType::Vec3: {
|
||||
if (n && n.IsSequence() && n.size() >= 3) {
|
||||
return {ReadVec3(n, glm::vec3{0.0f})};
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
ENGINE_WARN("Script param '{}' type mismatch, use default",
|
||||
d.name);
|
||||
return d.def;
|
||||
}
|
||||
|
||||
glm::vec4 ReadVec4(const YAML::Node& n, const glm::vec4& def)
|
||||
{
|
||||
if (!n || !n.IsSequence() || n.size() < 4) return def;
|
||||
@@ -452,6 +523,8 @@ namespace Engine::Serialization {
|
||||
<< cc->maxSlopeAngle;
|
||||
out << YAML::Key << "StepHeight" << YAML::Value
|
||||
<< cc->stepHeight;
|
||||
out << YAML::Key << "GravityScale" << YAML::Value
|
||||
<< cc->gravityScale;
|
||||
out << YAML::Key << "Active" << YAML::Value << cc->active;
|
||||
out << YAML::EndMap;
|
||||
}
|
||||
@@ -467,6 +540,39 @@ namespace Engine::Serialization {
|
||||
out << YAML::BeginMap;
|
||||
out << YAML::Key << "Name" << YAML::Value << item.name;
|
||||
out << YAML::Key << "Active" << YAML::Value << item.active;
|
||||
if (!item.params.empty()) {
|
||||
out << YAML::Key << "Params" << YAML::Value
|
||||
<< YAML::BeginMap;
|
||||
for (const auto& p : item.params) {
|
||||
switch (p.value.Type()) {
|
||||
case Engine::Scripting::ParamType::F32:
|
||||
out << YAML::Key << p.name << YAML::Value
|
||||
<< std::get<f32>(p.value.v);
|
||||
break;
|
||||
case Engine::Scripting::ParamType::I32:
|
||||
out << YAML::Key << p.name << YAML::Value
|
||||
<< std::get<i32>(p.value.v);
|
||||
break;
|
||||
case Engine::Scripting::ParamType::Bool:
|
||||
out << YAML::Key << p.name << YAML::Value
|
||||
<< std::get<bool>(p.value.v);
|
||||
break;
|
||||
case Engine::Scripting::ParamType::String:
|
||||
out << YAML::Key << p.name << YAML::Value
|
||||
<< std::get<std::string>(p.value.v);
|
||||
break;
|
||||
case Engine::Scripting::ParamType::Vec2:
|
||||
out << YAML::Key << p.name << YAML::Value;
|
||||
EmitVec2(out, std::get<glm::vec2>(p.value.v));
|
||||
break;
|
||||
case Engine::Scripting::ParamType::Vec3:
|
||||
out << YAML::Key << p.name << YAML::Value;
|
||||
EmitVec3(out, std::get<glm::vec3>(p.value.v));
|
||||
break;
|
||||
}
|
||||
}
|
||||
out << YAML::EndMap;
|
||||
}
|
||||
out << YAML::EndMap;
|
||||
}
|
||||
out << YAML::EndSeq;
|
||||
@@ -855,6 +961,7 @@ namespace Engine::Serialization {
|
||||
cc.mass = n["Mass"].as<f32>(cc.mass);
|
||||
cc.maxSlopeAngle = n["MaxSlopeAngle"].as<f32>(cc.maxSlopeAngle);
|
||||
cc.stepHeight = n["StepHeight"].as<f32>(cc.stepHeight);
|
||||
cc.gravityScale = n["GravityScale"].as<f32>(cc.gravityScale);
|
||||
cc.active = n["Active"].as<bool>(true);
|
||||
// 状态(速度/着地)由 PhysicsWorld 对账重建,不存
|
||||
}
|
||||
@@ -868,6 +975,27 @@ namespace Engine::Serialization {
|
||||
ECS::ScriptComponent::Item item;
|
||||
item.name = it["Name"].as<std::string>("");
|
||||
item.active = it["Active"].as<bool>(true);
|
||||
// 参数:按脚本 schema 对齐收值——未知键忽略(前向
|
||||
// 兼容),缺失键不存(运行时用声明默认)
|
||||
const YAML::Node ps = it["Params"];
|
||||
if (ps) {
|
||||
const auto& schema =
|
||||
Scripting::ScriptRegistry::Schema(item.name);
|
||||
for (const auto& kv : ps) {
|
||||
const std::string pname =
|
||||
kv.first.as<std::string>("");
|
||||
const auto dit = std::find_if(
|
||||
schema.begin(), schema.end(),
|
||||
[&](const Scripting::ParamBuilder::Desc& d) {
|
||||
return d.name == pname;
|
||||
});
|
||||
if (dit == schema.end()) {
|
||||
continue;
|
||||
}
|
||||
item.params.push_back(
|
||||
{pname, ReadParamValue(kv.second, *dit)});
|
||||
}
|
||||
}
|
||||
// 去重兜底:同名项只收第一份(UI 已杜绝重复,
|
||||
// 手编场景仍可能写出重复)
|
||||
const bool dup = std::any_of(
|
||||
|
||||
Reference in New Issue
Block a user