<< 返回目录

代码框架 & 工具

相比于 FPSSample, DOTS Sample 进行了更加 package 化的更动, 无关乎游戏本体的代码放在了 Unity.Sample.Core 等以 Unity.Sample. 命名的文件夹, 而游戏本体则放在 Assets/Scripts 目录下, 另外, 因为 DOTS 官方鼓吹将运行时和编辑时区别对待, 因此几乎每一个功能都分为Runtime 和 Authrong 两个部分, 比如 Unity.Sample.Core 和 Unity.Sample.Core.Authring, 在编辑器里面用到的东西都能在 xxx.Authring 里找到. 下文中我会将类似 Unity.Sample.Core 和 Unity.Sample.Core.Authroing 这样较为独立的存在称之为一个模块, 而 Assets/Scripts 里面的代码称为游戏代码.

首先简述一下每个模块各自主要都干了些什么.

  • Core: 如名字所暗示, 这时整个程序的核心部分, 几乎所有其他部分都依赖于它:

    • ConfigVars: 用于配置和序列化配置, 许多 FPS 游戏里用于配置游戏画面, 游戏模式, 或者服务器端配置参数这样的功能, 都由它来完成
    • Console: 玩过 CS 之类的游戏都知道游戏里包含一个控制台, 一些高级的命令可以通过控制台而不是图形化 UI 来完成, 这个模块对于高级玩家或者开发人员非常有帮助(尤其是网络驱动的游戏, 经常需要在线上环境进行调试)
    • DebugDisplay, DebugOverlay, GameDebug: 对于网络游戏的开发, 时常需要区分客户端/服务端的日志, 也需要经常在非开发环境查看日志, 该模块可以有效地实现这些功能, 同时还对 Burst 进行了适配, 基本上可以告别原始的 Debug.Log 了.
    • GameTime: 因为需要进行客户端&服务端的同步, Unity 自带的 Time 模块显然无法满足这些需求.
    • PrefabAssetRegistry: 这个模块的作用是将 prefab 的在 asset 数据库里的 guid 以弱引用的方式保存在序列化后的 scene 当中, 以便于后期在运行时可以通过这些引用来拿到 prefab 的引用, 并进行实例化, 这个模块在现阶段算是对 conversion workflow 的 Hack (更多关于这个主题, 可以参考我的这篇译文)
  • Game: 这个模块很容易和 Assets/Scripts 文件夹里的 Game 搞混, 两者其实在功能的角色方面区分不大, 但最大的区别是, Game模块几乎适用于大部分游戏, 而 Assets/Scripts/Game 只针对本游戏, 只需要记住这一点, 就能比较容易理解整体的代码结构逻辑了.
    • Ability: 这个系统和 unreal 的 gameplay 系统 有点类似, 定义了一套可用于玩法开发的数据结构和生命周期(当然也包括网络的同步), 不单单是游戏角色的技能可以基于此来开发, 几乎任何与"机制"相关的内容都可以放到这里来.
    • Animation&Animation Source: 这个部分的代码相对底层和工具属性, 包括利用 Unity.Animation 包创建 Graph 或者 Rig Attach 等, 原因可能是 Unity.Animation 目前还是非常初级的开发阶段, 提供的 API 都还比较原始, 未来也许会把这些内容集成到 Unity.Animation 包当中去. (更大的可能是提供类似 Animator/State Machine 这样更方便设计师/艺术家的编辑器工具)
    • Audio: 和 PrefabAssetRegistry 有点类似, 音频文件的管理也用了类似的方式, 不过要注意的是, 这个和未来可能发布的 DOTS audio 没有一点关系, 可以看成是 HybridAudio for ECS.
    • Effect: 这个同样可以理解为 HybridVisualEffects for ECS. (VisualEffects目前没有提供ECS Api)
    • GameBootStrap: Entities 包有一套默认的初始化方式, netcode 有一套基于它的初始化定制, 而 GameBootStrap 则是基于 netcode 的初始化定制.
    • Health: 这几乎是绝大部分游戏的通用系统了, 要注意不仅仅游戏的"角色" 可以用到, 游戏里的"物品"也可以用到.
    • HitCollider: 基于 Unity.Physics 的碰撞检测模块实现, 游戏里常见的命中检测, 抛射物, 溅射伤害都可以在这里找到.
    • Part: 该模块抽象用于动画系统的"部分"概念, 比如一个角色在不同摄像机距离下需要显示的动画不同(LOD), 那么不同的 LOD Rig, 就是属于该角色的不同 Part. 同样的, 有个类似PrefabAssetRegistry 的 PartRegistry 用于管理动画相关资源(比如 Rig 就以 blob )的动态创建.
    • Item & Inventory: 通常用于物品&背包的抽象, 比如武器(item)属于(own)某个角色, 同时可以位于某个技能栏(inventory)的空槽(slot). 要注意的是, Item 也可以拥有 Part.
    • Player: 抽象了玩家的概念, 其中包含玩家的输入, 相机 和 GameMode相关数据和处理逻辑.
  • BaseCharacter: 与表现层无关的角色相关代码
    • Character: 最重要的自然是 CharacterController, 用以处理角色创建, 地面检测, 碰撞集成等, 相关数据最终可以用于驱动动画系统的运行.
    • Abilities: 要注意的是移动相关的代码并不在 Character 里, 而是放在 Abilities 里, 对于一个角色来讲, 如何移动, 攻击方式, 是否死亡都属于 Ability 的一部分, 这样设计可以创建更加灵活的"英雄"配置系统.
  • Terraformer: 一个具体的角色实例, 基本只包含动画相关代码, 这样做的原因是, 类似守望先锋这样的英雄类游戏, 即便是相同的"能力", 不同英雄的动画集也完全不同. 另外对于复杂的动画控制需求, AnimatorController 这样的方案会很快让耦合的代码和 StateMachine 乱成一锅粥, 因此这里采用了 AnimSource 的概念, 将不同类型的动画控制(比如角色站立时的IK或者在空中的姿态)分别实现, 然后放置到相应的 AnimSource Stack里面, 最终 mix 后进行"渲染".
  • Editor Tools: 一些实用的编辑器工具, 实用到让人纳闷为什么 Unity 默认不支持这些功能. 诸如剪切粘贴 GameObject, 查询 Asset 的Guid或者依赖等, 这些功能可以在 A2 菜单里面找到.

