- 关于ECS
- 新的改变
- 代码示例
- 1. ForEach
- 小结
- ECS 逻辑图表
- 更新计划
- 作者的话
- ECS系列目录
- ECS官方示例1:ForEach
- ECS官方案例2:IJobForEach
- ECS官方案例3:IJobChunk
- ECS官方案例4:SubScene
- ECS官方案例5:SpawnFromMonoBehaviour
- ECS官方案例6:SpawnFromEntity
- ECS官方案例7:SpawnAndRemove
- ECS进阶:FixedTimestepWorkaround
- ECS进阶:Boids
- ECS进阶:场景切换器
- ECS进阶:MegaCity0
- ECS进阶:MegaCity1
- UnityMMO资源整合&服务器部署
- UnityMMO选人流程
- UnityMMO主世界
关于 ECS 我想大家都不陌生了,比较Unity已经强推这么久了,如果你还不知道的话,的确有点孤陋寡闻了(其实我也是才知道的LOL)。我是因为看到MegaCity的视频才了解到ECS的,当时被庞大复杂的场景震撼到了,一直以来Unity官方出的视频都带给我强大的视觉冲击。第一部让我自发宣传的是《Adam》,后面连续出了续集2和3,然后戛然而止,却意犹未尽。Unity不开发游戏真是太可惜了,这样好的创意和功力,无论做什么都会让人拍手称赞的。至于最新的《异教徒》,让我不知道说什么好了,真希望赶快放续集,这个视频创意比亚当三部曲还要赞,千万别只出三部啊! 跑题了,我是发散性思维,超级喜欢跑题。话说回来,ECS虽然现在还没有完善,但是真是值得期待。我陆陆续续在网上找了很多相关的知识来看,越看越觉得有意思,不知不觉中越陷越深了,原本只是看了一个视频而已。 我理解的ECS是相对于OOP的,OOP就是Object Oriented Program啦,面向对象编程,格言就是Everything is Object,一切都是对象,这种设计其实是来源于现实世界的,道法自然嘛,整个虚拟世界都是在模仿现实世界,编程思维和设计也不例外。 面向对象老生常谈了,我觉得没有必要多讲了,就说说ECS吧,E是Entity实体,相对于Object对象的存在;C是Component组件,其实我认为用Data来表达更好一些,C代表的就是数据;S是System系统,相对于原来的Controller控制器。 总体的意思就是用数据来驱动实体的系统,可以这么理解,反正我是这么狭隘的认为的。 ECS相对于OOP有什么优势吗? 国外的大佬写了教学和性能比对的文章Unity* 实体组件系统 (ECS)、C# 作业系统和突发编译器入门水木本源大佬转了过来,看了实在受益匪浅。 说白了,ECS比OOP更快,而且不是一般的快,在大量测试的情况下快了上百倍。
新的改变我总结了ECS带来的一些改变:
- 全新的编程设计 ,原来所有的都是对象,现在所有都是实体,我们原来是用控制器来操控对象,现在用System来操控Component里的数据,而数据通过实体表现出来;
- 大数据将以 大量实体 进行展示,因为速度的突破,不用担心渲染跟不上了,万人同屏不卡顿;
- 迎接 新一代引擎 架构,与ECS相伴的还有Jobs(C#任务系统),Burst编译器,三者构成DOTS(新的游戏开发架构);
- 全新的 编辑器 ,Unity的编辑器会重做,原来那套OOP的编辑器是看不见Entity的,毕竟已经不是对象了,至于做成什么样子,值得期待;
- 新的物理引擎Havok Physics ,Unity为了ECS还特别收购了这家公司;
- 看不见的 ECS底层架构 ,我们现在虽然已经可以使用了,但是底层还在完善,利用 DOTS Data-Oriented Tech Stack ,面向数据的堆栈技术,我们会跑得更快;
啰里啰唆了这么多,不如直接上代码来的直观,前戏虽有必要,但是我的大刀早已饥渴难耐! 0下载Unity编辑器(2019.1.0f1 or 更新的版本),if(已经下载了)continue; 1打开Git Shell输入: git clone https://github.com/Unity-Technologies/EntityComponentSystemSamples.git --recurse
or 点击Unity官方ECS示例下载代码 if(已经下载了)continue; 2用Unity Hub打开官方的项目:ECSSamples 3在Assets目录下找到HelloCube/1. ForEach,并打开ForEach场景
场景里总共有四个游戏对象,呃,等下就不是那么回事儿了!一切都源于一个神奇的脚本,我们来找到它:
- Main Camera ……主摄像机
- Directional Light……光源
- RotatingCube……旋转的方块
- ChildCube……子方块
打开RotatingCube上挂的ConvertToEntity脚本,这就是我们要找的神奇脚本,它将Unity的游戏对象GameObject转化成Entity,从而让游戏运行更加高效,详细的原理看一开始推荐国外大佬那篇文章。 下面我们来看看ConvertToEntity脚本是如何完成这项工作的:
///
/// 将游戏对象转化成ECS的实体Entity
///
[DisallowMultipleComponent]//不允许多组件
public class ConvertToEntity : MonoBehaviour
{
///
/// 转化模式枚举
///
public enum Mode
{
ConvertAndDestroy,//转化并摧毁,该模式下GameObject在被转化成实体Entity后原游戏对象被摧毁
ConvertAndInjectGameObject//转化并注入游戏对象,这个模式不会摧毁原来的游戏对象
}
///
/// 转化模式
///
public Mode ConversionMode;
void Awake()
{
if (World.Active != null)//这里新增了世界的概念,目的是划分OOP世界和ECS世界,游戏对象只存在于面向对象的世界中,实体同理
{
// Root ConvertToEntity is responsible for converting the whole hierarchy
//根节点的转化脚本负责转化整个层级,举个栗子:这个脚本挂在父节点RotatingCube上,那么子节点ChildCube也会被转化成实体
if (transform.parent != null && transform.parent.GetComponentInParent() != null)
return;//一个层级中只能在根节点上挂这个脚本,否则会在这里返回出去
//根据不同的模式调用不同的方法
if (ConversionMode == Mode.ConvertAndDestroy)
ConvertHierarchy(gameObject);
else
ConvertAndInjectOriginal(gameObject);
}
else
{
UnityEngine.Debug.LogWarning("ConvertEntity failed because there was no Active World", this);
}
}
///
/// 注入原始组件
///
/// 实体管理器
/// 实体
/// 变化组件
static void InjectOriginalComponents(EntityManager entityManager, Entity entity, Transform transform)
{
foreach (var com in transform.GetComponents())
{//这里遍历所有组件,并将实体注入到原始组件中,详询底层的代码
if (com is GameObjectEntity || com is ConvertToEntity || com is ComponentDataProxyBase)
continue;
//我们可以不关心更深层次的实现,只需了解这个表层的脚本功能即可,需要使用的时候把这个脚本当作组件来用
entityManager.AddComponentObject(entity, com);
}
}
///
/// 添加递归,把根节点下所有子节点都添加到实体管理器中
///
/// 实体管理器
/// 变化组件
public static void AddRecurse(EntityManager manager, Transform transform)
{
GameObjectEntity.AddToEntityManager(manager, transform.gameObject);
var convert = transform.GetComponent();
if (convert != null && convert.ConversionMode == Mode.ConvertAndInjectGameObject)
return;
foreach (Transform child in transform)
AddRecurse(manager, child);
}
///
/// 注入原始组件
///
/// 源游戏对象世界
/// 模拟世界,实体管理器所操控的实体世界
/// 变化组件
/// 循环递归直到层级中的所有对象都注入完,True:根节点
public static bool InjectOriginalComponents(World srcGameObjectWorld, EntityManager simulationWorld, Transform transform)
{
var convert = transform.GetComponent();
if (convert != null && convert.ConversionMode == Mode.ConvertAndInjectGameObject)
{
var entity = GameObjectConversionUtility.GameObjectToConvertedEntity(srcGameObjectWorld, transform.gameObject);
InjectOriginalComponents(simulationWorld, entity, transform);
transform.parent = null;
return true;
}
for (int i = 0; i
{
var deltaTime = Time.deltaTime;
rotation.Value = math.mul(math.normalize(rotation.Value),
quaternion.AxisAngle(math.up(), rotationSpeed.RadiansPerSecond * deltaTime));
});
}
}
小结
我们来看看这个小案例是如何实现ECS架构的:
ECSScripts继承EntityRotationSpeedAuthoring_ForEachIConvertGameObjectToEntityComponentRotationSpeed_ForEachIComponentDataSystemRotationSpeedSystem_ForEachComponentSystem通过上面这个对照表是不是理清开发思路了?下面通过逻辑图表来看看解耦的模式:
ECS 逻辑图表我大概是这么理解的,建议把Component换成Data,更容易看懂。:
如果喜欢我的文章可以点赞支持一下,谢谢鼓励!如果有什么疑问可以给我留言,有错漏的地方请批评指证! 如果有技术难题需要讨论,可以加入开发者联盟:566189328(付费群)为您提供有限的技术支持,以及,心灵鸡汤! 当然,不需要技术支持也欢迎加入进来,随时可以请我喝咖啡、茶和果汁!( ̄┰ ̄*)
ECS系列目录 ECS官方示例1:ForEach ECS官方案例2:IJobForEach ECS官方案例3:IJobChunk ECS官方案例4:SubScene ECS官方案例5:SpawnFromMonoBehaviour ECS官方案例6:SpawnFromEntity ECS官方案例7:SpawnAndRemove ECS进阶:FixedTimestepWorkaround ECS进阶:Boids ECS进阶:场景切换器 ECS进阶:MegaCity0 ECS进阶:MegaCity1 UnityMMO资源整合&服务器部署 UnityMMO选人流程 UnityMMO主世界