【Unity】【C#】クエリちゃんを飛行させる! 
2015/03/12 Thu [edit]
クエリちゃんにはデフォルトで飛行モーションが入っているのが良いね。せっかくなのでクエリちゃんを空に飛ばしたいと思い、アキバモデルのデモを見ながら、それに近い動きになるようにスクリプトを書いてみた。
※クエリちゃんのモデルのダウンロードからの場合は→こちら。
(※) Unity 4.6.1f2 / Windows8.1(x64) で確認
■【汎用】Input.GetKey() で Input.GetAxisRaw() のような仮想軸の値を返すクラス
■飛行用アニメーターを作る
■クエリちゃんモデルを飛行させる!
■クエリちゃん用 飛行移動スクリプト
ただ、なんとなくだけどアキバモデルのデモは操作とモーションの変化がかなり反応良いので、もしかしたらアニメーションのトランジションはスクリプトで組んでいるのかもね。また上下のモーションが優先的で左右のモーションより強い(同時押ししてみたりすると)。前進速度が無くなるとアイドル(停止)モーション固定になるという感じかな。コードの方がこういう動きは簡単な気がする。
私はアニメーターで作ってみたが、方向反転する場合、Input.GetAxis()/Input.GetAxisRaw() を使うとどんなに速くキーを操作しても、「上→前→下」のように中間のモーションを経由してしまう。また Input Manager の Gravity や Sensitivity を調整しても思うように「上→下」のように直接遷移してくれない(できなくはないが)。それにプロジェクト毎にInput Managerを調整するのも面倒だし、できればデフォルトで使いたいので、Input.GetKey() を擬似的に GetAxis() のように -1~0~1 の仮想軸の値を返すようなクラスを作ってみた。そしたら単純なアニメーターでもいい感じの動きになった。それになるべくスクリプトとアニメーターでロジックを分離した方が、色々流用もしやすくなるしね。
また、今回は作るものが複雑になったので、使用したスクリプトやアニメーターをあらかじめ用意した。記事を見ながらやればあっという間にできるだろう。ただ使うだけなら、「■クエリちゃんモデルを飛行させる!」まで読み飛ばしてもいい。
●クエリちゃん用 飛行移動スクリプト
using UnityEngine;
using System.Collections;
namespace QueryChan {
/**
* クエリちゃん用 飛行コントローラ
* 2015/3/12 Fantom
*/
[RequireComponent(typeof(CharacterController))]
[RequireComponent(typeof(QueryMechanimController))]
public class QueryFlyingController : MonoBehaviour {
/** 前進速度 */
private float forwardSpeed = 0f;
/** キーによる前進速度加速量 */
public float accelerarion = 0.05f;
/** 最小前進速度(下限速度 > 0) */
public float speedMin = 0f;
/** 最大前進速度(上限速度) */
public float speedMax = 10f;
/** 上下[左右]速度(Y軸[X軸:rotationMode=false]方向移動速度) */
public float moveSpeed = 3f;
/** 左右旋回モード(false = 左右移動) */
public bool rotationMode = true;
/** 左右旋回速度 */
public float rotationSpeed = 90f;
//------------------------------------------------//
//キー使用 ON/OFF
/** (前進)加速キーの使用 ON/OFF */
public bool useAccelKey = true;
/** (前進)減速キーの使用 ON/OFF */
public bool useBrakeKey = true;
//------------------------------------------------//
//キー設定
/** (前進)加速キー */
public KeyCode accelKey = KeyCode.LeftShift;
/** (前進)減速キー */
public KeyCode brakeKey = KeyCode.LeftControl;
/** 上昇キー */
[SerializeField]
private KeyCode upKey = KeyCode.UpArrow;
/** 下降キー */
[SerializeField]
private KeyCode downKey = KeyCode.DownArrow;
/** 左移動キー */
[SerializeField]
private KeyCode leftKey = KeyCode.LeftArrow;
/** 右移動キー */
[SerializeField]
private KeyCode rightKey = KeyCode.RightArrow;
//------------------------------------------------//
//仮想軸キー管理用
/** 左右仮想軸キー */
private AxisKey horizontalKey;
/** 上下仮想軸キー */
private AxisKey verticalKey;
//------------------------------------------------//
//実行用
/** 移動方向のローカル空間→ワールド空間変換用 */
private Vector3 moveDirection = Vector3.zero;
/** CharacterController キャッシュ */
private CharacterController controller;
/** QueryMechanimController(Script) キャッシュ */
private QueryMechanimController queryMechanim;
/** Animator キャッシュ */
private Animator animator;
//------------------------------------------------//
//ステート管理用
/** ステートハッシュと QueryChanAnimationType の変換テーブル */
private static Hashtable StateTable = new Hashtable() {
{Animator.StringToHash("Base Layer.Idle"), QueryMechanimController.QueryChanAnimationType.FLY_IDLE},
{Animator.StringToHash("Base Layer.Forward"), QueryMechanimController.QueryChanAnimationType.FLY_STRAIGHT},
{Animator.StringToHash("Base Layer.Left"), QueryMechanimController.QueryChanAnimationType.FLY_TOLEFT},
{Animator.StringToHash("Base Layer.Right"), QueryMechanimController.QueryChanAnimationType.FLY_TORIGHT},
{Animator.StringToHash("Base Layer.Up"), QueryMechanimController.QueryChanAnimationType.FLY_UP},
{Animator.StringToHash("Base Layer.Down"), QueryMechanimController.QueryChanAnimationType.FLY_DOWN},
};
/** 変換テーブルにないときのデフォルトハッシュ */
private int defaultState = Animator.StringToHash("Base Layer.Idle");
/** 現在のステートハッシュ */
private int currentState;
/** 1つ前のステートハッシュ */
private int oldState;
/** ステートによって表情や手も変更する ON/OFF */
public bool useEmotionAndHandChange = true;
//------------------------------------------------//
// Use this for initialization
void Start () {
//オブジェクトキャッシュ
controller = this.GetComponent<CharacterController>();
animator = this.GetComponentInChildren<Animator>();
queryMechanim = this.GetComponent<QueryMechanimController>();
queryMechanim.ChangeAnimation(QueryMechanimController.QueryChanAnimationType.FLY_IDLE, false);
AnimatorStateInfo animatorInfo = animator.GetCurrentAnimatorStateInfo(0); //BaseLayer
currentState = animatorInfo.nameHash;
oldState = currentState;
//キーを設定
horizontalKey = new AxisKey(rightKey, leftKey);
verticalKey = new AxisKey(upKey, downKey);
//初期化
forwardSpeed = Mathf.Clamp(forwardSpeed, speedMin, speedMax);
}
// Update is called once per frame
void Update () {
//入力による移動処理
bool accel = useAccelKey && Input.GetKey(accelKey);
bool brake = useBrakeKey && Input.GetKey(brakeKey);
forwardSpeed += (accel ? accelerarion : 0f) + (brake ? -accelerarion : 0f);
forwardSpeed = Mathf.Clamp(forwardSpeed, speedMin, speedMax);
float h = horizontalKey.GetAxis();
float v = verticalKey.GetAxis();
if (rotationMode) {
transform.Rotate(0f, h * rotationSpeed * Time.deltaTime, 0f);
moveDirection.Set(0f, v * moveSpeed, forwardSpeed);
} else {
moveDirection.Set(h * moveSpeed, v * moveSpeed, forwardSpeed);
}
moveDirection = transform.TransformDirection(moveDirection);
controller.Move(moveDirection * Time.deltaTime);
//------------------------------------------------//
//Animator
animator.SetFloat("Speed", forwardSpeed);
animator.SetFloat("Horizontal", h);
animator.SetFloat("Vertical", v);
//------------------------------------------------//
//ステートによって表情や手を変更する
if (useEmotionAndHandChange) {
AnimatorStateInfo animatorInfo = animator.GetCurrentAnimatorStateInfo(0); //BaseLayer
currentState = animatorInfo.nameHash;
if (oldState != currentState) {
oldState = currentState;
if (StateTable.ContainsKey(currentState)) {
queryMechanim.ChangeAnimation((QueryMechanimController.QueryChanAnimationType)StateTable[currentState], false);
} else {
queryMechanim.ChangeAnimation((QueryMechanimController.QueryChanAnimationType)StateTable[defaultState], false);
}
}
}
}
}
}
上下左右のキー設定を「SerializeField」の「private」スコープにしているが、これはこのスクリプト内では下記の「AxisKey」クラスを使っているためで、ユーザーにカスタム設定させたい場合は「horizontalKey」「verticalKey」を「Start()」の中にあるように「AxisKey」のインスタンスを再設定するメソッドなどを付け加えれば良い。
「rotationMode」はデフォルトで左右のキーを旋回にしてあるが、false にすれば単純な移動になる。シューティングには false の方がしっくり来るかもしれない。また「speedMin」「speedMax」で前進速度を制限してあるが、同じ値にすれば一定速度のみとなる。「speedMin」を初期値で0以上にしておけば、プレイ開始と同時に前進することになる。
またクエリちゃんは表情や手(通常、グー、パー)を「QueryMechanimController」でモーションと一緒に切り替えているようだ。そのためモーションを増やす場合には「StateTable」に登録する必要がある。このスクリプトでは「useEmotionAndHandChange = false」にすれば自動的には変化しなくなるようにしてあるが、アニメーションイベント仕様にしたり、自分で設定したい場合も false の方が良いだろう。また最低限の飛行モーションだけなら使わなくても見た目おかしくはないようだ。
クエリちゃんスクリプトをメニューでまとめるために namespace を「QueryChan」としているが、必要あれば変更するのも良い。
尚、キー操作には以下のクラスが必要である。汎用的なクラスなので他のものにも使える。
●【汎用】Input.GetKey() で Input.GetAxisRaw() のような仮想軸の値を返すクラス
using UnityEngine;
using System.Collections;
/**
* Input.GetKey() で Input.GetAxisRaw() のような仮想軸の値を返す。
* -1, 0, 1 のみで中間の値はない。
* 2015/3/12 Fantom
*/
public class AxisKey {
/** +1 を返すキー */
public KeyCode positiveKey = KeyCode.UpArrow;
/** -1 を返すキー */
public KeyCode negativeKey = KeyCode.DownArrow;
/** 現在押しっぱなしにされているキー */
private KeyCode holdKey = KeyCode.None;
//------------------------------------------------------//
//コンストラクタ
/**
* キー指定のコンストラクタ
* positiveKey = 1 / negativeKey = -1 となる。
*/
public AxisKey(KeyCode positiveKey, KeyCode negativeKey) {
this.positiveKey = positiveKey;
this.negativeKey = negativeKey;
}
//------------------------------------------------------//
/**
* Input.GetKey() で Input.GetAxisRaw() のような仮想軸の値を返す。
* positiveKey = 1 / negativeKey = -1 / 離したとき or それ以外は 0 を返す。
* Input.GetAxisRaw() は急激に反対のキーを押しても -1→0→1 のように 0 を通過するが、
* この関数は、-1→1 のように 0 を通過しない値を返す。
* 同時押しの場合は、先に押されていた方の値を返す。
*/
public float GetAxis() {
bool positive = Input.GetKey(positiveKey);
bool negative = Input.GetKey(negativeKey);
//同時押しの場合、先に押されていた方を優先する
if (holdKey == positiveKey && positive) {
return 1f; //holdKey は変更なし
}
if (holdKey == negativeKey && negative) {
return -1f; //holdKey は変更なし
}
if (positive) {
holdKey = positiveKey;
return 1f;
}
if (negative) {
holdKey = negativeKey;
return -1f;
}
holdKey = KeyCode.None;
return 0f;
}
}
■飛行用アニメーターを作る
※クエリちゃんのモデルのダウンロードからの場合は→こちら。
次にスクリプトから利用するアニメーターを作ろう。アニメーターとは複数のアニメーションクリップを遷移させるものと考えて良い。(SD)ユニティちゃんのときと違って、クエリちゃんは「face」(表情)レイヤーを使ってないので、新規で作成しても変わらない。アニメーションクリップはスクリプトで判別して遷移することもできるが、アニメーターを使った方がコードを大幅に省けるのでオススメだ。また新しいモーションを加えたりするときも、アニメーターを複製して編集した方が作業がラクになり、流用性も高い。
1.プロジェクトエクスプローラから「Create>Animator Controller」で新規作成する。名前はとりあえず「PQchan_Flying」としておいた。これをダブルクリックして「Animator」タブを開こう。
2.使用するパラメータはスクリプトを見てもわかるが、「Speed」「Horizontal」「Vertical」を「Float」 型で作れば良い。ちなみに「Speed」は前進方向の移動速度、「Horizontal」は左右方向、「Vertical」は上下方向の移動を表している。「Paramters」の横にある「+」ボタンを押して作ろう。

