Lifecycle

Note: This document relates to the MonoBehaviour workflow.

Before reading this document, make sure you are familiar with:


You can represent your SpatialOS entities as GameObjects. By representing your SpatialOS entity as a GameObject, you are able to interact with the underlying entity and its components.

This interaction is enabled through a few different objects:

To use these objects, you define fields in a MonoBehaviour that is attached to your linked GameObject and decorate these fields with the [Require] attribute.

[Require] private HealthReader healthReader;

The requirements depend on which types are marked as required in the MonoBehaviour.

  • A Reader requires that your worker has checked out the corresponding component on the linked SpatialOS entity. For example, the HealthReader requires that the Health component is checked out.
  • A Writer has the requirements of a Reader and requires that your worker has authority over the corresponding component on the linked SpatialOS entity.
  • A CommandSender only requires that your worker has checked out the linked SpatialOS entity. This is a pre-condition of even having a GameObject representation, so you can consider this one as always available.
  • A CommandReceiver has the same requirements as the Writer.

MonoBehaviour lifecycle

The first part of this lifecycle happens at build time or before you enter Play Mode in Unity.

At this time, the GDK iterates over every prefab that's declared in any EntityRepresentationMapping asset and will disable any MonoBehaviour that is marked with the [WorkerType] attribute or has one more more fields marked with the [Require] attribute. This ensures that these MonoBehaviours are only enabled when the underlying requirements are met.

Once all the requirements are met for every field on the MonoBehaviour, all the fields will be injected and the MonoBehaviour will be enabled. This then calls OnEnable().

If during runtime, some of the requirements are no longer met, all the fields will be un-injected, the objects set to be invalid, the fields set to null, and the MonoBehaviour will be disabled. This then calls OnDisable().

Note: Do not manually enable/disable these MonoBehaviours as it results in undefined behavior.

If you capture references to any field marked with [Require], you should ensure that the IsValid property is true before using it as we cannot control the lifecycle of any references that you capture.

For example:

public class MyBehaviour : MonoBehaviour
{
    [Require] private HealthReader healthReader;
    [Require] private PositionWriter positionWriter;
    [Require] private FireCommandSender fireCommandSender;
    [Require] private ExplodeCommandReceiver explodeCommandReceiver;

    public void OnEnable()
    {
        // At this point we are guaranteed that the above fields are valid and injected.
    }

    public void OnDisable() 
    {
        // The fields above are invalid and shouldn't be used.
    }

    public Action CaptureReferences() 
    {
        // We've captured a reference to the healthReader field in a closure which when 
        // executed may or may not be valid anymore. So we must check! 
        return () => 
        { 
            if (healthReader.IsValid)
            {
                // Do something with the health reader.
            }
        };
    }

    // Whoever calls this and stores the reference must be careful as it may or may not
    // be valid!
    public HealthReader GetHealthReaderReference() 
    {
        return healthReader;
    }
}

Note that there are some other times you'll want to check if a required field is present. For example, the OnTriggerEnter() and OnCollisionEnter() methods are called by Unity even if the MonoBehaviour is disabled. In the body of these functions, you should take care to check if your required fields are present.

public class ReadHealthBehaviour : MonoBehaviour
{
    [Require] private HealthReader healthReader;

    void OnEnable()
    {
        // The MonoBehaviour is automatically enabled and disabled based on
        // whether requirements for your injected types are met.
        // OnEnable() is only called when healthReader is available.
        Debug.Log($"Current health: {healthReader.Data.Value}");
    }

    void OnDisable()
    {
        // It will be automatically called when at least one requirements
        // for your declared Requirables is not met anymore.
        // Your injected types will be automatically disposed.
    }

    void OnTriggerEnter()
    {
        if (healthReader != null)
        {
            // OnTriggerEnter() can be called even if the MonoBehaviour
            // is disabled. You need to check whether the reader is available.
        }
    }

    void OnCollisionEnter()
    {
        if (healthReader != null)
        {
            // OnCollisionEnter() can be called even if the MonoBehaviour
            // is disabled. You need to check whether the reader is available.
        }
    }
}

Updated about a year ago


Lifecycle


Suggested Edits are limited on API Reference Pages

You can only suggest edits to Markdown body content, but not to the API spec.