Unityのアニメーションイベントで引数にScriptableObjectを渡す

# アニメーションイベント
新しい「タイムライン」機能ではなく、昔からあるアニメーションクリップについての話題である。
Unityのアニメーションクリップには任意のフレームで関数を呼び出す機能がある。
関数の引数はint, float, string, Enum, AnimationEvent, GameObject参照が1つだけである。
https://docs.unity3d.com/jp/540/ScriptReference/AnimationEvent.html

アニメーションと同期してなにか処理したいときには非常に便利な機能だが、呼び出せる引数の個数は1つで型もあまり自由にできるわけではない。
ゲームオブジェクト（プレハブなど）を渡すこともできるが、これはこれで大層である。そこで、ScriptableObjectを渡すことができれば、データ構造として多数の情報を引き渡し、なおかつGameObjectのように余計なものは付いてこないので良さげである。
早速試してみたところ、期待した動作になっていたのでここに記載する。

## 実装
なにかゲームオブジェクトを配置してAnimatorControllerを取り付け、AnimationClipを作成しておきます。
![コメント 2020-03-08 205621.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/30919/ea3f345b-4485-50d4-5516-016874c86eea.png)

以下のスクリプトも取り付けます。アニメーションから呼び出される関数を引数タイプごとに作成しています。

```csharp
using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class testobj : MonoBehaviour
{
    public enum TestEnum
    {
        ONE, TWO, THREE
    }
    // Start is called before the first frame update
    void Start()
    {
    }
    // Update is called once per frame
    void Update()
    {
    }
    public void Func01(int _arg)
    {
        Debug.Log($"int:{_arg}");
    }
    public void Func02(float _arg)
    {
        Debug.Log($"float:{_arg}");
    }
    public void Func03(string _arg)
    {
        Debug.Log($"string:{_arg}");
    }
    public void Func04(GameObject _arg)
    {
        Debug.Log($"GameObject:{_arg.name}");
    }
    public void Func05(TestEnum _arg)
    {
        Debug.Log($"Enum:{_arg}");
    }
    public void Func06(AnimationEvent _arg)
    {
        Debug.Log($"AnimationEvent float:{_arg}");
    }
}
```

AnimationClipにイベント設定します。
![コメント 2020-03-08 195434.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/30919/7f10932e-0939-9572-f611-37b4d1b6f4fd.png)

各イベントをInspectorで見てみるとこんな感じ。
int
![コメント 2020-03-08 200239.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/30919/ba20a236-8e5d-665b-c3f9-56d26ce32653.png)

float
![コメント 2020-03-08 200258.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/30919/7c1c69df-2a84-3ff3-1e6f-48c13d66a073.png)

string
![コメント 2020-03-08 200320.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/30919/823ad759-afcc-8f29-0dd4-b0d090975495.png)

Enum
![コメント 2020-03-08 200342.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/30919/cf4f493a-2b16-e302-2b3a-82de469b91b7.png)

GameObject(Prefab)
![コメント 2020-03-08 200409.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/30919/70e137fb-b8d9-ae00-19d0-8d3677453dea.png)

AnimationEvent
![コメント 2020-03-08 200433.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/30919/23d1c6b3-2a30-a02c-facd-a59c2a343451.png)

実行するとConsoleに結果が表示されます。
![コメント 2020-03-08 204719.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/30919/56a6e62b-978b-b0b0-ce13-0141c22fd8c4.png)

## ScriptableObjectを使う
まずはScriptableObjectをつくる。

```csharp
using System;
using System.Collections.Generic;
using UnityEngine;

[CreateAssetMenu]
public class EventData : ScriptableObject
{
    [SerializeField] private int intdata;
    [SerializeField] private float floatdata;

    public int Intdata { get => intdata; set => intdata = value; }
    public float Floatdata { get => floatdata; set => floatdata = value; }
}
```

クラスの宣言時に[createAssetMenu]を書くことでEditorで簡単にScriptableObjectが作成できる。
![コメント 2020-03-08 234237.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/30919/a49221e4-4446-e1ad-0ac8-2d9ce0e4ce7c.png)

こうしてイベントとして引き渡したい情報を設定したScriptableObjectを作っておく。

![コメント 2020-03-08 234526.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/30919/35ab2bfd-f4e3-4588-958f-e1ecfae2b5c2.png)

イベントとして呼び出す関数を追加。引数にはScriptableObjectの型を指定する。

```csharp
     :
    public void Func07(EventData _arg)
    {
        Debug.Log($"EventData Int:{_arg.Intdata}");
        Debug.Log($"EventData float:{_arg.Floatdata}");
    }
     :
```

AnimationClipにイベントを追加。EventDataのところには作成したScriptableObjectをドロップすることができる。
![コメント 2020-03-08 234810.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/30919/423e24fa-65c8-f6ce-0cc0-1a9bea89780b.png)

実行結果

![コメント 2020-03-08 234955.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/30919/b969434d-9d9d-8e85-9d02-468de93c4a91.png)
無事にデータが引き渡せた。

Githubで公開
https://github.com/sokude/UnityAnimationTimelineAndScriptableObject

## 使い所例
私がこのパターンを使った例は、シューティングゲームの敵機を実装したときでした。敵の移動の軌跡はAnimationClipでPositionとRotationを変化させていき、弾を撃ちたいところでイベント呼び出しています。このとき、弾撃ちのバリエーションごとに関数を作ってられないので、複数の情報を渡したいと考えて使ってみました。弾撃ちの方向と個数、開き角、スピードなどの情報をまとめたScriptableObjectを作成し、イベントでそのオブジェクトを引き渡し弾を撃たせることができました。
もっとも、あらかじめScriptableObjectを作っておかないといけないので、バリエーション増やす手間はかかります。完全にアニメーション上で設定できるわけではないのですが、シューティングゲームの弾撃ちバリエーションくらいならこの方法で対応は可能かと。