3.次にステートだが、「Idle」(停止)、「Forward」(前進)、「Left」(左移動)、「Right」(右移動)、「Up」(上移動)、「Down」(下移動)の6つを作る。Animator 上で右クリックし「Create State>Empty」で新規作成、名前を付けたら適当に配置する。配置は任意で構わないが、今回は「下←→前←→上」「左←→前←→右」だけでなく、「下←→上」「左←→右」のような遷移もしたいので、上下と左右はなるべく近い方が良いかもしれない。またデフォルトステート(オレンジ色)は「Idle」にしておく。ステート上を右クリックして「Set As Default」で設定できる。

4.ステートを作り終えたら、それぞれにアニメーションクリップを割り当てる。「Idle」をクリックし、インスペクターの「Motion」で「100_Fly_Idle」を選ぶ。「Forward」は「101_Fly_Straight」、「Right」は「102_Fly_toRight」、「Left」は「103_Fly_toLeft」、「Up」は「104_Fly_Up」、「Down」は「105_Fly_Down」にする。それぞれ2種類ずつ入っているが、下に表示される情報を見て、「~_Mechanim.fbx」となっている方を選ぶ。新しく作る場合は基本的にメカニム仕様となっているものを使った方が良い。

●「Forward」のモーション

●「Right」のモーション

