子ノ刻ラボラトリィ

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

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

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

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

今回もキャラクターの移動をやってみます。
前回はX方向だったので、今回はY方向(ジャンプとか)を。

今回やろうとしていること
  • ジャンプ処理の実装

コメントでご指摘いただいたのですが、
前回のソースだと、入力からキャラが動き出すまで1フレーム遅れますね…

入力判定 + 状態遷移で1フレーム。
遷移後の行動は次のフレームから始まる。

入力判定 + 状態判定のメソッド
走りやジャンプなど実際のアクションの実装(移動量の増減とか)は、
判定のメソッドと分けてFixedUpdate内で実装(実行)した方がいいのかな…

イメージ

Update(){  
  // アクション判定
  NowState = ActionState()[nowState];
}

FixedUpdate(){
  // アクション実装
  ActionImpl()[nowState];
}

idleState(){ // idle状態での状態遷移判定 }
idleImpl() { // idle状態の移動量実装(減速とか)}

もしくは、遷移すると同時に初速を与えてしまうか。

ひとまず、基本的な動作ができてから考えよ;

ジャンプ処理

ソース、ごちゃごちゃしてきた…

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

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

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

    // アクション関数格納用デリゲート
    public delegate int Action();

    public LayerMask MapLayer;

    // 状態マップ
    public Dictionary<int, Action> actMap = new Dictionary<int, Action>();
  
    // 状態
    public int NowState {get; set; }

    // 向き 左:-1 / 右:+1
    public int Dir { get; set; }

    // 空中フラグ
    private bool isAir { get; set; }

    // 走り速度
    public float RunAcc;
    // 走りブレーキ
    public float RunDec;

    // ジャンプ/落下速度
    public float JumpAcc;
    public float FallAcc;

    // 移動量
    public float MoveVecX { get; set; }
    public float MoveVecY { 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);

        // 初期値で向きをセット
        this.Dir = 1;
    }
	
    // Update is called once per frame
    void Update () {

        // 前処理
        // 空中判定
        this.isAir = !Physics2D.Linecast(
            transform.position + transform.up * 0.1f,
            transform.position,
            MapLayer
            );
        // 空中だったら、空中状態にセット
        if (this.isAir) { this.NowState = (int)State.Jump; }


	// 状態マップから次の状態を取得
        this.NowState = (int)actMap[this.NowState]();

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


        // 後処理
        // 向きによって画像を反転
        transform.localScale = new Vector3(this.Dir, 1, 1);
        // 空中だったら重力の影響を与える(落下速度を与える)
        if (this.isAir) { 
            this.MoveVecY -= this.FallAcc;
        }


        // 移動量を位置に反映
        transform.localPosition += new Vector3(this.MoveVecX * this.Dir, this.MoveVecY, 0);
        Debug.Log("NowState:" + this.NowState + "/ isAir:"+this.isAir);

    }

    /// <summary>
    /// 状態:立ち
    /// </summary>
    /// <returns></returns>
    private int idle(){
        // L/Rが押されたら走り状態に遷移
        if (Input.GetKey("left"))
        {
            this.Dir = -1;
            return (int)State.Run;
        }
        if(Input.GetKey("right"))
        {
            this.Dir = 1;
            return (int)State.Run;
        }
        // スペースが押された瞬間ジャンプ状態に遷移
        if (Input.GetKeyDown("space"))
        {
            // ジャンプの初速をセット
            this.MoveVecY = this.JumpAcc;
            return (int)State.Jump;
        }
        // 速度は減速し、0に収束する
        if ((this.MoveVecX -= this.RunDec) < 0.05f)
        {
            this.MoveVecX = 0.0f;
        }
        // 何もなければ立ち状態を継続
		return (int)State.Idle;
	}

    /// <summary>
    /// 状態:走り
    /// </summary>
    /// <returns></returns>
    private int run(){
        // スペースが押された瞬間ジャンプ状態に遷移
        if (Input.GetKeyDown("space"))
        {
            // ジャンプの初速をセット
            this.MoveVecY = this.JumpAcc;
            return (int)State.Jump;
        }

        // L/Rが押されていないならば立ち状態に遷移
        if (Input.GetKeyUp("left") || Input.GetKeyUp("right"))
        {
            return (int)State.Idle;
        }

        // 上記以外は、移動量(X)をセットし、走り状態を継続
        this.MoveVecX = this.RunAcc;
        return (int)State.Run;
    }

    /// <summary>
    /// 状態:ジャンプ
    /// </summary>
    /// <returns></returns>
    private int jump(){

        // 空中での移動判定(L /Rキー)
        if (Input.GetKey("left"))
        {
            this.Dir = -1;
            this.MoveVecX = this.RunAcc;
        }
        else if (Input.GetKey("right"))
        {
            this.Dir = 1;
            this.MoveVecX = this.RunAcc;
        }
        else
        {
            // どちらも押されてなかったら移動速度(X)を0にする
            this.MoveVecX = 0;
        }
        
        // スペースが離された
        if (Input.GetKeyUp("space"))
        {
            // かつ、上昇中の場合
            if (this.MoveVecY > 0)
            {
                // ジャンプ加速度を0にする。(落下しはじめる)
                this.MoveVecY = 0;
            }
        }

        if(this.isAir){
            // 空中の場合、ジャンプ状態を継続
            return (int)State.Jump;
        } else {
            // 地面の場合、落下速度を0にして立ち状態に遷移
            this.MoveVecY = 0;
            return (int)State.Idle; 
        }
    }

}

ジャンプの肝を抜き出す。

// 空中フラグ
private bool isAir { get; set; }

// ジャンプ/落下速度
public float JumpAcc;
public float FallAcc;

// 空中判定
this.isAir = !Physics2D.Linecast(
    transform.position + transform.up * 0.1f,
    transform.position,
    MapLayer
    );
if (this.isAir) { this.NowState = (int)State.Jump; }

// (1)スペースが押された瞬間ジャンプ状態に遷移
if (Input.GetKeyDown("space"))
{
    // ジャンプの初速をセット
    this.MoveVecY = this.JumpAcc;
    return (int)State.Jump;
}

// (2)空中だったら重力の影響を与える(落下速度を与える)
if (this.isAir) {
    this.MoveVecY -= this.FallAcc;
} else {
    // (3)地面の場合、落下速度を0にして立ち状態に遷移
    this.MoveVecY = 0;
    return (int)State.Idle; 
}

空中判定は、こちらのブログが詳しいです。
[Unity]Unity4.3の2Dツールを使ってみる -Physics2Dスクリプト編その1- | クスールブログ

ジャンプ処理のイメージ図
f:id:shinga0221:20140127003150p:plain

  1. ジャンプに遷移した瞬間に、移動量Yに初速(JumpAcc)をセット
  2. 空中にいる間は、移動量Yに落下速度(FallAcc)を与え続ける
  3. 着地したら、移動量Yを0にしておく

移動量Yは初速でバーンと増加、その後、落下速度の影響でじわじわと減少する。
物理演算とかしなくても、わりと自然な放物線を描きます。

あたり判定とかガタガタだけど、ひとまず…
サンプル

なかなか、考えなくちゃいけないことが多いなぁ…

あと、こちらを参考に今回からソースをリポジトリに登録するようにしました。
http://terasur.blog.fc2.com/blog-entry-171.html

ソース管理もできたし今回はこの辺りで。おやすみさい。