【Unity】StateMachineBehaviourからアニメーションの特定タイミングで処理を実行する方法

というわけで、ひさびさにUnity関係のお話です!
今回はタイトルにもある「StateMachineBehaviour」を使ったスクリプトのお話です。

shot2ss20161116195302218

「StateMachineBehaviour」を継承したクラスは、AnimatorController のステートに設定することができます。
主に「ステートに遷移した直後」「ステート中」「他のステートへ遷移する直前」のタイミングで関数が呼び出されますが、「ある特定のタイミングで関数を呼び出す」ということが出来ません。

アニメーションをインポートした際に ImportSettings の Events から出来るようですが、指定する関数名を直打ちするのがあまり好きではありません。
SendMessage() もそうですが、エディタのリファクタリング機能で置換できないので、後々変更があった場合に面倒なことになってしまいます。
アクセス修飾子が private であってもぶち抜いて実行されるのもどうかと思います。

特定タイミングで処理を実行するスクリプトの作成

冒頭で「Events をつかいたくない!」と言ったものの、現状の StateMachineBehaviour に「アニメーションの特定タイミングで処理を実行する」的な機能はありません。
なので自作するわけですが、ステート中に実行される OnStateUpdate() と 、AnimatorStateInfo.normalizedTime を組み合わせればいけそうです。

そんなわけで作ったスクリプトがこちら!

using UnityEngine;

namespace StateController.PlayerAnimator {

    public class ExecuteTest : StateMachineBehaviour {

        [SerializeField]
        protected float execute_time;

        private bool execute_flg;

        public override void OnStateEnter(Animator animator, AnimatorStateInfo stateInfo, int layerIndex) {
            execute_flg = false;
        }

        public override void OnStateUpdate(Animator animator, AnimatorStateInfo stateInfo, int layerIndex) {
            if (execute_time < stateInfo.normalizedTime && !execute_flg) {
                execute_flg = true;

                // 何らかの処理
                Debug.Log("hogehoge");
            }
        }

        public override void OnStateExit(Animator animator, AnimatorStateInfo stateInfo, int layerIndex) {
            execute_flg = false;
        }
    }
}

ポイントは OnStateEnter() と OnStateExit() 内で execute_flg をfalseに設定している部分です。
これがないと execute_flg が true から切り替わらず、2回目以降if文の中が実行されません。
Enter で行えば Exit では要らない気もしますが、念のため!

またif文の条件ですが、normalizedTime が f loat で細かく変化する関係上、「execute_time == stateInfo.normalizedTime」では不可能に近いです。
四捨五入することも考えましたが、近い値になると複数回通ってしまうためNG。

shot2ss20161116202600048

これを AnimatorController 上のステートに設定し、execute_time を設定すればOKです。
AnimatorStateInfo.normalizedTime はアニメーション実行時間に応じて0~1の値を取るので、execute_time も0~1で指定する必要があります。

ただし、ループするステートの場合は上手くいきません。
ループするステートの場合は normalizedTime が1でリセットされず、どんどん加算されていきます。

ということで、normalizedTime 小数点以下を取得して判定するように修正します。
直したのは OnStateUpdate() のif文判定だけです。

using UnityEngine;

namespace StateController.PlayerAnimator {

    public class ExecuteTest : StateMachineBehaviour {

        [SerializeField]
        protected float execute_time;

        private bool execute_flg;

        public override void OnStateEnter(Animator animator, AnimatorStateInfo stateInfo, int layerIndex) {
            execute_flg = false;
        }

        public override void OnStateUpdate(Animator animator, AnimatorStateInfo stateInfo, int layerIndex) {
            if (execute_time < stateInfo.normalizedTime % 1 && !execute_flg) {
                execute_flg = true;

                // 何らかの処理
                Debug.Log("hogehoge");
            }
        }

        public override void OnStateExit(Animator animator, AnimatorStateInfo stateInfo, int layerIndex) {
            execute_flg = false;
        }
    }
}

「剰余演算子」なるものを使えば簡単にできるみたいです。
ちなみに normalizedTime の整数部分がループ回数に相応するようです。
覚えておくと役に立つ・・・かも。

