插件简介
【Unity万人同屏插件】是Unity通用性能优化插件,超大规模渲染、寻路移动、索敌碰撞检测等全方位性能瓶颈解决方案,支持Animator、Animation、Spine转为GPU动画prefab,并支持多线程合批渲染大幅提升性能, 兼容Unity全平台(含微信/抖音小游戏)。
方案覆盖了游戏开发中大多数性能热点,打破技术瓶颈,传统开发模式获得极致性能。
新手友好、简单易用,无ECS技术门槛,传统开发方式,也适用于旧项目免重构性能提升。
十万人战斗实录
【Unity万人同屏插件】官网介绍:https://efunstudio.cn/unitymasskit
https://efunstudio.cn/unitymasskit
【性能/功能测试demo】下载:PC 安卓demo下载
https://assets.efunstudio.cn/SharedFiles/
【WebGL性能测试demo】WebGL性能测试
https://efunstudio.cn/web-demo/
WebGL实战demo
https://efunstudio.cn/web-demo2/
主要功能模块:
| 模块名称 | 功能 |
|---|---|
| GPUAnimation | 把Animator、Animation、Spine Skeleton资源直接转换成 100%纯GPU计算的GPU动画。支持平滑过渡、事件、挂载点记录、武器挂载、切换皮肤,支持Mesh / Material以及贴图合并、一键自动生成LOD简化Mesh 直接使用GPU动画prefab 以GameObject传统方式使用,相比直接使用Animator、Skeleton可提升10倍以上性能。 |
| ECSGraphics | 基于Entities Graphics(DOTS) API高度封装简化的高性能渲染接口,无需用户写ECS代码。支持把静态模型或GPUAnimation 通过DOTS技术高性能合批渲染,进一步大幅提升性能,是实现10万级同屏渲染的最佳方案。 同时提供了目标搜索/索敌/碰撞检测、Raycast / SphereCast检测 接口。内置两套Jobs多线程实现的空间划分算法,支持10w+单位高性能目标查找。 |
| RVO | 纯数据驱动、Jobs多线程计算,支持超大规模 避让移动、寻路/导航、支持Terrain 3D地形、目标搜索/索敌/碰撞检测、Raycast / SphereCast检测,全面覆盖游戏业务中常见性能热点。 动态避让移动支持精确控制哪些单位之间不碰撞、避让权重(boss不给小兵让路),Targeting索敌系统支持根据视野、攻击范围、阵营、标签、优先级等参数,精准筛选所需目标。 |
性能实测对比:
可下载demo自测:https://assets.efunstudio.cn/SharedFiles/
以下对比来自PC版demo实测,环境:i7-13700KF + RTX3070


| 3D动画测试 | FPS | 帧耗时(ms) | 性能 |
|---|---|---|---|
| 原始 Animator | 15.14 | 66.03 | 1.00x(基线) |
| GPU Animation | 130.07 | 7.69 | 8.59x |
| ECS Graphics + GPU Animation | 1183.65 | 0.84 | 78.18x |
| 2D动画测试 | FPS | 帧耗时(ms) | 性能 |
|---|---|---|---|
| 原始 Spine | 11.85 | 84.36 | 1.00x(基线) |
| GPU Spine | 142.69 | 7.01 | 12.04x |
| ECS Graphics + GPU Spine | 1434.62 | 0.70 | 121.06x |
插件用法
GPUAnimation
GPU动画是万人同屏插件的基石,把Animator/Animation 和 Spine 动画从CPU计算 完全 转变成GPU计算,从而完美支持合批渲染。
把Animator、Animation、Spine Skeleton资源直接转换成 100%纯GPU计算的GPU动画。支持平滑过渡、事件、挂载点记录、武器挂载、切换皮肤,支持Mesh / Material以及贴图合并、一键自动生成LOD简化Mesh
直接使用GPU动画prefab 以GameObject传统方式使用,相比直接使用Animator、Skeleton可提升10倍以上性能。
一. 创建GPU动画Builder
在Unity 资源管理界面 选中[Animator/Animation预制体] 或 [SkeletonDataAsset 资源] 鼠标右键 Create -> GPUAnim -> Create Builders From Selection 创建GPUAnim 或 GPUSpine转换文件:
二. GPU动画Builder配置
创建完GPU动画配置文件后,可在配置文件中设置各项转换参数以满足GPU动画不同需求。
1. GPU Spine (2D)
GPU Spine插件用法视频教程
https://www.bilibili.com/video/BV1GZ5t6MEqN/
GPUSpine
直接解析SkeletonDataAsset 精准还原动画、特效。除基本骨骼动画帧外,还额外支持 颜色/透明度 、Deform网格变形、Active显隐关键帧。
如下图所示,配置文件会自动根据Skeleton文件初始化,每项配置都有中 / 英 双语展示:
合并材质:支持把Spine的材质、贴图合并成一个材质/图集,优化成一个DrawCall,并且单材质支持Normal 和 Addictive两种常用Blend混合模式,既优化DrawCall 又兼顾混合效果。
动画/Clips:默认会把Spine文件所有动画添加进列表,根据也许需求 拖拽 排放动画索引顺序即可。支持为每个动画设置 速度、是否循环播放。
记录挂载点:点击 '+' 添加挂点Slots,在下拉列表中选择Skeleton的挂点,列表中的挂点会记录到GPU动画数据,用于把武器挂载到挂点上 或 运行时实时获取挂点位置/旋转。
构建按钮: ‘构建当前Skin’ 会只把当前选中的皮肤名称 构建成GPU Spine;‘构建全部Skins’ 会把Skeleton所有皮肤构建成GPU Spine.
2. GPU Anim (3D)
GPU Anim插件用法视频教程
https://www.bilibili.com/video/BV17Q5u68EQe/
GPUAnim
如下图所示,对于3D GPU动画,工具提供了全方位的功能需求
进阶用法:
ECSGraphics LOD
-
LOD功能 自动精简模型生成低模LOD Mesh