●「Left」のモーション

●「Up」のモーション

●「Down」のモーション

●ファイル選択は基本的に「~_Mechanim.fbx」の方で

5.各モーションの設定を終えたら、今度はそれぞれの遷移を作っていこう。まずは「Idle」と「Forward」の遷移を作る。停止と前進の条件なので、パラメータは「Speed」を使う。「Speed」が「0.1」より上のとき前進し、「0.1」より下のとき停止とする。ではやってみよう。「Idle」ステートを右クリックし、「Make Transition」で「Forward」へ矢印を伸ばす。「Forward」をクリックして矢印を繋いだら、今度はその矢印をクリックし、インスペクターを表示させる。「Exit Time」を「Speed」に変更したら、条件が「Greater」(より上)になっていることを確認し、値に「0.1」と入れる。これで停止→前進への遷移ができた。

●「Idle→Forward」のインスペクター(遷移条件)

6.今度は逆に「Forward→Idle」の遷移を作る。同じように「Forward」から「Make Transition」で「Idle」に繋いだ後、インスペクターで遷移条件を設定する。「Exit Time」を「Speed」に変更したら、条件を「Less」(より下)にし、値に「0.1」と入れれば、前進←→停止への遷移は完了だ。

●「Forward→Idle」のインスペクター(遷移条件)

