Writing Custom Actions (C#)

Other than using built-in actions, you can write your own via script (C#).

Be sure to read the Advanced Concepts page as well.


Different ways to create custom actions

Since Text Animator 3.0 you can create actions in many different ways, giving you even more flexibility based on your projects needs.

Creating Actions as Components

[System.Serializable]
class ExampleActionComponent : TypewriterActionScriptable
{
    [SerializeField] float timeToWait;
    
    // main logic here, 
    
    // ...either stateless
    protected override IActionState CreateCustomState(ActionMarker marker, object typewriter)
        => new ExampleState(timeToWait);
        
    // ...or as a Coroutine
    protected override IEnumerator PerformAction(TypingInfo typingInfo)
    {
        // yield return ...
    }
}

Creating Actions as Scriptable Objects

[System.Serializable]
[CreateAssetMenu(menuName = "Create Example Action")]
class ExampleActionScriptable : TypewriterActionScriptable
{
    [SerializeField] float timeToWait;
    
    // main logic here...
    
    // ...either stateless
    protected override IActionState CreateCustomState(ActionMarker marker, object typewriter)
        => new ExampleState(timeToWait);
        
    // ...or as a Coroutine
    protected override IEnumerator PerformAction(TypingInfo typingInfo)
    {
        // yield return ...
    }
}

Different ways to implement the actions logic

You can decide how to write the core logic of Actions.

  • Inside Coroutines (IEnumerator), or

  • Via a separate "tick" method (that returns if the action should keep running or if it has finished).

To start, import the correct namespaces:

using Febucci.TextAnimatorForUnity.Actions;
using Febucci.TextAnimatorCore.Typing;
using UnityEngine;

Creating a coroutine

Writing a coroutine is pretty straightforward!

For example, inside your TypewriterAction class (whether it's a Component or a Scriptable), just override the PerformAction method:

[SerializeField] AudioSource source;

protected override IEnumerator PerformAction(TypingInfo typingInfo)
{
    if (source != null && source.clip != null)
    {
        source.Play();
        yield return new WaitForSeconds(source.clip.length);
    }
}

Creating a stateless action

Creating a Stateless action on the other hand, requires you to create a custom struct that inherits from IActionState and that will perform the action (in this case: waiting a few seconds before progressing the typewriter), like:

struct ExampleState : IActionState // <--- must inherit from this
{
    float timePassed;
    readonly float timeToWait;
    public ExampleState(float timeToWait)
    {
        timePassed = 0;
        this.timeToWait = timeToWait;
    }
    
    public ActionStatus Progress(float deltaTime, ref TypingInfo typingInfo)
    {
        // increases time passed
        timePassed += deltaTime;
        
        // tells to continue or to stop based on time
        return timePassed >= timeToWait
            ? ActionStatus.Finished
            : ActionStatus.Running;
    }
    
    public void Cancel()
    {
        // use this for modifying 
    }
}

You can then instantiate this struct by overriding the CreateCustomState method inside your Action class (the one we saw here Different ways to create custom actions).

protected override IActionState CreateCustomState(ActionMarker marker, object typewriter)
        => new ExampleState(timeToWait);

Attributes

  • The marker paramater has useful info about your tag, for example the ID or if there are any parameters that come with it (e.g. <playSound=02>).

  • The typewriter references the Typewriter Component or AnimatedLabel that is currently performing the action

  • The typingInfo contains information such as the current typing speed (which you can modify) and time passed inside the typewriter.