洪流學堂,讓你快人幾步。你好,我是跟着大智學Unity的萌新,我叫小新,最近在探索DOTS。
由于ECS概念過于複雜,還是看看如何Code吧, 咱們先來看下如何編寫一個最簡單的ECS代碼。
ECS中的Hello World
這就又要從Hello World說起了。
首先需要把DOTS的環境搭好。本課程最開頭講了如何安裝Entities包,如果還沒安裝好需要去複習一下。
DOTS開發101
這個Hello World需要做什麼呢?咱們用ECS來控制一個Cube的旋轉。
ECS咱們已經知道了分三個部分,咱們來看看這三個部分如何建立。
1、E:在場景中建立一個Cube
按照和之前一樣的方法,在場景中建立一個Cube物體。
但這樣并不是ECS中的Entity,可以使用一個腳本
ConvertToEntity
,将GameOjbect轉換成Entity。

這個腳本有一個屬性:ConversionMode:
- Convert And Destroy:将GameObject轉換成Entities,并且銷毀GameObject。
- Convert And Inject GameObject:将GameObject轉換成Entities,保留GameObject。
咱們在這選擇Convert And Destroy。
這樣咱們就有了一個E,這時候如果你運作呢,你就會發現場景裡有一個Cube,但是在Hierarchy中看不到這個Cube。
那如何檢視這個Cube上面的資訊呢?這時候需要使用Entity Debugger。打開方式是:Unity菜單欄
Window > Analysis > Entitiy Debugger
。
2、C:建立Component
下面來建立Component,這個Component和原來MonoBehaviour繼承的Component不是一個東西。
ECS中的Component需要使用一個結構體,實作
IComponentData
接口。如下代碼:
[GenerateAuthoringComponent]
public struct RotationSpeed_ForEach : IComponentData
{
// 旋轉的速度
public float RadiansPerSecond;
}
這個結構體中定義了一個float類型的資料,代表旋轉速度。
那上面的那個
[GenerateAuthoringComponent]
是做什麼的呢?
正常情況下,ECS中的Component并不能直接拖到物體上跟物體綁定。但是加了
[GenerateAuthoringComponent]
屬性後,可以将這個Component拖到GameObject上,并在腳本
ConvertToEntity
轉換時,自動将Component和轉換出來的Entities綁定。
現在寫好了就把這個RotationSpeed_ForEach元件添加到Cube上面吧。
3、S:用System控制Cube旋轉
using Unity.Entities;
using Unity.Mathematics;
using Unity.Transforms;
// 這個系統會更新所有擁有RotationSpeed_ForEach和Rotation元件的實體
public class RotationSpeedSystem_ForEach : SystemBase
{
// OnUpdate在主線程上執行
protected override void OnUpdate()
{
float deltaTime = Time.DeltaTime;
// 排程job來讓cube進行旋轉
Entities
.WithName("RotationSpeedSystem_ForEach")
.ForEach((ref Rotation rotation, in RotationSpeed_ForEach rotationSpeed) =>
{
rotation.Value = math.mul(
math.normalize(rotation.Value),
quaternion.AxisAngle(math.up(), rotationSpeed.RadiansPerSecond * deltaTime));
})
.ScheduleParallel();
}
}
1、首先注意命名空間的引用,在ECS中并沒有引用比如
UnityEngine
這種在Mono中常見的命名空間。
通常引用的命名空間就是這三個:
using Unity.Entities;
using Unity.Mathematics;
using Unity.Transforms;
2、實作一個class,繼承SystemBase,然後重寫父類SystemBase裡的
OnUpdate
方法。
OnUpdate
方法和Mono中的Update方法類似,都是每幀執行一次,然們可以在裡面做更新的操作。
3、
float deltaTime = Time.DeltaTime;
這句代碼用來擷取目前幀的時間,但是要注意這裡面的Time并不是
UnityEngine.Time
,而是父類中定義的的Time。一定不要用混了。
4、Entities.WithName僅僅是調試使用,調試資訊或者Profiler中會顯示這個名字,友善你找到對應的代碼。
5、核心代碼是Entities.ForEach中的代碼,ForEach的參數是一個lambda匿名方法。其中ref的參數表示會寫入,in的參數表示是隻讀的參數。注意這些參數的修飾一定要準确使用,這樣Job才能正确的判斷每種資料的用途。
ECS中有實體查詢(entity query),可以根據傳入的參數類型,找到所有同時擁有這些Component的實體。比如上面代碼就會找到同時擁有Rotation和RotationSpeed_ForEach元件的所有實體。
6、lambda中的方法執行具體的内容。在這需要**注意需要使用新的數學庫
Unity.Mathematics
**而不是原來的
UnityEngine.Mathf
。上面代碼中是用原來的旋轉,乘以需要旋轉的四元數,得到旋轉後的結果。
咱們場景中隻有一個Cube,是以查詢到的Entity就隻有一個。如果有多個Entity都符合條件,那麼這個System就可以同時處理這些Entity。
總結
好了,到這咱們的ECS Hello World就完成了,但是其中很多代碼為什麼這麼寫,以及背後的原理都沒有講清楚。後面咱們詳細拆解一下每一部分代碼,掌握ECS的核心。
擴充閱讀
【擴充學習】在洪流學堂公衆号回複 DOTS
可以閱讀本系列所有文章,更有視訊教程等着你!
呼~ 今天小新絮絮叨叨的真是夠夠的了。沒講清楚的地方歡迎評論,咱們一起探索。
我是大智(微信:zhz11235),你的技術探路者,下次見!
别走!點贊,收藏哦!
好,你可以走了。