子ノ刻ラボラトリィ

子ノ刻[23:00-01:00]に右脳系ディベロッパが時々お送りするAndroidアプリ開発日誌。主にUnity。最近LINEスタンプ。時々雑記。

ロックマン的なものでUnity2Dを勉強 その5

こんばんは! しんがです。

前回に引き続き、ロックマン的なものでUnity2Dのお勉強。

今回も状態管理についてやってみよー

今回やろうとしていること
  • 状態遷移の仕組みを作る
  • 状態とアニメーションの連動

ひとまず今回の状態遷移では、下の表の黄色い基本的な部分だけ。
そして、ジャンプもボタンでジャンプと立ちの状態を切替えるだけ。
キャラクターの移動もまだ実装しない。

f:id:shinga0221:20140121014053p:plain

状態遷移の仕組みを作る

ぼくのかんがえたさいきょうのじょうたいせんいのしくみ(仮)。

とりあえずソースをバーン

using UnityEngine;
using System.Collections.Generic;
using System.Reflection.Emit;

// 状態列挙体
public enum State : int {
    Idle=0, // 立ち
    Run,    // 走り
    Jump,   // ジャンプ
    Dmage,  // ダメージ
    Thiun   // ティウン
};

// (1)アクションメソッド格納用デリゲート
public delegate int Action();

/// <summary>
/// Numbersクラス
/// キャラクターのベースとなるクラス。
/// 基本のアクションを実装する。
/// </summary>
public class Numbers : MonoBehaviour {

    // (2)状態マップ
    public Dictionary<int, Action> actMap = new Dictionary<int, Action>();
  
    // 状態
    public int NowState {get; set; }
    
    // Use this for initialization
    void Start () {
        // 状態をマップを作成
        actMap.Add((int)State.Idle, this.idle);
        actMap.Add((int)State.Run,  this.run);
        actMap.Add((int)State.Jump, this.jump);
    }
	
    // Update is called once per frame
    void Update () {
	// (3)状態マップから次の状態を取得
        this.NowState = (int)actMap[this.NowState]();

        //Animatorコンポーネントの「NowState」プロパティに現在の状態を渡す
        GetComponent<Animator>().SetInteger("NowState", this.NowState);

        Debug.Log("NowState:" + this.NowState);
    }

    // (4)アクションメソッド
    /// <summary>
    /// 状態:立ち
    /// </summary>
    /// <returns></returns>
    private int idle(){
        // L/Rが押されたら走り状態に遷移
        if (Input.GetKey("left") || Input.GetKey("right"))
        {
            return (int)State.Run;
        }
        // スペースが押された瞬間ジャンプ状態に遷移
        if (Input.GetKeyDown("space"))
        {
            return (int)State.Jump;
        }

        // 何もなければ立ち状態を継続
	return (int)State.Idle;
    }

    /// <summary>
    /// 状態:走り
    /// </summary>
    /// <returns></returns>
    private int run(){
        // スペースが押された瞬間ジャンプ状態に遷移
        if (Input.GetKeyDown("space"))
        {
            return (int)State.Jump;
        }
        // L/Rが押されているならば走り状態を継続
        if (Input.GetKey("left") || Input.GetKey("right"))
        {
            return (int)State.Run;
        }
        // 何も押されていなければ立ち状態に遷移
        return (int)State.Idle;
    }

    /// <summary>
    /// 状態:ジャンプ
    /// </summary>
    /// <returns></returns>
    private int jump(){
        // ひとまず仮の実装
        // スペースが押されているならばジャンプ状態を継続
        if (Input.GetKey("space"))
        {
            return (int)State.Jump;
        }
        // 何も押されてなければ立ち状態に遷移
        return (int)State.Idle;
    }
}

デリゲートを初めて使ってみました。

メソッドを変数につっこんで、実行できる便利なもの。

このあたりがミソでしょうか。

