kikki's tech note

技術ブログです。UnityやSpine、MS、Javaなど技術色々について解説しています。

UnityのMecanimをC#で作り差し替えてみた

本章では、UnityのAnimatorControllerをプログラムで動的に作り、差し替える方法について共有します。

Mecanim

3Dモデルの制作の流れ

3Dモデル制作の流れを確認します。制作のフローを大きく分類すると、以下のように分類できます。

  1. モデリング
  2. テクスチャリング
  3. セットアップ
  4. アニメーション

これらは、基本的にMayaやMaxといった3DCGソフトウェアを利用して制作します。ゲーム開発の現場では、3DCGソフトウェアで制作したモデルをUnityにインポートさせて、利用するケースが大半を占めます。

Unityでの3Dモデルを扱うには

エンジニアがUnityで3Dモデルを直接利用することは難しいです。そこでUnityでは、3Dモデルを簡単に扱う機構として、「Mecanim」があります。Mecanimには、キャラクタの動きを簡単に制御するための機能として、「ブレンドツリー」や「ステートマシーン」などがあります。これらは、GUIで視覚的に編集ができるため、エンジニアでない人にも簡単に扱えます。

MecanimをC#で作り替えた背景

ステートマシーンは、Macanimの中でもエンジニアが利用する機会が非常に多いです。ステートマシーンは、手で図面を描いていくため、アニメーションの遷移が複雑になると、スパゲティ状態になります。そこで、私はステートマシーンをプログラムで編集し、整理したいと考えるようになりました。

今回は、C#でStateMachineを扱う方法について解説します。

スクリプト

以下に、Animator ControllerをC#で作り差し替えるプログラムを示します。

#if UNITY_EDITOR
        AnimatorController mecanimController = null;
        // Animation Controllerファイルを新規作成
        mecanimController = UnityEditor.Animations.AnimatorController.CreateAnimatorControllerAtPath("Assets/Resources/Mecanims/HeroMecanim.controller");
        // Animation Controllerにアニメーションを遷移させるための条件となるパラメータを追加
        mecanimController.AddParameter("RunTrigger", AnimatorControllerParameterType.Trigger);
        mecanimController.AddParameter("JumpTrigger", AnimatorControllerParameterType.Trigger);
        mecanimController.AddParameter("SlidingTrigger", AnimatorControllerParameterType.Trigger);
        mecanimController.AddParameter("DownTrigger", AnimatorControllerParameterType.Trigger);

        // rootのState Machineを取得
        var rootStateMachine = mecanimController.layers[0].stateMachine;
        // 「Entry State」と「Any State」の位置座標を変更
        rootStateMachine.entryPosition = new Vector3(0f, 0f, 0f);
        rootStateMachine.anyStatePosition = new Vector3(0f, 50f, 0f);

        // 各アニメーションのState Machineを作成・配置
        var runState = rootStateMachine.AddState("runState", new Vector3(200f, 0f, 0f));
        mecanimController.SetStateEffectiveMotion(runState, (Motion)Resources.Load<AnimationClip>("AnimationClips/hero_run"));
        var jumpState = rootStateMachine.AddState("jumpState", new Vector3(200f, 100f, 0f));
        mecanimController.SetStateEffectiveMotion(jumpState, (Motion)Resources.Load<AnimationClip>("AnimationClips/hero_jump"));
        var slidingState = rootStateMachine.AddState("slidingState", new Vector3(200f, 200f, 0f));
        mecanimController.SetStateEffectiveMotion(slidingState, (Motion)Resources.Load<AnimationClip>("AnimationClips/hero_sliding"));
        var downState = rootStateMachine.AddState("downState", new Vector3(200f, 300f, 0));
        mecanimController.SetStateEffectiveMotion(downState, (Motion)Resources.Load<AnimationClip>("AnimationClips/hero_down"));

        // アニメーションの遷移条件を設定
        runState.AddTransition(jumpState).AddCondition(AnimatorConditionMode.If, 0, "JumpTrigger");
        runState.AddTransition(slidingState).AddCondition(AnimatorConditionMode.If, 0, "SlidingTrigger");
        runState.AddTransition(downState).AddCondition(AnimatorConditionMode.If, 0, "DownTrigger");
        jumpState.AddTransition(runState).AddCondition(AnimatorConditionMode.If, 0, "RunTrigger");
        jumpState.AddTransition(downState).AddCondition(AnimatorConditionMode.If, 0, "DownTrigger");
        slidingState.AddTransition(runState).AddCondition(AnimatorConditionMode.If, 0, "RunTrigger");
        slidingState.AddTransition(downState).AddCondition(AnimatorConditionMode.If, 0, "DownTrigger");
        downState.AddTransition(runState).AddCondition(AnimatorConditionMode.If, 0, "RunTrigger");
#else
        // UnityのEditor環境でない場合、ResourceフォルダからAnimation Controllerを取得
        RuntimeAnimatorController mecanimController = Resources.Load<RuntimeAnimatorController>("Mecanims/HeroMecanim");
#endif

        // Animatorが設定されているオブジェクトを取得
        this.heroAnimator = gameObject.GetComponent<Animator>();

#if UNITY_EDITOR
        // 上記で作成したAnimation ControllerをAnimatorのruntimeAnimatorControllerに設定
        this.heroAnimator.runtimeAnimatorController = new AnimatorOverrideController() { runtimeAnimatorController = (RuntimeAnimatorController)mecanimController };
#else
        // 上記で取得したAnimation ControllerをAnimatorのruntimeAnimatorControllerに設定
        this.heroAnimator.runtimeAnimatorController = new AnimatorOverrideController() { runtimeAnimatorController = mecanimController };
#endif

注意点として、Animator側のAnimator Controllerには、Runtime Animator ControllerにAnimator Controllerを設定する必要があります。またEditor上でのみ、Animator Controllerを作れるので、本番環境では生成されたAnimator Controllerを利用しましょう。

総括

Unityは、GUIで開発ができ、自由度の高い設計となっているため、ゲーム開発が比較的簡単に行えます。一方で、成果物がスパゲティになりやすいため、整理しながら開発する必要があります。今回の試みもその一環として解説しました。開発の一助となれば幸いです。

以上、「UnityのMecanimをC#で作り差し替えてみた」でした。


※無断転載禁止 Copyright (C) kikkisnrdec All Rights Reserved.