使用例

自分が使っている一例として、「特定タイミングで iTween.MoveBy() を実行しキャラクターを移動させる」があります。
上で載せたスクリプトを少し修正します。

using UnityEngine;

namespace StateController.PlayerAnimator {

    public class MoveCharacter : StateMachineBehaviour {

        [SerializeField]
        protected float execute_time;

        [SerializeField]
        protected Vector3 velocity;

        [SerializeField]
        protected float move_time;

        private bool execute_flg;

        public override void OnStateEnter(Animator animator, AnimatorStateInfo stateInfo, int layerIndex) {
            execute_flg = false;
        }

        public override void OnStateUpdate(Animator animator, AnimatorStateInfo stateInfo, int layerIndex) {
            if (execute_time < stateInfo.normalizedTime % 1 && !execute_flg) {
                execute_flg = true;

                iTween.Stop(animator.gameObject);
                iTween.MoveBy(animator.gameObject, iTween.Hash(
                    "amount", velocity,
                    "time", move_time
                ));
            }
        }

        public override void OnStateExit(Animator animator, AnimatorStateInfo stateInfo, int layerIndex) {
            execute_flg = false;
        }
    }
}

shot2ss20161116202724703

インスペクター上で実行するタイミングと移動ベクトル、移動時間を設定します。
アニメーションの途中で「1歩踏み込んで攻撃」的なモーションがあっても安心です。

20161116_01

実際に動かすと上のような感じ。
分かりにくいですが、縦斬り→薙ぎ払いまで1モーションで、execute_time は0.5に設定してあります。

別に iTween でなくても良いので、animator.gameObject.getComponent() とかと組み合わせればもっといろいろ出来ます。
StateMachineBehaviour のスクリプト内に処理をあれこれ書きたくない方は、素直に Player クラスあたりを getComponent() して処理させるのも有りだと思います。

まとめ

そんなわけで、StateMachineBehaviour 上から特定タイミングで処理を実行する方法について考えてみました!
特定タイミングであれこれしたい場面は多いので、活用する機会は多くなりそうです。

【Unityメモ】エディタ拡張でタグを選択できるようにする

というわけで、絶賛仕事が忙しいりべるんです。
ブログのネタはおろか、Unity すらまともに触れていない日々です。
すっぽかすわけにもいかないので、何か簡単に記事にできそうなものについて書いていきます。

shot2ss20161019200247596

今回のお題はタイトル通り、「エディタ拡張でタグマネージャのタグを選択できるようにする」ことです!
ぐぐるとそれなりにヒットするので今更感はありますが、自分用のメモとして書いておきます。

拡張用クラスの作成

エディタ拡張用のクラスを作成します。
Editor ディレクトリの中に保存する必要があります。

using UnityEngine;
using UnityEditor;
using System.Collections;

[CustomEditor(typeof(Block))]
public class TagSelecter : Editor {
    SerializedProperty trigger_tag;

    void OnEnable() {
        trigger_tag = serializedObject.FindProperty("trigger_tag");
    }

    public override void OnInspectorGUI() {
        base.OnInspectorGUI();

        serializedObject.Update();

        trigger_tag.stringValue = EditorGUILayout.TagField("Trigger Tag", trigger_tag.stringValue);

        serializedObject.ApplyModifiedProperties();
    }
}

Unity5.3から SerializedProperty を使用した形式でないと値が保存されないようです。
OnInspectorGUI() 内で Update() と ApplyModifiedProperties() を呼ぶようにします。

ちなみにエディタ拡張で指定した際、クラスのフィールドがインスペクターに表示されなくなってしまいますが、親の OnInspectorGUI() を呼んであげれば上手くいくようです。
割と盲点だったので気を付けるようにします。

拡張先のクラス修正

タグマネージャから選択させる理由として、「ブロックが特定のタグと接触した場合のみ壊れる」といったことをしたいためです。
インスペクターから直接打ち込んでも良いのですが、タグマネージャから選択できるほうが便利で確実です。