// (1)アクションメソッド格納用デリゲート
public delegate int Action();
    ⇒ 返り値がint、引数なしのメソッドを格納できるデリゲート


// (2)状態マップ
public Dictionary<int, Action> actMap = new Dictionary<int, Action>();
    ⇒ intをキーに、アクションメソッドを格納するハッシュマップ
  ⇒ キーはState列挙体をintに変換したものを使用
  ⇒ アクションメソッドとしてidle()、run()、jump()などを格納する


// (3)状態マップからアクション次の状態を取得
this.NowState = (int)actMap[this.NowState]();
    ⇒ State列挙体の値が格納された現在の状態(NowState)をキーにして
    デリゲートに格納したアクションメソッドを呼び出す

// (4)アクションメソッド
private int idle(){
    // L/Rが押されたら走り状態に遷移
    if (Input.GetKey("left") || Input.GetKey("right"))
    {
        return (int)State.Run;
    }
    // スペースが押された瞬間ジャンプ状態に遷移
    if (Input.GetKeyDown("space"))
    {
        return (int)State.Jump;
    }
    // 何もなければ立ち状態を継続
    return (int)State.Idle;
}
  ⇒アクション毎に、キーやあたり判定等による状態の遷移、移動量の設定などを実装
   今回はキーによる状態の遷移のみ仮実装

アクション毎にメソッド実装してやらないといけないけど、キャラ毎の固有のアクションとかも実装しやすくなると思う。
たとえば

  1. Rockmanクラスをつくって
  2. enum RockState : int { sSiding=100 }; // スライディング
  3. Rockmanクラスにidle(), run()をつくって、スライディングに遷移する条件のみ追加
  4. sliding()を実装
  5. NumbersのactMapにState.Idel、State.Run、RockState.Slidingをキーに、idle()、run()、sliding()を追加

デリゲートは1つのデリゲートに複数メソッドを格納できる。
そして、実行するときは格納した順番に実行してくれるという。

なので、State.Idleのキーのところに、Numbers.idle()とRockman.idle()を格納すれば、
Numbers.idle()の次にRockman.idle()が実行されるはず!(未検証)

Rockman.idle()でNowStateをRockState.Slidingにしてあげれば、Rockman.sliding()が呼ばれるはず!

アクションの優先順位とかは考えないといけないけど、今度試してみよー

状態とアニメーションの連動

さて、内部的に状態は変わってるけど、アニメーションが変わらないとよくわからないので連動します。

f:id:shinga0221:20140121015620p:plain
その3で作ったAnimetorを作り直す。

  1. AnimeorのParametersを一度削除
  2. ParametersにInteger型のNowStateを追加
  3. 遷移条件を以下のように設定
  • idle -> run : NowState Equals 1
  • idle -> jump : NowState Equals 2
  • run -> idle : NowState Equals 0
  • run -> jump : NowState Equals 2
  • jump -> idle : NowState Equals 0

スクリプト側は、現在の状態をSetIntegerしてあげればOK!

//Animatorコンポーネントの「NowState」プロパティに現在の状態を渡す
GetComponent<Animator>().SetInteger("NowState", this.NowState);

f:id:shinga0221:20140121020901p:plain
わかりずらいけど、スペースを押すとジャンプの画像にかわるよ!

そしてUnity Web Playerのサンプル

今後やりたいこと
  • 動きの実装
  • 固有アクションの実験
  • キー操作管理


あ、あとUnityのEditorをMonoDevelopからVisualStudio2013に変えました。
日本語が打てるだけでやる気が全然ちがう!!

やりかたは色々なサイトに書いてあるので割愛。

今回はちょっとがんばったー
それでは、おやすみなさい。


メガミックスの世界観で作りたい

ロックマンメガミックス Vol.1 (ブレインナビ コミックス) (BN COMICS)

ロックマンメガミックス Vol.1 (ブレインナビ コミックス) (BN COMICS)