Unityを触り始めた頃は、つい「とりあえず全部を1つのPlayerスクリプトのUpdateに書く」という実装をしがちですよね。移動、アニメーション、ジャンプ、入力、エフェクト…すべてが1クラスに詰め込まれていくと、少し仕様を変えたいだけでも地獄のような修正作業になります。
たとえば「2段ジャンプを追加したい」「ジャンプ時にパーティクルを出したい」といった要件が出たとき、巨大なPlayerスクリプトに if 文を足しまくると、あっという間にカオスになります。
そこで今回は、「ジャンプ処理だけを担当する小さなコンポーネント」として、2段ジャンプ付きの DoubleJump コンポーネントを作っていきます。
Rigidbody ベースのシンプルなプレイヤーにポン付けできて、空中で1回だけ追加ジャンプ、さらにジャンプ時にパーティクルを再生できるようにしてみましょう。
【Unity】2段ジャンプで操作感アップ!「DoubleJump」コンポーネント
フルソースコード
using UnityEngine;
/// <summary>
/// シンプルな2段ジャンプコンポーネント。
/// - Rigidbody を使ったジャンプ力の付与
/// - 地面判定
/// - 空中で1回だけ追加ジャンプ
/// - ジャンプ時のパーティクル再生
///
/// 入力は「Jump」ボタン(旧InputManagerのSpaceなど)を想定。
/// 新InputSystemを使う場合は、入力部分を書き換えてください。
/// </summary>
[RequireComponent(typeof(Rigidbody))]
public class DoubleJump : MonoBehaviour
{
[Header("ジャンプ設定")]
[SerializeField] private float jumpForce = 7.0f; // 1回のジャンプに与える上方向の力
[SerializeField] private float extraJumpForceScale = 1.0f; // 2段ジャンプ時の力倍率(1.0で同じ)
[Header("地面判定設定")]
[SerializeField] private Transform groundCheck; // 足元の位置(空オブジェクトなど)
[SerializeField] private float groundCheckRadius = 0.2f; // 地面判定の半径
[SerializeField] private LayerMask groundLayer; // 地面とみなすレイヤー
[Header("パーティクル設定")]
[SerializeField] private ParticleSystem jumpParticle; // ジャンプ時に再生するパーティクル
[Header("デバッグ")]
[SerializeField] private bool drawGizmos = true; // シーンビューに地面判定を表示するか
// コンポーネント内部状態
private Rigidbody rb;
private bool isGrounded;
private bool canDoubleJump;
// 入力の立ち上がり検出用
private bool jumpButtonHeldLastFrame = false;
private void Awake()
{
// 必須コンポーネントの取得
rb = GetComponent<Rigidbody>();
// groundCheck が未設定の場合は、自分のTransformを使う(足元に別オブジェクトを置かない場合)
if (groundCheck == null)
{
groundCheck = this.transform;
}
}
private void Update()
{
// 毎フレーム、地面に接しているかどうかを更新
UpdateGroundedStatus();
// 入力の立ち上がり(ボタンを押した瞬間)を検出
bool jumpButtonDown = GetJumpButtonDown();
if (jumpButtonDown)
{
TryJump();
}
}
/// <summary>
/// 地面に接しているかどうかを Physics.CheckSphere で判定。
/// </summary>
private void UpdateGroundedStatus()
{
// 指定した位置と半径で、groundLayer に属するコライダーがあるかチェック
isGrounded = Physics.CheckSphere(groundCheck.position, groundCheckRadius, groundLayer, QueryTriggerInteraction.Ignore);
if (isGrounded)
{
// 地面に着地した瞬間に、2段ジャンプ権をリセット
canDoubleJump = true;
}
}
/// <summary>
/// ジャンプボタンの「押した瞬間」を取得する簡易実装。
/// 旧InputManagerの "Jump" を利用。
/// </summary>
/// <returns>このフレームでジャンプボタンが押されたか</returns>
private bool GetJumpButtonDown()
{
// 現在のボタン状態を取得
bool isHeld = Input.GetButton("Jump"); // 旧InputManagerの "Jump"(デフォルトでSpaceキー)
// 前フレームは離されていて、今フレーム押されている => 立ち上がり
bool isDown = !jumpButtonHeldLastFrame && isHeld;
// 状態を保存
jumpButtonHeldLastFrame = isHeld;
return isDown;
}
/// <summary>
/// 実際にジャンプを試みる処理。
/// - 地上なら通常ジャンプ
/// - 空中かつ canDoubleJump が true なら2段ジャンプ
/// </summary>
private void TryJump()
{
if (isGrounded)
{
// 地上ジャンプ
PerformJump(jumpForce);
}
else if (canDoubleJump)
{
// 空中で1回だけ許可される2段ジャンプ
float doubleJumpForce = jumpForce * extraJumpForceScale;
PerformJump(doubleJumpForce);
// 2段ジャンプ権を消費
canDoubleJump = false;
}
else
{
// 地上でもなく、2段ジャンプ権もない => 何もしない
}
}
/// <summary>
/// 実際にRigidbodyに上方向の速度を与える共通処理。
/// </summary>
/// <param name="force">上方向に与える力(速度)</param>
private void PerformJump(float force)
{
// 現在の速度を取得
Vector3 velocity = rb.velocity;
// 上方向の速度だけを上書きすることで、落下中でもちゃんと跳ねる
velocity.y = 0f;
rb.velocity = velocity;
// 上方向にインパルスを与える
rb.AddForce(Vector3.up * force, ForceMode.VelocityChange);
// パーティクル再生(設定されている場合のみ)
PlayJumpParticle();
}
/// <summary>
/// ジャンプ時のパーティクル再生処理。
/// null チェック付きなので、未設定でもエラーにはならない。
/// </summary>
private void PlayJumpParticle()
{
if (jumpParticle == null)
{
return;
}
// すでに再生中でも、位置を足元に合わせてから再生し直す
jumpParticle.transform.position = groundCheck.position;
// Stop -> Play で確実に再生をリスタート
jumpParticle.Stop(true, ParticleSystemStopBehavior.StopEmittingAndClear);
jumpParticle.Play();
}
/// <summary>
/// シーンビューで地面判定の範囲を可視化する。
/// レベルデザイン時にかなり便利。
/// </summary>
private void OnDrawGizmosSelected()
{
if (!drawGizmos || groundCheck == null)
{
return;
}
Gizmos.color = Color.yellow;
Gizmos.DrawWireSphere(groundCheck.position, groundCheckRadius);
}
}
使い方の手順
ここでは「プレイヤーキャラクターに2段ジャンプを付ける」ケースを例にします。敵キャラや動く足場などにも同じ手順で使い回せます。
-
プレイヤーに Rigidbody と Collider を用意する
- 空の GameObject を作成し、名前を
Playerにします。 Rigidbodyコンポーネントを追加します。- Use Gravity: チェックあり
- Constraints: 回転を固定したい場合は Freeze Rotation X/Z などをオン
CapsuleColliderやBoxColliderを追加して当たり判定を付けます。
- 空の GameObject を作成し、名前を
-
DoubleJump コンポーネントを追加する
Playerオブジェクトに、上記のDoubleJumpスクリプトをアタッチします。- インスペクターで以下を設定します。
- Jump Force: 7〜10 くらいから調整
- Extra Jump Force Scale: 1.0 なら1段目と同じ高さ、1.2 などにすると2段目が少し高くなります
-
地面判定用のオブジェクトとレイヤーを設定する
Playerの子として空の GameObject を作成し、名前をGroundCheckにします。GroundCheckを足元(キャラクターの底辺あたり)に移動します。DoubleJumpの Ground Check に、このGroundCheckをドラッグ&ドロップします。- 地面となるオブジェクト(床や地形)に、共通のレイヤー(例:
Ground)を設定します。 DoubleJumpの Ground Layer に、その Ground レイヤーを指定します。
-
ジャンプ用パーティクルを設定する
- ヒエラルキーで右クリック →
Effects > Particle Systemを作成し、名前をJumpParticleにします。 - 見た目を調整します(例: 小さな砂煙・キラキラなど)。
JumpParticleの Play On Awake のチェックを外しておきます(スクリプトから再生するため)。DoubleJumpの Jump Particle に、このJumpParticleをドラッグ&ドロップします。- ゲームを再生し、Space キー(InputManager の “Jump”)を押してジャンプすると、1段目・2段目の両方でパーティクルが再生されます。
- ヒエラルキーで右クリック →
このコンポーネントは「ジャンプの制御とエフェクト再生」に責務を限定しているので、移動処理(左右移動、カメラ追従など)は別スクリプトとして分けておくと、さらに保守しやすくなります。
メリットと応用
DoubleJump をコンポーネントとして切り出しておくと、次のようなメリットがあります。
- プレハブの再利用性が高い
プレイヤー用のプレハブにこのコンポーネントを付けておけば、敵キャラや協力プレイ用の2Pキャラにも、そのままコピーして2段ジャンプを有効化できます。必要なら敵側ではextraJumpForceScaleを変えて挙動を差別化するだけです。 - レベルデザインが楽になる
「このステージでは2段ジャンプを禁止したい」といったギミックも、ステージ開始時にenabledを切り替えるだけで実現できます。スクリプト内部を触らずに、インスペクターやトリガーから制御できるのが嬉しいですね。 - Godクラス化を防げる
移動・攻撃・ジャンプ・エフェクトを全部1つの Player スクリプトに書くのではなく、「DoubleJump はジャンプだけを見る」ように分割しておけば、デザイナーや他のプログラマが安心して手を入れられます。
さらに、改造案として「空中で2段ジャンプした瞬間に、少しだけ横方向の速度もブーストする」ような演出も簡単に追加できます。例えば、以下のようなメソッドを追加して PerformJump から呼び出すのもアリです。
/// <summary>
/// 2段ジャンプ時に、移動方向へ少しだけスピードブーストを与える例。
/// (Input.GetAxisRaw を使った簡易実装)
/// </summary>
private void ApplyDoubleJumpHorizontalBoost(float boostSpeed)
{
// 入力方向を取得(左右キーやA/Dキー)
float horizontal = Input.GetAxisRaw("Horizontal");
// 入力がない場合は何もしない
if (Mathf.Approximately(horizontal, 0f))
{
return;
}
Vector3 velocity = rb.velocity;
velocity.x = horizontal * boostSpeed;
rb.velocity = velocity;
}
このように、小さなコンポーネントとして作っておけば、「2段ジャンプ時だけ横ブースト」「3段ジャンプに拡張」「ジャンプ回数をUIに表示」など、責務を保ちながら機能追加していきやすくなります。ぜひ自分のプロジェクト用にアレンジしてみてください。