ゲーム上のブロックは Block クラスを継承しているため、Block クラスにエディタ拡張を設定します。
エディタ拡張クラスで [CustomEditor] 属性で Block を指定しておきます。

using UnityEngine;
using System.Collections;

public class Block : MonoBehaviour {

    [HideInInspector]
    public string trigger_tag;

    public void OnTriggerEnter(Collider c){
        if (c.gameObject.tag == trigger_tag) {
            Destroy(gameObject);
        }
    }
}

とりあえず最低限の動作ですが、こんな感じでしょうか。
フィールド名はエディタ拡張クラスの FindProperty() の引数と合わせる必要があります。

public の場合、フィールド自体の項目とエディタ拡張の項目で二重に表示されてしまいます。
他から変えてもらっても困るので private か protected が妥当なところですが、今の構造上変えるのも面倒だったので、[HideInInspector] 属性を付けてインスペクター上から隠してみました。

まとめ

すごく短いですが、エディタ拡張でタグを選択できるようにしてみました!
所謂「マジックストリング」的なコーディングは避けたいところなので、こういった便利機能を入れていきたいですね。

【Unity】パーティクルシステムで光剣っぽいエフェクトの作成

というわけで、仕事繁忙のため全然更新できていませんが、今回のお題はこちらです!

shot2ss20161001091241801

剣のモデルから「光の剣」のようなものを出してみました!
「武器に光が纏って強くなる」なんてシチュエーションはファンタジーなゲームでよくありますよね!
プレイヤーがこの状態になると攻撃力やリーチが上がり、見た目と相まってテンションが上がったりします。

パーティクルシステムの Renderer モジュールで、生成するパーティクルの形状を変化させることができます。
初期状態では Billborad ですが、これを Mesh にしてモデルを指定すればOKです。

このあたりはテラシュールブログさんに分かりやすく記載されております。

下準備

パーティクルのベースとなる剣のモデルを作成します。
剣といっても柄の部分は不要で、刃の部分だけそれっぽくなっていればいいので、割と適当でも問題ありません。
そんなわけで、Blender でさくっと作ってみました。

shot2ss20161001100028913

今回は専用のモデルを作りましたが、キャラクターが剣を持っている場合はそのメッシュを使ってしまっても大丈夫です。
ただし剣の刃と柄が1オブジェクトに結合されている場合、パーティクルのエフェクトに柄も反映されてしまうので注意が必要です。
専用のモデルを作ると剣の形に依存しなくもなるので、モデルの状態や作成するゲームの雰囲気に合わせて・・・といった感じでしょうか。

ベースエフェクトの作成

光の剣となるエフェクトを作成します。
新規でパーティクルシステムを作成し、パラメータを設定していきます。

shot2ss20161001092021719

shot2ss20161001092101239

ポイントはやはり Renderer モジュールのメッシュ指定です。
これを剣の位置・移動に合わせるため、 剣に近い bone の子オブジェクトに設定し微調整します。

地味に詰まった点として、RenderMode を Mesh にし、シェーダーを Particle~ 系にすると、画面上に表示されなくなってしまいました。
ProjectSettings→Player→Other Settings→Auto Graphics API for Windows にチェックを入れることで解決しました。

shot2ss20161001092242887

新規プロジェクト作成時はチェックが入っているので外れている理由が分かりませんが、Windows10 にしたから・・・かも?
7→10のアップデート時にいろいろトラブったので、関係がないとは言い切れなそうです。

shot2ss20161001093940980

出来上がったのがこちら!
ちょっと安っぽい感じはしますが、雰囲気だけならそれなりに出来ているのではないでしょうか。
マテリアルの設定とかテクスチャとか拘ればもっと見た目はよくなると思われます。

20161001_01

振り回すとこんな感じに。
見た目は剣のリーチが伸びたように見えますが、現状では攻撃判定が伴っていない詐欺エフェクトだったりします。
通常時と光剣時で攻撃判定を分ける必要がありそうです。

ちなみに剣の軌跡の部分は「X-WeaponTrail」というアセットで簡単かつ綺麗に作れます。
下の記事で紹介しています。

