ECS is a data oriented programming idea. Entitas is an excellent framework under this idea. It is written according to the rules of the framework. When the program scale is relatively large, it is also easy to expand and maintain the code. It is suitable for people who are object-oriented programming but are not familiar with design patterns and do not want to learn design patterns, but it is a completely different development concept from object-oriented programming, Therefore, even OOP programmers who have written for several years will be confused when they come into contact with entitas for a period of time. This is generally because online tutorials generally do not clearly explain the specific contents of entitas.
Entitas is a constraint framework in terms of writing. It does not integrate common functional modules, and there are few commercial ones. It is also written in ECS and inherits some common mature functional modules. The framework used more in commercial companies is ET framework. Both can use the same set of code on the server and client.
According to international practice, here is the first hello world tutorial written with Entitas according to the elementary article of Entitas tutorial of siki college. In order to better use the framework, there should be records of other demos of the tutorial in the follow-up to record their understanding and facilitate review.
In Unity, the overall development idea of Entitas is that any component (C) is combined into an entity (E), and the system (S) is triggered by some changes of these entities to initiate some operations, or operate these entities when necessary when the program is running, such as the beginning stage of the game, the running time of each frame of the game, the end stage of some Unity scenes, etc.
code
First look at the code in the whole HelloWorld
[Game] public class LogComponent : IComponent { public string message; }
public class LogSystem : ReactiveSystem<GameEntity> { public LogSystem(Contexts context) : base(context.game) { } protected override ICollector<GameEntity> GetTrigger(IContext<GameEntity> context) { return context.CreateCollector(GameMatcher.Log); } protected override bool Filter(GameEntity entity) { return true; } protected override void Execute(List<GameEntity> entities) { foreach (GameEntity entity in entities) { Debug.Log(entity.log.message); } } }
public class InitSystem : IInitializeSystem { private readonly GameContext _gameContext; public InitSystem(Contexts contexts) { _gameContext = contexts.game; } public void Initialize() { _gameContext.CreateEntity().AddLog("Hello World"); } }
public class AddGameSystem : Feature { public AddGameSystem(Contexts contexts) : base("AddGameSystem") { Add(new InitSystem(contexts)); Add(new LogSystem(contexts)); } }
public class GameController : MonoBehaviour { private Systems _systems; // Start is called before the first frame update void Start() { var context = Contexts.sharedInstance; _systems = new Feature("Systems").Add(new AddGameSystem(context)); _systems.Initialize(); } // Update is called once per frame void Update() { _systems.Execute(); _systems.Cleanup(); } }
Component
C in ECS is used to be called Component. The code of Component part is purely a collection of data, excluding data processing logic. The idea is to separate data from performance, which is a recognized development idea in the development community. The Component part in HelloWorld is as follows:
using System.Collections; using System.Collections.Generic; using Entitas; using UnityEngine; [Game] public class LogComponent : IComponent { public string message; }
Entity
E and Entity in ECS represent an Entity, which is similar to the GameObject concept of Unity. They are empty. Because components are mounted, they represent different objects. In the Demo of HelloWorld, an Entity is created and a component LogComponent is added to it.
Context
The Chinese name is used to be called context. Context is a large container in Entitas, which contains a large number of entities
In the default setting, there are only two contexts, GameContext and InputContext
In the code of Component
The tag [Game] indicates that this Component only exists in the Context of Game, and the Entity entity with this Component cannot be found in the Context of Input.
Note that the prompt of Game will not appear in the brackets until you click the green button Generate
System
S in ECS is the logic processing part. The operation object in the System is Entity, which performs some logical processing operations on a class of entities.
Here is the first System
using System.Collections; using System.Collections.Generic; using Entitas; using UnityEngine; public class LogSystem : ReactiveSystem<GameEntity> { public LogSystem(Contexts context) : base(context.game) { } protected override ICollector<GameEntity> GetTrigger(IContext<GameEntity> context) { return context.CreateCollector(GameMatcher.Log); } protected override bool Filter(GameEntity entity) { return entity.hasLog; } protected override void Execute(List<GameEntity> entities) { foreach (GameEntity entity in entities) { Debug.Log(entity.log.message); } } }
This is a ReactiveSystem. ReactiveSystem means that it will respond to the occurrence of some events of interest. The events of interest are written in the GetTrigger function. When some entities have the events represented in GetTrigger, the System will be triggered, and then the System will use the filter function to filter the conditions of the entities that have this event again, Filter out the non-conforming entities, and then put the filtered entities into the Execute function to perform the operation.
start
public class LogSystem : ReactiveSystem<GameEntity> { public LogSystem(Contexts context) : base(context.game) { }
It's a fixed way of writing,
GameEntity corresponds to context Game, because GameEntity is only in context Game exists
GameEntity and context will not appear until you click Generate Game generation,
GetTrigger
protected override ICollector<GameEntity> GetTrigger(IContext<GameEntity> context) { return context.CreateCollector(GameMatcher.Log); }
here
context.CreateCollector(GameMatcher.Log);
GetTrigger is a trigger condition function about the change of components, which means that the specified operation of a specified component will attract the attention of the System
This indicates that all entities related to Log component events will be brought into the System, including adding Log components, removing components, replacing component contents, etc.
Filter
protected override bool Filter(GameEntity entity) { return entity.hasLog; }
This seems to be the same as the judgment in GetTrigger. In fact, the judgment logic in the Filter function is more biased towards business logic. For example, some condition judgments. Return true in Filter can also print HelloWorld, because the System will also be triggered when removing components. If return true is written in this way, the function access in the Execute function will report null and error.
. hasLog is the code automatically generated by the component, which represents the ownership of a component, of course hasLog is only useful for GameEntity, because GameEntity only exists in the Context of Game, and the component Log is declared at the time of definition, so it is. I'll meet all kinds of people in the future hasXXX. All the same.
Entitas can be written in such a way that each System can do only one thing and each System can do only one thing. It is easy to replace the System. Cancel the registration of the original System and add it. Under the same conditions, other systems with different processing can be used.
protected override void Execute(List<GameEntity> entities) { foreach (GameEntity entity in entities) { Debug.Log(entity.log.message); } }
After the entity passes the screening of the above two functions, it will be added to this function,
Here, we traverse all the entities that pass the filter and print the information of their log components
Here is the second System
using System.Collections; using System.Collections.Generic; using Entitas; using UnityEngine; public class InitSystem : IInitializeSystem { private readonly GameContext _gameContext; public InitSystem(Contexts contexts) { _gameContext = contexts.game; } public void Initialize() { _gameContext.CreateEntity().AddLog("Hello World"); } }
InitSystem is called when the game is just running. Here, find the GameContext and create an entity under it, and then add the Log component. Because the Log component is under the GameContext, this should be done. After the Initialize() function is executed, the LogSystem GetTrigger will be triggered, and then the GetTrigger will Execute its Filter if it passes, and then Execute if it passes.
There are five types of systems in Entitas,
->. Iinitializesystem is called only once during initialization. The initialization implementation can be written in the Initialize method.
->. Iexecutesystem executes once per frame. The execution logic can be written in the Execute method.
->. Icleanupsystem will execute once per frame after other systems are completed. Recycling logic can be written in the Cleanup method.
->. Reactivesystem will Execute once when the Group changes. The execution logic can be written in the Execute method.
->The feature will integrate and create the above systems.
If other types of are encountered in use, please record in detail.
beginning
Only these are not enough. There is no code inside the framework to automatically start the whole framework with the game running.
You also need to add a Feature
public class AddGameSystem : Feature { public AddGameSystem(Contexts contexts) : base("AddGameSystem") { Add(new LogSystem(contexts)); Add(new InitSystem(contexts)); } }
Feature is also a System. It is a kind of System written in the framework. It is regarded as a collection of a kind of System. It mainly adds various types of systems required. The addition of feature is a fixed writing method. All kinds of projects written with Entitas will have this script as the general System summary registration. At present, the order of addition has no effect.
Finally, create a script to associate the Entitas framework with Unity
using System.Collections; using System.Collections.Generic; using Entitas; using UnityEngine; public class GameController : MonoBehaviour { private Systems _systems; // Start is called before the first frame update void Start() { var context = Contexts.sharedInstance; _systems = new Feature("Systems").Add(new AddGameSystem(context)); _systems.Initialize(); } // Update is called once per frame void Update() { _systems.Execute(); _systems.Cleanup(); } }
This association script is also the inherent writing method of Entitas framework, which binds the System in Entitas to the life cycle. All kinds of projects written with Entitas will have this script as an association. The only difference is_ systems = new Feature("Systems").Add(new AddGameSystem(context)); In this sentence, several features may be added when the project is large
reference resources
Entitas in-depth study (1): environment installation and HelloWorld
Unity mobile game actual combat: SLG from 0 - ECS combat (I) ECS design idea
Unity mobile game actual combat: SLG from 0 - ECS combat (II) Entitas plug-in
Unity mobile game actual combat: SLG from 0 - ECS combat (III) separation of logic and performance
Unity mobile game actual combat: SLG from 0 - ECS combat (IV) actual combat ECS architecture and optimization
RomanZhu/Endless-Runner-Entitas-ECS