Unityで「キャラクターが地面に着地しているかを判定する」のを実装していきます。いわゆる接地判定というものですね。
色々な方法がありますが、今回はRaycastを利用していきます。Raycastは接地だけでなく、オブジェクト(物)の有無判定にも使えます。
はじめに
「Unity 2020.3.26f1」ヴァージョンで作成しています。
2Dゲームで「プレイヤー(キャラクター)が地面に着地している時だけジャンプできる」のを実装していきます。2段ジャンプをできないようにするという感じですね。
実装開始
実装は「フィールド作成」→「プレイヤー作成」→「判定追加(Raycast)」の順番で行います。
フィールド作成
まずは簡単に画面を作成。
「2Dオブジェクト」→「スプライト」→「正方形」を追加。

名前をPlayerにしておきます。

同じく、正方形のオブジェクトを追加。位置、スケール、色を変更して地面にします。

下記の様な感じにします。白い四角がプレイヤー、黒い下部分が地面です。

プレイヤーのジャンプ
プレイヤーがジャンプできるように「Box Collider 2D」と「Rigidbody 2D」をアタッチ。

地面には「Box Collider 2D」を追加。

PlayerJumpと言う名前で、下記のスクリプトを作成スペースキーを押したらジャンプするという簡単なものです。
using System.Collections; using System.Collections.Generic; using UnityEngine; public class PlayerJump : MonoBehaviour { [SerializeField] private float jumpPower; private Rigidbody2D _rigidbody; void Start() { _rigidbody = GetComponent<Rigidbody2D>(); } void Update() { if (Input.GetKey(KeyCode.Space)) { _rigidbody.velocity = new Vector2(_rigidbody.velocity.x, jumpPower); } } }
プレイヤーにアタッチします。

スペースキーを押してジャンプすればOK、連続でスペースを押すとジャンプし続けます。
接地判定追加
スクリプトを変更して「プレイヤーが地面に着地している時だけ」ジャンプできるようにします。
下記の様に変更します。
using System.Collections; using System.Collections.Generic; using UnityEngine; public class PlayerJump : MonoBehaviour { [SerializeField] private float jumpPower; [SerializeField] private LayerMask groundLayer; private Rigidbody2D _rigidbody; void Start() { _rigidbody = GetComponent<Rigidbody2D>(); } void Update() { if (Input.GetKey(KeyCode.Space) && isGrounded()) { _rigidbody.velocity = new Vector2(_rigidbody.velocity.x, jumpPower); } } private bool isGrounded() { RaycastHit2D raycastHit = Physics2D.Raycast(transform.position, Vector2.down, 0.6f, groundLayer); return raycastHit.collider != null; } }
Raycastを直訳すると「Ray=光線」、「cast=投げる」と言う感じですかね。センサーを出して、接触したオブジェクトを検知するという感じです。
上記の場合のパラメータは、「センサーの発射位置」、「方向」、「長さ」、「対象のレイヤー」です。
プレイヤーの原点から下方向に短いセンサーを飛ばし、当たったか当たっていないかを判定しているという感じです。
Groundのレイヤーに「Ground」を追加し、Playerのスクリプトのパラメータをセット。


これでプレイヤーが地面に着地している時しかジャンプ出来なければOK。
ギズモ
実際にセンサーがどう出ているのかをGizmoで表示してみます。
スクリプトに下記を追加。
void OnDrawGizmos() { Gizmos.color = Color.red; Gizmos.DrawRay(transform.position, new Vector3(0, -0.6f, 0)); }
画面に赤線が出ます。この赤線にGroundが触れると検知されます。

下記スクリプトに変更すると、プレイヤーの下からセンサーが出ている感じになります。
using System.Collections; using System.Collections.Generic; using UnityEngine; public class PlayerJump : MonoBehaviour { [SerializeField] private float jumpPower; [SerializeField] private LayerMask groundLayer; private Rigidbody2D _rigidbody; void Start() { _rigidbody = GetComponent<Rigidbody2D>(); } void Update() { if (Input.GetKey(KeyCode.Space) && isGrounded()) { _rigidbody.velocity = new Vector2(_rigidbody.velocity.x, jumpPower); } } private bool isGrounded() { RaycastHit2D raycastHit = Physics2D.Raycast(_rayposition(), Vector2.down, 0.1f, groundLayer); return raycastHit.collider != null; } void OnDrawGizmos() { Gizmos.color = Color.red; Gizmos.DrawRay(_rayposition(), new Vector3(0, -0.1f, 0)); } private Vector3 _rayposition() { return new Vector3(transform.position.x, transform.position.y - (transform.localScale.y / 2), transform.position.z); } }

下記の様な場合、線ではなくボックスでセンサーを飛ばすBoxCastを利用すると正しく判定出来たり。

「何」を「どう動かしたいか」で利用するのが変わってきますね。
接地を判定するので下方向に飛ばしていますが、上に飛ばしたり、横に飛ばすことで他にも利用できますね。