「X-WeaponTrail」を使った武器の軌跡について

サブエフェクトの作成

↑だけでは寂しいので、何か適当にエフェクトを追加してみます。
関連オブジェクトをまとめるための空オブジェクトを作成し、先ほどのパーティクルを子オブジェクトとした上で追加していきます。

まず簡単にできるエフェクトとして、剣から少しオーラが出ているような見た目にしてみます。
メインエフェクトを複製し、Start Speed を少しだけ上げてみました。

shot2ss20161001095516161

もし「水の剣」など流動的なものを想定する場合は Start Speed ではなく、 Gravity Modifier を使うとそれっぽい見た目になります。
上げすぎると自己主張が強くなるので、少しだけ上げましょう。

shot2ss20161001100342543

あとちょっとしたエフェクトとして、剣の周りに粒子を出してみます。
今度は Shape を Mesh にして先程と同じ剣のモデル選択し、RenderMode は Billborad にします。
その他はデフォルトのマテリアルで適当に。

shot2ss20161001095638977

エフェクトとはちょっと違いますが、光剣という名目なのでライトが欲しいところです。
適当に PointLight を追加します。

shot2ss20161001095747584

上は Range が50、 Intensity を1.5に設定してあります。
Directional Light が効いていると微妙ですが、暗い場所なら目立つ・・・かも。

まとめ

そんなわけで、パーティクルシステムを使った光の剣 (っぽいもの) について考えてみました!
攻撃時の見栄えがかなりよくなるので、強力な攻撃の際には付けると良さそうです。

【Unity】CharacterMotor使用時のキャラクター移動を2Dに限定する

というわけで、今回の目標はタイトル通り「CharacterMotor 使用時のキャラクター移動を2Dに限定する」です!

shot2ss20160918121015592

「3Dベースで局所的に2D」は前に一度チャレンジしましたが、キャラクターの移動や敵の行動、オブジェクトの動きなどを全て2D向けに合わせなければなりません。
Unityには2Dモードもありますが、あれは本当に真横から見たドット絵のゲームを作るのに適したもので、3Dゲームで局所的に2Dっぽくするには向かない気がします。
元々3Dを想定して作っていたので、プレイヤーの移動関連はコンポーネントに丸投げで、当たり判定も CharacterController のコライダーをそのまま使っている状態です。

何故こんなことをしたいかと言うと、「場面に応じて2Dと3Dを切り替えたいから」です。
全エリア自由に3次元移動ができる場合でも、カメラワークとステージ構成次第で「2Dっぽいステージ」は出来ますが、画面の手前・奥にどれだけ移動させるかを制御するのが大変です。
所謂「見えない壁」を設置するのが手っ取り早い方法ですが、「行けそうなのに行けない」というのは私的に嫌いな要素です。
それなら特定のエリアだけ2Dにしてしまいたいと言うわけです。

前置きが長くなりましたが、今回はとりあえず自キャラクターの移動制御を2Dに限定させれるようにしてみました!
当たり判定自体は3Dであること、3Dモードにもスイッチできるようにしたいことから、CharacterController + CharacterMotor + PlatformInputController が付いた状態でやります。

移動入力を2D方向へ限定させる方法

CharacterMotorはソースコードが公開されており、C#版を作っている方もいたので、どんな処理をやっているか確認できます。
相応に複雑なので簡単なことではありませんが・・・。

http://forum.unity3d.com/threads/charactermotor-fpsinputcontroller-platforminputcontroller-in-c.64378/

本来は外部的なコンポーネントには手を加えたくないところです。
が、CharacterMotor を Player クラス側からあれこれしても上手くいかないので、(なるべく) 元の動きを崩さないように修正するしかありませんでした。
今回は上記のC#版をインポートして作っているため、既存スクリプトとの重複やメンバのアクセスレベルに注意する必要がありました。
特に「アクセス修飾子が省略されていてprotected扱い」というものが多かったです。

実際にキャラクター移動している部分は UpdateFunction() になります。
その中で「Move our character!」とド直球なコメントが書いてあるあたりを修正します。