GPUAnim Builder 内置 多级细节(LOD) 配置栏,可自动生成简化 Mesh 并写入 LOD 级别。该配置最多支持 5 个实际 LOD 级别,适用于大规模同屏时进一步降低远距离顶点开销。
| 参数 | 作用 | 说明 / 建议 |
|---|---|---|
启用自动多级细节(LODs.Enabled) | 总开关,控制是否生成 LOD。 | 关闭后不会生成额外 LOD 资源。 |
LOD1~LOD5 Enabled | 启用每一级 LOD。 | 级别是连续启用规则:启用高一级会自动启用前面级别;关闭某级会同时关闭后续级别。 |
Quality(1~100) | 简化质量(越低简化越激进)。 | 100 表示复用原 Mesh,不做简化;常用做法是 LOD1=100,再逐级递减。 |
Transition(1~100) | 屏幕相对切换阈值(Screen Relative Transition Height)。 | 值越大越早使用该级别;建议从近到远逐级递减(如 75/60/38/20/8)。 |
保护边界 / 保留细节 / 保持对称 / 保护硬边 / 保持形体 | 网格简化保护选项。 | 角色类资源建议优先开启 保护边界 与 保持形体,避免轮廓塌陷。 |
-
启用分组构建功能,精确控制Mesh / Material,按组合并
第一步:点 ‘+’ 添加Mesh Group 组,这些组是用于把模型分成多个Sub Mesh块,每个Sub Mesh块就可以单独用一个Material渲染。
第二步:在Render 分配栏,通过下拉框选择每个Mesh对应的组。
Mesh Group的应用场景(举个例子):
例子1:角色身体、头发、衣服都需要用不同的材质(shader)才能渲染出对应的质感,就可以用Mesh Group分组进行合并。最终生成的GPU动画资源就是 单 Mesh + 多Materials,以解决不同部位用不同材质的需求。
例子2:换装,对于换装需求,角色身上的衣服是需要动态切换的,不能直接合并到Mesh里。就可以把 ‘衣服’ 建组,同时勾选 【作为挂载物单独导出】,‘衣服’就会独立作为GPU动画资源导出。
-
记录挂载点
GPUAnim / GPUSpine 都支持记录挂载点,挂载点可用于挂载GPU动画武器、皮肤等挂载物。
支持运行时 实时获取挂点position / rotation / scale 数据。例如: GPU动画 坦克,炮弹需要从炮管端点创建并发出,就可以在炮管端点处添加一个挂载点,就能在GPU动画播放的任意时刻拿到准确的炮管端点位置。
点‘+’添加 【挂点】,直接拖拽挂点GameObject添加。

-
静态 / 动态武器(挂载物)
对于静态模型挂载物,直接把prefab配置到静态挂载物列表。
对于GPU动画挂载物(也就是武器自己也有GPU动画),可以把其它GPU动画构建文件直接配置到列表,支持嵌套。构建GPU动画时会自动构建出 静态 / 动态 挂载物。

3. GPU帧动画

GPUAnimation 还支持序列帧贴图方案(Flipbook / Sprite Sheet)。内置了 GPUFrameAnim 采样函数,提供 ASE Shader(GPUAnim/ASE/GPUFrameAnim)与 ShaderGraph shader。
| 参数 | 作用 | 说明 |
|---|---|---|
_AnimTex | 序列帧贴图。 | 整张图按行列切分为帧。 |
_FrameCol / _FrameRow | 列数 / 行数。 | 总帧数 = 列 × 行。 |
_StartTime | 播放起始时间。 | 重播时重置为当前时间即可。 |
_AnimSpeed | 播放速度。 | 1 表示约 1 秒播完整段;支持负值反播。 |
_StartFrame | 起始帧索引。 | 从该帧开始播放。 |
_MaxFrame | 结束帧索引上限。 | -1 表示播放到最后一帧。 |
_IsLoop | 是否循环。 | 1 循环,0 单次播放。 |
4. GPU动画资源
GPU Spine (2D动画):
①GPU Spine预制体:可以直接只用GameObject传统开发模式,也可直接拖拽到ECSGraphicsComponent面板 注册为渲染物,以使用ECSGraphics高性能合批渲染。
②GPU Spine网格:从SkeletonData烘焙得到的运行时网格。
③AnimTex:骨骼动画贴图
④StateTex:颜色/透明度、Active显隐关键帧贴图
⑤DeformTex:Deform网格变形关键帧贴图
⑥动画事件GPUAnimEventsData:记录了动画名、动画事件触发帧等信息,用于运行时触发GPU动画事件
GPU Anim (3D动画):
①GPU Anim预制体:烘焙完成后生成GPU动画预制体,可以直接只用GameObject传统开发模式,也可直接拖拽到ECSGraphicsComponent面板 注册为渲染物,以使用ECSGraphics高性能合批渲染。
②GPU Anim网格:由原始 Prefab 中的 `SkinnedMeshRenderer` / `MeshRenderer` 烘焙得到。构建时会先采集源 `Mesh` 的顶点、UV、法线、切线和骨骼绑定数据,Skinned 物体写入骨骼索引 / 权重,Rigid 物体则绑定到自身刚体节点,最后把几何信息编码成 GPU 运行时可采样的网格。
每个 `SubMesh` 会按原始材质槽拆分;如果开启 `CombineMesh`,多个输出还会按 `sortingOrder` 和生成顺序继续合并,保证最终渲染顺序和原始层级一致。
③AnimTex:动画贴图,保存骨骼运动、关键帧、挂载点等核心动画数据。
④Material:GPU动画材质,绑定动画贴图协议和渲染所需的材质参数。通过切换GPU动画Blend shader可以支持平滑过渡切换
⑤动画 事件 GPUAnimEventsData:记录动画名、动画事件触发帧等信息,用于运行时触发GPU动画事件。
如果角色还带有武器、饰品、翅膀这类挂载物,也会继续沿用这一套资源协议导出,方便在运行时做嵌套挂载和皮肤切换。
5. Mesh Simplifier模型简化工具

除了 Builder 里 LOD 转化功能外,也可以直接打开独立窗口:Tools/GPUAnim/Mesh Simplifier。用于根据简化强度生成简化 Mesh 资产,降低模型顶点/面数。
| 参数 / 项 | 作用 | 说明 |
|---|---|---|
源 Mesh | 输入待简化网格。 | 要求可读(Read/Write 开启)。 |
输出目录 | 导出简化资产路径。 | 必须在 Assets/ 目录内。 |
保护边界/保留细节/保持对称/保护硬边/保持形体 | 简化约束选项。 | 用于平衡轮廓保持与面数下降幅度。 |
简化等级 + 质量(1~100) | 配置多级简化质量。 | 支持多级预览,质量越低简化越激进。 |
刷新预览 / 导出全部 | 生成预览 / 批量导出。 | 导出命名示例:Mesh_LOD1_Q70.asset。 |
三. GPU动画Runtime用法
1. 动画控制器GPUAnimController
用于控制GPU动画播放、事件触发等,Inspector面板提供了动画播放进度、动画时长、动画切换按钮等功能。

2. 挂载物控制器GPUAnimAttachmentBinding
用于挂载物切花挂点。例如武器切换到左手或右手

3.挂载点Debug脚本GPUSpineAttachDebugDraw / GPUAnimAttachDebugDraw
用于Debug显示GPU动画 / GPU Spine的挂点信息,通过Gizmos绘制挂点的位置、轴向、坐标数据等,便于调试。