7.他の遷移も要領は同じだ。今度は前進から上下のステートの遷移を作ろう。「Forward」から「Make Transition」で「Up」「Down」にも同じように繋いでいく。パラメータは「Vertical」(上下方向)を使えば良い。正の値は上方向、負の値は下方向を表すので、正は「0.1」、負は「-0.1」をしきい値とする。インスペクターで次々と設定していこう。

●「Forward→Up」のインスペクター(遷移条件)

●「Up→Forward」のインスペクター(遷移条件)

●「Forward→Down」のインスペクター(遷移条件)

●「Down→Forward」のインスペクター(遷移条件)

8.これで「Up←→Forward←→Down」の遷移はできたが、このままではキーを「上→下」「下→上」と急激に反転したとき、「Forward」を通過してしまう。そうなると少し動きがきごちなくなるので、そうならないように「Up←→Down」経由の遷移も作ろう。遷移条件は「Forward」のときと同じとなる。またこの経由を作りたかったので、前述した「AxisKey」というクラスを使っている。具体的に言えば、上から下に急反転したしたとき「1 → -1」となり、下から上に急反転したしたとき「1 → -1」と値が設定される。Input.GetAxisRaw() だと、デフォルト設定では「1 → 0 → -1」「-1 → 0 → 1」となり、前進モーションを経由してしまう。調整しても良いが、できればデフォルトでいい動きになった方が、改造したいときもラクだからだ。