可以看到, 这些近似于 package 的内容使得拿来主义变得更加容易, 甚至不排除未来部分模块将被打造为官方的 package 的可能性.

游戏相关代码(Scripts 目录)

这里包含了游戏相关的代码, 如果你要开发 FPS 游戏, 那么这里的部分内容很容易可以拿来复用, 依然按照目录简述一下主要内容:

  • Game: 玩法和UI相关的内容都在这里.

    • Character: 角色 UI 的内容, 包括血量, 技能等等, 由于目前 UI 模块并不支持 ECS, 因此这里的实现主要是使用 Hybrid 模式.
    • Chat: 聊天模块, 和上面的 Character UI 类似, 也使用 hybrid 模式实现.
    • Core: 包含统计游戏状态的 GameStatistics, 和用于管理 Scene 的 LevelManager.
    • Frontend: 从 FPSSample 里面遗留下来但并未被使用或者 DOTS 化的代码, 比如菜单界面的 UI 相关的内容.
    • GameMode: FPS游戏经常会有各种模式, 比如死斗, 站点, 救援等等, 这里实现了 GameMode 常见的两种模式, 以及计分板, 队伍等逻辑.
    • Movable: 已无用的FPSSample 的遗留代码, 用来实现移动平台.
    • ServerCamera: 已无用的FPSSample 的遗留代码, 用来实现服务端可以观战的摄像头.
    • Teleporter: 传送机制
    • Turret: 无用代码, 用于实现炮台机制.
    • Main: 这是目前最重要的部分, 游戏的初始化和游戏循环在此实现, 前文提到的 GameBootStrap便是调用此处的 Game.cs 来启动游戏的. 同时一个从 FPSSample遗留下来的 GameLoop 概念依然保留了一部分, 因此这部分的实现并不够 DOTS.
  • Networking: 这里大部分的代码都是遗留代码, 相关内容已经转移到 Unity.Netcode 或者Unity.Transport 里, 下面只说说有用的部分.
    • Generated: netcode 生成的代码, 这个目录可以自由指定.
    • V2: 基于 Unity.Netcode 网络相关的代码, 包括RPC 定义, 连接管理等内容.
  • Render: 大部分内容也不再适用, 如果你需要像大多FPS游戏那样允许玩家精细控制画面设定, 那么 (基于 HDRP 的) RenderSetting 会比较有用.
  • Utils: 一些实用代码, 比如窗口控制, 朴素的状态机实现, 循环列表等, 很多内容也属于遗留项.

