107_System 系統
System 系統
ECS或是面向數據設計最主要的目標就是從行為中分理出狀態(State from behaviour)。System是我們定義行為的地方。我們可以在system種利用代碼來創建新的狀態,改變或是刪除已有的狀態。
在Entitas-CSharp中,我們有幾個介面需要實現以標記一個類是system。 ISystem
是最基礎的介面,我們不需要實現。他只是一個用於內部的標記作用(所謂的幽靈協議)。
如果我們需要需要一個持續執行的system,我們需要實現IExecuteSystem
。這個介面只有一個方法void Execute();
。這是我們寫需要每tick都需要執行的代碼的地方。
另外一個systm也是持續執行的是ICleanupSystem
。這個system是為了我們在執行完所有的IExecuteSystem
之後執行的邏輯所設立的。像他名字建議的一樣,你應該放清理(clean up)的代碼到它的void Cleanup();
方法中去。他們都只是介面,你可以有用一個類同時實現這些協議,有時候這樣做從遊戲邏輯上來說非常有意義。
Setup and Teardown 建立與銷毀
通常來說,當我們開始一個遊戲,我們需要先創建初始狀態。這就是為什麼在Entitas-CSharp中我們會有IInitializeSystem
介面了。它有一個void Initialize();
方法用於存儲你遊戲中的初始化邏輯,基本上來說就是創建所有開始遊戲所需要的Entitas和其他狀態。
與IInitializeSystem
相對應的是ITearDownSystem
。這個介面擁有void TearDown();
方法。我們可以將遊戲/關卡/場景(任何符合你使用場景的地方)結束後執行的邏輯放到這裡。
Composing systems 組合系統
知道現在為止,我在這個章節中描述的所有東西都是為了更方便的分離行為代碼的介面。我看見過有些人在項目中使用Entitas而不用Systems。他們實現了自己的命令模式(Command Pattern)。但是如果你想要使用systems這種方式的話,你可能想要將systems組合成一個明確的層級。為了做到這一點,我們提供了一個Sysetms
類,實現了 IInitializeSystem, IExecuteSystem, ICleanupSystem, ITearDownSystem
介面。我們可以Add
一個system到Systems
的實例中,然後當我們調用Systems上的Execute(), Cleanup(), Initialize(), TearDown()
方法,它會調用所有添加了的system的對應方法。Systems
是一個在組合模式中典型的 Parent Node(父節點)。
當我們再MatchOne的例子時,會發現其實我們其實不會直接使用Systems
類:
public class MatchOneSystems : Feature { public MatchOneSystems(Contexts contexts) { // Input Add(new InputSystems(contexts)); // Update Add(new GameBoardSystems(contexts)); Add(new GameStateSystems(contexts)); // Render Add(new ViewSystems(contexts)); // Destroy Add(new DestroySystem(contexts)); }}
在這裡,我們擴展了一個Feature
類。根據我們選擇是否開啟 visual debugging ,會生成一個Feature 類集成Systems
或者DebugSystems
類。VIsual debugging消耗非常多的資源,所以當你在移動設備上運行或者製作產品Build的時候,你應該將VIsual debugging關閉掉。所以Feature就是為了讓我們的代碼更加簡單而創造出來的類。
從上面代碼片段我們可以得知的另一件事是,我們傳入了一個Contexts
類。Contexts
類是另一個為了方便我們引用不同的Context實例而在ntitas-CSharp 中創建出來的類。這個生成的代碼用於每種context類型的實例的獲取器(在appliance章節會有更多關於code generator的內容)。
How do we execute the systems 我們怎麼執行Systems
當我們實現完這些系統並且將他們組合成層級結構之後,我們需要在某處調用 Execute(), Cleanup(), Initialize(), TearDown()
方法。我們通產貴創建一個MonoBehaviour
來做這件事情。如果你不是使用Unity3D的話,就需要自己找一個合適的位置觸發這些方法。我會再次用MatchOne這個例子來告訴你這個MonoBehaviour
大概是什麼樣子的:
using Entitas;using UnityEngine;public class GameController : MonoBehaviour { Systems _systems; void Start() { Random.InitState(42); var contexts = Contexts.sharedInstance; _systems = new MatchOneSystems(contexts); _systems.Initialize(); } void Update() { _systems.Execute(); _systems.Cleanup(); } void OnDestroy() { _systems.TearDown(); }}
持續性的systems是否應該在FixedUpdate
而不是Update
中執行是一個經常出現的問題。這個問題一般是需要你自己做決定。我一般會將system放在Update
中,如果你的的情況需要放在 FixedUpdate
或者甚至LateUpdate
中,最終怎麼做都是取決於你。你甚至還可以更瘋狂一些,擁有多個system層級,有些在Update
中執行,有些在FixedUpdate
中執行,但我不確定這是不是一個好的想法。
How do I implement a typical execute system? 我怎麼實現一個典型的執行系統?
一個執行system會周期性的運行,所以我們通常會做的是:設定一個或多個groups在sysem的構造函數里,然後在Execute
中我們會迭代這些group中的entity,改變他們或者是創造新的Entity。
總的來說說,我們會從context拉去數據並且針對這些數據做一些事情。我們將會在下一個章節了解到在Entitas-CSharp中,還有另一種方式去處理這些數據。
推薦閱讀: