Unity 角色换装系统
1. 模块概述
角色换装系统是项目中负责角色外观动态构建与实时切换的模块。
通过骨骼共享 + 蒙皮重绑(SkinnedMeshRenderer 换骨)的方式合并到角色根骨架上,外观即时呈现,不存在多体模型同步动画的开销。
模块同时集成 MagicaCloth2 物理布料和自定义 ColliderComponent,实现”装上披风→披风跟着角色其它部位的胶囊体做物理碰撞”、”卸下盔甲→盔甲自己带的碰撞胶囊体也从其它布料的碰撞列表中干净移除”这种布料/碰撞拓扑的双向同步。最后通过 RendererMaterialManager 通知外观渲染层重建 Renderer 索引,保证材质/特效系统总是看到当前最新的 SkinnedMeshRenderer 集合。
2. 设计思路与架构决策2.1 为什么用 SkinnedMeshRenderer 换骨而不是子节点挂载?
问题:玩家装备过多的情况下,如果每件装备都是一个独立 GameObject 自带 Animator/骨架,多体动画同步会出现帧延迟、布料抖动、性能成倍开销。
方案:所有装备 Prefab 内只保留 SkinnedMeshRender ...
UE5 八向移动 简介(参考 Lyra)
1. 模块概述
本模块是一套基于 UE5 CMC 的,覆盖了从输入采集 → 角色 Yaw 跟随策略切换 → 动画状态机 4 主方向 + 8 方向贴合 → 起步/停步/急转/撞墙感知 → Distance Matching / Stride Warping → 多端动画一致性同步的完整链路。
模块在功能上回答了三类问题:
怎么把”玩家面向(相机)”和”实际移动方向(输入)”解耦 —— 为索敌/瞄准等场景提供 360° 横向跨步(Strafe)能力,同时保留非索敌时”跑哪儿朝哪儿”的轻盈感
怎么把连续的运动数据(速度向量、加速度向量、空中状态)转换成动画图能用的离散信号 —— 4 主方向枚举 + 不对称迟滞 + 双门闩 Pivot + 撞墙判定
怎么让动画切换在三端(Server / Autonomous / SimulatedProxy)一致 —— 关键洞察:LinkAnimClassLayers 是纯本地的 AnimInstance 节点树重建,不可跨网复制,必须把权威态挂在 Character 上 + 在每端 OnRep 本地重演
2. 技术架构2.1 网络同步方案
状态 ...
UE5 HUD 简介(参考 Lyra)
1. 模块概述
「全局 UI 容器」——基建层的 UPrimaryGameLayout / UUIManagerSubSystem / UHUDLayout / AHUD 提供整套基于 CommonUI Layer/Stack 范式的 UI 注入框架,支撑全项目所有 UI 的注册、激活、HitTest 联动、GameFeature 接入、手柄热插拔、键鼠/手柄输入模式自适配等横切关注点。这层基建是项目里所有 UI(背包、技能、菜单、加载、登录等)能够工作的前提。
模块的关键设计:
Layer 自动收集 + HitTest 联动:UPrimaryGameLayout 通过 RequestGameplayTagChildren 自动收集所有 UI.Layer.* 子 Tag 注册的 Layer,按面板 ChildIndex 隐式排优先级,监听每个 Layer 的 OnDisplayedWidgetChanged,高优先级有内容时自动把低优先级 Layer 设为 HitTestInvisible 防止焦点穿透。
手柄热插拔 + PS/Xbox 自动识别:UUIManagerSubSystem ...
碎玉零珠———— Unreal Engine 5
一个完整 Game Thread 帧的执行流123456789101112131415FEngineLoop::Tick() ├─ UpdateTimeAndHandleMaxTickRate() // 计算 DeltaTime / 帧率限制 ├─ ENQUEUE_RENDER_COMMAND(BeginFrame) // 把"开始渲染帧"投递到 RT 队列 ├─ GEngine->Tick(DeltaSeconds, ...) // 进入 UWorld::Tick │ └─ UWorld::Tick() │ ├─ SetupPhysicsTickFunctions() // 配置 StartPhysics/EndPhysics 这两个特殊 Tick │ ├─ RunTickGroup(TG_PrePhysics) // ⭐ 角色逻辑、骨骼动画 TickPose 都在这里 │ ├─ RunTickGroup(TG_StartPhysics) // 给 Chaos Solver ...
UE5 背包系统 简介(参考 Lyra)
1. 模块概述
Inventory 模块是项目的物品/背包子系统,负责”任意 Actor(玩家、NPC、可拾取物)持有一组可堆叠物品”这一核心抽象。它不直接绑定 Pawn,而是作为一个 UActorComponent 挂在任意 Actor(PlayerController、PlayerState、Pickup 道具等)上,因此可同时支撑:玩家个人背包、世界拾取物的”暂存仓”、AI 单位的随身携带物品、以及未来可能出现的箱子/储物柜。
模块在结构上分三层:
Definition(数据资产层):只读、Designer 在编辑器里配置,由 UInventoryItemDefinition + 一组 UInventoryItemFragment 组合而成(Composition over Inheritance)。
Instance(运行时实例层):UInventoryItemInstance 是真正存在于运行时的物品对象,承载该物品当前的”状态数据”(弹药数量、耐久、Buff 计数……),通过一个 Tag→Count 的 GameplayTagStack 容器统一表达。
Manager(容 ...
UE5 指示器系统 简介(参考 Lyra)
1. 模块概述
IndicatorSystem 是 3D-to-2D 屏幕指示器系统,解决”把 3D 世界中的目标投影到 2D HUD 上”的核心 UX 需求:敌人头顶血条、锁敌标记、屏幕外敌人箭头指示。
核心价值拆成三点:
性能:多名敌人同屏战斗场景下,3D 投影 + 布局 + Widget 更新在一次 OnArrangeChildren 内完成;Widget 实例走 FUserWidgetPool 复用避免 GC 抖动;FAsyncMixin 异步加载 TSoftClassPtr 不阻塞主线程;FActiveTimer 在没有指示器时 Stop 让面板脱离帧循环。
解耦:
Framework 层(IndicatorManagerComponent / IndicatorDescriptor / SActorCanvas / IIndicatorWidgetInterface)无业务感知;
业务层翻译战斗事件为 Descriptor 配置;
Widget 层通过接口实现自主订阅数据源。
四层独立,新增一类指示器(友军血条、地图传送门、任务目标)零框架改动。
表现:5 种投影模式( ...
UE5 UI 模块简介(参考 Lyra)
1. 基本框架(CommonUI + CommonGame 分层)
大致UI框架如上图所示(简化了部分),最顶层的 Layout 由 GameUISubSystem 驱动创建,创建的时机是 LocalPlayer 被添加的时候,具体由 GameUIPolicy 来执行
123456789101112131415161718192021222324252627void UGameUIPolicy::NotifyPlayerAdded(UCommonLocalPlayer* LocalPlayer){ LocalPlayer->OnPlayerControllerSet.AddWeakLambda(this, [this](UCommonLocalPlayer* LocalPlayer, APlayerController* PlayerController) { NotifyPlayerRemoved(LocalPlayer); if (FRootViewportLayoutInfo* LayoutInfo = RootV ...
JPS 算法
JPS 算法概念基础
JPS(Jump Point Search,跳点搜索)算法是对 A* 算法的一种优化(附A* 算法)。在 A* 算法中,当前节点周围的所有可搜索邻居都会被加入到 OpenList 中。JPS 算法则在保持 A* 算法基本框架的基础上,采用了一种更为高效的策略来确定哪些点需要被加入到 OpenList 中。这种方法能够显著减少需要评估的节点数量,从而提高路径搜索的效率。
自然邻居
自然邻居指的是在路径搜索过程中,某些节点的直接邻居是可以直接移动到达的节点,而不需要经过其他节点的中转。这些自然邻居是基于当前节点的移动方向和位置来确定的。如图所示,红色块是自然邻居(其中 p(x) 为父节点,x为当前节点)。
强迫邻居
为了减少需要评估的节点数量,JPS 算法引入了强迫邻居的概念。强迫邻居指的是在路径搜索过程中,某些节点的直接邻居是必须被考虑的,因为它们是达到更远距离节点的必经之路。在 JPS 算法中,强迫邻居用于确保搜索过程不会错过任何可能的路径选择。
强迫邻居的定义:当节点 x 的 8 个邻居中存在障碍,且节点 x 的父节点 p 经过节点 x 到达节点 n 的 ...
A* 算法
A* 算法概念基础Dijkstra算法
Dijkstra算法用来寻找图形中节点之间的最短路径。
在Dijkstra算法中,需要计算每一个节点距离起点的总移动代价。同时,还需要一个优先队列结构。对于所有待遍历的节点,放入优先队列中会按照代价进行排序。
在算法运行的过程中,每次都从优先队列中选出代价最小的作为下一个遍历的节点。直到到达终点为止。
下面对比了不考虑节点移动代价差异的广度优先搜索与考虑移动代价的Dijkstra算法的运算结果:
最佳优先搜索
在一些情况下,如果我们可以预先计算出每个节点到终点的距离,则我们可以利用这个信息更快的到达终点。
其原理也很简单。与Dijkstra算法类似,我们也使用一个优先队列,但此时以每个节点到达终点的距离作为优先级,每次始终选取到终点移动代价最小(离终点最近)的节点作为下一个遍历的节点。这种算法称之为最佳优先(Best First)算法。
这样做可以大大加快路径的搜索速度,如下图所示:
但这种算法会不会有什么缺点呢?答案是肯定的。
因为,如果起点和终点之间存在障碍物,则最佳优先算法找到的很可能不是最短路径,下图描述了这种情况。
A* ...
移动施法的动画分层
移动施法的动画分层
动画分层是指:当你希望《使命召唤》里的士兵一边奔跑一边换弹而不会跳帧时所用的技术。你可以在骨盆及以下部位播放奔跑动画,同时在上半身播放与武器相关的动画。听起来很简单,对吧?但实际操作并不总是那么容易。那如果你想把奔跑和挥剑动作结合起来呢?又或者你希望角色在奔跑和站立状态下都能使用同一套动画,又该怎么办?
接下来我将介绍一种方法:角色骨架、实时 IK 和嵌套混合树来解决上述问题
为了说明我们的问题,我们首先创建一个 Animator,并设置两个图层:一个用于腿部动画,另一个用于上半身动画。腿部图层通常包含一个用于处理多方向移动的混合树,所以我们会加入这个部分。
接着,我们在上半身图层添加一个 Avatar Mask,用来屏蔽腿部,然后在该图层播放一个近战攻击动画。网上有无数教程都是这么做,并且就到此为止,但问题也正是从这里开始显现的。
仅仅使用 Avatar Mask 会导致胸部的旋转不正确。
拉直脊柱
首先,躯干动画的旋转会出现错误,是因为骨骼层级中位置更高的骨盆旋转不再与技能动画匹配。由于骨盆在 Avatar Mask 中被屏蔽,只受到奔跑动画的影响,因此会影响 ...









