ECS框架學習

簡介

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框架學習的源碼地址

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章