四. 自定义GPU动画shader
GPUAnimation将所有核心底层计算封装为6个hlsl文件,通过ASE、Shader Graph调用hlsl函数对GPU动画方法添加了一层图形化函数封装,完美支持 手写shader 和 ASE / ShaderGraph图形化shader编辑器,并达到了跨渲染管线的需求。
1. 手写自定义GPU动画shader
手写 shader 时,3D 直接 include GPUAnim.hlsl,2D 直接 include GPUSpine.hlsl。在shader中直接调用GPU动画API即可。
下面展示一个最简unlit shader示例:
Shader "Custom/GPUAnim/UnlitExample"
{
Properties
{
_BaseMap ("Base Map", 2D) = "white" {}
_AnimTex ("Anim Tex", 2D) = "white" {}
_AnimMeta ("Anim Meta", Vector) = (1, 1, 0, 0)
_ClipState ("Clip State", Vector) = (0, 0, 0, 0)
_AnimSpeed ("Anim Speed", Float) = 1
_VertexMode ("Vertex Mode", Float) = 1
}
SubShader
{
Tags
{
"RenderType" = "Opaque"
"Queue" = "Geometry"
"RenderPipeline" = "UniversalPipeline"
}
Pass
{
Name "Unlit"
Tags { "LightMode" = "UniversalForward" }
HLSLPROGRAM
#pragma target 4.5
#pragma vertex vert
#pragma fragment frag
#include "Packages/com.unity.render-pipelines.universal/ShaderLibrary/Core.hlsl"
#include "GPUAnim.hlsl"
TEXTURE2D(_BaseMap);
SAMPLER(sampler_BaseMap);
TEXTURE2D(_AnimTex);
SAMPLER(sampler_AnimTex);
float4 _AnimTex_TexelSize;
float4 _AnimMeta;
float4 _ClipState;
float _AnimSpeed;
float _VertexMode;
struct Attributes
{
float4 positionOS : POSITION;
float3 normalOS : NORMAL;
float4 tangentOS : TANGENT;
float2 uv : TEXCOORD0;
float4 boneIndices : BLENDINDICES;
float4 boneWeights : BLENDWEIGHTS;
};
struct Varyings
{
float4 positionCS : SV_POSITION;
float2 uv : TEXCOORD0;
};
Varyings vert(Attributes input)
{
Varyings output;
float4 animatedPositionOS;
float3 animatedNormalOS;
float4 animatedTangentOS;
GPUAnimAtTime(
_AnimTex,
sampler_AnimTex,
_AnimTex_TexelSize,
_AnimMeta,
_ClipState,
_AnimSpeed,
_Time.y,
_VertexMode,
input.boneIndices,
input.boneWeights,
input.positionOS,
input.normalOS,
input.tangentOS,
animatedPositionOS,
animatedNormalOS,
animatedTangentOS);
output.positionCS = TransformObjectToHClip(animatedPositionOS.xyz);
output.uv = input.uv;
return output;
}
half4 frag(Varyings input) : SV_Target
{
half4 baseColor = SAMPLE_TEXTURE2D(_BaseMap, sampler_BaseMap, input.uv);
return baseColor;
}
ENDHLSL
}
}
}
2. ASE / Shader Graph 图形化自定义shader
GPUAnimation插件已经提供了全部所需 ASE 和 Shader Graph 图形化GPU动画函数,直接调用节点即可。3D 侧常用节点名是 GPUAnim、GPUAnimBlend、GPUAnimAttach、GPUAnimAttachBlend、GPUAnimAttachStatic、GPUAnimAttachBlendStatic;2D 侧常用节点名是 GPUSpine、GPUSpineBlend、GPUSpineAttach、GPUSpineAttachBlend、GPUSpineAttachStatic、GPUSpineAttachBlendStatic
ECSGraphics 渲染器
3D GPU动画
基于Entities Graphics(DOTS) API高度封装简化的高性能渲染接口,无需用户写ECS代码。支持把静态模型或GPUAnimation 通过DOTS技术高性能合批渲染,进一步大幅提升性能,是实现10万级同屏渲染的最佳方案。
同时提供了目标搜索/索敌/碰撞检测、Raycast / SphereCast检测 接口。内置两套Jobs多线程实现的空间划分算法,支持10w+单位高性能目标查找。
一. 注册渲染物
支持批量推拽prefab注册渲染物,自动同步Mesh、多Materials 以及从LODGroup自动同步LODs配置,自动识别透明渲染物并开启TransparencySortingOn (透明物渲染排序)。
注册渲染物后可在面板设置详细参数:

