WallFracture 碎裂手感修正:爆炸飞散 → 玻璃式碎裂(初速距离衰减 + 法线剥落外推 + 网格切细至 8×6)
This commit is contained in:
@@ -1,6 +1,12 @@
|
|||||||
//
|
//
|
||||||
// WallFracture.cpp:侧线A 验收脚本——可碎墙 + 碎片刚体群 + 寿命清理。
|
// WallFracture.cpp:侧线A 验收脚本——可碎墙 + 碎片刚体群 + 寿命清理。
|
||||||
//
|
//
|
||||||
|
// 碎裂语义 = 玻璃/陶瓷式("碎掉"),非爆炸飞散:
|
||||||
|
// 碎片沿裂纹网格分离,初速 = 径向 × 距离指数衰减(只有命中点
|
||||||
|
// 周边的碎片被推动,远处几乎原地悬置)+ 沿命中法线轻微外推
|
||||||
|
// (朝弹侧剥落)+ 微弱上抛——重力是塌落的主导,整体呈
|
||||||
|
// "裂纹分离 → 孔洞塌落 → 碎块堆在墙脚"的观感。
|
||||||
|
//
|
||||||
// WallFracture(挂墙实体:Static Box 刚体 + MeshRenderer 立方体):
|
// WallFracture(挂墙实体:Static Box 刚体 + MeshRenderer 立方体):
|
||||||
// OnCollisionEnter(Dynamic 体以足够速度命中)→ 触发一次:
|
// OnCollisionEnter(Dynamic 体以足够速度命中)→ 触发一次:
|
||||||
// 藏墙(MeshRenderer/RigidBody.active = false,PhysicsWorld 对账
|
// 藏墙(MeshRenderer/RigidBody.active = false,PhysicsWorld 对账
|
||||||
@@ -8,8 +14,8 @@
|
|||||||
// 实体(共享一份程序化立方体模型 + Dynamic Box 刚体),初速记入
|
// 实体(共享一份程序化立方体模型 + Dynamic Box 刚体),初速记入
|
||||||
// 待办表
|
// 待办表
|
||||||
// OnUpdate:碎片簿记——body 建好(下一帧 Step 对账,同 Bullet
|
// OnUpdate:碎片簿记——body 建好(下一帧 Step 对账,同 Bullet
|
||||||
// OnCreate 施速的时序契约)即统一施加径向初速(命中点→碎片方向 +
|
// OnCreate 施速的时序契约)即统一施加初速;寿命到期统一 Destroy;
|
||||||
// 向上偏置);寿命到期统一 Destroy;总量上限防泄漏
|
// 总量上限防泄漏
|
||||||
//
|
//
|
||||||
// 碎片不挂脚本:单一 WallFracture 集中管理全部碎片的施速/寿命/
|
// 碎片不挂脚本:单一 WallFracture 集中管理全部碎片的施速/寿命/
|
||||||
// 清理(一帧 spawn 几十个实体,每颗一个脚本实例纯属浪费——
|
// 清理(一帧 spawn 几十个实体,每颗一个脚本实例纯属浪费——
|
||||||
@@ -37,6 +43,7 @@
|
|||||||
#include <glm/glm.hpp>
|
#include <glm/glm.hpp>
|
||||||
#include <glm/gtc/quaternion.hpp>
|
#include <glm/gtc/quaternion.hpp>
|
||||||
|
|
||||||
|
#include <cmath>
|
||||||
#include <random>
|
#include <random>
|
||||||
|
|
||||||
using namespace Engine; // u32/f32/Entity 等基础类型 + 门面(同 TestShoot)
|
using namespace Engine; // u32/f32/Entity 等基础类型 + 门面(同 TestShoot)
|
||||||
@@ -62,21 +69,24 @@ namespace {
|
|||||||
|
|
||||||
void DeclareParams(Engine::Scripting::ParamBuilder& pb) override {
|
void DeclareParams(Engine::Scripting::ParamBuilder& pb) override {
|
||||||
// 碎片网格:每轴切几刀(总碎片 = x*y*z;占位档位 = 每碎片
|
// 碎片网格:每轴切几刀(总碎片 = x*y*z;占位档位 = 每碎片
|
||||||
// 一实体,网格 6*4*1=24 起步,中等规模才考虑 instanced draw)
|
// 一实体,网格 8*6*1=48 起步,中等规模才考虑 instanced draw)
|
||||||
pb.Field("GridX", &m_GridX, 6);
|
pb.Field("GridX", &m_GridX, 8);
|
||||||
pb.Field("GridY", &m_GridY, 4);
|
pb.Field("GridY", &m_GridY, 6);
|
||||||
pb.Field("GridZ", &m_GridZ, 1);
|
pb.Field("GridZ", &m_GridZ, 1);
|
||||||
pb.Field("BurstSpeed", &m_BurstSpeed, 4.5f); // 径向初速 m/s
|
// 玻璃/陶瓷式碎裂(裂纹分离 + 重力塌落),非爆炸飞散:
|
||||||
pb.Field("UpBias", &m_UpBias, 2.0f); // 向上偏置 m/s
|
// 初速 = 命中点径向 × 距离指数衰减(近动远不动)+ 法线
|
||||||
|
// 轻微外推(剥落)+ 微弱上抛;重力是塌落的主导
|
||||||
|
pb.Field("BurstSpeed", &m_BurstSpeed, 1.2f); // 近点径向初速 m/s
|
||||||
|
pb.Field("Falloff", &m_Falloff, 1.0f); // 距离衰减率 1/m
|
||||||
|
pb.Field("NormalPop", &m_NormalPop, 0.4f); // 法线外推 m/s
|
||||||
|
pb.Field("UpBias", &m_UpBias, 0.3f); // 上抛偏置 m/s
|
||||||
pb.Field("Lifetime", &m_Lifetime, 6.0f); // 碎片寿命(秒)
|
pb.Field("Lifetime", &m_Lifetime, 6.0f); // 碎片寿命(秒)
|
||||||
pb.Field("MaxFragments", &m_MaxFragments, 96); // 活碎片上限
|
pb.Field("MaxFragments", &m_MaxFragments, 128); // 活碎片上限
|
||||||
pb.Field("MinImpactSpeed", &m_MinImpactSpeed, 2.0f); // 触发阈值
|
pb.Field("MinImpactSpeed", &m_MinImpactSpeed, 2.0f); // 触发阈值
|
||||||
}
|
}
|
||||||
|
|
||||||
void OnCollisionEnter(u32 other, const glm::vec3& point,
|
void OnCollisionEnter(u32 other, const glm::vec3& point,
|
||||||
const glm::vec3& normal) override {
|
const glm::vec3& normal) override {
|
||||||
(void)normal; // 法线留给将来定向喷射
|
|
||||||
|
|
||||||
if (m_Fired) return;
|
if (m_Fired) return;
|
||||||
|
|
||||||
// 触发条件:对方是活的 Dynamic 刚体且速度够快(过滤
|
// 触发条件:对方是活的 Dynamic 刚体且速度够快(过滤
|
||||||
@@ -96,7 +106,7 @@ namespace {
|
|||||||
App().GetPhysics().GetLinearVelocity(other));
|
App().GetPhysics().GetLinearVelocity(other));
|
||||||
if (speed < m_MinImpactSpeed) return;
|
if (speed < m_MinImpactSpeed) return;
|
||||||
|
|
||||||
Fire(point);
|
Fire(point, normal);
|
||||||
}
|
}
|
||||||
|
|
||||||
void OnUpdate(f32 dt) override {
|
void OnUpdate(f32 dt) override {
|
||||||
@@ -106,8 +116,9 @@ namespace {
|
|||||||
|
|
||||||
private:
|
private:
|
||||||
// ---- 触发碎裂:藏墙 + 切网格 + spawn 碎片群 ----
|
// ---- 触发碎裂:藏墙 + 切网格 + spawn 碎片群 ----
|
||||||
void Fire(const glm::vec3& hitPoint) {
|
void Fire(const glm::vec3& hitPoint, const glm::vec3& hitNormal) {
|
||||||
m_Fired = true;
|
m_Fired = true;
|
||||||
|
m_HitNormal = hitNormal;
|
||||||
|
|
||||||
// 藏墙:渲染/拾取跳过 + body 对账销毁(组件级 active,
|
// 藏墙:渲染/拾取跳过 + body 对账销毁(组件级 active,
|
||||||
// 与 Unity Renderer.enabled + Rigidbody 摘除同语义)
|
// 与 Unity Renderer.enabled + Rigidbody 摘除同语义)
|
||||||
@@ -145,9 +156,9 @@ namespace {
|
|||||||
(j + 0.5f) * fragSize.y - wallSize.y * 0.5f,
|
(j + 0.5f) * fragSize.y - wallSize.y * 0.5f,
|
||||||
(k + 0.5f) * fragSize.z - wallSize.z * 0.5f};
|
(k + 0.5f) * fragSize.z - wallSize.z * 0.5f};
|
||||||
const glm::vec3 jitter{
|
const glm::vec3 jitter{
|
||||||
Rand(-0.2f, 0.2f) * fragSize.x,
|
Rand(-0.3f, 0.3f) * fragSize.x,
|
||||||
Rand(-0.2f, 0.2f) * fragSize.y,
|
Rand(-0.3f, 0.3f) * fragSize.y,
|
||||||
Rand(-0.2f, 0.2f) * fragSize.z};
|
Rand(-0.3f, 0.3f) * fragSize.z};
|
||||||
const glm::vec3 pos =
|
const glm::vec3 pos =
|
||||||
wallPos + wallRot * (cell + jitter);
|
wallPos + wallRot * (cell + jitter);
|
||||||
|
|
||||||
@@ -180,8 +191,10 @@ namespace {
|
|||||||
frb.friction = 0.7f;
|
frb.friction = 0.7f;
|
||||||
frb.restitution = 0.05f;
|
frb.restitution = 0.05f;
|
||||||
|
|
||||||
// 初速待办:body 下一帧建好,OnUpdate 统一施加
|
// 碎裂初速(玻璃式,非爆炸):离命中点越远越
|
||||||
//(同 Bullet OnCreate 施速的时序契约)
|
// 接近静止(指数衰减)——只有孔洞周边的碎片被
|
||||||
|
// 冲击波推动,整体呈"裂纹分离→重力塌落";
|
||||||
|
// 法线外推让碎片朝弹侧剥出,而非向墙内挤压
|
||||||
glm::vec3 dir = pos - hitPoint;
|
glm::vec3 dir = pos - hitPoint;
|
||||||
const f32 len = glm::length(dir);
|
const f32 len = glm::length(dir);
|
||||||
if (len < 1e-3f) {
|
if (len < 1e-3f) {
|
||||||
@@ -190,9 +203,12 @@ namespace {
|
|||||||
} else {
|
} else {
|
||||||
dir /= len;
|
dir /= len;
|
||||||
}
|
}
|
||||||
|
const f32 falloff = std::exp(-len * m_Falloff);
|
||||||
const glm::vec3 vel =
|
const glm::vec3 vel =
|
||||||
dir * m_BurstSpeed * Rand(0.6f, 1.3f) +
|
dir * m_BurstSpeed * falloff *
|
||||||
glm::vec3{0.0f, m_UpBias * Rand(0.5f, 1.0f),
|
Rand(0.4f, 1.1f) +
|
||||||
|
hitNormal * m_NormalPop * Rand(0.3f, 0.8f) +
|
||||||
|
glm::vec3{0.0f, m_UpBias * Rand(0.0f, 0.5f),
|
||||||
0.0f};
|
0.0f};
|
||||||
|
|
||||||
// 总量上限:超限立即回收最老碎片(防泄漏的
|
// 总量上限:超限立即回收最老碎片(防泄漏的
|
||||||
@@ -258,15 +274,18 @@ namespace {
|
|||||||
bool applied = false; // 初速已注入
|
bool applied = false; // 初速已注入
|
||||||
};
|
};
|
||||||
|
|
||||||
int m_GridX = 6;
|
int m_GridX = 8;
|
||||||
int m_GridY = 4;
|
int m_GridY = 6;
|
||||||
int m_GridZ = 1;
|
int m_GridZ = 1;
|
||||||
f32 m_BurstSpeed = 4.5f;
|
f32 m_BurstSpeed = 1.2f;
|
||||||
f32 m_UpBias = 2.0f;
|
f32 m_Falloff = 1.0f;
|
||||||
|
f32 m_NormalPop = 0.4f;
|
||||||
|
f32 m_UpBias = 0.3f;
|
||||||
f32 m_Lifetime = 6.0f;
|
f32 m_Lifetime = 6.0f;
|
||||||
int m_MaxFragments = 96;
|
int m_MaxFragments = 128;
|
||||||
f32 m_MinImpactSpeed = 2.0f;
|
f32 m_MinImpactSpeed = 2.0f;
|
||||||
|
|
||||||
|
glm::vec3 m_HitNormal{0.0f, 0.0f, 1.0f}; // 命中法线(剥落方向)
|
||||||
bool m_Fired = false; // 一次性触发保护
|
bool m_Fired = false; // 一次性触发保护
|
||||||
std::vector<Fragment> m_Fragments; // 活碎片簿记
|
std::vector<Fragment> m_Fragments; // 活碎片簿记
|
||||||
};
|
};
|
||||||
|
|||||||
Reference in New Issue
Block a user