-
Notifications
You must be signed in to change notification settings - Fork 77
Migration to Actors 2020.06
This is a guide to help you migrate to the new version of Actors (2020.06 and later). Please note that it may be incomplete. If you didn't find something then feel free to ask on the Discord server.
The old version of Actors was built around the service locator, Toolbox. It provided all the API related to the global game state. It's a simple approach but doesn't play well when your game has several states that could work together. This naturally leads to bad multi-scene support.
The new version of Actors was designed around having independent game states called layers. You can have one or two or how many you like layers working at once and they treat each other nicely. Of course, everything has its price and the price is that your API becomes layer-specific and you have to somehow provide the layer info to your code.
| Starters | Layers |
|---|---|
| You can have only one starter active. | You can have many layers at the same time. |
| You can use starter only on the main scene. | You may have a layer per scene. |
| Starters are only providers to the global game state. They don't hold any logic. | The layer is a game state and holds all services and framework related API for the state. There are no global states of the game at all. |
You directly call API like Entity.Create
|
You call layer API like Layer.Entity.Create
|
| When the main scene is reloaded or changed the global state of the game is cleaned. Removing/Adding sub-scenes doesn't affect the global state. | The specific layer is cleaned when the related scene is removed/changed. |
| Old | New |
|---|---|
Entity.Create |
Layer.Entity.Create |
Actor.Create |
Layer.Actor.Create |
ProcessorUpdate |
Layer.Engine |
this.ValueChange |
Layer.Observer.Add |
Obj.Spawn |
Layer.Obj.Create |
ProcessorScene.Get |
Layer.GetObj,Layer.GetObjByTag
|
ProcessorSignals.Send |
Layer.Send |
entity.Get(out T) |
entity.TryGet(out T) |
entity.AddGet |
entity.Get |
All API for routines are injected into the layer. The API mostly the same but you need to call it from the layer.
| Old | New |
|---|---|
routines.run(YourIenumerator()) |
Layer.Run(YourIenumerator()) |
coroutune as a callback. |
RoutineCall as a callback. |
There are some cases where you don't have to use layer keyword. There are 3 types that are aware of their layer by the default and inject layer services to itself.
- Processor: Know about the layer its injected to.
- Monocached: Knows about the layer it belongs to. Any objects on the scene with a layer belong to that layer.
- Actor: Actors are inherited from the Monocached class.
In the case of these types, you can write any layer-specific module without a layer keyword.
public class ProcessorTest: Processor, ITick
{
public void Tick(float dt)
{
if (Input.GetKeyDown(KeyCode.Space))
{
// We refer to the ProcessorTest layer.
Obj.Create("Alpaca");
// We ca write like this but it's not necessary.
Layer.Obj.Create("Alpaca");
}
}
}ent entity; // some entity
var layer = entity.layer; // layer entity belongs to.GameObject go; // some object
go.GetLayer(); // layer obj belongs to.This is possible. You can easily touch any layer just by starting your command with the layer name keyword.
var e = LayerApp.Entity.Create();
var e2 = LayerLevel.Entity.Create();
var e3 = LayerGameUi.Entity.Create();Signals are located inside of the layer. You send signals to a specific layer. If you need to send a signal to every active layer then use this pattern:
SignalTest mysignal;
Every.Layer.Send(mysignal);Use HandleEcsEvents instead of HandleEvents
public class ProcessorBlockers : Processor
{
Group<ComponentBlocker> blockers;
public override void HandleEcsEvents()
{
foreach (var blocker in blockers.added)
{
// do stuff.
}
}
}Processor<T> was a class that created a group for you. This group was called source and was filtered by types you add to your Processor<T>
You can look at how it works now here.
Instead of using timers use Layer.WaitFor(1.0f, YourAction());. Note that this is a routine so it can be stopped and cached like any other routine.
Use entity.Get instead of entity.Add. The reason for this change is that if you know that component exists you can easily get it with entity.YourComponentName() pattern. entity.Get will check if there is no component and auto-create it.
Use entity.Get instead of entity.AddGet. The reason for this change is that if you know that component exists you can easily get it with entity.YourComponentName() pattern. entity.Get will check if there is no component and auto-create it.