阶段性合并:动画重定向、碎裂共享库、第三人称玩法、编辑器面板与 vendored ufbx

- 动画:HumanoidRig 三层架构(standard 标准槽 / dialects 方言映射 / mapper 配对),
  指/趾统一走世界增量两遍式烘焙;AnimationAttach 拆为纯算法 + facade;修正 HumanoidBasis
  行走基朝向与跨骨架去重、根运动通道
- 物理:CharacterController 角色互碰撞 / shapeOffset / 接触事件;PhysicsWorld 接触合成与
  字符冲量判定
- 渲染与材质:Model 多材质按材质拆子 mesh 与贴图路径解析、DDS 直传、GLB 内嵌纹理落盘、
  不透明通道 alpha 固定 1.0、glTF FlipUVs 对齐;SceneSerializer 支持 meshIndex 与材质覆盖
- 脚本:FractureLib 抽为共享库(HostView 桥接)、WallFracture 参数直绑、新增
  ThirdPersonCamera / Follower / WASDMove 玩法;移除 GameDirector / PlayerController / Target
- 编辑器:新增 GamepadPanel、LogPanel(LogRing),改进 Inspector / 材质 / 统计 / 视口面板
- 依赖:vendored ufbx(FBX 5.x/6.x/7.x 全版本解析,替代 assimp 对 FBX 6.1 的拒读)
This commit is contained in:
2026-10-11 22:22:19 +08:00
parent 334819bf97
commit beba491bcb
57 changed files with 43200 additions and 1378 deletions
+203
View File
@@ -0,0 +1,203 @@
//
// Follower.cpp:跟随者行为(NPC 自动跟随主角,UE AIController 的简化位)。
//
// 挂在每个 NPC 实体上(各自 CharacterController + Animation)。主角由
// TargetName 指定(默认 "player",按 NameComponent 名称匹配——引擎无实体
// 引用参数类型,名称匹配是标准做法)。逻辑分四层,与 WASDMove 同一套手感:
// - 距离闸门(迟滞):拉开到 StopDistance+ResumeGap 才起步,靠近到
// StopDistance 即停——"到附近就停止跟随",边界不抖动
// - 速度分档(迟滞):距离超过 RunDistance 改跑,回落到 0.85× 才退回
// 走——"距离越大走/跑越快"
// - 动量整形:真实速度逐帧逼近期望速度(起步渐快、到点刹停),帧率无关
// - 动画按真实速度分档(idle/walk/run),迟滞防抖
// 移动驱动走 PhysicsWorld 的 CharacterController 门面(水平喂意图速度,
// Y 归物理重力);无 CC 时回退直接平移。多个跟随者用 Offset 错开站位。
//
#include <Engine/Scripting/Script.h>
#include <Engine/Core/Application.h>
#include <Engine/Physics/PhysicsWorld.h>
#include "ScriptLog.h"
#include <entt/entt.hpp>
#include <glm/glm.hpp>
#include <glm/gtc/quaternion.hpp>
#include <cmath>
using namespace Engine;
namespace {
class Follower final : public Engine::Scripting::ScriptBehavior {
public:
const char* Name() const override { return "Follower"; }
void DeclareParams(Engine::Scripting::ParamBuilder& pb) override {
pb.Field("TargetName", &m_TargetName, std::string("player"));
// 相对目标的站位偏移(世界系;多个跟随者错开,防叠一起)
pb.Field("Offset", &m_Offset, glm::vec3{0.0f});
pb.Field("WalkSpeed", &m_WalkSpeed, 3.0f); // 走路 m/s
pb.Field("RunSpeed", &m_RunSpeed, 6.0f); // 跑步 m/s
pb.Field("StopDistance", &m_StopDistance, 2.5f); // 到点停(米)
pb.Field("ResumeGap", &m_ResumeGap, 1.5f); // 再起步的距离余量
pb.Field("RunDistance", &m_RunDistance, 10.0f); // 超过即改跑(米)
pb.Field("TurnResponse", &m_TurnResponse, 10.0f);// 转向阻尼
pb.Field("AccelResponse", &m_AccelResponse, 6.0f); // 加速响应
pb.Field("StopResponse", &m_StopResponse, 12.0f); // 制动响应
// clip 按名引用(Unity anim.Play("Walk") 语义)
pb.Field("IdleClip", &m_IdleClip, std::string("idle"));
pb.Field("WalkClip", &m_WalkClip, std::string("walk"));
pb.Field("RunClip", &m_RunClip, std::string("run"));
}
void OnStart() override {
if (ResolveTarget()) {
SLOG_INFO("[Follower] 跟随目标 '{}' 就绪", m_TargetName);
} else {
SLOG_WARN("[Follower] 未找到目标 '{}'(按 NameComponent 匹配)",
m_TargetName);
}
}
void OnUpdate(float dt) override {
auto& reg = App().GetScene().GetRegistry();
// 目标失效(被销毁/未就绪)→ 重解析;仍无则静止待命
if (m_Target == entt::null || !reg.valid(m_Target)) {
m_Target = entt::null;
if (!ResolveTarget()) {
return;
}
}
const glm::vec3 tp =
reg.get<Engine::ECS::TransformComponent>(m_Target)
.GetPosition();
const glm::vec3 self = Transform().GetPosition();
glm::vec3 delta = (tp + m_Offset) - self;
delta.y = 0.0f; // 只取水平面(角色不俯仰)
const float dist = glm::length(delta);
// ---- 距离闸门(迟滞):到点停、拉开距离再起步 ----
if (!m_Moving) {
if (dist > m_StopDistance + m_ResumeGap) {
m_Moving = true;
}
} else if (dist <= m_StopDistance) {
m_Moving = false;
}
// ---- 速度分档(迟滞):远则跑、近则走 ----
glm::vec3 desired{0.0f};
if (m_Moving && dist > 1e-4f) {
if (!m_Sprinting && dist >= m_RunDistance) {
m_Sprinting = true;
} else if (m_Sprinting && dist <= m_RunDistance * 0.85f) {
m_Sprinting = false;
}
const float speed = m_Sprinting ? m_RunSpeed : m_WalkSpeed;
desired = (delta / dist) * speed;
}
// ---- 动量整形:真实速度逐帧逼近期望(起步渐快/到点刹停)----
const float response =
glm::length(desired) > 0.01f ? m_AccelResponse : m_StopResponse;
m_PlanarVel +=
(desired - m_PlanarVel) * (1.0f - std::exp(-response * dt));
if (glm::length(desired - m_PlanarVel) < 0.02f) {
m_PlanarVel = desired; // 贴齐,防低速蠕动
}
// ---- 驱动:角色控制器喂水平速度(Y 归物理);无则直接平移 ----
auto& phy = App().GetPhysics();
const bool hasCC =
TryGetComponent<Engine::ECS::CharacterControllerComponent>() !=
nullptr;
if (hasCC) {
const glm::vec3 cur = phy.GetCharacterVelocity(EntityBits());
phy.SetCharacterVelocity(
EntityBits(), {m_PlanarVel.x, cur.y, m_PlanarVel.z});
} else {
Transform().SetPosition(self + m_PlanarVel * dt);
}
// ---- 朝向:面向真实速度方向(模型默认 +Z),低速锁定 ----
if (glm::length(m_PlanarVel) > 0.3f) {
const float targetYaw =
std::atan2(m_PlanarVel.x, m_PlanarVel.z);
const glm::vec3 modelFwd =
Transform().GetRotationQuat() * glm::vec3(0.0f, 0.0f, 1.0f);
const float curYaw = std::atan2(modelFwd.x, modelFwd.z);
const float dyaw = std::atan2(std::sin(targetYaw - curYaw),
std::cos(targetYaw - curYaw));
const float k = 1.0f - std::exp(-m_TurnResponse * dt);
Transform().SetRotationQuat(
glm::angleAxis(curYaw + dyaw * k, glm::vec3(0, 1, 0)));
}
// ---- 动画:按真实速度分档(迟滞防临界抖)----
const float planarSpeed = glm::length(m_PlanarVel);
std::string target;
if (planarSpeed < 0.15f) {
target = m_IdleClip;
} else if (m_CurClip == m_RunClip) {
target = (planarSpeed >= m_RunSpeed * 0.7f) ? m_RunClip
: m_WalkClip;
} else {
target = (planarSpeed >= m_RunSpeed * 0.9f) ? m_RunClip
: m_WalkClip;
}
if (target != m_CurClip) {
if (auto* anim =
TryGetComponent<Engine::ECS::AnimationComponent>();
anim != nullptr && anim->Play(target, 0.35f)) {
m_CurClip = target; // 播放成功才记账(模型未就绪下帧重试)
}
}
}
private:
// 按 NameComponent 名称解析目标(跳过自身);失败返回 false
bool ResolveTarget() {
auto& reg = App().GetScene().GetRegistry();
auto view = reg.view<Engine::ECS::NameComponent>();
for (auto e : view) {
if (static_cast<u32>(entt::to_integral(e)) == EntityBits()) {
continue;
}
if (view.get<Engine::ECS::NameComponent>(e).name ==
m_TargetName) {
m_Target = e;
return true;
}
}
return false;
}
std::string m_TargetName = "player";
glm::vec3 m_Offset{0.0f};
float m_WalkSpeed = 3.0f;
float m_RunSpeed = 6.0f;
float m_StopDistance = 2.5f;
float m_ResumeGap = 1.5f;
float m_RunDistance = 10.0f;
float m_TurnResponse = 10.0f;
float m_AccelResponse = 6.0f;
float m_StopResponse = 12.0f;
std::string m_IdleClip = "idle";
std::string m_WalkClip = "walk";
std::string m_RunClip = "run";
entt::entity m_Target = entt::null; // 运行时状态(非参数)
bool m_Moving = false; // 距离闸门状态(迟滞)
bool m_Sprinting = false; // 速度档状态(迟滞)
glm::vec3 m_PlanarVel{0.0f}; // 平滑后的水平真实速度
std::string m_CurClip = "idle"; // 当前 clip(边沿检测基准)
};
ENGINE_REGISTER_SCRIPT(Follower)
} // namespace