VRChat ワールドでアバターをアニメーションさせる

# やったこと
ワールド内でアバターにアニメーションさせる
ここではcubeをインタラクトすると死亡アニメーションが再生されて5秒後に復帰するものを作りました。
![2022-11-23_14-32-48_AdobeExpress.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2984897/073cfa30-abec-13f9-a01e-85e73b8e5254.gif)


# 環境
・VCC (SDKバージョン3.1.10)
・U# 1.1.5

# 1. アバターのアニメーションを置き換えれるようにする
VRChatのワールドのSDKだけではアバターのトラッキングのアニメーションは上書きできないため、そのままだと手や頭がトラッキングされて想定通りのアニメーションをしてくれない。そのため、アバターのトラッキングを上書きできるようにする

## 方法
VCCでアバタープロジェクトを作成します。
アバタープロジェクトから`Packages/com.vrchat.avatars/Runtime/VRCSDK/Plugins/VRCSDK3A.dll`と
`Packages/com.vrchat.avatars/Editor/VRCSDK/SDK3A/Components3/VRCAnimatorTrackingControlEditor.cs`をコピーします。

つまり以下の構造のフォルダを作ります。
```
com.vrchat.avatars
├─Editor
│  └─VRCSDK
│      └─SDK3A
│          └─Components3
│                  VRCAnimatorTrackingControlEditor.cs
└─Runtime
    └─VRCSDK
        └─Plugins
                VRCSDK3A.dll
```
作成したこのフォルダをワールドプロジェクトのAssets内に入れます。

# 2. アニメーションの作成
アニメーションをインポートします。今回はMixamoでダウンロードしたものを使います。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2984897/56ccf6b3-17da-9764-a6bc-3e3f741015d9.png)
アニメーションの設定でRigをHumanoidにします。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2984897/84233fef-298e-e90d-dc52-4142950bcf5d.png)
倒れるアニメーションの場合は、`Root Transform Position(Y)`の`Based Upon`をFeetにします。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2984897/8d463b1c-f173-c1a8-7857-699f2c6228da.png)
アニメーターコントローラーを新規作成し、`Create State` -> `Empty`でStateを作成します。
作ったStateで`Add Behaviour`から`VRC Animator Tracking Control`を追加します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2984897/df593fe6-b105-7d62-67bb-858f26399f80.png)
全て`Animation`にします。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2984897/deadbc1d-579d-313a-d301-837f13f675d6.png)
アニメーションを追加して次のようにつなげます
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2984897/88f8d282-6e3b-019b-8034-12171ffbd8b3.png)

# 3. Stationの作成
空オブジェクトを作成して`VRC Station`をアタッチします。
ワールド用とアバター用2つある場合は`API Object`という項目がある方がワールド用のものです。(`Controls Object`ではない)
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2984897/16ef9137-2159-3af2-c406-db0e68579c3d.png)

パラメーターは以下のようにします
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2984897/0ff2ed75-5072-ce86-7bf4-564175274661.png)

Animator Controllerは先ほど作成したもの、Seatedのチェックを外し、EnterとExit LocationにこのGameObject自身のTransformを指定しています。

今回はUdonで座らせるようにするので、プレイヤーが自分で座れないようにするため、Box ColliderをアタッチしてそのBox Colldierを無効にします。そうするとプレイヤーが触ることはできなくなります。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2984897/4aa71040-91e7-9de4-312a-a30a877b4962.png)

:::note warn
**注意**
Stationのパラメーターを編集した後は別のGameObjectを作るなどしてシーンに変更を加えてください。それをしないとシーンに変更が加えられたというフラグが立たず、パラメーターの変更が保存されません。
:::

# 4. アニメーションを再生する
今回はCubeをインタラクトしたら再生するようにします。
Cubeを作成して位置とサイズをいい感じに調整します。
以下のUdonを書いてアタッチします。
インタラクトしたらアニメーションが再生されて5秒後に復帰するUdonです。
```csharp
[SerializeField] VRCStation station;

public override void Interact()
{
    station.UseStation(Networking.LocalPlayer);
    SendCustomEventDelayedSeconds(nameof(EndAnimation), 5.0f);
}

public void EndAnimation()
{
    station.ExitStation(Networking.LocalPlayer);
}
```

アタッチしたらスクリプトの`Station`パラメーターにStationをアタッチしたオブジェクトを入れます。

これで一応完成ですが、このままだとStationから降りても手などがトラッキングされないままになってしまうので、修正します。

# 5. Stationから降りた後にトラッキングを戻す
新たにアニメーターコントローラーを作成し、Stateを作成し、VRC Animator Tracking Controlを追加し、すべてTrackingにします。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2984897/08d31e04-72e0-4593-5bc5-e2f9b9303567.png)
VRC Stationをアタッチしたオブジェクトを`Ctrl+D`で複製します。
複製したオブジェクトの`VRC Station`の`Animator Controller`を先ほど作成したものに変更してください、

手順4で作成したUdonを少し書き加えます。
```csharp
[SerializeField] VRCStation station;
[SerializeField] VRCStation ResumeTrackingStation;

public override void Interact()
{
    station.UseStation(Networking.LocalPlayer);
    SendCustomEventDelayedSeconds(nameof(EndAnimation), 5.0f);
}

public void EndAnimation()
{
    station.ExitStation(Networking.LocalPlayer);
    ResumeTrackingStation.UseStation(Networking.LocalPlayer);
    SendCustomEventDelayedFrames(nameof(ExitTrackingStation), 1);
}

public void ExitTrackingStation()
{
    ResumeTrackingStation.ExitStation(Networking.LocalPlayer);
}
```
書き換えたら、スクリプトの`Resume Tracking Station`に先ほど複製したオブジェクトを入れてください。
これで完成です。

# 参考記事
[SDK3/SDK2両対応アニメーションを置き換えるワールドのVRCStationの作り方](https://kurotorimkdocs.gitlab.io/kurotorimemo/010-VR/010_VRChat/UDON/VRCStationAnimation/ "SDK3/SDK2両対応アニメーションを置き換えるワールドのVRCStationの作り方")