60 秒限时打靶场玩法原型:碎裂逻辑提取 FractureLib 共享库(HostView 桥接 protected 底座)+ 玻璃靶 Target(跨脚本参数计分)+ GameDirector(靶阵/倒计时/重开/图元 HUD)

This commit is contained in:
2026-10-09 13:00:18 +08:00
parent 6854d53f11
commit f15b104e77
4 changed files with 1103 additions and 497 deletions
+118
View File
@@ -0,0 +1,118 @@
//
// Target.cpp:打靶场玻璃靶子脚本(侧线"玩法原型验证")。
//
// 职责(挂靶子实体:MeshRenderer 玻璃立方 + RigidBody Static Box +
// 本脚本;通常由 GameDirector 运行时成批 spawn,也可编辑器手动摆):
// OnCollisionEnter → 玻璃外观套用(半透明 + 靶色自发光)→
// Fracture::Burst::TryFire(一次性保护 + Dynamic 速度校验 +
// 藏靶 + Voronoi 切分 + spawn 玻璃碎片群,共享库内完成)
// ReportScore:向 Director 实体的 GameDirector 脚本报分(Score +1,
// 写穿 Item.params,PIE Inspector 实时反映);Director=0 表示
// 独立靶不计分;导演 AcceptHits=false(回合已结束)不报
// OnUpdate/OnDestroy → Burst::Tick/Shutdown(碎片施速/寿命/回收)
//
// 覆盖的引擎设施:跨脚本参数访问(ScriptRuntime::GetParam/SetParam,
// GameDirector ↔ Target 的计分通道)、共享碎裂库(FractureLib.h,
// 与 WallFracture 同一条碎片路径)、玻璃材质(透明队列 + 自发光)。
//
// 演示步骤:任意实体挂 GameDirector → Play → 左键射击靶阵:
// 靶子玻璃式碎裂 + 计分,60 秒倒计时,时间到结算,R 重开。
//
#include <Engine/Scripting/Script.h>
#include <Engine/Scripting/ScriptRuntime.h> // GetScripts() 跨脚本参数访问
#include <Engine/Core/Types.h>
#include "FractureLib.h"
using namespace Engine; // i32/f32/u32 基础类型(同 WallFracture)
namespace {
// ---- 玻璃靶子 ----
class Target final : public Engine::Scripting::ScriptBehavior {
public:
const char* Name() const override { return "Target"; }
void DeclareParams(Engine::Scripting::ParamBuilder& pb) override {
// 击碎报分去向:Director 实体 bits(0 = 独立靶不计分);
// GameDirector spawn 时写进脚本项参数
pb.Field("Director", &m_Director, 0);
// 靶色(玻璃染色 + 自发光基色;碎片同色系)
pb.Field("Color", &m_Color,
glm::vec3{1.0f, 0.3f, 0.25f});
// 碎裂参数(同 WallFracture 语义;靶面小 → 种子更密)
pb.Field("Count", &m_Burst.p.count, 22);
pb.Field("SeedRadius", &m_Burst.p.seedRadius, 0.35f);
pb.Field("MinSeedDist", &m_Burst.p.minSeedDist, 0.07f);
pb.Field("BurstSpeed", &m_Burst.p.burstSpeed, 1.6f);
pb.Field("Falloff", &m_Burst.p.falloff, 2.0f);
pb.Field("NormalPop", &m_Burst.p.normalPop, 0.6f);
pb.Field("UpBias", &m_Burst.p.upBias, 0.4f);
pb.Field("Lifetime", &m_Burst.p.lifetime, 6.0f);
pb.Field("MaxFragments", &m_Burst.p.maxFragments, 96);
pb.Field("MinImpactSpeed", &m_Burst.p.minImpactSpeed, 2.0f);
}
void OnCollisionEnter(u32 other, const glm::vec3& point,
const glm::vec3& normal) override {
// 玻璃外观:半透明 + 靶色自发光(Fire 前套用,碎片沿用;
// 每次覆写代价可忽略,且吃 Inspector 实时改色)
m_Burst.look.baseColor = glm::vec4{m_Color, 0.45f};
m_Burst.look.transparent = true;
m_Burst.look.emissive = m_Color * 0.7f;
m_Burst.look.emissiveStrength = 2.0f;
if (m_Burst.TryFire(HV(), other, point, normal)) {
ReportScore();
}
}
void OnUpdate(f32 dt) override { m_Burst.Tick(HV(), dt); }
// 靶销毁(PIE stop / 重开 / 手动删除):碎片模型批量回收
void OnDestroy() override { m_Burst.Shutdown(HV()); }
private:
// 库桥接:本成员函数内可访问 protected 基类设施(App/Spawn/
// Destroy),库内只走公共 API——见 FractureLib.h HostView 注
Engine::Fracture::HostView HV() {
return {App(), EntityBits(),
[this](const std::string& n) { return Spawn(n); },
[this](Engine::ECS::Entity e) { Destroy(e); }};
}
// 击碎报分:读导演 AcceptHits 门(回合结束不报)→ Score +1。
// 参数层 = 跨脚本通信协议(与序列化同一条路,C# 反射缺失下
// 的按名消息);导演实例未建(罕见:同帧开局碰撞)时忽略
void ReportScore() {
if (m_Director == 0) return;
auto& scripts = App().GetScripts();
const u32 director = static_cast<u32>(m_Director);
// 参数层按名访问(ScriptRuntime 只认 ParamValue 变体,
// 取值自行 std::get_if 校型)
using Engine::Scripting::ParamValue;
ParamValue v{true};
if (scripts.GetParam(director, "GameDirector",
"AcceptHits", v)) {
if (const auto* b = std::get_if<bool>(&v.v); b && !*b) {
return; // 回合已结束(Director 收靶前的漏网命中)
}
}
i32 score = 0;
if (scripts.GetParam(director, "GameDirector", "Score", v)) {
if (const auto* s = std::get_if<i32>(&v.v)) {
score = *s;
}
}
scripts.SetParam(director, "GameDirector", "Score",
ParamValue{static_cast<i32>(score + 1)});
}
i32 m_Director = 0; // 导演实体 bits(0 = 独立靶)
glm::vec3 m_Color{1.0f, 0.3f, 0.25f};
Engine::Fracture::Burst m_Burst; // 碎片簿记(look 每次命中前套)
};
} // namespace
ENGINE_REGISTER_SCRIPT(Target)