●「Down→Up」のインスペクター(遷移条件)

●「Up→Down」のインスペクター(遷移条件)

9.ただこの状態では、まだ直接「Down→Up」「Up→Down」の遷移はしないと思う。というのは遷移条件を考えてみれば当然で、例えば急激に「上→下」キーを動かしたとき、パラメータ「Vertical = -1」となるが、これは「Up→Forward」の遷移条件「Vertical < 0.1」と、「Up→Down」の遷移条件「Vertical < -0.1」の両方に被るからだ。そういうときは遷移の優先順位を付けてやれば良い。やり方は「Up」のステートをクリックし、インスペクターで「Transitions」の順番をドラッグで変える。つまり「Up→Down」を「Up→Forward」より上にする。同じ条件に当てはまる場合、上にあるほど優先順位が高くなると考えて良い。同じように「Down」ステートもインスペクターで「Down→Up」を「Down→Forward」よりも上にしよう(BaseLayer の名前は気にしなくて良い)。

●「Down」のインスペクター(優先順位)

10.考え方は同じで「Left←→Forward←→Right」「Left←→Right」の遷移も作ってみよう。左右の場合はパラメータが「Horizontal」になるだけで、「Horizontal < -0.1」は左へ、「Horizontal > 0.1」は右へ遷移すると考える。設定し終わったら、「Left」「Right」ステートのインスペクターを見て、「Left→Right」「Right→Left」の遷移が「Forward」の遷移より上にすることを忘れないようにしよう。

●「Forward→Left」のインスペクター(遷移条件)

●「Left→Forward」のインスペクター(遷移条件)

●「Forward→Right」のインスペクター(遷移条件)

●「Right→Forward」のインスペクター(遷移条件)

●「Left→Right」のインスペクター(遷移条件)

●「Right→Left」のインスペクター(遷移条件)

●「Left」のインスペクター(優先順位)

●「Right」のインスペクター(優先順位)

11.ここまでで十分いい感じに動くようになったので完成でも良いと思うが、アキバモデルのデモを見ると、移動中でも前進速度が無くなったとき、「Idle」モーションになってるね。とりあえずそれをシミュレートしてみよう。といっても考え方は簡単で「Forward→Idle」の遷移条件「Speed < 0.1」と同じものを各ステートにも追加するだけだ。

●「Up→Idle」「Down→Idle」「Left→Idle」「Right→Idle」のインスペクター(遷移条件)[※すべて同じ]

12.最後に「Idle」状態から前進+上下左右したとき、各ステートに直接遷移する設定もしておこう。つまり先ほどの逆の動きとなる。遷移条件はすべて「Speed > 0.1」を含む必要があるが、上下左右にはそれに加えて「Horizontal」(左右)、「Vertical」(上下)の条件も含めないといけない。遷移条件の下にある「+」ボタンを押して、条件を2つずつにしよう。また設定し終えたら、「Idle」のステートをクリックし、インスペクターで遷移の優先順位も変えておく。並び順は自分の任意で構わないが、「Idle→Forward」だけは一番下にしておかないと上手く遷移しない。アキバモデルのデモに合わせて、上下を優先、左右を真ん中に置いておいた。まぁ、完全に同じ動きにはならなかったが、全体的にコンパクトで、十分いい動きになったと思う。これでアニメーターは完成だ。

●「Idle→Up」のインスペクター(遷移条件)

●「Idle→Down」のインスペクター(遷移条件)

●「Idle→Left」のインスペクター(遷移条件)

●「Idle→Right」のインスペクター(遷移条件)

●「Idle」ステートのインスペクター(優先順位)

