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>
    {
        /// 
        /// 中心点及半径数据
        /// 
        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框架学习的源码地址

你可能感兴趣的:(Unity,ECS,Unity)