# 编写自定义动作（C#）

除了使用 [内置动作](https://docs.febucci.com/text-animator-unity/typewriter/wait-actions-when-typing)之外，你可以通过脚本（C#）编写自己的动作。

{% hint style="info" %}
务必阅读 [高级概念](/text-animator-unity/3.x-zh/bian-xie-zi-ding-yi-lei/gao-ji-gai-nian.md) 页面。
{% endhint %}

***

## 创建自定义动作的不同方式 <a href="#actions-base-class" id="actions-base-class"></a>

自 Text Animator 3.0 起，你可以通过多种方式创建动作，根据项目需求提供更多灵活性。

### 将动作作为组件创建

{% hint style="success" %}
作为组件创建的动作允许你更容易地引用场景对象
{% endhint %}

```csharp
[System.Serializable]
class ExampleActionComponent : TypewriterActionScriptable
{
    [SerializeField] float timeToWait;
    
    // 主逻辑在这里， 
    
    // ...可以是无状态的
    protected override IActionState CreateCustomState(ActionMarker marker, object typewriter)
        => new ExampleState(timeToWait);
        
    // ...或作为协程
    protected override IEnumerator PerformAction(TypingInfo typingInfo)
    {
        // yield return ...
    }
}
```

### 将动作作为 ScriptableObject 创建

{% hint style="success" %}
作为 ScriptableObject 的动作可以重复使用并在无需加载场景的情况下被引用
{% endhint %}

```csharp
[System.Serializable]
[CreateAssetMenu(menuName = "Create Example Action")]
class ExampleActionScriptable : TypewriterActionScriptable
{
    [SerializeField] float timeToWait;
    
    // 主逻辑在这里...
    
    // ...可以是无状态的
    protected override IActionState CreateCustomState(ActionMarker marker, object typewriter)
        => new ExampleState(timeToWait);
        
    // ...或作为协程
    protected override IEnumerator PerformAction(TypingInfo typingInfo)
    {
        // yield return ...
    }
}
```

{% hint style="warning" %}
附言：别忘了在项目视图中创建你的动作 ScriptableObject，并将其添加到动作数据库中。
{% endhint %}

***

## 实现动作逻辑的不同方式 <a href="#actions-base-class" id="actions-base-class"></a>

你可以决定如何编写动作的核心逻辑。&#x20;

* 在协程（IEnumerator）内部，或
* 通过单独的“tick”方法（该方法返回动作是否应继续运行或已完成）。

首先，导入正确的命名空间：

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

### 创建协程 <a href="#actions-base-class" id="actions-base-class"></a>

编写协程非常简单！&#x20;

例如，在你的 TypewriterAction 类（无论是组件还是 Scriptable）中，只需重写 PerformAction 方法：

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

### 创建无状态动作 <a href="#actions-base-class" id="actions-base-class"></a>

另一方面，创建无状态动作需要你创建一个继承于自定义结构体， **IActionState** 并且该结构体将执行动作（在此示例中：在继续打字器之前等待几秒），例如：

```csharp
struct ExampleState : IActionState // <--- 必须继承自此
{
    float timePassed;
    readonly float timeToWait;
    public ExampleState(float timeToWait)
    {
        timePassed = 0;
        this.timeToWait = timeToWait;
    }
    
    public ActionStatus Progress(float deltaTime, ref TypingInfo typingInfo)
    {
        // 增加已过时间
        timePassed += deltaTime;
        
        // 根据时间决定继续还是停止
        return timePassed >= timeToWait
            ? ActionStatus.Finished
            : ActionStatus.Running;
    }
    
    public void Cancel()
    {
        // 在此用于修改 
    }
}
```

然后你可以通过在你的动作类中重写 CreateCustomState 方法来实例化此结构体（我们在这里看到的那个 [#actions-base-class](#actions-base-class "mention")).

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

### 属性 <a href="#attributes" id="attributes"></a>

* 该 `标记` 参数包含关于你的标签的有用信息，例如 ID 或是否有随之而来的任何参数（例如 `<playSound=02>`).
* 该 `typewriter` 引用当前正在执行该动作的 Typewriter 组件或 AnimatedLabel
* 该 `typingInfo` 包含诸如当前打字速度（你可以修改）和打字器内已过时间等信息。

***

{% hint style="success" %}
完成！通过这个简单的步骤，你可以添加任何你想要的自定义动作。
{% 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/3.x-zh/bian-xie-zi-ding-yi-lei/bian-xie-zi-ding-yi-dong-zuo-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.