开发模式

这里先预先介绍一下在 DOTS 下开发的常见编程模式, 这样有助于理解项目具体功能的设计过程.

ComponentSystemGroup

Netcode 覆盖了 entities 默认提供的 SystemGroup, 你可以随需取用他们, 不过更好的办法是按照大的逻辑模块来自行定义所需 SystemGroup, 比如 DOTS Sample 里的 Abilities 模块就定义了如下的 SystemGroup 结构:

[DisableAutoCreation]
public class AbilityUpdateSystemGroup : ManualComponentSystemGroup[UpdateInGroup(typeof(AbilityUpdateSystemGroup))]
[DisableAutoCreation]
public class BehaviourRequestPhase : ManualComponentSystemGroup[UpdateInGroup(typeof(AbilityUpdateSystemGroup))]
[UpdateAfter(typeof(BehaviourRequestPhase))]
[DisableAutoCreation]
public class MovementUpdatePhase : ManualComponentSystemGroup[UpdateInGroup(typeof(AbilityUpdateSystemGroup))]
[UpdateAfter(typeof(MovementUpdatePhase))]
[DisableAutoCreation]
public class MovementResolvePhase : ManualComponentSystemGroup[UpdateInGroup(typeof(AbilityUpdateSystemGroup))]
[UpdateAfter(typeof(MovementResolvePhase))]
[DisableAutoCreation]
public class AbilityPreparePhase : ManualComponentSystemGroup[UpdateInGroup(typeof(AbilityUpdateSystemGroup))]
[UpdateAfter(typeof(AbilityPreparePhase))]
[DisableAutoCreation]
public class AbilityUpdatePhase : ManualComponentSystemGroup

可以清晰的看到代码执行的顺序, 同时以模块化的方式避免使用传统 Unity 的 Script Execution Order 带来的全局耦合和混乱, [DisableAutoCreation]则让你能精细的控制相关系统执行的环境(比如区别对待client/server)

功能模块

以角色移动功能简化后的代码为例来说明功能模块通常的实现方案, 具体的解释附在注释当中:

// 这里的 class 主要是充当命名空间的功能.
// 一个功能模块通常不止一个 ComponentSystem 或者 Data 结构, 这样就避免了他们的名称过长的问题.
public class AbilityMovement
{// 通常移动功能会允许设计师在编辑器里调整移动速度等参数, 需要导出到编辑器的 IComponentData 都命名为 Settings 并支持序列化.[Serializable]public struct Settings : IComponentData{...}// 将 PredictedState 和 InterpolatedState 区别开来有两个目的//   1. netcode 的 ghost 默认设置粒度是基于 IComponentData, 这可以免去//   2. 从名称上可以提示你在实现时要不要增加客户端预测的检测代码.public struct PredictedState : IComponentData{[GhostDefaultField] public LocoState locoState;...// 别忘了 C# 里可以自由地在 struct 添加方法public bool IsOnGround(){...}}public struct InterpolatedState : IComponentData{...}// 多个 System 在代码里的位置相同, 但是执行顺序则由 Group 来确定.[UpdateInGroup(typeof(BehaviourRequestPhase))]// 虽然这里指定了 DisableAutoCreation, 但你在初始化过程并不需要初始化该系统, 而只需要初始化 BehaviourRequestPhase 即可[DisableAutoCreation] [AlwaysSynchronizeSystem]public class IdleUpdate : JobComponentSystem{protected override JobHandle OnUpdate(JobHandle inputDeps){}}[UpdateInGroup(typeof(MovementUpdatePhase))][DisableAutoCreation][AlwaysSynchronizeSystem]public class ActiveUpdate : JobComponentSystem{protected override JobHandle OnUpdate(JobHandle inputDeps){inputDeps.Complete();var updateJob = new UpdateJob{time = GetEntityQuery(ComponentType.ReadOnly<GlobalGameTime>()).GetSingleton<GlobalGameTime>().gameTime,// 尽可能使用不同的 entity 来"存储"数据, 使用 GetComponentDataFromEntity 来访问 entity 之间的 "关系", 这样能有效地拆分耦合的代码.playerControlledStateFromEntity = GetComponentDataFromEntity<PlayerControlled.State>(true),...};// 本模块只需要简单声明 Settings, 引用其他模块的数据结构时则需要使用 "全名"Entities.ForEach((ref Ability.EnabledAbility activeAbility, ref Ability.AbilityStateActive stateActive, ref Settings settings, ref PredictedState predictedState) =>{...}).Run();return default;}}
}

