我用的Unity版本是Unity 2019.4.19f1c1 (64-bit)
Unity中的PacketManager中下载Entities,再下载Hybrid Rer用于显示。
Dots入门
1.打印一个数字
先创建一个Component
using System.Collections;
using System.Collections.Generic;
using Unity.Entities;
using UnityEngine;
public struct PrintComponentData : IComponentData
{
public float printData;
}
再创建一个System
using System.Collections;
using System.Collections.Generic;
using Unity.Entities;
using UnityEngine;
public class PrintSystem : ComponentSystem
{
protected override void OnUpdate()
{
Entities.ForEach((ref PrintComponentData printComponentData) =>
{
Debug.Log(printComponentData.printData);
});
}
}
最后创建转换Entity的类
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using Unity.Entities;
public class PrintAuthor : MonoBehaviour, IConvertGameObjectToEntity
{
public float printData;
public void Convert(Entity entity, EntityManager dstManager, GameObjectConversionSystem conversionSystem)
{
dstManager.AddComponentData(entity, new PrintComponentData() { printData = printData });
}
}
在Unity中创建一个空物体,然后在空物体上添加Convert To Entity组件,再把上面写好的PrintAuthor脚本挂载上,运行后就能打印出数值了。
2.默认组件的position修改
using System.Collections;
using System.Collections.Generic;
using Unity.Entities;
using Unity.Mathematics;
using Unity.Transforms;
using UnityEngine;
//[DisableAutoCreation]
public class TranslationSystem : ComponentSystem
{
protected override void OnUpdate()
{
Entities.ForEach((ref Translation translationData) =>
{
translationData.Value = new float3(1, 1, 1);
});
}
}
这样即可修改默认组件的值。[DisableAutoCreation]标签的意思是不启用该系统,可屏蔽不同系统之间的影响。
3.生成预制体
用下面的方法可生成100*100的预制体
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using Unity.Entities;
using Unity.Transforms;
public class PrefabCreator : MonoBehaviour
{
public int interval = 2;
public int num = 100;
public GameObject cube;
// Start is called before the first frame update
void Start()
{
GameObjectConversionSettings settings = GameObjectConversionSettings.FromWorld(World.DefaultGameObjectInjectionWorld,null);
Entity entityPrefab = GameObjectConversionUtility.ConvertGameObjectHierarchy(cube, settings);
EntityManager manager = World.DefaultGameObjectInjectionWorld.EntityManager;
Translation translation = new Translation();
translation.Value.x = -interval;
for (int i = 0; i < num; i++)
{
for (int j = 0; j < num; j++)
{
translation.Value.x += interval;
Entity entity = manager.Instantiate(entityPrefab);
manager.SetComponentData(entity, translation);
}
translation.Value.x = -interval;
translation.Value.y += interval;
}
}
}
World的使用
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using Unity.Entities;
public class TestWorld : MonoBehaviour
{
// Start is called before the first frame update
void Start()
{
//查询
foreach (var item in World.DefaultGameObjectInjectionWorld.Systems)
{
Debug.Log(item.ToString());
}
PrintSystem printSystem = World.DefaultGameObjectInjectionWorld.GetOrCreateSystem<PrintSystem>();
//创建
World world = new World("worldName");
//删除
InitializationSystemGroup group = World.DefaultGameObjectInjectionWorld.GetExistingSystem<InitializationSystemGroup>();
group.RemoveSystemFromUpdateList(printSystem);
printSystem = world.AddSystem<PrintSystem>(printSystem);
//world.DestroySystem(printSystem);
}
}
创建多个实体
//2.从0开始创建Entity
//Entity tempEntity = World.DefaultGameObjectInjectionWorld.EntityManager.
//CreateEntity(typeof(RotationEulerXYZ));
//World.DefaultGameObjectInjectionWorld.EntityManager.Instantiate(tempEntity);
EntityArchetype tempEntityArchetype = World.DefaultGameObjectInjectionWorld.EntityManager.
CreateArchetype(typeof(PrintComponentData1), typeof(RotationEulerXYZ));
//for (int i = 0; i < 100; i++)
//{
// World.DefaultGameObjectInjectionWorld.EntityManager.CreateEntity(tempEntityArchetype);
//}
NativeArray<Entity> tempNativeArray = new NativeArray<Entity>(5, Allocator.Temp);
World.DefaultGameObjectInjectionWorld.EntityManager.CreateEntity(tempEntityArchetype, tempNativeArray);
//World.DefaultGameObjectInjectionWorld.EntityManager.Instantiate(tempEntity, tempNativeArray);
查找实体
//查找
//NativeArray<Entity> tempEntitis = World.DefaultGameObjectInjectionWorld.EntityManager.GetAllEntities();
//foreach (var item in tempEntitis)
//{
// Debug.Log(item.Index);
//}
EntityQuery tempEntityQuery = World.DefaultGameObjectInjectionWorld.EntityManager.
CreateEntityQuery(typeof(PrintComponentData1), typeof(RotationEulerXYZ));
NativeArray<Entity> tempEntities2 = tempEntityQuery.ToEntityArray(Allocator.TempJob);
foreach (var item in tempEntities2)
{
//Debug.Log(item.Index);
}
tempEntities2.Dispose();
删除实体
//删除
//World.DefaultGameObjectInjectionWorld.EntityManager.DestroyEntity(tempEntity);
//World.DefaultGameObjectInjectionWorld.EntityManager.DestroyEntity(tempNativeArray);
//World.DefaultGameObjectInjectionWorld.EntityManager.DestroyEntity(tempEntityQuery);
给实体添加组件
//World.DefaultGameObjectInjectionWorld.EntityManager.AddComponent(tempEntity, typeof(PrintComponentData1));
//World.DefaultGameObjectInjectionWorld.EntityManager.AddComponent<PrintComponentData1>(tempEntity);
//World.DefaultGameObjectInjectionWorld.EntityManager.AddComponent<PrintComponentData1>(tempNativeArray);
//World.DefaultGameObjectInjectionWorld.EntityManager.AddComponent<RotationEulerYXZ>(tempEntityQuery);
//World.DefaultGameObjectInjectionWorld.EntityManager.AddComponents(tempEntity,
//new ComponentTypes(typeof(PrintComponentData1), typeof(RotationEulerYXZ)));
上面这几种方式都可以给实体添加组件,但是不能在添加组件的同时给组件初始化,所以用以下的方法来添加组件并初始化。
World.DefaultGameObjectInjectionWorld.EntityManager.AddComponentData(tempEntity, new PrintComponentData1()
{
printData = 5
});
而添加完组件后,我们想要在unity上直接修改组件的值可以通过一个转换类实现。
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using Unity.Entities;
public class PrintAuthor : MonoBehaviour, IConvertGameObjectToEntity
{
public float printData;
public void Convert(Entity entity, EntityManager dstManager, GameObjectConversionSystem conversionSystem)
{
dstManager.AddComponentData(entity, new PrintComponentData() { printData = printData });
}
}
但是如果每个组件都做一个转换类,那就比较繁琐了。所以unity给我们提供了一个可以直接使用的特性。[GenerateAuthoringComponent]
using System.Collections;
using System.Collections.Generic;
using Unity.Entities;
using UnityEngine;
[GenerateAuthoringComponent]
public struct PrintComponentData : IComponentData
{
public float printData;
}
添加了该特性的组件可以直接拖拽到gameobject上进行赋值操作。
组件的获取与修改
获取组件
RotationEulerXYZ tempRotationEulerXYZ = World.DefaultGameObjectInjectionWorld.EntityManager.GetComponentData<RotationEulerXYZ>(tempEntity);
Debug.Log(tempRotationEulerXYZ);
修改组件的时候或许有人会这样做
tempRotationEulerXYZ.Value = new float3(-1,-1,-1);
但是这样是不行的,因为它是一个struct值类型,而传统的方式可以通过这样进行修改是因为传统方式用的是引用类型,会自动映射上去。所以要用下面的方式对组件中的值进行修改。
World.DefaultGameObjectInjectionWorld.EntityManager.SetComponentData(tempEntity, new RotationEulerXYZ() { Value = new float3(-55, -1, -1) });
实体删除组件
//删除
//World.DefaultGameObjectInjectionWorld.EntityManager.RemoveComponent<RotationEulerXYZ>(tempEntity);
//World.DefaultGameObjectInjectionWorld.EntityManager.RemoveComponent<PrintComponentData1>(tempNativeArray);
//World.DefaultGameObjectInjectionWorld.EntityManager.RemoveComponent<PrintComponentData1>(tempEntityQuery);
//World.DefaultGameObjectInjectionWorld.EntityManager.RemoveComponent<PrintComponentData1>(tempEntity);
//World.DefaultGameObjectInjectionWorld.EntityManager.RemoveComponent(tempEntityQuery,
//new ComponentTypes(typeof(PrintComponentData1), typeof(RotationEulerXYZ)));
共享组件的使用
假如有很多个实体,每个实体上都挂有同一个组件,那么每个组件都要开辟一片内存,这是比较耗内存的,为了节约内存,我们可以使用共享组件。
using System;
using Unity.Entities;
public struct ShareComponent6 : ISharedComponentData
{
public int data;
}
共享组件继承自ISharedComponentData,然后需要一个转换类进行挂载在gameobject上(共享组件无法使用[GenerateAuthoringComponent]特性)
using System.Collections;
using System.Collections.Generic;
using Unity.Entities;
using UnityEngine;
public class Authoring6 : MonoBehaviour, IConvertGameObjectToEntity
{
public void Convert(Entity entity, EntityManager dstManager, GameObjectConversionSystem conversionSystem)
{
dstManager.AddSharedComponentData(entity, new ShareComponent6() { data = 5 });
}
}
接下来是共享组件的增删插改的使用
using System.Collections;
using System.Collections.Generic;
using Unity.Collections;
using Unity.Entities;
using UnityEngine;
public class test6 : MonoBehaviour
{
// Start is called before the first frame update
void Start()
{
//添加
//World.DefaultGameObjectInjectionWorld.EntityManager.AddSharedComponentData()
//修改
NativeArray<Entity> tempEntitis = World.DefaultGameObjectInjectionWorld.EntityManager.GetAllEntities();
//foreach (var item in tempEntitis)
//{
// Debug.Log(item.Index);
//}
//World.DefaultGameObjectInjectionWorld.EntityManager.SetSharedComponentData(tempEntitis[0],
//new ShareComponent6() { data = 10 });
//查找
ShareComponent6 tempShareComponent6 = World.DefaultGameObjectInjectionWorld.EntityManager.GetSharedComponentData<ShareComponent6>(tempEntitis[0]);
Debug.Log(tempShareComponent6);
//删除
//World.DefaultGameObjectInjectionWorld.EntityManager.RemoveComponent<ShareComponent6>(tempEntitis[0]);
}
}
共享组件注意事项:
使用共享组件是非常要注意的,我们要尽量选择那些不会经常变动的组件作为共享组件,因为修改实体的共享组件的值,会导致这个实体被移动到新的块(Chunk)中,这种移动块的操作是比较耗费时间的。(修改普通组件的值是不会改变实体的块的)。共享组件可以节省内存,特别是要创建大量相同物体时,但也不要滥用,否则有可能降低效率。
ShareComponent之间是否相等的判断
using System;
using Unity.Entities;
[Serializable]
public struct ShareComponent6 : ISharedComponentData, IEquatable<ShareComponent6>
{
public int data;
public bool Equals(ShareComponent6 other)
{
return data == other.data;
}
public override int GetHashCode()
{
int tempHash = 0;
tempHash ^= data.GetHashCode();
return tempHash;
}
}
需要继承IEquatable<>接口,并且要添加Equals方法和重写GetHashCode()
ShareComponent6 tempShareComponent6_0 = World.DefaultGameObjectInjectionWorld.EntityManager.
GetSharedComponentData<ShareComponent6>(tempEntitis[0]);
ShareComponent6 tempShareComponent6_1 = World.DefaultGameObjectInjectionWorld.EntityManager.
GetSharedComponentData<ShareComponent6>(tempEntitis[1]);
Debug.Log(tempShareComponent6_0.Equals(tempShareComponent6_1));
通过这样来判断两个共享组件是否相等,如果某个共享组件的值被修改过了,那么它们不相等,反正如果没有修改过,就会相等。
注意事项:
1.对于拥有相同类型组件的实体–原型(Archetype)相同,它们会保存在同一个块中(Chunk),不管是否包含了共享组件。
2.一旦修改实体的共享组件的值,则该实体会被存放到一个新的块中,因为它的共享组件发生了变化,相当于使用了新的共享组件。
3.一旦实体新增了其他任意组件,则该实体会被存放到一个新的块,因为它的原型发生了改变。
状态组件
ECS的使用是没有回调的,所以我们可以使用状态组件,销毁了实体之后,状态组件还是存在的。
using System.Collections;
using System.Collections.Generic;
using Unity.Entities;
using UnityEngine;
[GenerateAuthoringComponent]
public struct StateComponent7 : ISystemStateComponentData
{
public int data;
}
状态组件继承自ISystemStateComponentData
使用:
//查询到状态组件
EntityQuery tempEntityQuery = World.DefaultGameObjectInjectionWorld.EntityManager.
CreateEntityQuery(typeof(StateComponent7));
//销毁状态组件
World.DefaultGameObjectInjectionWorld.EntityManager.DestroyEntity(tempEntityQuery);
//转化为数组
NativeArray<Entity> tempEntities2 = tempEntityQuery.ToEntityArray(Allocator.TempJob);
//设置值
World.DefaultGameObjectInjectionWorld.EntityManager.SetComponentData(tempEntities2[0],
new StateComponent7() {data = 10 });
//这样才能销毁状态组件
World.DefaultGameObjectInjectionWorld.EntityManager.RemoveComponent<StateComponent7>(tempEntities2[0]);
BufferElement(挂同一个组件)
一个实体上是不允许直接挂两个相同的组件的,所以我们需要借助BufferElement来挂同一个组件,BufferElement和我们平常使用的List有点相似。
using System.Collections;
using System.Collections.Generic;
using Unity.Entities;
using UnityEngine;
[GenerateAuthoringComponent]
public struct BufferComponent8 : IBufferElementData
{
public int data;
}
这样可以直接挂上去后有个地方添加多个值。但是如果里面的字段不只是一个data,还有data1,data2之类的,就不能使用[GenerateAuthoringComponent]进行挂载。所以就要使用一个中间转换类进行挂载,如下:
using System.Collections;
using System.Collections.Generic;
using Unity.Entities;
using UnityEngine;
public struct BufferComponent8 : IBufferElementData
{
public int data;
public int data1;
}
using System.Collections;
using System.Collections.Generic;
using Unity.Entities;
using UnityEngine;
public class Authoring8 : MonoBehaviour, IConvertGameObjectToEntity
{
public void Convert(Entity entity, EntityManager dstManager, GameObjectConversionSystem conversionSystem)
{
DynamicBuffer<BufferComponent8> tempBuffer = dstManager.AddBuffer<BufferComponent8>(entity);
tempBuffer.Add(new BufferComponent8() { data = 1, data1 = 3});
tempBuffer.Add(new BufferComponent8() { data = 2, data1 = 2 });
tempBuffer.Add(new BufferComponent8() { data = 3, data1 = 1 });
}
}
这样就可以对同一个值进行多次赋值了。
BufferElement的获取与修改
EntityQuery tempEntityQuery = World.DefaultGameObjectInjectionWorld.EntityManager.
CreateEntityQuery(typeof(BufferComponent8));
NativeArray<Entity> tempEntities1 = tempEntityQuery.ToEntityArray(Allocator.TempJob);
DynamicBuffer<BufferComponent8> tempBuffer = World.DefaultGameObjectInjectionWorld.EntityManager
.GetBuffer<BufferComponent8>(tempEntities1[0]);
tempBuffer.Insert(0, new BufferComponent8() { data = 11, data1 = 12 });
//tempBuffer.RemoveAt(0);
foreach (var item in tempBuffer)
{
Debug.Log(item.data +" " + item.data1);
}
tempEntities1.Dispose();
ChunkComponent介绍(块组件)
using System.Collections;
using System.Collections.Generic;
using Unity.Entities;
using UnityEngine;
public struct ChunkComponent9 : IComponentData
{
public int data;
}
块组件的增删查改:
EntityArchetype tempEntityArchetype = World.DefaultGameObjectInjectionWorld.EntityManager.
CreateArchetype(ComponentType.ChunkComponent(typeof(ChunkComponent9)));
Entity tempEntity = World.DefaultGameObjectInjectionWorld.EntityManager.CreateEntity(tempEntityArchetype);
World.DefaultGameObjectInjectionWorld.EntityManager.CreateEntity(tempEntityArchetype);
ArchetypeChunk tempChunk = World.DefaultGameObjectInjectionWorld.EntityManager.GetChunk(tempEntity);
World.DefaultGameObjectInjectionWorld.EntityManager.SetChunkComponentData(tempChunk,
new ChunkComponent9() { data = 10 });
//World.DefaultGameObjectInjectionWorld.EntityManager.GetChunkComponentData()
//World.DefaultGameObjectInjectionWorld.EntityManager.RemoveChunkComponent()
//World.DefaultGameObjectInjectionWorld.EntityManager.AddChunkComponentData();
//World.DefaultGameObjectInjectionWorld.EntityManager.HasChunkComponent()
块组件和共享组件的区别主要是修改值方面,块组件修该某个值时,所有相同的块组件的值都被修改了,而共享组件只会修改它修改的值,其他使用了同个块组件的值并没有被修改。