Skip to content

Referencing Objects created at Runtime

DDevilISL edited this page Jan 20, 2018 · 1 revision

Future References

Okay, so now we know how to reference objects inside a scene, but what if we want to reference a object that is not yet in a scene, but will be created when the cutscene is executed at runtime?

The answer: FutureReferences

Basically, they are references to an object that is expected be instantiated at runtime by special tokens called "Future Providers", here are what they look like on the inspector:

Using a future reference is very similar to using exposed references, except for the fact that unity doesn't allow custom generic types to be serialized, so unfortunately, FutureReference isn't a generic type.
Here is an example of how they can be used:

public class SetFutureTextToken : IToken {
    [FutureType(typeof(Text))]
    public FutureReference Text;

    public Font Font;
    public string Content = "Hello from ShiroiCutscenes!";
    public Vector2 Position;
    public Vector2 Size;

    public IEnumerator Execute(CutscenePlayer player) {
        var t = Text.Resolve<Text>(player);
        t.text = Content;
        t.font = Font;
        var rect = t.rectTransform;
        rect.position = Position;
        rect.sizeDelta = Size;
        yield break;
    }
}

See? Very similar.
The only diferences are that:

  • The generic parameter was moved to the Resolve method in order to preserve serialization
  • Since we can't use generics, a FutureType annotation was introduced to help filter available types in the inspector

Creating Future Providers

To create a future provider, you must have your token implement IFutureProvider, which has a single method:

void RegisterFutures(Cutscene cutscene);  

This method is called once your token has been added to your cutscene, and it is where you must notify your cutscene using

int NotifyFuture<T>(IFutureProvider provider, string futureName);

The returned int is a generated id that identifies the future, you must store and serialize it because this will be the id used at runtime to provide the created object, so let's see how we would go on implementing a CreateTextToken:

public class CreateTextToken : IToken, IFutureProvider {
    public string TextName = "future_text";
    public string Text = "Hello world!";
    [SerializeField]
    private int textId;

    public IEnumerator Execute(CutscenePlayer player) {
        //Implement in a second
        yield break;
    }


    public void RegisterFutures(Cutscene cutscene) {
        // This is us basically saying: "HEY! WE WILL CREATE A TEXT OBJECT WHEN WE ARE EXECUTED"
        // "AND YOU CAN REFERENCE IT! JUST SO YOU KNOW!"
        textId = cutscene.NotifyFuture<Text>(this, TextName);
        // and the cutscene replying: "OKAY I EXPECT YOU TO NOTIFY ME WHEN THE TEXT OBJECT IS CREATED
        // AT THIS ADDRESS (textId), OH AND BRING IT WITH YOU
    }
}

So now the cutscene knowns that a text object will be available at runtime at the address "textId", however, when we instantiate the object, we will inform the CutscenePlayer instead, because since exposed references are also stored on the player, it's kinda becoming the norm to store references of anything that is in the scene in the player, so we will continue doing that to keep things consistent.

"But aren't futures stored in the cutscene?"
Yes, but a future is not a reference to object, think about futures this way:
"Futures are the embodiment of the expectation that a object will be available at runtime" So how do we actually notify the player that our object has been created? That is actually really simple

public class CreateTextToken : IToken, IFutureProvider {
    public string TextName = "future_text";
    public string Text = "Hello world!";
    [SerializeField]
    private int textId;

    public IEnumerator Execute(CutscenePlayer player) {
        // Create and setup our text
        var textObj = new GameObject(TextName).AddComponent<Text>();
        textObj.text = TextName;
        // Notify player with "HEY this is the object created for future with id (textId)
        player.ProvideFuture(textObj, textId);
        yield break;
    }


    public void RegisterFutures(Cutscene cutscene) {
        textId = cutscene.NotifyFuture<Text>(this, TextName);
    }
}

And now any token can access this future at runtime. However, if you do not provide an object at runtime, an exception will be raised every time a token attempts to access this future. Remember: With great powers, come great responsibilities.