基于Unity2019最新ECS架构开发MMO游戏笔记1
中文版一图读懂ECS
前言
在上一篇基于Unity2019最新ECS架构开发MMO游戏笔记0中讲了官方的第一个案例,通过图文并茂的分析,相信各位大佬已经对ECS有了一定的认识,这一篇会继续分析官方案例。
关于源码,如果有意想深入了解的,请看大鹏的专栏!
官方案例解析2
开始之前的准备工作:
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/2. IJobForEach ,并打开IJobForEach 场景
2. IJobForEach
和第一个案例1. JobForEach中的场景一样,里面仅有四个游戏对象,那么这个案例究竟有啥子不同呢?下面一探究竟:
- Main Camera ……主摄像机
- Directional Light……光源
- RotatingCube……旋转的方块
- ChildCube……子方块
和案例1一样RotatingCube上同样挂了ConvertToEntity脚本,它将Unity的游戏对象GameObject转化成Entity,从而让游戏运行更加高效,脚本的工作原理已经在上一篇讲过了,此处跳过。
RotatingCube上还挂了另外一个脚本RotationSpeedAuthoring_IJobForEach,下面我们来看一下这个脚本:
/// <summary>
/// 这就是ECS中的E了,它将数据交给C
/// </summary>
[RequiresEntityConversion]//必须实体转化
public class RotationSpeedAuthoring_IJobForEach : MonoBehaviour, IConvertGameObjectToEntity
{//继承接口IConvertGameObjectToEntity,并实现Convert
public float DegreesPerSecond = 360;
/// <summary>
/// 这个方法上一篇讲过,通过该方法,实体将数据交给组件储存起来
/// </summary>
/// <param name="entity">实体</param>
/// <param name="dstManager">目标实体管理器</param>
/// <param name="conversionSystem">转化系统</param>
public void Convert(Entity entity, EntityManager dstManager, GameObjectConversionSystem conversionSystem)
{
var data = new RotationSpeed_IJobForEach { RadiansPerSecond = math.radians(DegreesPerSecond) };
dstManager.AddComponentData(entity, data);
}
}
代码和上一篇的脚本RotationSpeedAuthoring_ForEach几乎一毛一样,区别在于IJob关键字上了,我猜后面一定会使用C#的Job系统,来充分利用多线程。按照流程,Entity把数据通过Convert方法传给Component组件了,下面我们来看RotationSpeed_IJobForEach 脚本:
/// <summary>
/// 我啥也不干,就放数据
/// </summary>
[Serializable]
public struct RotationSpeed_IJobForEach : IComponentData
{
public float RadiansPerSecond;
}
这里提一下IComponentData,它就是一个空接口而已,作用只是表面自己是Component的身份,从而让System识别,非常纯粹的一个脚本。下面是这个案例的重点RotationSpeedSystem_IJobForEach:
// This system updates all entities in the scene with both a RotationSpeed_IJobForEach and Rotation component.
/// <summary>
/// 这里利用了Jobs和Burst编译器,它们和ECS共同组成DOTS,使代码运行更加高效
/// </summary>
public class RotationSpeedSystem_IJobForEach : JobComponentSystem
{//通过继承JobComponentSystem来利用Jobs多线程的特性
// Use the [BurstCompile] attribute to compile a job with Burst. You may see significant speed ups, so try it!
[BurstCompile]//使用这个定语来利用Burst编译器,需要在Unity编辑器菜单栏Jobs-》Burst-》Enable Compilition来激活编译器
struct RotationSpeedJob : IJobForEach<Rotation, RotationSpeed_IJobForEach>
{
public float DeltaTime;
// The [ReadOnly] attribute tells the job scheduler that this job will not write to rotSpeedIJobForEach
/// <summary>
/// 该方法会在OnUpdate中每帧执行
/// </summary>
/// <param name="rotation">旋转</param>
/// <param name="rotSpeedIJobForEach">[ReadOnly]定语告诉Jobs任务系统预定器这项任务不需要写入,这样会更快速</param>
public void Execute(ref Rotation rotation, [ReadOnly] ref RotationSpeed_IJobForEach rotSpeedIJobForEach)
{
// Rotate something about its up vector at the speed given by RotationSpeed_IJobForEach.
//Component的数据在这里使用,使方块旋转起来
rotation.Value = math.mul(math.normalize(rotation.Value), quaternion.AxisAngle(math.up(), rotSpeedIJobForEach.RadiansPerSecond * DeltaTime));
}
}
// OnUpdate runs on the main thread.
/// <summary>
/// OnUpdate在主线程上运行,上面写的旋转任务会在这里加入到计划当中
/// </summary>
/// <param name="inputDependencies">输入依赖</param>
/// <returns></returns>
protected override JobHandle OnUpdate(JobHandle inputDependencies)
{
var job = new RotationSpeedJob
{
DeltaTime = Time.deltaTime
};
return job.Schedule(this, inputDependencies);
}
}
和第一个案例大同小异,不过更加先进了,从1到2的过程是循序渐进的,注释还算明了吧?
下面还是按照老规格理清一下思路好了!
小结
我们来对比案例一:
ECS | Scripts | 继承 |
---|---|---|
Entity | RotationSpeedAuthoring_ForEach | IConvertGameObjectToEntity |
Component | RotationSpeed_ForEach | IComponentData |
System | RotationSpeedSystem_ForEach | ComponentSystem |
案例二:
ECS | Scripts | Inherit |
---|---|---|
Entity | RotationSpeedAuthoring_IJobForEach | IConvertGameObjectToEntity |
Component | RotationSpeed_IJobForEach | IComponentData |
System | RotationSpeedSystem_IJobForEach | JobComponentSystem |
通过上面两个对照表是不是理清开发思路了?区别在于System的迭代进化,充分利用了Jobs和Burst编译器,因此会跑得更快!
DOTS 逻辑图表
上面大概表现了几者的关系,我是从表象上这么理解的,至于底层怎么弄的,I don‘t care。流程大体如下:
更新计划
作者的话
如果喜欢我的文章可以点赞支持一下,谢谢鼓励!如果有什么疑问可以给我留言,有错漏的地方请批评指证!
如果有技术难题需要讨论,可以加入开发者联盟:566189328(付费群)为您提供有限的技术支持,以及,心灵鸡汤!
当然,不需要技术支持也欢迎加入进来,随时可以请我喝咖啡、茶和果汁!( ̄┰ ̄*)