■クエリちゃんモデルを飛行させる!
さてここまで来たらあと一息。必要なものは揃ったので、あとはモデルに組み込むだけだ。空に飛ばすのでシーンは何でも構わないが、床くらいはあった方がわかりやすいので、以前クエリちゃんを歩行させた時のように「Plane」にテクスチャでも貼って、適当に背景でも作ろう。今回はその辺りは割愛して、クエリちゃんを配置する所からはじめる。以前作ったシーンがあれば、[Ctrl - D](Duplicate)で複製して、それを使っても良い。
1.プロジェクトエクスプローラで「PQAssets>Query-Chan>Prefabs」フォルダを開き、「Query-Chan_Locomotion」をヒエラルキーにドラッグ&ドロップしよう。このプレハブは歩行用のものだが、「Query Emotional Controller」(表情)や「Query Mechanim Controller」(モーションや手など)、「Character Controller」(コライダ)などが初めから組み込まれているので採用した。ハロウィンコスやサンタコスでも構わないが、その場合はこれらのコンポーネントを手動で追加する必要がある。

2.モデルをヒエラルキーに追加したら、インスペクターで少しばかり Y 軸の配置と「Character Controller」の「Center」を調整しよう。編集画面上ではモデルとコライダがずれているように見えるが、飛行モーションはどうやら基準座標は真ん中になっているようなので(歩行モーションは足元)、実行したとき、少しばかり空中に浮いていないと、床にめり込んでしまうのだ。


3.次いで「Query Locomotion Controller」(歩行用スクリプト)を前述の「Query Flying Controller」(飛行用スクリプト)に入れ替えよう。「Script」の右にある「○」ボタンから選択しても良い。

4.次にアニメーターを設定しよう。ヒエラルキーで「Query-Chan_Locomotion」の▼を開いて、「BodyParts」を選択したら、インスペクターで「Animator>Controller」を「PQchan_Movement」(歩行用)から「PQchan_Flying」(飛行用)に変更する。「PQchan_Flying」は先に作ったアニメーターのことだ。


5.最後にカメラを追従させよう。超簡単な方法は「Main Camera」を「Query-Chan_Locomotion」の子要素にしてしまう方法だ。ヒエラルキーで「Main Camera」を「Query-Chan_Locomotion」にドラッグ&ドロップして、カメラの位置を調整するだけで良い。こうするとクエリちゃんと一緒にカメラも動く。ここでは微妙に上から見下ろす角度にしておいた。これでめでたく完成だ。シーンは適当な名前で保存しよう。


デフォルトでは[↑][↓]で上昇下降、[←][→]で左右旋回、[Shift](左)で前進加速、[Ctrl](左)で前進減速となっている。「Query Flying Controller」のインスペクターで「Rotation Mode」のチェックを外せば、左右キーはシューティングの様な移動になる。シーンを再生して確かめてみよう。改造はスクリプトの解説を参考にして欲しい。
以上お疲れ様でした。前述したようにこの飛行コントローラは「ハロウィンコス」や「サンタコス」でも使えます。その場合は「クエリちゃん動かす!」のときのように、「Query Emotional Controller」「Query Mechanim Controller」「Character Controller」などをインスペクターでそっくりコピペすると良いでしょう。ただし、ハロウィンコスやサンタコスには専用の飛行アニメーションクリップはないので、ハロウィンコスはスティックが体を突き抜けたり、サンタコスは履いてない疑惑が暴露(笑)されたりという問題がありますが…(〇〇には見えない下着とか、夢を忘れた人には見えないパ○ツという設定にする手もある(?))。
また、目パチもしたい人は「クエリちゃんを目パチさせる」も参考にして下さい。
(関連記事)
【Unity】クエリちゃんを動かす!
【Unity】【C#】クエリちゃんを目パチさせる
【Unity】【C#】SDクエリちゃんを飛行させる!
【Unity】【C#】ユニティちゃんを飛行させる!
【Unity】プロ生ちゃんを飛行させる!
【Unity】【C#】SmoothFollow に回転アングルと距離の遠近機能を付けてみる
【Unity】【C#】FPS(フレームレート)をリアルタイムに測定して表示するv2(4隅選択可能で、画面サイズの変更にも対応版)

- 関連記事
トラックバック
トラックバックURL
→http://fantom1x.blog130.fc2.com/tb.php/168-ee0fc4a0
この記事にトラックバックする(FC2ブログユーザー)
| h o m e |