Unity2Dアクション「ジャンプ」ゲーム制作講座
はじめに
前回は2Dアクションゲームの重要な要素「移動」を説明致しました。今回は、2Dアクションゲームの「ジャンプ」に関するUnity2D制作講座です。
「PlayerController.cs」全体コードサンプル
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class PlayerController : MonoBehaviour
{
[SerializeField] float addMoveSpeed = 5f;
[SerializeField] float jumpForce = 10f;
[SerializeField][Range(0, 20)] float fHorizontalDampingMovingSpeed = 10f;
[SerializeField][Range(0, 1)] float fHorizontalDampingBasic = 0.5f;
[SerializeField][Range(0, 1)] float fHorizontalDampingWhenStopping = 0.5f;
[SerializeField][Range(0, 1)] float fHorizontalDampingWhenTurning = 0.5f;
private Rigidbody2D rb2d;
private bool isGrounded;
public Transform groundCheck;
public float groundCheckRadius = 0.2f;
public LayerMask whatIsGround;
void Start()
{
rb2d = GetComponent<Rigidbody2D>();
}
void Update()
{
if (Input.GetButtonDown("Jump") && isGrounded)
{
rb2d.AddForce(new Vector2(0f, jumpForce), ForceMode2D.Impulse);
}
}
void FixedUpdate()
{
MovePlayer();
CheckGroundStatus();
}
void MovePlayer()
{
float moveSpeed = Input.GetAxisRaw("Horizontal") * addMoveSpeed;
if (Mathf.Abs(moveSpeed) < 0.01f)
{
moveSpeed *= Mathf.Pow(1f - fHorizontalDampingWhenStopping, Time.deltaTime * fHorizontalDampingMovingSpeed);
}
else if (Mathf.Sign(moveSpeed) != Mathf.Sign(rb2d.velocity.x))
{
moveSpeed *= Mathf.Pow(1f - fHorizontalDampingWhenTurning, Time.deltaTime * fHorizontalDampingMovingSpeed);
}
else
{
moveSpeed *= Mathf.Pow(1f - fHorizontalDampingBasic, Time.deltaTime * fHorizontalDampingMovingSpeed);
}
rb2d.velocity = new Vector2(moveSpeed, rb2d.velocity.y);
}
void CheckGroundStatus()
{
isGrounded = Physics2D.OverlapCircle(groundCheck.position, groundCheckRadius, whatIsGround);
}
}
[jumpForce]
- 型: float
- 説明: ジャンプする際の力を定義します。この値が高いほど、プレイヤーは高く跳べます。
[isGrounded]
- 型: bool
- 説明: プレイヤーが地面に接地しているかどうかを示すブール値です。地面にいる場合はtrue、空中にいる場合はfalseです。
[groundCheck]
- 型: Transform
- 説明: 地面接地判定を行うための位置を指定します。通常、プレイヤーキャラクターの足元に配置される小さなオブジェクトです。
[groundCheckRadius]
- 型: float
- 説明: 地面判定用のオーバーラップサークルの半径を指定します。
[whatIsGround]
- 型: LayerMask
- 説明: 地面として判定するレイヤーを指定します。UnityのLayerMaskを利用して、どのオブジェクトが地面として認識されるかを設定します。
手順1:地面接地判定用ゲームオブジェクトの作成
前回の講座で使用したプロジェクトを開いてください。
ヒエラルキーウインドウにある「Player」を右クリックしてクリックメニューを表示してください。
クリックメニューから「Create Empty」を選択して、空のオブジェクトを作成してください。
以下の画像の通り、親が「Player」オブジェクト、その子が「空のオブジェクト」になっていることを確認してください。この空のオブジェクトの名前を「GroundCheck」としてください。
「Player」オブジェクトの子「GroundCheck」を選択し、右側にあるインスペクターウインドウの左上にある「アイコン表示」を選択します。
ここで任意のアイコンを選択します。
次に「Player」オブジェクトの子「GroundCheck」のY軸を下げます。
Position | X = 0, Y = -0.35, Z = 0 |
シーンウィンドウでこのような表示になっていれば、地面接地判定のオブジェクト配置は完了です。
手順2:地面レイヤーの設定
次に、どのオブジェクトが地面であるかをレイヤーで指定します。
この講座の例では、「Floor」オブジェクトが地面なので、こちらを選択します。
「Floor」のゲームオブジェクトを選択後、右のインスペクターのLayerが「Default」になっているのを確認してください。この「Default」を変更するために、クリックしてください。
クリックするとメニューが表示されます。「Add Layer…」から新規レイヤーを設定します。
どのレイヤーでも良いのですが、後の拡張性を高めるために、少し間を開けて設定します。
今回は「User Layer 10」を「Ground」としてください。
レイヤーを作る事が出来ましたので、再び「Floor」のゲームオブジェクトをクリックしてください。
そして再度右のインスペクターを確認し、レイヤーの「Default」をクリックしてください。
クリックメニューに先ほど作成したレイヤー「Ground」をクリックして指定してください
以下の画像のようになっていれば完了です。
手順4:ジャンプスクリプトの作成
下準備が完了したので、早速スクリプトを記述していきます。プロジェクトビューにある「PlayerController.cs」をクリックして開いてください。
前回の「移動」スクリプトで、このような状態になっているかを確認してください。
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class PlayerController : MonoBehaviour
{
[SerializeField] float addMoveSpeed = 5f;
[SerializeField][Range(0, 20)] float fHorizontalDampingMovingSpeed = 10f;
[SerializeField][Range(0, 1)] float fHorizontalDampingBasic = 0.5f;
[SerializeField][Range(0, 1)] float fHorizontalDampingWhenStopping = 0.5f;
[SerializeField][Range(0, 1)] float fHorizontalDampingWhenTurning = 0.5f;
private Rigidbody2D rb2d;
void Start()
{
rb2d = GetComponent<Rigidbody2D>();
}
void FixedUpdate()
{
MovePlayer();
}
void MovePlayer()
{
float moveSpeed = Input.GetAxisRaw("Horizontal") * addMoveSpeed;
if (Mathf.Abs(moveSpeed) < 0.01f)
{
moveSpeed *= Mathf.Pow(1f - fHorizontalDampingWhenStopping, Time.deltaTime * fHorizontalDampingMovingSpeed);
}
else if (Mathf.Sign(moveSpeed) != Mathf.Sign(rb2d.velocity.x))
{
moveSpeed *= Mathf.Pow(1f - fHorizontalDampingWhenTurning, Time.deltaTime * fHorizontalDampingMovingSpeed);
}
else
{
moveSpeed *= Mathf.Pow(1f - fHorizontalDampingBasic, Time.deltaTime * fHorizontalDampingMovingSpeed);
}
rb2d.velocity = new Vector2(moveSpeed, rb2d.velocity.y);
}
}
次に、「ジャンプ」で使用する変数を宣言します。
[jumpForce]
- 型: float
- 説明: ジャンプする際の力を定義します。この値が高いほど、プレイヤーは高く跳べます。
[isGrounded]
- 型: bool
- 説明: プレイヤーが地面に接地しているかどうかを示すブール値です。地面にいる場合はtrue、空中にいる場合はfalseです。
[groundCheck]
- 型: Transform
- 説明: 地面接地判定を行うための位置を指定します。通常、プレイヤーキャラクターの足元に配置される小さなオブジェクトです。
[groundCheckRadius]
- 型: float
- 説明: 地面判定用のオーバーラップサークルの半径を指定します。
[whatIsGround]
- 型: LayerMask
- 説明: 地面として判定するレイヤーを指定します。UnityのLayerMaskを利用して、どのオブジェクトが地面として認識されるかを設定します。
以上で「移動」に関する変数の宣言は完了です。以下のスクリプトの通りにしてください。
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class PlayerController : MonoBehaviour
{
[SerializeField] float addMoveSpeed = 5f;
[SerializeField] float jumpForce = 10f;
[SerializeField][Range(0, 20)] float fHorizontalDampingMovingSpeed = 10f;
[SerializeField][Range(0, 1)] float fHorizontalDampingBasic = 0.5f;
[SerializeField][Range(0, 1)] float fHorizontalDampingWhenStopping = 0.5f;
[SerializeField][Range(0, 1)] float fHorizontalDampingWhenTurning = 0.5f;
private Rigidbody2D rb2d;
private bool isGrounded;
public Transform groundCheck;
public float groundCheckRadius = 0.2f;
public LayerMask whatIsGround;
void Start()
{
rb2d = GetComponent<Rigidbody2D>();
}
void FixedUpdate()
{
MovePlayer();
}
void MovePlayer()
{
float moveSpeed = Input.GetAxisRaw("Horizontal") * addMoveSpeed;
if (Mathf.Abs(moveSpeed) < 0.01f)
{
moveSpeed *= Mathf.Pow(1f - fHorizontalDampingWhenStopping, Time.deltaTime * fHorizontalDampingMovingSpeed);
}
else if (Mathf.Sign(moveSpeed) != Mathf.Sign(rb2d.velocity.x))
{
moveSpeed *= Mathf.Pow(1f - fHorizontalDampingWhenTurning, Time.deltaTime * fHorizontalDampingMovingSpeed);
}
else
{
moveSpeed *= Mathf.Pow(1f - fHorizontalDampingBasic, Time.deltaTime * fHorizontalDampingMovingSpeed);
}
rb2d.velocity = new Vector2(moveSpeed, rb2d.velocity.y);
}
}
次に、Updateメソッドを追加します。FixedUpdateメソッドの上に記述してください。
void Update()
{
}
このUpdateメソッドの中に、「プレイヤーが地面に接地している時にジャンプキーが押されたら、プレイヤーにジャンプの力が加わり、ジャンプする」を記述します。
void Update()
{
if (Input.GetButtonDown("Jump") && isGrounded)
{
rb2d.AddForce(new Vector2(0f, jumpForce), ForceMode2D.Impulse);
}
}
- void Update()はUnityのライフサイクルメソッドの一つで、フレームごとに呼び出されます。これはゲーム内の論理を更新するために頻繁に使用される場所です。
- Input.GetButtonDown(“Jump”)は、プレイヤーが”Jump”という名前のボタン(通常はスペースバーまたはゲームパッドのジャンプボタンに割り当てられています)を押したかどうかを検出します。このメソッドは、ボタンが押された瞬間だけtrueを返します。
- isGroundedは、プレイヤーが地面に接しているかどうかを示すブール変数です。この変数がtrueである場合のみ、ジャンプのアクションが可能です。
- rb2d.AddForce(new Vector2(0f, jumpForce), ForceMode2D.Impulse);は、プレイヤーのRigidbody2Dに瞬間的な力を加えることでジャンプを実現します。力は上向き(y軸正)に加えられ、ForceMode2D.Impulseを使用することで、その力は一瞬で全て加えられます。
次に、プレイヤーが地面に接地しているかどうかを判定するメソッドを記載します。MovePlayerメソッドの下に記述してください。
void CheckGroundStatus()
{
isGrounded = Physics2D.OverlapCircle(groundCheck.position, groundCheckRadius, whatIsGround);
}
- Physics2D.OverlapCircle(groundCheck.position, groundCheckRadius, whatIsGround)は、groundCheckオブジェクトの位置を中心に、groundCheckRadiusで指定された半径の円を描き、この円とwhatIsGroundレイヤーに属するオブジェクトが重なるかどうかを検出します。このメソッドは、重なっている場合にtrueを返し、そうでない場合にはfalseを返します。
- isGrounded変数はこのメソッドによって更新され、プレイヤーが地面に接しているかどうかの状態が保持されます。この情報は、ジャンプが可能かどうかを判断するためにUpdateメソッドで使用されます。
- このメソッドは、プレイヤーの動作制御において基本的かつ重要な機能を担っており、地面接触判定とジャンプ機能の実装には欠かせない部分です。
そして、このCheckGroundStatusメソッドを呼び出すために、FixedUpdateメソッドに記述します。MovePlayerメソッドの下に記述してください。
void FixedUpdate()
{
MovePlayer();
CheckGroundStatus();
}
- FixedUpdate() メソッドは、Unityのライフサイクルメソッドの一つで、フレームレートに依存せず一定の間隔で呼び出されます。この性質から、物理演算を含む処理に適しています。FixedUpdateは特に、Rigidbody(剛体)を使った物理計算を行う場合に使用されることが推奨されています。
お疲れ様です。これでスクリプトの記述は完了です。
以下の通りになっているかを確認してください。
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class PlayerController : MonoBehaviour
{
[SerializeField] float addMoveSpeed = 5f;
[SerializeField] float jumpForce = 10f;
[SerializeField][Range(0, 20)] float fHorizontalDampingMovingSpeed = 10f;
[SerializeField][Range(0, 1)] float fHorizontalDampingBasic = 0.5f;
[SerializeField][Range(0, 1)] float fHorizontalDampingWhenStopping = 0.5f;
[SerializeField][Range(0, 1)] float fHorizontalDampingWhenTurning = 0.5f;
private Rigidbody2D rb2d;
private bool isGrounded;
public Transform groundCheck;
public float groundCheckRadius = 0.2f;
public LayerMask whatIsGround;
void Start()
{
rb2d = GetComponent<Rigidbody2D>();
}
void Update()
{
if (Input.GetButtonDown("Jump") && isGrounded)
{
rb2d.AddForce(new Vector2(0f, jumpForce), ForceMode2D.Impulse);
}
}
void FixedUpdate()
{
MovePlayer();
CheckGroundStatus();
}
void MovePlayer()
{
float moveSpeed = Input.GetAxisRaw("Horizontal") * addMoveSpeed;
if (Mathf.Abs(moveSpeed) < 0.01f)
{
moveSpeed *= Mathf.Pow(1f - fHorizontalDampingWhenStopping, Time.deltaTime * fHorizontalDampingMovingSpeed);
}
else if (Mathf.Sign(moveSpeed) != Mathf.Sign(rb2d.velocity.x))
{
moveSpeed *= Mathf.Pow(1f - fHorizontalDampingWhenTurning, Time.deltaTime * fHorizontalDampingMovingSpeed);
}
else
{
moveSpeed *= Mathf.Pow(1f - fHorizontalDampingBasic, Time.deltaTime * fHorizontalDampingMovingSpeed);
}
rb2d.velocity = new Vector2(moveSpeed, rb2d.velocity.y);
}
void CheckGroundStatus()
{
isGrounded = Physics2D.OverlapCircle(groundCheck.position, groundCheckRadius, whatIsGround);
}
}
手順5:インスペクターでの設定
作成したスクリプトの設定をUnity上で行います。ゲームオブジェクト「Player」をクリックしてください。
以下の画像の通り、変数が追加されています。
- Jump Forece
- Ground Check (ゲームオブジェクト「GroundCheck」を指定する変数)
- Ground Check Radius (GroundCheckの半径)
- What Is Ground (Groundレイヤーの指定変数)
ゲームオブジェクト「Player」を選択して、ゲームオブジェクト「GroundCheck」を変数「Ground Check」にドラッグして入れます。
以下の画像のようにゲームオブジェクト「GroundCheck」が入っていれば完了です。
次に、レイヤー変数「What Is Ground」で「Ground」レイヤーを指定します。
そして、現在の設定では、宇宙空間でジャンプするかのようにフワフワするので、コンポーネント「Rigidbody2D」の「Gravity Scale」を「4」に設定して、重力を強めます。
ジャンプの力を決める変数「Jump Force」や、地面と接地したかを判定するオブジェクトの半径の変数「Ground Check Radius」などは好みに設定してください。
手順6:テストプレイ
それでは実際にテストプレイをします。
画面上部中央にある再生ボタンを押してください。
ジャンプキー、Spaceキーを入力すると小さくジャンプします。
パラメータを操作して、自分好みのジャンプを実現して下さい。
さいごに
これでUnity2Dアクションゲームの「ジャンプ」の制作講座を終了します。
次回は、「見た目」の制作講座を行います。引き続きどうぞよろしくお願い致します。
絵を描いたり、音楽作ったり、ゲーム作ったり、映像作ったりが大好きな平成元年男
職業:デザイナー / IT系専門学校非常勤講師 / 音楽スクール非常勤講師 / その他