ECS
关于实体组件系统(ECS)
ECS是一种编写代码的方式,它专注于您正在解决的实际问题,即构成游戏的数据和行为。
使用ECS,除了能够为游戏设计更好地处理游戏编程之外,还能让您找到利用Unity的C#Job System和Burst
编译器的理想方式,让您充分利用多核处理器。使用ECS,我们正在从以对象为导向的设计转到以数据为导向的设计,这意味着重用代码更为容易,也更易于他人掌握。
GameObject & Component
在ECS之前,是以GameObject & Component这样的模式为主,在GameObject上添加若干个Component,
编写的脚本继承的是MonoBehaviour,包含了数据和定义行为的方法。
public class Demo : MonoBehaviour
{
..
}
而在Pure-ECS中,不继承MonoBehaviour,而选择IComponentData,并且编写的是结构体,IComponentData
是纯粹的ECS类型的组件,意味着他不定义行为,只包含数据。
public struct Demo : IComponentData {
..
}
IComponentData是结构体(struct)而非类(class),所以他们只进行值拷贝而不是引用拷贝。
在ECS模型中,Component(组件)只包含数据。
ComponentSystem 则包含行为,一个 ComponentSystem 更新所有与之组件类型匹配的GameObject。这种模式更加轻量
ECS
传统方式的内存管理是离散式的,即物体和它的组件(Component)并非在同一个内存区段,每次存取都非常耗时。而ECS会确保所有的组件资料(Component
Data)都紧密的连接再一起,这样就能确保存取内存资料时以最快的速度存取。
如上图所示,每个GameObject有着与之对应的Renderer、Rigidbody、Transform、MonoBehaviour等等,而它们都无规律的分散在各个存储块,这意味着读写是及其耗时
创建若干个Entity
如下图,在Scene窗口中,Entity是无法被选中的,在Hierarchy也是找不到所创建的Entity
public static EntityArchetype entityArchetype;
public Entity entity;
public EntityManager entityManager;
public Mesh mesh;
public Material material;
public int numEntities = 5;
[RuntimeInitializeOnLoadMethod(RuntimeInitializeLoadType.BeforeSceneLoad)]
public static void initialization(){
EntityManager entityManager = World.Active.GetOrCreateManager<EntityManager>();
entityArchetype = entityManager.CreateArchetype(
typeof(Position)
);
}
// Use this for initialization
void Start () {
entityManager = World.Active.GetExistingManager<EntityManager>();
for(int i = 0;i<numEntities;i++){
entity = entityManager.CreateEntity(entityArchetype);
entityManager.SetComponentData(entity,new Position{Value = new int3(i*2,0,0)});
entityManager.AddComponentData(entity,new BlockTag{});
entityManager.AddSharedComponentData(entity,new MeshInstanceRenderer{
mesh = mesh,
material = material
});
}
}
关于EntityArchetype
一个EntityArchetype是一个ComponentType构成的独特数组。
EntityManager使用它来对所有使用相同ComponentType的Entity进行分组。
每个Entity的ComponentData都储存于我们称之为内存块(chunk)的地方。ComponentData以流的方式布局,意味着:所有类型为A的Component,都被紧紧地包裹在一个数组里面。紧跟着的是包裹着B类型Component的数组,再紧跟着的是包裹着C类型Component的数组,以此类推。
每个内存块总是链接到一个具体的EntityArchetype上,从而在一个块里的所有Entity都遵循完全相同的内存布局。在遍历Component时,块内Component的内存访问总是完全线性的,并没有浪费加载到缓存行中。这点必须要保证。
ComponentDataArray本质上是一个迭代器,它能够遍历全部与所需Component兼容的EntityArchetype。就访问EntityArchetype来说,就是去遍历所有与它相匹配的块;就访问块来说,就是去遍历所有在那个块里的Entity。
模拟MineCraft地图
下面以模仿《MineCraft》为例子,聊聊Unity中ECS的运用
这里的Batches值在200左右
图中的Entity的数量已经达到5万多个,不过运行起来还算流畅
如果用的GameObejct+MonoBehaviour的方式,
Perlin Noise
perlin noise 具备的特性:
1.连续性,不至于让产生的地图出现断层
2.自然性随机,这也是原因之一
3.唯一性:噪音函数:一个种子随机发生器,以一个整数为参数返回根据这个参数产生的一个随机数,如果两次传进同一个参数则会产生两次相同的数
perlin noise 的原理:
.
.
方块的创建与销毁
下面是创建方块和销毁