从这个简短的例子就可以看到 ECS 架构的优势所在, 和 MonoBehaviour 相比, 整个过程中, 我完全不用关心我的 GameObject 是谁? 需要如何设置回调函数? 是否有其他代码访问我的类产生副作用? 所有的代码只需要关心两件事: 1. 系统需要读写什么数据 2. 系统的执行顺序是什么

数据的初始化通常交给编辑器端的 GameObject/Prefab, 这和传统的 Unity 开发没什么两样, 这被称为 conversion workflow, 通常你需要编写一个这样的 Authoring Component:

using Unity.Entities;
using UnityEngine;// 现目前的 SubScene Workflow 还不够完善, 当你无法触发自动 Convert 的时候, 可以尝试更改这里的版本号
[ConverterVersion("filod", 20)]
public class AbilityMovementAuthoring : MonoBehaviour, IConvertGameObjectToEntity
{// 这里你可以预设一些数据public AbilityMovement.Settings settings = new AbilityMovement.Settings {playerSpeed = 5,...};public void Convert(Entity entity, EntityManager dstManager, GameObjectConversionSystem conversionSystem){// GameObject 和 Entity 并非一一对应的关系, 你可以自由设计运行时的 Entity 结构 // Authoring 组件的设计则主要考量设计师的方便.dstManager.AddComponentData(entity, new Ability.AbilityTag { Value = Ability.AbilityTagValue.Movement });dstManager.AddComponentData(entity, settings);// 这些数据无需暴露给策划dstManager.AddComponentData(entity, new AbilityMovement.PredictedState());dstManager.AddComponentData(entity, new AbilityMovement.InterpolatedState());
// setName 可以方便在 Entity Debugger 查看和调试, 但是在最终构建中并无用处
#if UNITY_EDITORdstManager.SetName(entity,name);
#endif}
}

在代码结构上 Authoring 组件和 Runtime 组件是完全分离的, 这是因为在构建后的程序里, 你完全用不上Authoring 组件的 Assembly, 因为这些都被存储在 Entities Cache 里了.

至此, DOTSSample 的整体概念已经介绍完毕, 后续文章里, 我会详细介绍重要的模块的设计和实现过程.

