传统OOP下的MonoBehaviour/GameObject模式, 可以非常方便的为创作游戏编写代码, 但是往往在后期会使得代码难以阅读, 维护, 优化, 游戏开销大而性能低, 这是由一系列因素导致的:
ESC(Entity-Component-System)是unity中DOTS(Data-Oriented Tech Stack)的核心(还有Burst Compile和Job System), 分为三个主要部分:
作为取代GameObject/Component的模式, 其模式遵循组合优于继承原则, 游戏内的每一个基本单元都是一个Entity, 每个Entity又是由一个或者多个Component构成, 每个Component仅仅包含代表其特性的数据(即Component中没有任何方法). System是来处理具有一个或多个Component组件的Entity集合的工具, 只拥有行为(即在System中没有任何数据).
Entity和Component是一对多的关系, Entity拥有怎样的能力, 完全取决于有哪些Component, 通过动态添加或者删除Component, 可以在运行时改变Entity的行为.
class Rotator : MonoBehaviour {
public float RadiansPerSecond;
public override void Update() {
transform.rotation *= Quatern.AngleAxix(Time.deltaTime * RadiansPerSecond, Vector3.up);
}
}
继承自MonoBehabiour的GameObject即包含了数据(数据域), 又包含了行为(Update).
// RotationSpeed.cs
using System;
using Unity.Entities;
[Serializable]
public struct RotationSpeed : IComponentData {
public float RadiansPerSecond;
}
// RotationSpeedProxy.cs
using Unity.Entities;
using Unity.Mathematics;
using UnityEngine;
[RequiresEntityConversion]
public class RotationSpeedProxy : MonoBehaviour, IConvertGameObjectToEntity {
public float DegreesPerSecond;
public void Convert(Entity entity, EntityManager dstManager, GameObjectConversionSystem conversionSystem) {
var data = new RotationSpeed {
RadiansPerSecond = math.radians(DegreesPerSecond)
};
dstManager.AddComponentData(entity, data);
}
}
// RotationSpeedSystem.cs
using Unity.Burst;
using Unity.Collections;
using Unity.Entities;
using Unity.Jobs;
using Unity.Mathematics;
using Unity.Transforms;
using UnityEngine;
public class RotationSpeedSystem : JobComponentSystem {
private ComponentGroup _componentGroup;
protected override void OnCreateManager() {
_componentGroup = GetComponentGroup(typeof(Rotation), ComponentType.ReadOnly<RotationSpeed>());
}
[BurstCompile]
private struct RotationSpeedJob : IJobChunk {
public float DeltaTime;
public ArchetypeChunkComponentType<Rotation> RotationType;
[ReadOnly] public ArchetypeChunkComponentType<RotationSpeed> RotationSpeedType;
public void Execute(ArchetypeChunk chunk, int chunkIndex, int firstEntityIndex) {
var chunkRotations = chunk.GetNativeArray(RotationType);
var chunkRotationSpeeds = chunk.GetNativeArray(RotationSpeedType);
for (var i = 0; i < chunk.Count; i++) {
var rotation = chunkRotations[i];
var rotationSpeed = chunkRotationSpeeds[i];
chunkRotations[i] = new Rotation {
Value = math.mul(math.normalize(rotation.Value),
quaternion.AxisAngle(math.up(), rotationSpeed.RadiansPerSecond * DeltaTime))
};
}
}
}
protected override JobHandle OnUpdate(JobHandle inputDeps) {
var rotationType = GetArchetypeChunkComponentType<Rotation>();
var rotationSpeedType = GetArchetypeChunkComponentType<RotationSpeed>();
var job = new RotationSpeedJob {
DeltaTime = Time.deltaTime,
RotationType = rotationType,
RotationSpeedType = rotationSpeedType
};
return job.Schedule(_componentGroup, inputDeps);
}
}
我们可以看到ECS的工作模式: