【Unity万人同屏插件 重磅更新】使用手册 保姆级教程 GPU动画 GPU Spine 10万人同屏 渲染 移动/导航/寻路 索敌/目标查找/碰撞检测/射线检测 全能方案

插件简介

【Unity万人同屏插件】是Unity通用性能优化插件,超大规模渲染、寻路移动、索敌碰撞检测等全方位性能瓶颈解决方案,支持Animator、Animation、Spine转为GPU动画prefab,并支持多线程合批渲染大幅提升性能, 兼容Unity全平台(含微信/抖音小游戏)。

方案覆盖了游戏开发中大多数性能热点,打破技术瓶颈,传统开发模式获得极致性能。

新手友好简单易用无ECS技术门槛传统开发方式,也适用于旧项目免重构性能提升。

十万人战斗实录

【Unity万人同屏插件】官网介绍:https://efunstudio.cn/unitymasskithttps://efunstudio.cn/unitymasskit

【性能/功能测试demo】下载:PC 安卓demo下载https://assets.efunstudio.cn/SharedFiles/

【WebGL性能测试demo】WebGL性能测试https://efunstudio.cn/web-demo/

WebGL实战demohttps://efunstudio.cn/web-demo2/

主要功能模块:

模块名称功能

GPUAnimation

AnimatorAnimationSpine 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)性能
原始 Animator15.1466.031.00x(基线)
GPU Animation130.077.698.59x
ECS Graphics + GPU Animation1183.650.8478.18x
2D动画测试FPS帧耗时(ms)性能
原始 Spine11.8584.361.00x(基线)
GPU Spine142.697.0112.04x
ECS Graphics + GPU Spine1434.620.70121.06x

插件用法

GPUAnimation

GPU动画是万人同屏插件的基石,把Animator/Animation 和 Spine 动画从CPU计算 完全 转变成GPU计算,从而完美支持合批渲染。

AnimatorAnimationSpine 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 Spine构建配置
创建3D GPU动画构建配置

二. GPU动画Builder配置

创建完GPU动画配置文件后,可在配置文件中设置各项转换参数以满足GPU动画不同需求。

1. GPU Spine (2D)

GPU Spine插件用法视频教程https://www.bilibili.com/video/BV1GZ5t6MEqN/

GPUSpine

直接解析SkeletonDataAsset 精准还原动画、特效。除基本骨骼动画帧外,还额外支持 颜色/透明度 、Deform网格变形、Active显隐关键帧。

GPUSpine完美还原Spine动画、特效

如下图所示,配置文件会自动根据Skeleton文件初始化,每项配置都有中 / 英 双语展示:

合并材质:支持把Spine的材质、贴图合并成一个材质/图集,优化成一个DrawCall,并且单材质支持Normal 和 Addictive两种常用Blend混合模式,既优化DrawCall 又兼顾混合效果。 

动画/Clips:默认会把Spine文件所有动画添加进列表,根据也许需求 拖拽 排放动画索引顺序即可。支持为每个动画设置 速度、是否循环播放。

记录挂载点:点击 '+' 添加挂点Slots,在下拉列表中选择Skeleton的挂点,列表中的挂点会记录到GPU动画数据,用于把武器挂载到挂点上 或 运行时实时获取挂点位置/旋转。

构建按钮: ‘构建当前Skin’ 会只把当前选中的皮肤名称 构建成GPU Spine;‘构建全部Skins’ 会把Skeleton所有皮肤构建成GPU Spine.

GPUSpine配置界面
2. GPU Anim (3D)

GPU Anim插件用法视频教程https://www.bilibili.com/video/BV17Q5u68EQe/

GPUAnim

如下图所示,对于3D GPU动画,工具提供了全方位的功能需求

GPUAnim配置界面

进阶用法:

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 分组合并Mesh、Material
  • 记录挂载点

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 侧常用节点名是 GPUAnimGPUAnimBlendGPUAnimAttachGPUAnimAttachBlendGPUAnimAttachStaticGPUAnimAttachBlendStatic;2D 侧常用节点名是 GPUSpineGPUSpineBlendGPUSpineAttachGPUSpineAttachBlendGPUSpineAttachStaticGPUSpineAttachBlendStatic

ASE自定义GPU动画shader
ShaderGraph自定义GPU动画shader

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 读取当前 _ClipStateGetGPUAnimClipInfo 读取指定动画片段信息,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 固定单目标。OverlapSphereRaycast / 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 | CheckEffectRangeWithinSeekRange → 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 用来验证骨骼挂点读取与挂载物绑定流程(武器、特效、血条等)。

重点看 AddAttachmentSetAttachmentBoneIndexRemoveAttachment 运行时切换是否正确,挂点跟随是否稳定。

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 侧的目标搜索接口:NearestOverlapSphereRaycastSphereCast

重点看筛选参数(阵营、层、标签、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 下发执行链路。

重点看网格参数(pathfindingGridRowColpathfindingCellSizepathfindingOrigin)与动态障碍增删后的路径稳定性。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值