// Move our character!
currentMovementOffset.z = 0;
movement.collisionFlags = controller.Move(currentMovementOffset);

「currentMovementOffset」の分だけ CharacterController.Move() で動かしているようなので、その手前でZ軸を0にしてしまいます。

また、上記コードのちょっと上に MovingPlatform に関する処理が書いてあります。
ステージ上に動く足場がなければ問題ありませんが、こちらもZ軸を0にしておきます。

 
moveDistance.z = 0;
if (moveDistance != Vector3.zero)
    controller.Move(moveDistance);

どうでもいい話ですが、ブロックのないif文って個人的に嫌いなんですよね。
ちょっとした処理だとしてもブロック付けてインデントして・・・が見やすいです。

これで移動に関しては2Dっぽくなります!
雰囲気でいえば「スマブラ」や「ロックマンX8」あたりに近いです。

ただし現状では CharacterMotor を直接書き換えた関係上、3Dで動きたいときでも2D方向に限定されてしまいます。
適当にフラグを1つ持たせて制御するのが手っ取り早そうです。

 
// Move our character!
if (invalidMoveZ) {
    currentMovementOffset.z = 0;
}
movement.collisionFlags = controller.Move(currentMovementOffset);

invalidMoveZ というフィールドを定義し、制御してみました!
あとは Player クラスから CharacterMotor を GetComponent() して切り替えたりすれば、移動に関しては問題なさそうですね。

回転方向を2D方向に限定させる方法

現状では PlatformInputController が入力方向にキャラクターを回転させてしまいます。
つまり移動方向は2D限定なのにZ軸 (手前や奥) に振り向くことができます。

空間は3D・移動は2Dのゲームで左右に振り向く際、回転を入れずに瞬時に振り向いているゲームが多い気がします。
2D限定なら手前や奥にキャラクターを向ける必要もないので、当然といえば当然ではあります。
リアリティなんて要らないので、自分もこの方法を取り入れてみました。

ということで、PlatformInputController をいじってみます。
まずは入力方向に一瞬で振り向くようにするため、ConstantSlerp() を修正します。

Vector3 ConstantSlerp(Vector3 from, Vector3 to, float angle)
{
    float value = Mathf.Min(1, angle / Vector3.Angle(from, to));

    if (invalidRotateZ) {
        value = 1;
    }

    return Vector3.Slerp(from, to, value);
}

CharacterMotor と同じように、invalidRotateZ というフィールドを定義して制御します。
Vector3.Slerp() で入力方向に振り向く際の角度を補完しているようなので、第3引数に1を指定すれば瞬時に向き直すようになります。

もう1つ、Z軸に向かないようにするための修正を加えます。
コメント「Set rotation to the move direction」のあたりです。

// Set rotation to the move direction   
if (autoRotate && directionVector.sqrMagnitude > 0.01)
{
    Vector3 newForward = ConstantSlerp(transform.forward, directionVector, maxRotationSpeed * Time.deltaTime);
    newForward = ProjectOntoPlane(newForward, transform.up);

    if (invalidRotateZ) {
        newForward.z = 0;
    }

    if (newForward != Vector3.zero) {
        transform.rotation = Quaternion.LookRotation(newForward, transform.up);
    }
}

newForward が振り向く方向のようなので、これのZ軸を0にすればOKです。
ただしこれだと零ベクトルが渡された場合に奥を向いてしまう上、Unity から警告が出たりしてしまいます。
なので、零ベクトルだったらそもそも LookRotation() を実行しないようにしました。

20160910

これで移動も回転も2Dに限定にすることができました。
GIFアニメーションでは分かりませんが、Z軸に移動入力しても移動・回転が行われていません。

まとめ

そんなわけで、CharacterMotor 使用時の移動方向を2Dに限定させてみました!
キャラクターの動き的にはだいぶ近づいた気がします。

ただし敵は自在に3Dで動いていたりするので、ゲーム的な意味で2Dにするのはなかなか大変ですね。
とはいえ、敵やオブジェクトは RigidBody の Constraint である程度制御できるので、プレイヤーキャラクターの制御よりは楽かもしれません。
今度また挑戦してみます!