二. 接口用法
GPU动画挂载
1. 添加 / 移除
Add 会直接实例化一个 ECS 实体,参数里最常用的是 renderKey、位置、旋转、缩放。parentEntity 不为空时会自动建立父子层级,customId 则适合你自己给业务对象做标记。
Remove 会销毁实体本身,并递归清理它的子实体;RemoveAll 则是清空当前组件已经创建的全部实体。
通过ECSGraphics渲染物体,避免了使用GameObject 加载资源、实例化的严重耗时,通过DOTS Instancing合批技术,实现比原Animator/Animation/SkeletonAnimation 提升数十倍 到 百倍的渲染性能。
using cn.efunstudio.ecsgraphics;
Entity player = ECSGraphicsComponent.Instance.Add("Hero", new float3(0, 0, 0), quaternion.identity, 1f);
ECSGraphicsComponent.Instance.Remove(player);
2. 挂载子物体 添加 / 移除
GPU 动画挂载物体用 AddAttachment 创建。它会把挂载物作为子物体添加,并在父物体切换 GPU 动画时自动同步GPU动画参数(状态或速度)
如果挂载点要切骨骼,可以用 SetAttachmentBoneIndex;需要移除单个挂载物体时用 RemoveAttachment,需要一次清空所有挂载物体时用 RemoveAllAttachments。
var weapon = ECSGraphicsComponent.Instance.AddAttachment("Sword", player, 1, float3.zero, quaternion.identity, 1f);
//切换挂载点, 如武器 切换到左手 或 右手
ECSGraphicsComponent.Instance.SetAttachmentBoneIndex(weapon, 1);
//删除玩家的武器挂载物
ECSGraphicsComponent.Instance.RemoveAttachment(player, weapon);
3. 切换皮肤
接口为SetRender(entity, renderKey) / SetRender(entity, renderId), 用于运行时替换渲染资源。切换时会重新绑定 Mesh、Material、LOD 和透明排序设置,并保持当前实体的动画状态、速度。
ECSGraphicsComponent.Instance.SetRender(player, "Hero_SkinB");
4. GPU动画接口
ECSGraphics完美支持GPUAnimation所有功能,播放动画、挂载点切换、动画事件等。
常用接口包括 PlayGPUAnim 播放动画,SetAnimSpeed / GetAnimSpeed 设置和读取动画速度,GetGPUAnimClipState 读取当前 _ClipState,GetGPUAnimClipInfo 读取指定动画片段信息,GetGPUAnimBone 读取挂点骨骼世界变换,GetGPUAnimClipsCount 读取动画片段数量。以及GetGPUAnimBone 实时获取当前动画帧骨骼的transform信息。
//播放GPU动画
ECSGraphicsComponent.Instance.PlayGPUAnim(player, 2);
//设置GPU动画播放速度
ECSGraphicsComponent.Instance.SetAnimSpeed(player, 1.25f);
//实时获取当前动画帧 指定骨骼的transform信息
var boneTransform = ECSGraphicsComponent.Instance.GetGPUAnimBone(player, 1);
var clipState = ECSGraphicsComponent.Instance.GetGPUAnimClipState(player);
var clipInfo = ECSGraphicsComponent.Instance.GetGPUAnimClipInfo(player, (int)clipState.x);
var clipCount = ECSGraphicsComponent.Instance.GetGPUAnimClipsCount(player);
var animSpeed = ECSGraphicsComponent.Instance.GetAnimSpeed(player);
GPU动画事件触发检测接口 GPUAnimEventsUpdate(ref NativeQueue<GPUAnimEventInfo>),全局在某个Update中实时调用,就能每帧拉取触发结果,内部由Jobs多线程并行计算,性能足以支持数十万以上单位:
using Unity.Collections;
using UnityEngine;
using cn.efunstudio.ecsgraphics;
public class GPUAnimEventReader : MonoBehaviour
{
private NativeQueue _events;
private void Update()
{
if (!ECSGraphicsComponent.Instance.GPUAnimEventsUpdate(ref _events)) return;
while (_events.TryDequeue(out var evt))
{
Debug.Log($"触发了GPU动画事件: entity={evt.ECSEntity.Index}, render={evt.ECSRenderIndex}, clip={evt.ClipIndex}, event={evt.EventId}, frame={evt.TriggerAtFrame}");
}
}
private void OnDestroy()
{
if (_events.IsCreated) _events.Dispose();
}
}
5. 索敌系统
ECSGraphicsTargeting
独立的索敌系统,两套Jobs高性能空间划分、目标搜索算法(Grid / Octree Jobs)。只需为Entity添加ECSTargetingData组件即可参与目标搜索。
索敌系统的核心数据 ECSTargetingData:
| 字段名 | 作用 | 对应接口 / 说明 |
|---|---|---|
FactionMask | 自身阵营掩码。查询时若与目标阵营有交集(按位与不为 0)则过滤该目标,用于“同阵营不互相锁定”。 | SetTergetingCamp;过滤条件:(query.FactionMask & candidate.FactionMask) != 0 |
LayerMask | 实体层标签(位枚举)。本字段主要作为“被检索目标”的层,供别人的 IgnoredLayerMask 做过滤。 | SetTergetingLayer |
IgnoredLayerMask | 查询方忽略层掩码。若目标 LayerMask 命中该掩码,则目标被过滤。 | SetTergetingIgnoreLayer;过滤条件:(query.IgnoredLayerMask & candidate.LayerMask) != 0 |
BodyRadius | 实体半径。用于比较"自身点到目标边"的距离,参与距离判定与射线/球投射距离; | SetTergetingRadius;作为 candidateRadius 参与判定 |
VisionRadius | 视野半径。用于 Vision 过滤(FilterVisionRange / WithinVisionRange),判定也是“查询点到目标边界”。 | SetTergetingSeekRadius |
VisionAngle | 视野角度(相对查询前向)。仅在需要 Vision 角度过滤时生效;>= 360 视为全向。 | SetTergetingSeekAngle |
EffectRadius | 作用半径。用于 Nearest 的 Effect 状态判定、OverlapSphere范围判定,以及 Raycast/SphereCast 的射线长度。 | SetTergetingAttackRadius |
EffectAngle | 作用角度。用于 Effect 角度过滤;在 SphereCast 中复用为球体半径。 | SetTergetingAttackAngle |
MaxTargetCount | 单次查询最多返回目标数。主要作用于 OverlapSphere/ Raycast / SphereCast;Nearest 固定单目标。 | OverlapSphere/ Raycast / SphereCast |
CanAcquireTargets | 是否允许该查询方发起索敌。为 false 时查询作业会直接返回空结果。 | 直接字段控制(无专用 Setter) |
CanBeTargeted | 是否允许该实体被锁定。为 false 时会在合法性过滤阶段被剔除。 | 直接字段控制(无专用 Setter) |
| ECSTargetingFilterFlags | 作用 |
|---|---|
None | 只返回最近目标,不额外检查范围状态。 |
CheckEffectRange | 返回最近目标,并额外标记是否在 EffectRadius / EffectAngle 内。 |
FilterVisionRange | 只保留 VisionRadius / VisionAngle 内的目标。 |
WithinVisionRange | 等价于 FilterVisionRange | CheckEffectRange。 |
索敌接口用法
① Nearest 搜索最近目标
Nearest 用于批量搜索每个 Entity 或查询点的最近目标。配合 ECSTargetingFilterFlags 时,可以只返回最近目标,也可以额外检查 EffectRadius / EffectAngle 范围状态,或先按 VisionRadius / VisionAngle 过滤。
var (queryEntities, queryEntitiesLength) = ECSGraphicsComponent.Instance.GetAllTargetingEntitiesRef();
//批量查询所有实体的最近目标
using var nearest = ECSGraphicsComponent.Instance.Nearest(
queryEntities,
queryEntitiesLength,
ECSTargetingFilterFlags.CheckEffectRange);
for (int i = 0; i < nearest.QueryCount; i++)
{
Entity target = nearest.GetTarget(i);
if (target == Entity.Null)
{
continue;
}
bool inEffectRange = nearest.IsInEffectRange(i);//是否在作用范围
}
② OverlapSphere搜索范围内多目标 AOE
OverlapSphere用于返回每个查询源范围内的多个目标,适合 AOE 伤害、范围治疗、警戒圈、附近单位收集等业务。返回数量受 MaxTargetCount 或接口传入的 maxCountPerQuery 控制。
var (queryEntities, queryEntitiesLength) = ECSGraphicsComponent.Instance.GetAllTargetingEntitiesRef();
using var within = ECSGraphicsComponent.Instance.OverlapSphere(
queryEntities,
queryEntitiesLength,
sortResultByDistance: true);
for (int i = 0; i < within.QueryCount; i++)
{
foreach (Entity target in within.GetTargets(i))
{
if (target == Entity.Null) continue;
// 对命中目标执行 AOE 逻辑
}
}
③ Raycast 射线检测
Raycast 类似 Physics.RaycastAll,但查询对象来自 ECSTargetingData数据,而不是 Unity 物理碰撞体。适合直线技能、子弹穿透、激光、点击选中射线等场景。
NativeArray<float3> pointsA = new NativeArray<float3>(1, Allocator.TempJob);
NativeArray<float3> pointsB = new NativeArray<float3>(1, Allocator.TempJob);
float3 castFrom = transform.position;
float3 castTo = castFrom + (float3)transform.forward * 20f;
pointsA[0] = castFrom;
pointsB[0] = castTo;
var rayQuery = new ECSTargetingData(
ECSTargetingLayer.NONE,
ECSTargetingLayer.NONE,
ECSTargetingLayer.NONE,
bodyRadius: 0.5f, // 点查询重载里该值不用于查询半径计算;主要用于实体作为“被检索目标”时的边界
effectRadius: 20f,
maxTargetCount: 16,
effectAngle: 1f);
using var rayHits = ECSGraphicsComponent.Instance.Raycast(pointsA, pointsB, rayQuery);
foreach (var query in rayHits)
{
foreach (Entity target in query.Targets)
{
if (target == Entity.Null) continue;
// 对射线命中目标执行逻辑
}
}
pointsA.Dispose();
pointsB.Dispose();
④ SphereCast 球体投射
SphereCast 是带宽度的投射检测,适合冲锋、长条形 AOE、较宽弹道、怪物碰撞检测等技能索敌场景。
NativeArray<float3> pointsA = new NativeArray<float3>(1, Allocator.TempJob);
NativeArray<float3> pointsB = new NativeArray<float3>(1, Allocator.TempJob);
float3 castFrom = transform.position;
float3 castTo = castFrom + (float3)transform.forward * 18f;
pointsA[0] = castFrom;
pointsB[0] = castTo;
var sphereQuery = new ECSTargetingData(
ECSTargetingLayer.NONE,
ECSTargetingLayer.NONE,
ECSTargetingLayer.NONE,
bodyRadius: 0.5f, // 点查询重载里该值不用于查询半径计算;主要用于实体作为“被检索目标”时的边界
effectRadius: 18f,
maxTargetCount: 24,
effectAngle: 1.5f); // SphereCast 半径(复用 EffectAngle)
using var sphereHits = ECSGraphicsComponent.Instance.SphereCast(pointsA, pointsB, sphereQuery);
foreach (var query in sphereHits)
{
foreach (Entity target in query.Targets)
{
if (target == Entity.Null) continue;
// 对球体投射命中目标执行逻辑
}
}
pointsA.Dispose();
pointsB.Dispose();
RVO 移动/导航
RVO Pathfinding
纯数据驱动、Jobs多线程计算,支持超大规模 避让移动、寻路/导航、支持Terrain 3D地形、目标搜索/索敌/碰撞检测、Raycast / SphereCast检测,全面覆盖游戏业务中常见性能热点。
动态避让移动支持精确控制哪些单位之间不碰撞、避让权重(boss不给小兵让路),Targeting索敌系统支持根据视野、攻击范围、阵营、标签、优先级等参数,精准筛选所需目标。
RVO 模块的入口是 RVOComponent.Instance。支持 3D XZ 与 2D XY 两种轴模式,编辑器面板里可以通过 AXIS_2D_MODE 宏切换2D游戏模式。
可通过RVOComponent面板RuntimeFeatures枚举控制启用哪些功能,用不到的功能不开启,避免任何不必要算力消耗:

