簡介
ECS在各種Unity版本上表現都不一樣,官方給的例子如果用Unity2018.3.1打開就會滿處飄紅。坑很多,官方文檔像屎一樣。好在有大神們在,看了很多博客,終於找到一個能用的,完成了一個小demo,十萬個小立方體圍着中間轉,有三種不同的材質,最後跑了70幀,大功告成,可喜可賀。
一開始有點困惑,不過好在之前接觸過StrangeIOC框架,用抽象的概念去理解,很快就明白了。(大部分時間都坑在ECS迷幻的版本更新上了)
ECS 就是Entity Component System的縮寫
-
Entity 實體,只存儲數據。自己寫的時候繼承自IComponentData,
但是這裏面和StrangeIOC的數據接口不一樣,要求數據不能是Class,而必須是Struct.看了別人的博客,說用struct存儲會直接寫入到數據棧中,是一連串緊密的數據結構,節約cpu開銷,增加速度 -
Component 這裏的Component和之前有很大區別,之前Component就是在物體上掛n個monobehavior,ECS框架裏面的組件把Position,Rotation,Scale……能拆的全拆開了,用到哪個再把哪個掛上,再也不用擔心一個遊戲物體掛一萬個Monobehavior
-
System 轉門處理所有的運算邏輯,最好配合JobSystem開多線程,卡頓什麼的再也不存在了。
Entity例子
[Serializable]
public struct MovementData : IComponentData
{//不允許有邏輯和對外依賴
public float theta;
public float3 center;//軸心
public float omega;//角速度
public float radius;//半徑
public float p;
}
除了之前提到的地方,Entity還有一處和以往不一樣,不用vector3而是使用float3,float3也是簡化版的vector3,基本無縫對接到vector3,用起就對了。
Component例子
public class MovementComponent : ComponentDataWrapper<MovementData> {
}
定義的Component包裹着這一組數據,這個在2018.3.1當中變成了ComponentDataProxy,這個組件相當於簡易版的Monobehaivor,需要手動或者用代碼掛到物體上
System例子
public class MovementSystem : JobComponentSystem
{
[BurstCompile]
struct MovementJob : IJobProcessComponentData<Position,Rotation,MovementData>
{
/// <summary>
/// 中心點及半徑數據
/// </summary>
public quaternion targetRotation;
public float deltaTime;
public void Execute(ref Position position, ref Rotation rotation,ref MovementData data)
{
float theta = data.theta+ data.omega* deltaTime;
float x = data.center.x + data.radius* Mathf.Cos(theta)*Mathf.Sin(data.p);
float y = data.center.y+ data.radius *Mathf.Cos(data.p);
float z = data.center.z + data.radius * Mathf.Sin(theta) * Mathf.Sin(data.p);
position = new Position
{
Value = new float3(x, y, z)
};
rotation = new Rotation
{
Value = targetRotation
};
data = new MovementData
{
theta=theta,
center = data.center,
radius = data.radius,
omega = data.omega,
p=data.p
};
}
}
protected override JobHandle OnUpdate(JobHandle inputDeps)
{
MovementJob moveJob = new MovementJob
{
deltaTime = Time.deltaTime,
targetRotation = Quaternion.LookRotation(Camera.main.transform.up)
};
moveJob.deltaTime = Time.deltaTime;
JobHandle jobHandle = moveJob.Schedule(this, inputDeps);
return jobHandle;
}
}
在這裏會遍歷所有Entity,挨個執行程序。因爲會開多線程所以速度會非常快
最後掛一個Monobehaviour掛腳本,執行安裝程序(其實這部分也可以不掛腳本用靜態類來實現,Prefab,Metarial從Resources.Load當中讀取等等,理論上可以實現一行腳本都不掛的跑程序。)
public class GameManager : MonoBehaviour {
public EntityManager entityManager;
public EntityArchetype entityArchetype;
public int createPrefabCout;
public GameObject prefab;
Mesh mesh;
public Material[] materials;
public Transform centerTrans;
// Use this for initialization
void Start () {
mesh = prefab.GetComponent<MeshFilter>().sharedMesh;
entityManager = World.Active.GetOrCreateManager<EntityManager>();
NativeArray<Entity> entities = new NativeArray<Entity>(createPrefabCout, Allocator.Temp);
entityManager.Instantiate(prefab, entities);
for (int i = 0; i < createPrefabCout; i++)
{
//設置組件
entityManager.SetComponentData(entities[i], new Position { Value = UnityEngine.Random.insideUnitSphere * 100 });
entityManager.SetComponentData(entities[i], new Rotation { Value = quaternion.identity });
float randRadius = UnityEngine.Random.Range(2, 1000);
float randOmega = UnityEngine.Random.Range(-10, 10);
float randTheta = UnityEngine.Random.Range(0, 360);
float randp= UnityEngine.Random.Range(0, 360);
entityManager.SetComponentData(entities[i], new MovementData
{
center = centerTrans.position,
omega = randOmega,
radius = randRadius,
theta = randTheta,
p=randp
});
int colorIndex = UnityEngine.Random.Range(0, 3);
Debug.Log(colorIndex);
//添加並設置組件
entityManager.AddSharedComponentData(entities[i], new MeshInstanceRenderer
{
mesh = this.mesh,
material = this.materials[colorIndex],
});
}
entities.Dispose();
}
}
最終結果:
幀率:75左右,相當不錯
生成的片有10w個。
源碼地址:ECS框架學習的源碼地址