[Unity] シンプルな2Dアクションを作る


アクションというかプラットフォーマーていうんですかね、足場があってジャンプするやつ。これの、可能な限り必要最小限のものを作ってみます。何かを作り始める際の取っ掛かりとしていかがでしょう。という気持ち。
なお[こちらのブログ](https://qiita.com/yuji_yasuhara/private/6f50ecdd5d59e83aac99)の内容を踏襲しており、いわば実践編ですが、特に参照する必要はありません。今回のコードの根拠を掘り下げたいときに参考にするぐらいで。

#キャラの身長を決める

作り始めにスケールを決めるのはけっこう大事なことです。ま、身長は1mにしときましょう。つまり1.0です。
ちなみに、これは自分に植え付けられた本能でもあるんですが、どんなスケールのゲームでもメインキャラを1.0にしとくと謎の罠に落ちる確率が下がります。ワールド全ての辻褄を合わせるのが大変でなければ、1.0にしましょう。

#キャラをインポートする

適当なテクスチャを用意して、プレイヤーキャラにします。
テクスチャを持ってきたら、インポート設定で、テクスチャタイプを「スプライト（2DとUI）」にしましょう。

![Kobito.Ls2Svj.png](https://qiita-image-store.s3.amazonaws.com/0/90046/ceb7cbae-c06f-1435-0a37-103088df2981.png "Kobito.Ls2Svj.png")


サイズを1.0にする方法はカンタンで、インポートしたテクスチャの縦のドット数を「１ユニットのピクセル数」に入れれば良いです。ここの「１ユニット」というのは1.0のことですからね。

![Kobito.AtfyQl.png](https://qiita-image-store.s3.amazonaws.com/0/90046/d7cd58e9-871b-38d5-87be-264e61a22900.png "Kobito.AtfyQl.png")



#キャラをシーンに配置

いろんなやり方がありますが、インポートしたスプライトをシーンにドラッグ＆ドロップしても良いです。
コンポーネントには

* 2D リジッドボディ
* 2D ボックスコライダー

を追加しましょう。

![Kobito.YjWv2n.png](https://qiita-image-store.s3.amazonaws.com/0/90046/97bfe15b-b8b4-cf4c-9cf2-3a2cb292909e.png "Kobito.YjWv2n.png")

2D リジッドボディでは、回転しないようにConstraintを設定しておきます。

![Kobito.J1KJBV.png](https://qiita-image-store.s3.amazonaws.com/0/90046/8ed23eb8-f69f-5ac9-98cb-7edc1f876ad4.png "Kobito.J1KJBV.png")


#スクリプト記述

##初期状態
Player というスクリプトを新規作成し、Player.csの記述を開始します。これはスクリプト作成直後。

```C#:Player.cs
using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class Player : MonoBehaviour {

	// Use this for initialization
	void Start () {
		
	}
	
	// Update is called once per frame
	void Update () {
		
	}
}
```

##左右移動
左右に移動するところを書きます。用意してくれたStartとUpdateはとりあえず消しちゃって、以下のように FixedUpdateを記述します。

```C#:Player.cs
using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class Player : MonoBehaviour {
    // 追加
	void FixedUpdate() {
        var hori = Input.GetAxisRaw("Horizontal");
        var rb = GetComponent<Rigidbody2D>();
        var vel = rb.velocity;
        vel.x = hori * 2f;
        rb.velocity = vel;
    }
}
```
まあ手抜きかもしれませんけど、シンプルに記述するために左右の移動はvelocityに直接代入しちゃいます。
Input.GetAxisRawこれは状態を返すので、FixedUpdateで呼んでも大丈夫です。（[ブログ参照](https://qiita.com/yuji_yasuhara/private/6f50ecdd5d59e83aac99)）

これだけでもう、左右には移動するんですよね。実行すると重力により落下し続けてしまうので、適当な足場を作れば動かして試すことも可能です。

##ジャンプ
次はジャンプです。Update関数を記述します。

```C#:Player.cs
using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class Player : MonoBehaviour {
	void FixedUpdate() {
        var hori = Input.GetAxisRaw("Horizontal");
        var rb = GetComponent<Rigidbody2D>();
        var vel = rb.velocity;
        vel.x = hori * 2f;
        rb.velocity = vel;
    }
    // 追加
    void Update() {
        if (Input.GetButtonDown("Jump")) {
            var rb = GetComponent<Rigidbody2D>();
            rb.AddForce(new Vector2(0f, 5f), ForceMode2D.Impulse);
        }
	}
}
```

Input.GetButtonDownは瞬間を返すので、Updateで呼ばないといけません。（[ブログ参照](https://qiita.com/yuji_yasuhara/private/6f50ecdd5d59e83aac99)）
さらにAddForceをUpdateから呼んでしまいます。あれFixedUpdateじゃなくていいの？ForceMode2D.Impulseつまり力積なので大丈夫です。（[ブログ参照](https://qiita.com/yuji_yasuhara/private/6f50ecdd5d59e83aac99)）


##接地判定
もっともシンプルな実装はもう完了していますが、まあせめて、空中でもジャンプ可能なのを抑制しておきましょうか。
以下、コメントで // 追加 および // 変更 としてあるところです。

```C#:Player.cs
using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class Player2 : MonoBehaviour {
    [SerializeField] private LayerMask m_WhatIsGround; // 追加
    
	void FixedUpdate() {
        var hori = Input.GetAxisRaw("Horizontal");
        var rb = GetComponent<Rigidbody2D>();
        var vel = rb.velocity;
        vel.x = hori * 2f;
        rb.velocity = vel;
    }

    void Update() {
        var foot_pos = new Vector2(transform.position.x, transform.position.y - 0.6f); // 追加
        bool floating = (Physics2D.OverlapPoint(foot_pos, m_WhatIsGround) == null); // 追加
        if (!floating && Input.GetButtonDown("Jump")) { // 変更
            var rb = GetComponent<Rigidbody2D>();
            rb.AddForce(new Vector2(0f, 5f), ForceMode2D.Impulse);
        }
	}
}
```

Physics2D.OverlapPoint
で足元の位置のコリジョンを検出したら、ジャンプ可能としているわけです。身長は１mなので、足元は中心から数えて-0.6fすることで指定しています。やっぱりスケールを最初に決めておくと楽ですね。
そして、このときプレイヤー自身のコリジョンを検出してしまうのを避けるため、LayerMask メンバーを[SerializeField]して、地面のレイヤーに限定します。その上で、足場となるコリジョンをシーンに配置するときはこの地面のレイヤーに設定するようにしましょう。ここだけちょっと面倒ですけど、この先応用しようと思うと必要になるのでやっておきましょう。

##完成
![ScreenFlow.gif](https://qiita-image-store.s3.amazonaws.com/0/90046/0a54c758-a1f1-b669-a08a-beb93db9fc2d.gif "ScreenFlow.gif")

リジッドボディを使うと斜めの足場もカンタンに作れて楽しいですね。めでたしめでたし。