MovePath :启用RVO支持多路径点移动
Pathfinding: 启用寻路导航功能,会自动依赖 MovePath
Terrain:支持在3D 地形 Terrain上移动,仅3D模式有效。
一. 动态避让
1. RVOAgent 参数
动态避让由 RVOComponent 统一调度。业务侧把单位注册为 RVOAgent 后,设置targetPosition目标点即可自动避让移动;可设置每个Agent对象避让参数,也可以使用 RVOComponent 面板里的 DefaultAgent 作为默认模板。
| 参数 | 作用 | 备注 |
|---|---|---|
targetPosition | 移动目标点。 | 每帧可更新;ORCA 会朝该点推进并参与避让。 |
radius | 单位-单位避让半径。 | 影响单位之间的最小安全间距。 |
radiusObst | 单位-障碍避让半径。 | 越大越早绕开障碍物。 |
maxSpeed | 最大移动速度。 | 直接限制速度上限。 |
maxNeighbors | 参与避让计算的最大邻居数。 | 越大计算更稳,开销也更高。 |
neighborDist | 邻居搜索半径。 | 与 maxNeighbors 共同决定局部拥挤响应。 |
timeHorizon | 单位-单位时间预测窗口。 | 越大越保守,越早减速避让。 |
timeHorizonObst | 单位-障碍时间预测窗口。 | 越大越保守,障碍规避更提前。 |
avoidWeight | 避让权重。 | 值越小越“强势”,更不让路(如 Boss)。 |
layerOccupation | 自身所在 RVOLayer。 | 用于分层避让规则。 |
layerIgnore | 忽略的层掩码。 | 命中掩码的层不参与当前 Agent 的避让计算。可精确控制不同兵种之间忽略碰撞 |
navigationEnabled | 是否启用导航推进。 | 关闭后ORCA系统不再托管位置移动,可以手动修改position。 |
collisionEnabled | 是否启用碰撞避让。 | 关闭后可穿透,不参与避让解算。 |
stopMoveSelf | 停止自身主动移动。 | 常用于站桩施法/攻击,自身不移动,但会被其它单位推挤位移。 |
2. RVOAgent 单目标点移动
给定targetPosition,ORCA系统会自动朝该点推进移动并参与避让。
用法示例代码:
// 1) 创建一个 RVOAgent(使用 DefaultAgent 模板,并绑定 Transform)
RVOAgent agent = RVOComponent.Instance.AddAgent(transform);
// 2) 配置移动速度并确保启用导航
agent.maxSpeed = 5f;
agent.navigationEnabled = true;
// 3) 设置一个目标点,Agent 会自动朝该点移动并参与动态避让
agent.targetPosition = new float3(10f, 0f, 10f);
3. RVOAgent 路径移动
首先要确保RVOComponent面板RuntimeFeatures功能枚举启用了【Move Path】功能。
开启 MovePath 后,Agent 不只可以移动到单个目标点,也可以沿一组路径点依次移动。路径点可以来自自己的地图导航、服务器下发路径,或者下一节的 寻路系统 获得的路径结果。调用 SetMovePath 后,RVO 会在每段路径上继续参与局部动态避让。
用法示例代码:
NativeArray<float3> pathPoints = new NativeArray<float3>(3, Allocator.TempJob);
pathPoints[0] = new float3(5f, 0f, 0f);
pathPoints[1] = new float3(5f, 0f, 8f);
pathPoints[2] = new float3(12f, 0f, 8f);
agent.SetMovePath(pathPoints, 0, pathPoints.Length);
// SetMovePath 会把路径拷贝进 Agent 内部缓存,临时 NativeArray 可在调用后释放。
pathPoints.Dispose();
4. 障碍物
①BoxObstacle:矩形障碍物
设置Box Size控制矩形障碍物大小
②CircleObstacle:圆形障碍物,RVO单位可以通过推挤绕开圆形障碍物
Radius 为 圆形障碍物半径;
Vertex Count 为圆形的顶点数量,数量越高圆弧越平滑。
③PolygonObstacle:多边形障碍物,可以点击添加顶点、拖拽顶点位置,适合复杂障碍物

