# Writing Custom Actions (C#)

Other than using [built-in actions](https://docs.febucci.com/text-animator-unity/typewriter/wait-actions-when-typing), you can write your own via script (C#).

{% hint style="info" %}
Be sure to read the [Advanced Concepts](/text-animator-unity/writing-custom-classes/advanced-concepts.md) page as well.
{% endhint %}

***

## Different ways to create custom actions <a href="#actions-base-class" id="actions-base-class"></a>

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

{% hint style="success" %}
Actions created as Components allow you to reference scene objects more easily
{% endhint %}

```csharp
[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

{% hint style="success" %}
Actions as ScriptableObjects can be reused and referenced without the need for a scene loaded
{% endhint %}

```csharp
[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 ...
    }
}
```

{% hint style="warning" %}
P.S. Don’t forget to create your action ScriptableObject in the ProjectView, and add it to an actions Database.
{% endhint %}

***

## Different ways to implement the actions logic <a href="#actions-base-class" id="actions-base-class"></a>

You can decide how to write the core logic of Actions.&#x20;

* 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:

<pre class="language-csharp"><code class="lang-csharp"><strong>using Febucci.TextAnimatorForUnity.Actions;
</strong><strong>using Febucci.TextAnimatorCore.Typing;
</strong>using UnityEngine;
</code></pre>

### Creating a coroutine <a href="#actions-base-class" id="actions-base-class"></a>

Writing a coroutine is pretty straightforward!&#x20;

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

```csharp
[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 <a href="#actions-base-class" id="actions-base-class"></a>

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:

```csharp
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 [#actions-base-class](#actions-base-class "mention")).

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

### Attributes <a href="#attributes" id="attributes"></a>

* 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.

***

{% hint style="success" %}
Done! With this simple procedure, you can add any Custom Action you want.
{% endhint %}


---

# Agent Instructions: Querying This Documentation

If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://docs.febucci.com/text-animator-unity/writing-custom-classes/writing-custom-actions-c.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
