横スクロールのアクションゲームを作ろうとすると、まず困るのが「キャラクターを左右に動かして、ジャンプさせる」ところ。6-8では3Dのキャラクターを動かしましたが、2DではRigidbody 2DやVector2を使うので、書き方が少し変わります。今回は2Dキャラクターの移動・ジャンプ・接地判定をまとめてご紹介します!
目次
この記事で学べること
- 2Dキャラクター用のRigidbody 2Dの設定
- Input Systemの入力で左右に動かす方法
- ジャンプと、地面に立っているかの判定(接地判定)
- キャラクターの向きの反転
- 壁に張り付いてしまう問題の直し方
- 見下ろし型の2Dゲームでの動かし方
前提知識
- 2Dゲーム制作の基本操作:スプライトとRigidbody 2D
- Input Systemで入力を受け取ろう:InputSystem_Actionsの準備(Generate C# Class)
- オブジェクトを動かそう:位置・向き・Time.deltaTime:3Dでの動かし方
3Dと2Dの違い
6-8の3D版PlayerMoverとの主な違いです。
| 3D | 2D | |
|---|---|---|
| 物理のコンポーネント | Rigidbody、Box Colliderなど | Rigidbody 2D、Box Collider 2Dなど |
| 位置や速度の型 | Vector3(x, y, z) | Vector2(x, y) |
| 上下の軸 | Y(床の上はXとZで動く) | Y(横スクロールならXで左右、Yで上下) |
| 重力の向き | Y軸のマイナス | Y軸のマイナス(Rigidbody 2DのGravity Scaleで強さを調整) |
3D用と2D用のコンポーネントは混ぜて使えないので、2Dのキャラクターには「2D」が付いたものだけを使いましょう(3-4の記事でも説明しました)。
キャラクターと地面の準備
プロジェクトとキャラクター
- テンプレートはUniversal 2D
- Hierarchyで右クリック>2D Object>Sprites>Square(仮のキャラクター。名前はPlayer)
- PlayerにAdd Component>Physics 2D>Box Collider 2D
- PlayerにAdd Component>Physics 2D>Rigidbody 2D
Rigidbody 2Dの設定
- Body Type:Dynamic(物理で動かす)
- Gravity Scale:3ぐらい(初期値の1だと、ふわっとした落ち方になります)
- Collision Detection:Continuous(速く動いても地面をすり抜けにくくなる)
- Interpolate:Interpolate(カメラで追いかけたときのカクつきを減らす)
- Constraints>Freeze Rotation Z:チェック(キャラクターが転がらないようにする)
地面
- Hierarchyで右クリック>2D Object>Sprites>Square(名前はGround)
- Scaleを(20, 1, 1)ぐらいに広げて、Playerより下に置く
- Add Component>Physics 2D>Box Collider 2D(地面にはRigidbody 2Dは付けなくてOK)
Tilemapで地面を作る場合は、Tilemapの使い方の記事で紹介しているTilemap Collider 2Dを付けます。
地面のレイヤーを作る
接地判定で「地面だけ」を調べるために、地面にレイヤーを設定します。
- Groundを選び、Inspector右上のLayer>Add Layer…
- 空いているUser Layerに「Ground」と入力
- もう一度Groundを選び、LayerをGroundにする
足元の目印(groundCheck)
- Playerを右クリック>Create Emptyで子オブジェクトを作り、名前をGroundCheckにする
- Positionを(0, -0.5, 0)にして、キャラクターの足元に置く
移動とジャンプのスクリプト
「PlatformerController2D」という名前でスクリプトを作り、Playerにアタッチします。
using UnityEngine;
[RequireComponent(typeof(Rigidbody2D))]
public class PlatformerController2D : MonoBehaviour
{
[SerializeField] private float moveSpeed = 6f;
[SerializeField] private float jumpPower = 12f;
[Header("接地判定")]
[SerializeField] private Transform groundCheck;
[SerializeField] private float groundCheckRadius = 0.15f;
[SerializeField] private LayerMask groundLayer;
private InputSystem_Actions inputActions;
private Rigidbody2D rb;
private SpriteRenderer spriteRenderer;
private float moveX;
private bool jumpRequested;
private bool isGrounded;
void Awake()
{
inputActions = new InputSystem_Actions();
rb = GetComponent<Rigidbody2D>();
spriteRenderer = GetComponent<SpriteRenderer>();
}
void OnEnable()
{
inputActions.Enable();
}
void OnDisable()
{
inputActions.Disable();
}
void Update()
{
// 入力はUpdateで読む
moveX = inputActions.Player.Move.ReadValue<Vector2>().x;
// ジャンプは「押した瞬間」をフラグにしておき、FixedUpdateで使う
if (inputActions.Player.Jump.WasPressedThisFrame() && isGrounded)
{
jumpRequested = true;
}
// 進む方向に向きを変える
if (moveX > 0.01f)
{
spriteRenderer.flipX = false;
}
else if (moveX < -0.01f)
{
spriteRenderer.flipX = true;
}
}
void FixedUpdate()
{
// 足元に地面があるか調べる
isGrounded = Physics2D.OverlapCircle(groundCheck.position, groundCheckRadius, groundLayer);
// 左右の移動。上下の速さ(落下・ジャンプ)はそのまま残す
rb.linearVelocity = new Vector2(moveX * moveSpeed, rb.linearVelocity.y);
// ジャンプ
if (jumpRequested)
{
rb.linearVelocity = new Vector2(rb.linearVelocity.x, 0f); // 落下中の勢いを消してから
rb.AddForce(Vector2.up * jumpPower, ForceMode2D.Impulse);
jumpRequested = false;
}
}
// シーンビューで接地判定の円を表示する(Playerを選んだときだけ)
void OnDrawGizmosSelected()
{
if (groundCheck == null)
{
return;
}
Gizmos.color = Color.yellow;
Gizmos.DrawWireSphere(groundCheck.position, groundCheckRadius);
}
}
Inspectorで次の3つを設定します。
- Ground Check:子オブジェクトのGroundCheckをドラッグ&ドロップ
- Ground Check Radius:0.15ぐらい
- Ground Layer:Ground
再生して、A/Dか左右キーで走り、Spaceでジャンプ出来れば成功!
スクリプトのポイント
- 入力はUpdate、物理はFixedUpdate
WasPressedThisFrame()はそのフレームだけtrueになるので、Updateで受け取ってフラグ(jumpRequested)に残し、FixedUpdateで使います- FixedUpdateで直接
WasPressedThisFrame()を見ると、押したのにジャンプしないことがあります
- 左右は速度、ジャンプは力
- 左右:
rb.linearVelocityのxだけを書き換える。yはそのまま残すので、重力で落ちる動きはRigidbody 2Dに任せられます - ジャンプ:
AddForce(方向 * 強さ, ForceMode2D.Impulse)で、一瞬だけ上向きの力を加えます
- 左右:
- 接地判定:Physics2D.OverlapCircle
- 足元に小さな円を置いて、その中にGroundレイヤーのColliderがあるかを調べます
- 地面の上にいるときだけジャンプ出来るので、空中で何度もジャンプ出来なくなります
- 向きの反転:flipX
- SpriteRendererのflipXをtrueにすると、画像が左右反転します
- [Header(“接地判定”)]
- Inspectorに見出しを付けて、項目を見やすくするための書き方です
スライムジャンプボタンを押しても、たまに反応しない・・・



接地判定の円が地面に届いているか、Playerを選んでシーンビューの黄色い円を見てみてね!足元より少しだけ下にはみ出すぐらいがちょうどいいよ
壁に張り付く問題を直す
空中で壁に向かって左右キーを押し続けると、キャラクターが壁に引っかかって落ちてこないことがあります。壁との間の摩擦が原因です。
- Projectウィンドウで右クリック>Create>2D>Physics Material 2D(名前はNoFriction)
- Friction:0
- Bounciness:0
- PlayerのBox Collider 2DのMaterialに、NoFrictionをドラッグ&ドロップ
見下ろし型の2Dゲームの場合
上から見下ろすタイプ(RPGのフィールドなど)では、重力を使わずに上下左右に動かします。
- Rigidbody 2DのGravity Scale:0
- Constraints>Freeze Rotation Z:チェック
using UnityEngine;
[RequireComponent(typeof(Rigidbody2D))]
public class TopDownMover2D : MonoBehaviour
{
[SerializeField] private float moveSpeed = 5f;
private InputSystem_Actions inputActions;
private Rigidbody2D rb;
private Vector2 moveInput;
void Awake()
{
inputActions = new InputSystem_Actions();
rb = GetComponent<Rigidbody2D>();
}
void OnEnable()
{
inputActions.Enable();
}
void OnDisable()
{
inputActions.Disable();
}
void Update()
{
// 斜めが速くならないように、長さを1以下にする
moveInput = Vector2.ClampMagnitude(inputActions.Player.Move.ReadValue<Vector2>(), 1f);
}
void FixedUpdate()
{
rb.linearVelocity = moveInput * moveSpeed;
}
}
Moveの入力のx(左右)とy(上下)を、そのまま2Dの向きとして使えるのが見下ろし型のポイントです。
よくあるエラーと対処法
キャラクターが地面をすり抜けて落ちる
- 原因:地面にBox Collider 2Dが付いていない、または3D用のBox Colliderを付けている
- 対処法:地面とPlayerの両方に「2D」の付いたColliderを付ける
ジャンプ出来ない
- 原因:Ground CheckやGround Layerが設定されていない、地面のLayerがGroundになっていない、または接地判定の円が地面に届いていない
- 対処法:Inspectorの3つの設定と、地面のLayerを確認する。シーンビューで黄色い円の位置を確認する
空中で何回もジャンプ出来る
- 原因:Ground LayerにPlayer自身のレイヤーも含まれていて、自分のColliderを「地面」だと判定している
- 対処法:Ground LayerはGroundだけにする
キャラクターがくるくる回る
- 原因:Freeze Rotation Zにチェックが入っていない
- 対処法:Rigidbody 2DのConstraints>Freeze Rotation Zにチェックを入れる
‘Rigidbody2D.velocity’ is obsolete
- 原因:古い書き方を使っている
- 対処法:
linearVelocityに書き換える
まとめ
- 2DのキャラクターはRigidbody 2DとBox Collider 2Dなど「2D」のコンポーネントで作る
- 左右の移動は
rb.linearVelocityのxを書き換え、yは重力に任せる - ジャンプはUpdateで入力を受けてフラグ、FixedUpdateでAddForce
- 接地判定はPhysics2D.OverlapCircleとGroundレイヤー
- 向きの反転はflipX、壁に張り付くなら摩擦0のPhysics Material 2D
- 見下ろし型はGravity Scale 0で、Moveの入力をそのまま使う
次のステップ
- 衝突を検知しよう:当たり判定のイベントとタグ:2Dのコインやゴールを作るときに
- Unityスタートガイド 目次
コメント