# 커스텀 동작 작성(C#)

사용하는 것 외에도 [내장 동작](https://docs.febucci.com/text-animator-unity/typewriter/wait-actions-when-typing)을(를) 스크립트(C#)로 직접 작성할 수 있습니다.

{% hint style="info" %}
반드시 읽어보세요 [advanced-concepts](https://docs.febucci.com/text-animator-unity/3.x-ko/writing-custom-classes/advanced-concepts "mention") 페이지도.
{% 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 ...
    }
}
```

### 스크립터블 오브젝트로서 동작 생성하기

{% hint style="success" %}
스크립터블오브젝트로서의 동작은 씬이 로드되어 있지 않아도 재사용 및 참조할 수 있습니다
{% 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) 내부에서, 또는
* 별도의 "틱" 메서드(동작을 계속 실행해야 하는지 또는 완료되었는지 반환하는)로.

시작하려면 올바른 네임스페이스를 임포트하세요:

<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 클래스(컴포넌트든 스크립터블이든) 내부에서 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);
    }
}
```

### 무상태(stateless) 동작 만들기 <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()
    {
        // 수정할 때 이걸 사용하세요 
    }
}
```

그런 다음 Action 클래스 내부에서 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 %}