unity现代人物含代码动画_深入了解 Unity DOTS Sample (一): 代码框架 amp; 工具 amp; 开发模式相关推荐

  1. 代码逻辑分析_双十一模块 79.34% 的代码是怎样智能生成的

    作为今年阿里经济体前端委员会的四大技术方向之一,前端智能化方向一被提及,就不免有人好奇:前端结合 AI 能做些什么,怎么做,未来会不会对前端产生很大的冲击等等.本篇文章将围绕这些问题,以「设计稿自动生 ...

  2. java 静态代码块_关于Java你不知道的那些事之代码块

    前言 普通代码块:在方法或语句中出现的{},就被称为代码块 静态代码块:静态代码块有且仅加载一次,也就是在这个类被加载至内存的时候 普通代码块和一般语句执行顺序由他们在代码中出现的次序决定,先出现先执 ...

  3. svn如何隐藏代码路径_程序员课堂—如何通过改善代码风格来消灭隐藏bug

    写在前面:一名有三年Android开发经验的女程序员(欢迎大家关注我 ~期待和大家一起交流和学习Android的相关知识) 正如食物腐烂之前,可能会发出异味.当代码存在隐藏问题时,代码也会表现出一些异 ...

  4. 代码逻辑分析_致C++完美主义者:使用Visual Studio新工具分析你的代码

    官宣IntelliSense Code Linter for C++ 在Visual Studio 2019 v16.6 Preview 2中,我们高兴的宣布一项针对C++开发者的新功能:Intell ...

  5. Java源码 JavaWeb开发框架 代码 SSH SSM OA ERP CRM Java项目[Java通用框架源码及开发视频教程]

    Java源码 JavaWeb开发框架 代码 SSH SSM OA ERP CRM Java项目 功能简介: A.代码生成器(开发利器) 生成Java各层次的类和JSP等文件,提高开发效率 B.阿里巴巴 ...

  6. 开发接口文档_更优更稳更好,看文档驱动开发模式在AIMS中的优势

    ​[摘要]程序员常会说:我最讨厌别人写的代码没有文档,我也最讨厌自己需要写文档. 有一个很老的梗: 我最讨厌别人写的代码没有文档,我也最讨厌自己需要写文档. 有这种想法的程序员应该算是一个老鸟了,对于 ...

  7. java ioc是什么意思_什么是IOC?好处是什么?与传统的开发模式有什么区别?

    一.含义 其实就是依赖注入或者控制反转,主要用于: 1.对象创建 2.依赖管理 二.好处 目的就是为了解耦合,使代码具有更高的扩展性和维护性 1.耦合度和扩展性的关系 耦合性越高,加入新功能就需要大量 ...

  8. unity 添加人物控制器行走动画

    效果:上下左右 控制人物行走 1.添加人物模型,(一个双臂张开的稻草人一动不动地矗在那) 2.给人物添加animator组件,并双击编辑 3.拖入人物的站立,行走,跑动等姿势,并建立关联,右键make ...

  9. unity如何往下挖地形_如何在unity地形上挖坑?

    其实在很久以前我就研究过在Unity里如何对模型进行顶点控制的一些东西,并能够实现点筛选和挖坑.只是很大程度上挖坑的精确度取决于模型网格的疏密程度.如果网格顶点过于密集的话,就会影响运算的效率.关于挖 ...

最新文章

  1. 插入u盘计算机未响应,win7系统插入u盘死机怎么办|win7插入u盘无响应的解决方法...
  2. 《预训练周刊》第29期:Swin Transformer V2:扩大容量和分辨率、SimMIM:用于遮蔽图像建模的简单框架...
  3. python tushare获取股票数据_Python 金融: TuShare API 获取股票数据 (1)
  4. 数集合有多少个TOJ(2469)
  5. 技术动态 | 跨句多元关系抽取
  6. Android 开发之旅:深入分析布局文件又是“Hello World!”
  7. CocoStudio1.3 场景编辑器使用
  8. python一键打包32版exe脚本
  9. 【Pix4d精品教程】Pix4d修编正射影像DOM的两种方法案例详解
  10. CausalImpact
  11. 【摘抄】为什么要学C语言
  12. 用稳压管搭建的恒流源电路分析
  13. ADO方法操作数据库
  14. Dima and Salad 01背包变形
  15. 永久免费的数据库防火墙(堡垒机)
  16. 安全多方学习开源框架调研
  17. android仿网易云音乐引导页、仿书旗小说Flutter版、ViewPager切换、爆炸菜单、风扇叶片效果等源码
  18. 全球尺度的高分辨率遥感产品
  19. 微信支付之微信小程序支付
  20. 阿里云 数加 · DataWorks 数据同步

热门文章

  1. FM收音机芯片TEA5767
  2. js获取大小写字母的方法
  3. SQL报错Data truncated for column ‘password‘ at row 8
  4. [演练]使用Expression Blend或代码创建Silverlight时钟
  5. 【报告分享】2021中国互联网医疗内容行业研究报告-亿欧智库(附下载)
  6. java 常量定义_Java中常量定义的几种方式
  7. linux 运行ctl文件_linux journalctl 命令
  8. java 阶乘 最大整数_Java版超大整数阶乘算法-10,0000级
  9. 第十七届全国大学生智能车竞赛西部赛区获奖队伍-盖章
  10. 数据结构基本概念和数据结构类型