今日のイラスト

本ブログのマスコット (?) のシルリスを描いてみました。
メインキャラ3匹のデザインを一新するため、シルリスも新しいデザインを考えているところです。

また、線画でペンタブを使う練習も兼ねていたりします。
パスツールで書くより難しいですが、機械的ではない線が描けるため味が出ます。
ただ影の描き方がだいぶ適当な気もします・・・。

shilriss7

【Unity】パーティクルシステムのSkinned Mesh Rendererについて

というわけで、Unity5.3 からパーティクルシステム (Shuriken) の Shape に「Skinned Mesh Renderer」が加わったそうです!
ちょっと気になったので、どんな感じの機能か使ってみました!

パーティクル設定

当然ですが、「Skinned Mesh Renderer」が付いたオブジェクトが必要です。
3Dモデルをインポートすればメッシュに付いてくるので、適当な自作キャラクターの体を使います。

shot2ss20160907233803185

シーン内の Skinned Mesh Renderer を指定すると、パーティクル本体をどこに配置しても、指定したメッシュに合わせてレンダリングされます。
故に空オブジェクト内に整理して置いたり、キャラクターの子オブジェクトにしても問題ないようです。
他にもメッシュからの生成位置(オフセット)を調節したり、マテリアルの色をメッシュに合わせたりできます。

shot2ss20160908001214777

この状態で再生すると、上のようにキャラクターのメッシュに合わせてパーティクルが生成されます。
体からオーラが出ているような感じですね。
「Start Speed」や「Color Over Lifetime」をいじるともっとそれっぽくなります。

キャラクターのメッシュを分けている場合、その分だけパーティクルを作成・設定しておく必要があります。
画像のマリンパも「頭」「上半身」「下半身」と無駄に分かれていたりします。

何が良いかと言えば、「アニメーションの動きに合わせてパーティクルを出せる」ことでしょうか。
今までは固定のメッシュに対してレンダリングするのが限界だったので猶更ですね。
「ダッシュ中にキャラクターの形に合わせてエフェクトを出す」といったことも簡単にできるようになるかも。

いじって分かったこと

「アニメーションの動きに合わせてパーティクルを出せる」と書きましたが、その時点のメッシュの位置に合わせて生成するだけです。
生成後もメッシュに合わせて追従してくれる・・・なんてことはありません。
「Simulation Space」を Local にするとオブジェクト自体の移動には付いてきてくれますが、メッシュの移動には付いてきてくれません。
「アニメーションに合わせてリアルタイムで動かしたい」というケースでは不向きかと思われます。

また、流石に Cloth コンポーネント適用時の動きには対応してくれないようです。

shot2ss20160907232506900

マント (赤い部分) に Cloth を追加し、軽く動かした状態でパーティクルを出してみましたが、(分かりにくいですが) SkinnedMeshRenderer 元々の位置にレンダリングされます。
Cloth の動きに合わせてパーティクルを出すのは大変そうなので、こればかりはしょうがない部分でしょうか。

という点を踏まえると、使い道がありそうでないかもしれません。
手持ちの武器を光らせるのに手っ取り早いかなーと思いきや、パーティクルが追従してくれないので微妙でした。

応用例

「生成後はアニメーションの動きに合わせて移動しない」ことから、アニメーション中に断続的に生成すると残像っぽいエフェクトになります。
これを高速で動くオブジェクトに適用すると面白そう。

shot2ss20160910201151286

「Duration」と「Start Lifetime」の値を短めに調節します。
短時間にドバッとパーティクルを出す場合、Rate を0にして Bursts を設定するといい感じです。
「Simulation Space」は World に設定。
色やサイズ、スピード等はお好みで。

20160910_01

自分のキャラは小さめなのであまり実感はありませんが、頭身の高い人型ならもっと見栄えはよくなるかも。
もっとサイズやスピードを上げても良さそうですね。

まとめ

そんなわけで、パーティクルシステムの「Skinned Mesh Renderer」を試してみました!
ネタがないときはこんな感じで「Unityの気になった機能」を調べてみるのもいいかもしれません。