Skip to content

Migration to Actors 2020.06

Dmitry Mitrofanov edited this page Jun 20, 2020 · 12 revisions

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.

🚧 Architecture differences

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

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.

API changes

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

Routines changes

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.

💬 When I can drop Layer keyword?

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");
      }
    }
  }

💬 Can I get a layer from an entity or a game object?

ent entity; // some entity
var layer = entity.layer; // layer entity belongs to.
GameObject go; // some object
go.GetLayer(); // layer obj belongs to.

💬 Can I access another layer from my current layer?

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();

💬 How signals are working now?

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);

Processor Changes

Use HandleEcsEvents instead of HandleEvents

public class ProcessorBlockers : Processor
{
  Group<ComponentBlocker> blockers;
  public override void HandleEcsEvents()
  {
      foreach (var blocker in blockers.added)
      {
        // do stuff.
      }
  }
}

💬 What Processor<T> do?

Old

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>

New

You can look at how it works now here.

🚧 Removed API

Timers

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.

Entity.Add

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.

Entity.AddGet

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.

Clone this wiki locally