④EdgeObstacle:边界线(空气墙)障碍物,可以把RVO单位圈在Edge范围内移动
同PolygonObstacle支持点击添加顶点、拖拽顶点位置。
二. 索敌 / 目标搜索
RVOTargeting
基于RVO系统构建的空间划分KDTree 直接应用于目标搜索,全部使用Jobs多线程并行计算,性能强悍,支持超大规模批量目标搜索。
RVO 自带一套不依赖 ECS 的 Targeting 系统,核心结构是 TargetingData。下面字段解释按当前源码实现整理,和 ECS 口径保持一致。
| 字段名 | 作用 | 对应接口 / 说明 |
|---|---|---|
FactionMask | 查询阵营掩码。若与目标阵营有交集(按位与不为 0)则过滤该目标。 | 按位过滤:(query.FactionMask & candidate.FactionMask) != 0 |
LayerMask | 实体层标签(位枚举),供别人的 IgnoredLayerMask 过滤使用。 | 目标层标记 |
IgnoredLayerMask | 查询方忽略层掩码。若目标层命中该掩码,则目标被过滤。 | 按位过滤:(query.IgnoredLayerMask & candidate.LayerMask) != 0 |
VisionRadius | 视野半径。用于 Vision 过滤(FilterVisionRange / WithinVisionRange)。 | Nearest 视野过滤 |
VisionAngle | 视野角度(相对查询前向);>= 360 视为全向。 | Nearest 视野角度过滤 |
EffectRadius | 作用半径。用于 Effect 状态判定、OverlapSphere范围判定,以及 Raycast/SphereCast 射线长度。 | Nearest / OverlapSphere/ Raycast / SphereCast |
EffectAngle | 作用角度。用于 Effect 角度过滤;在 SphereCast 中复用为球体半径。 | Nearest / OverlapSphere/ SphereCast |
MaxTargetCount | 单次查询最多返回目标数(小于等于 0 时直接返回空结果)。 | OverlapSphere/ Raycast / SphereCast |
Priority | 目标优先级。配合 TargetingFilterFlags.PriorityFirst 参与“高优先级优先,优先级相同再比距离”。 | Nearest(Priority 模式) |
CanAcquireTargets | 是否允许该查询方发起索敌。为 false 时查询直接返回空结果。 | 直接字段控制(无专用 Setter) |
CanBeTargeted | 是否允许该实体被锁定。为 false 时会在合法性过滤阶段被剔除。 | 直接字段控制(无专用 Setter) |
| TargetingFilterFlags | 作用 | 命名更新 |
|---|---|---|
None | 只返回最近目标,不附加范围状态。 | - |
FilterVisionRange | 仅保留 Vision 范围内目标。 | FilterSeekRange → FilterVisionRange |
FilterEffectRange | 仅保留 Effect 范围内目标。 | FilterAttackRange → FilterEffectRange |
CheckVisionRange | 返回最近目标,并额外标记是否在 Vision 范围内。 | CheckSeekRange → CheckVisionRange |
CheckEffectRange | 返回最近目标,并额外标记是否在 Effect 范围内。 | CheckAttackRange → CheckEffectRange |
CheckVisionEffectRanges | 同时返回 Vision 和 Effect 两种状态位。 | CheckBothRanges → CheckVisionEffectRanges |
WithinVisionRange | 等价于 FilterVisionRange | CheckEffectRange。 | WithinSeekRange → WithinVisionRange |
PriorityFirst | 优先按目标 TargetingData.Priority 选择,优先级相同再比距离。 | 新增优先级筛选流程(与上面状态/过滤组合受规则约束) |
目标查询接口返回的是 TargetingNearestResults / TargetingMultiResults(内部持有 Native 容器)。结果对象建议使用 using var 自动释放;临时 NativeArray 仍需及时 Dispose()。用法详情见下方示例代码。
1. Nearest 搜索最近目标
获取以点 或 RVO单位坐标为中心,查找最近的一个目标。配合 TargetingFilterFlags 可以只检查范围状态,也可以过滤到 Vision / Effect 范围内的目标,适合普通怪索敌、追击、攻击距离判断。
用法示例代码:
agent.targetingData = new TargetingData(
TargetingLayer.L0, // factionMask: 自身阵营,同阵营会被过滤
TargetingLayer.L1, // layerMask: 自身层标记
TargetingLayer.NONE, // ignoredLayerMask: 额外忽略的目标层
8f, // effectRadius: 作用半径
1, // maxTargetCount: 最大返回目标数
120f, // effectAngle: 作用扇形角度
15f, // visionRadius: 视野半径
180f); // visionAngle: 视野角度
using var nearest = RVOComponent.Instance.Nearest(TargetingFilterFlags.CheckEffectRange);
foreach (var item in nearest)
{
int selfId = item.Query.id;
int targetId = item.Id;
bool inEffectRange = item.IsInEffectRange;
//inEffectRange 为最近目标是否在Effect范围
}
2. OverlapSphere搜索范围内多目标 AOE
获取以点 或 RVO单位坐标为中心,以targetingData.EffectRadius(半径)和EffectAngle(角度)为范围,查找最多返回targetingData.MaxTargetCount个目标。常用于 AOE 技能、范围治疗、爆炸伤害、警戒圈等逻辑。
用法示例代码:
NativeArray<float3> points = new NativeArray<float3>(1, Allocator.TempJob);
points[0] = transform.position;
var queryData = new TargetingData(
TargetingLayer.L0,
TargetingLayer.L1,
TargetingLayer.NONE,
effectRadius: 6f,
maxTargetCount: 8);
using var results = RVOComponent.Instance.OverlapSphere(points, points.Length, queryData, new float3(0f, 0f, 1f));
for (int i = 0; i < results.QueryCount; i++)
{
foreach (int targetId in results.GetTargets(i))
{
// targetId 是命中的 RVOAgent.Id。
}
}
points.Dispose();
3. Raycast 射线检测
作用类似Physics.RaycastAll(),同样支持以点 或 RVO单位两种重载查询方式,射线检测返回多个目标,返回目标个数取决于RVO单位的targetingData.MaxTargetCount 或 查询时指定的 maxTargetCount。它适合直线技能、子弹穿透、激光、点击选中射线等场景。返回数量建议用 CalculateMaxTargetingCountRaycast 估算,避免结果缓冲过小。
用法示例代码:
NativeArray<float3> pointsA = new NativeArray<float3>(1, Allocator.TempJob);
NativeArray<float3> pointsB = new NativeArray<float3>(1, Allocator.TempJob);
pointsA[0] = transform.position;
pointsB[0] = transform.position + transform.forward * 30f;
int maxCount = RVOComponent.CalculateMaxTargetingCountRaycast(pointsA[0], pointsB[0], 0f, 0.5f);
var rayData = new TargetingData(
TargetingLayer.NONE, // factionMask: 查询方阵营掩码(与目标阵营有交集时会被过滤)
TargetingLayer.NONE, // layerMask: 查询方层标记(供对方 ignoredLayerMask 过滤使用)
TargetingLayer.NONE, // ignoredLayerMask: 查询时额外忽略的目标层
effectRadius: 1f, // effectRadius: 作用半径。注意,对于用点查询的重载接口,Raycast射线长度由 pointsA 到 pointsB两点距离决定。
maxTargetCount: maxCount); // maxTargetCount: 单次查询最多返回目标数
using var rayResults = RVOComponent.Instance.Raycast(pointsA, pointsB, pointsA.Length, rayData);
foreach (var query in rayResults)
{
foreach (int targetId in query.Targets)
{
// 命中直线路径上的 RVOAgent.Id。
}
}
pointsA.Dispose();
pointsB.Dispose();
4. SphereCast 球体投射
作用类似Physics.SphereCastAll(),球体投射返回指定数量个目标。SphereCast 可以理解为带宽度的 Raycast,适合冲锋、扇出弹道、长条形 AOE、怪物碰撞检测等场景,可营造技能收割、一次杀伤一大片的快感。RVO 实现中球体半径通过 TargetingData.EffectAngle 传入;点查询重载接口下射线长度由 pointsA/pointsB 决定,不由 effectRadius 决定。
用法示例代码:
NativeArray<float3> pointsA = new NativeArray<float3>(1, Allocator.TempJob);
NativeArray<float3> pointsB = new NativeArray<float3>(1, Allocator.TempJob);
pointsA[0] = transform.position;
pointsB[0] = transform.position + transform.forward * 20f;
float debugRadius = 1.5f;
int maxCount = RVOComponent.CalculateMaxTargetingCountRaycast(pointsA[0], pointsB[0], debugRadius, 0.5f);
var sphereData = new TargetingData(
TargetingLayer.NONE, // factionMask: 查询方阵营掩码(与目标阵营有交集时会被过滤)
TargetingLayer.NONE, // layerMask: 查询方层标记(供对方 ignoredLayerMask 过滤使用)
TargetingLayer.NONE, // ignoredLayerMask: 查询时额外忽略的目标层
effectRadius: 1f, // effectRadius: 作用半径(此点查询 SphereCast 重载的射线长度由 pointsA/pointsB 决定)
maxTargetCount: maxCount, // maxTargetCount: 单次查询最多返回目标数
effectAngle: debugRadius); // effectAngle: SphereCast 球体半径(真正控制“宽度”)
using var sphereResults = RVOComponent.Instance.SphereCast(pointsA, pointsB, pointsA.Length, sphereData);
foreach (var query in sphereResults)
{
foreach (int targetId in query.Targets)
{
// 命中胶囊路径范围内的 RVOAgent.Id。
}
}
pointsA.Dispose();
pointsB.Dispose();
三. 寻路 / 导航
首先要确保RVOComponent面板RuntimeFeatures功能枚举启用了【Path finding】功能。
底层算法是JPS跳点寻路 + 缓存提效,支持PC下10万数量级超低耗时路径查询。开启寻路功能后,添加RVO Obstacles障碍物会自动同步到寻路系统,参与寻路绕点。把寻路获得路径通过rvoAgent.SetMovePath设置后即可按照路径移动。
1. 功能开关与网格配置
寻路基于 JPS 网格,需要启用 RVOFeatures.Pathfinding。源码里 Pathfinding 会自动依赖 MovePath(归一化时自动补齐),因此路径结果可直接下发给 RVOAgent.SetMovePath。
| 参数 | 作用 | 调优方向 |
|---|---|---|
pathfindingGridRowCol | 网格尺寸(列, 行)。 | 越大覆盖范围越广,但内存和计算开销更高。 |
pathfindingCellSize | 网格单元边长(世界单位)。 | 越小路径更贴合障碍,但查询成本更高。 |
pathfindingOrigin | 网格中心点(平面中心)。 | 决定网格覆盖到地图的哪个区域。 |
pathfindingObstacleSafetyInflation | 障碍膨胀安全边距。 | 越大路径越保守,离障碍更远。 |
2. FindPath 批量路径查询
FindPath 支持起点/终点批量查询,返回 pathResults[i](是否成功 + 路径点数)与扁平路径点数组。每个查询占用固定槽位:baseIndex = i * perAgentMaxPathPoints。
RVOComponent.Instance.EnableFeature(RVOFeatures.Pathfinding, true);
const int perAgentMaxPathPoints = 64;
NativeArray<float3> starts = new NativeArray<float3>(1, Allocator.TempJob);
NativeArray<float3> ends = new NativeArray<float3>(1, Allocator.TempJob);
starts[0] = agent.position;
ends[0] = new float3(20f, 0f, 20f);
NativeArray<PathResult> pathResults;
NativeArray<float3> pathPoints = RVOComponent.Instance.FindPath(
starts, // startPoints:批量起点数组(长度 = 查询数量)
ends, // targetPoints:批量终点数组(长度需与 starts 一致)
out pathResults, // out PathResult[]:每个查询的成功状态与路径点数量
perAgentMaxPathPoints // perAgentMaxPathPoints:单个查询可写入的最大路径点数
);
if (pathResults.IsCreated && pathResults.Length > 0 && pathResults[0].Success)
{
int baseIndex = 0 * perAgentMaxPathPoints;
agent.SetMovePath(
pathPoints, // pathPoints:FindPath 返回的扁平路径点数组
baseIndex, // beginIndex:当前查询在扁平数组中的起始偏移
pathResults[0].PathPointCount// pointsCount:当前查询的有效路径点数量
);
}
pathPoints.Dispose();
pathResults.Dispose();
starts.Dispose();
ends.Dispose();
3. FindPathNonAlloc(预分配输出)
高频批量寻路建议使用 FindPathNonAlloc,复用业务侧分配好的缓冲区,减少临时分配。
const int queryCount = 32;
const int perAgentMaxPathPoints = 64;
NativeArray<float3> starts = new NativeArray<float3>(queryCount, Allocator.TempJob);
NativeArray<float3> ends = new NativeArray<float3>(queryCount, Allocator.TempJob);
NativeArray<float3> outPathPoints = new NativeArray<float3>(queryCount * perAgentMaxPathPoints, Allocator.TempJob);
NativeArray<PathResult> outResults = new NativeArray<PathResult>(queryCount, Allocator.TempJob);
RVOComponent.Instance.FindPathNonAlloc(
starts, // startPoints:批量起点数组
ends, // targetPoints:批量终点数组
outPathPoints, // outPathPoints:调用方预分配的扁平路径点输出缓冲
outResults, // outResults:调用方预分配的 PathResult 输出缓冲
perAgentMaxPathPoints // perAgentMaxPathPoints:每条路径占用的固定槽位长度
);
starts.Dispose();
ends.Dispose();
outPathPoints.Dispose();
outResults.Dispose();
4. 路径下发与执行(SetMovePath)
SetMovePath 负责把路径写入 Agent 的 MovePath 缓存。传入 1 个点时会退化为“单目标点移动”;多点路径会逐段推进,并在每段继续参与 RVO 动态避让。
NativeArray<float3> pathPoints = new NativeArray<float3>(3, Allocator.TempJob);
pathPoints[0] = new float3(5f, 0f, 0f);
pathPoints[1] = new float3(5f, 0f, 8f);
pathPoints[2] = new float3(12f, 0f, 8f);
agent.SetMovePath(
pathPoints, // pathPoints:待下发的路径点数组
0, // beginIndex:起始索引(从第 0 个点开始)
pathPoints.Length // pointsCount:本次下发的路径点数量
);
pathPoints.Dispose();
四. Terrain 3D地形
RVOTerrain
首先要确保RVOComponent面板RuntimeFeatures功能枚举启用了【Terrain】功能。
Terrain 功能用于 3D 模式下把 Agent 的高度同步到 Unity Terrain 高度图。开启 RVOFeatures.Terrain 后,可以通过 SetTerrain(Terrain) 或 SetTerrain(Texture2D) 设置高度来源。
可以直接把Terrain拖拽赋值到RVOComponent面板,也可以通过以下运行时代码:
RVOComponent.Instance.EnableFeature(RVOFeatures.Terrain, true);
RVOComponent.Instance.SetTerrain(terrain);
RVOAgent agent = RVOComponent.Instance.AddAgent(transform);
agent.targetPosition = new float3(30f, 0f, 30f);
万人同屏插件附赠Demo
针对万人同屏插件每项重要功能都提供了用法示例demo场景和示例脚本,只需阅读demo就能清楚用法,快速上手。
1. 多模型/动画同屏渲染 Animals.unity
Assets/Plugins/eFunStudio/Samples/Scenes/Demo/Animals.unity 是基础总览场景,用来确认多模型、多动画、多材质同时存在时的渲染与实例化流程是否正常。
重点看三项:共享材质和动画纹理是否生效、单位增量后批渲染是否稳定、CPU 主线程是否避免逐 Animator 更新。
2. 2D Spine性能对比 2DSpinePerformance.unity
Assets/Plugins/eFunStudio/Samples/Scenes/Demo/2DSpinePerformance.unity 用于对比传统 Spine 与 GPU Spine 的运行时开销差异。
重点看 DrawCall、Batches、CPU 动画更新耗时,以及大量角色异步播放动画时的帧稳定性。
【插图占位:2DSpinePerformance.unity 性能对比截图,左侧传统 Spine,右侧 GPU Spine】
3. 3D 动画性能对比 3DPerformance.unity
Assets/Plugins/eFunStudio/Samples/Scenes/Demo/3DPerformance.unity 用于验证 3D SkinnedMesh 角色切到 GPU Anim 后的收益与正确性。
重点检查动画循环/切换、包围盒裁剪、材质 Shader 接入 GPU 动画函数是否完整。
4. LOD 切换验证 LODs.unity
Assets/Plugins/eFunStudio/Samples/Scenes/Demo/LODs.unity 用来验证远近距离下的模型层级切换策略是否稳定。
重点看不同 LOD 是否保持动画资源和材质参数一致,避免切换瞬间出现姿态跳变、材质错乱或透明排序异常。
5. 挂点 / 换装 Attachments.unity
Assets/Plugins/eFunStudio/Samples/Scenes/Demo/Attachments.unity 用来验证骨骼挂点读取与挂载物绑定流程(武器、特效、血条等)。
重点看 AddAttachment、SetAttachmentBoneIndex、RemoveAttachment 运行时切换是否正确,挂点跟随是否稳定。
6. RVO 动态避让 RVODemo.unity
Assets/Plugins/eFunStudio/Samples/Scenes/Demo/RVODemo.unity 用来验证大规模单位局部避让、目标推进与障碍绕行。
建议先小规模确认半径/速度/邻居参数,再放大数量观察 Jobs 调度、KDTree 查询和整体帧耗变化。
7. ECS 索敌 ECSTargetingDemo.unity
Assets/Plugins/eFunStudio/Samples/Scenes/Demo/ECSTargetingDemo.unity 用来验证 ECSGraphics 侧的目标搜索接口:Nearest、OverlapSphere、Raycast、SphereCast。
重点看筛选参数(阵营、层、标签、Vision/Effect 范围)与结果结构体释放时机(using var)是否正确。
【插图占位:ECSTargetingDemo.unity 场景截图,展示 ECS 目标搜索结果】
8. RVO 索敌 RVOTargetingDemo.unity
Assets/Plugins/eFunStudio/Samples/Scenes/Demo/RVOTargetingDemo.unity 用来验证 RVO 侧 Targeting(非 ECS 依赖)与 ECS 接口对仗能力。
重点看 TargetingData 参数组合、TargetingFilterFlags 结果过滤、Raycast/SphereCast 查询行为与预期是否一致。
9. Terrain 地形同步 RVOTerrainDemo.unity
Assets/Plugins/eFunStudio/Samples/Scenes/RVOTerrainDemo.unity 用来验证 3D XZ 模式下 Agent 高度与 Terrain 高度图同步。
重点看坡地移动时的高度贴合、障碍避让连贯性,以及切换 RVOFeatures.Terrain 前后行为是否符合预期。
10. JPS 寻路 PathfindingDemo.unity
Assets/Plugins/eFunStudio/Samples/Scenes/PathfindingDemo.unity 用来验证 JPS 网格寻路、路径输出和 SetMovePath 下发执行链路。
重点看网格参数(pathfindingGridRowCol、pathfindingCellSize、pathfindingOrigin)与动态障碍增删后的路径稳定性。

1万+

被折叠的 条评论
为什么被折叠?



