Unityを触り始めたころは、ついプレイヤーの移動・ジャンプ・アニメ・当たり判定の制御などを、全部ひとつの Update() に書いてしまいがちですよね。
でもそれを続けると、

  • 機能追加のたびに巨大な PlayerController.cs が肥大化する
  • 「どこでバグってるのか」が追いにくくなる
  • 敵やギミックにも同じ処理を使いたくなったときにコピペ地獄になる

そこでおすすめなのが、「やりたいこと1つにつき1コンポーネント」に分割するやり方です。
この記事では、2Dアクションでよくある「下入力+ジャンプで片方向床(One Way Platform)をすり抜ける」機能だけを担当するコンポーネント PlatformDropper を作ってみます。

プレイヤーの移動ロジックとは別コンポーネントにすることで、

  • 「床を抜ける」という振る舞いだけを簡単にオン/オフできる
  • 敵や動く床など、別オブジェクトにも再利用しやすい

といったメリットがあります。

【Unity】下入力+ジャンプですり抜け!「PlatformDropper」コンポーネント

ここでは、Unityの PlatformEffector2D を使った2Dプラットフォーマーを想定しています。
「下+ジャンプ」で、足元の One Way Platform の当たり判定を一時的に無効化し、プレイヤーを下方向に落とすコンポーネントを実装していきます。

前提とする構成(ざっくり)

  • プレイヤーには Rigidbody2D(Dynamic)と Collider2D が付いている
  • 片方向床には PlatformEffector2DCollider2D が付いている
    • Collider2DUsed By Effector にチェック
    • PlatformEffector2DUse One Way をオン
  • プレイヤーの足元に「接地判定用」小さめの Collider2D または Raycast などがある(この記事では簡易的なオーバーラップ判定を使います)

ソースコード(PlatformDropper.cs)


using System.Collections;
using UnityEngine;
using UnityEngine.InputSystem; // 新Input Systemを使用する場合

/// <summary>
/// 下入力+ジャンプで、足元の One Way Platform(PlatformEffector2D)を一時的にすり抜け可能にするコンポーネント。
/// - プレイヤーのジャンプ処理とは独立している(SRP)
/// - 「床抜けするかどうか」だけを責務として持つ
/// </summary>
[RequireComponent(typeof(Rigidbody2D))]
public class PlatformDropper : MonoBehaviour
{
    // ====== インスペクタから設定するパラメータ ======

    [Header("入力設定")]
    [SerializeField]
    private string verticalAxisName = "Vertical"; // 旧InputSystem用の縦入力軸名(新InputSystemを使う場合は無視してもOK)

    [SerializeField]
    private bool useNewInputSystem = true; // 新Input Systemを使うかどうか

    [Header("床判定設定")]
    [SerializeField]
    private LayerMask platformLayerMask; // OneWayPlatform が属するレイヤーを指定

    [SerializeField]
    private Vector2 groundCheckOffset = new Vector2(0f, -0.1f); // キャラ中心からのオフセット(足元方向)

    [SerializeField]
    private float groundCheckRadius = 0.2f; // 足元の当たり判定半径

    [Header("床抜け設定")]
    [SerializeField]
    private float dropDuration = 0.3f; // 何秒間、床を無効化するか

    [SerializeField]
    private float downInputThreshold = -0.5f; // どの程度下入力されたら「下入力」とみなすか(-1 ~ 0)

    [SerializeField]
    private float smallDownwardImpulse = -1.0f; // 床抜け開始時に少しだけ下向きに押す力(任意)

    // ====== 内部参照 ======

    private Rigidbody2D rb;

    // 新Input System用:PlayerInput から送られてくる値
    private float cachedVerticalInput;
    private bool cachedJumpPressed;

    // 同時に複数の床をすり抜けないように制御するフラグ
    private bool isDropping = false;

    private void Awake()
    {
        rb = GetComponent<Rigidbody2D>();
    }

    private void Update()
    {
        // 旧Input Systemを使う場合はこちらで入力を取得
        if (!useNewInputSystem)
        {
            cachedVerticalInput = Input.GetAxisRaw(verticalAxisName);
            cachedJumpPressed = Input.GetButtonDown("Jump");
        }

        // 下入力+ジャンプが押されたら床抜け処理を試みる
        if (!isDropping && IsDownInput() && IsJumpPressed())
        {
            TryDropThroughPlatform();
        }

        // 1フレームだけ有効なジャンプ入力フラグをリセット(新Input System用)
        cachedJumpPressed = false;
    }

    /// <summary>
    /// 下方向入力が入っているかどうかを判定
    /// </summary>
    private bool IsDownInput()
    {
        return cachedVerticalInput <= downInputThreshold;
    }

    /// <summary>
    /// ジャンプボタンが押された瞬間かどうか
    /// </summary>
    private bool IsJumpPressed()
    {
        return cachedJumpPressed;
    }

    /// <summary>
    /// 足元にある One Way Platform を探し、あれば一定時間すり抜け可能にする
    /// </summary>
    private void TryDropThroughPlatform()
    {
        // 足元の位置を計算
        Vector2 checkPosition = (Vector2)transform.position + groundCheckOffset;

        // 足元にあるコライダーをサークルで検出
        Collider2D hit = Physics2D.OverlapCircle(
            checkPosition,
            groundCheckRadius,
            platformLayerMask
        );

        if (hit == null)
        {
            // 足元に床がないなら何もしない
            return;
        }

        // PlatformEffector2D を持っているかチェック
        PlatformEffector2D effector = hit.GetComponent<PlatformEffector2D>();
        if (effector == null)
        {
            // PlatformEffector2D が無ければ One Way Platform とみなさない
            return;
        }

        // 実際に床抜け処理を開始
        StartCoroutine(DropCoroutine(hit));
    }

    /// <summary>
    /// 指定した床コライダーを一定時間だけ無効化し、プレイヤーを下に落とすコルーチン
    /// </summary>
    private IEnumerator DropCoroutine(Collider2D platformCollider)
    {
        isDropping = true;

        // 一時的にコライダーの衝突を無視する
        Collider2D myCollider = GetComponent<Collider2D>();
        if (myCollider != null)
        {
            Physics2D.IgnoreCollision(myCollider, platformCollider, true);
        }

        // 少しだけ下向きの速度を与える(任意)
        if (smallDownwardImpulse < 0f)
        {
            Vector2 v = rb.velocity;
            if (v.y > smallDownwardImpulse)
            {
                v.y = smallDownwardImpulse;
                rb.velocity = v;
            }
        }

        // 指定時間だけ待つ
        float elapsed = 0f;
        while (elapsed < dropDuration)
        {
            elapsed += Time.deltaTime;
            yield return null;
        }

        // 衝突判定を元に戻す
        if (myCollider != null)
        {
            Physics2D.IgnoreCollision(myCollider, platformCollider, false);
        }

        isDropping = false;
    }

    // ====== 新Input System用のコールバック ======
    // PlayerInput コンポーネントから呼び出してもらう想定

    /// <summary>
    /// 新Input Systemの Move アクション(Vector2)から呼ばれる想定。
    /// PlayerInput の Events でこの関数を登録する。
    /// </summary>
    /// <param name="context">入力コンテキスト</param>
    public void OnMove(InputAction.CallbackContext context)
    {
        if (!useNewInputSystem) return;

        Vector2 input = context.ReadValue<Vector2>();
        cachedVerticalInput = input.y;
    }

    /// <summary>
    /// 新Input Systemの Jump アクション(Button)から呼ばれる想定。
    /// PlayerInput の Events でこの関数を登録する。
    /// </summary>
    /// <param name="context">入力コンテキスト</param>
    public void OnJump(InputAction.CallbackContext context)
    {
        if (!useNewInputSystem) return;

        // ボタンが押された瞬間だけ true にする
        if (context.performed)
        {
            cachedJumpPressed = true;
        }
    }

    // ====== デバッグ表示 ======
    private void OnDrawGizmosSelected()
    {
        // 足元の判定範囲を Scene ビューに可視化
        Gizmos.color = Color.cyan;
        Vector2 checkPosition = (Vector2)transform.position + groundCheckOffset;
        Gizmos.DrawWireSphere(checkPosition, groundCheckRadius);
    }
}

使い方の手順

ここでは「プレイヤーが One Way Platform を下抜けできる」例で説明します。

  1. レイヤーと床のセットアップ
    • Unity の LayerOneWayPlatform などのレイヤーを作成します。
    • 床用の GameObject(例: OneWayPlatform プレハブ)に以下を追加します:
      • BoxCollider2D(または CompositeCollider2D
      • PlatformEffector2D
    • 床のコライダーの
      • Used By Effector にチェック
      • 床オブジェクトの Layer を先ほど作った OneWayPlatform に設定
  2. プレイヤーのセットアップ
    • プレイヤー GameObject に以下のコンポーネントを付けます:
      • Rigidbody2D(Body Type: Dynamic)
      • CapsuleCollider2DBoxCollider2D などのメインコライダー
      • PlatformDropper(この記事のスクリプト)
    • PlatformDropper のインスペクタで:
      • Platform Layer MaskOneWayPlatform レイヤーを指定
      • Ground Check Offset をプレイヤーの足元に来るように調整(例: (0, -0.3)
      • Ground Check Radius をプレイヤーの足幅に合わせて調整(例: 0.2
      • Drop Duration は 0.2~0.4 秒程度がおすすめ
  3. 入力の紐付け

    新 Input System を使う場合:

    • プレイヤーに PlayerInput コンポーネントを追加
    • Input Actions アセットで
      • Move(タイプ: Value, Vector2)
      • Jump(タイプ: Button)

      を用意し、Keyboard の WASD / ↑↓←→ やゲームパッドにバインド

  4. PlayerInputBehavior を「Send Messages」にし、
    • Move アクション → OnMove
    • Jump アクション → OnJump

    が呼ばれるように設定

  5. PlatformDropperUse New Input System をオンにする
  6. 旧 Input Manager を使う場合:

  • Edit > Project Settings > Input Manager
    • Vertical 軸(↑↓キー / W,S)
    • Jump ボタン(Space など)

    が設定されていることを確認

  • PlatformDropperUse New Input System をオフにする
  • 実行して挙動を確認
    • プレイヤーを One Way Platform の上に立たせる
    • 下方向入力(↓キーやスティック下)を入れながらジャンプボタンを押す
    • 指定時間だけ床の当たり判定が無効化され、プレイヤーが下に落ちていけば成功です
  • 同じコンポーネントを敵キャラに付けて、AI側から OnMove / OnJump 相当の値を渡せば、
    「敵も特定タイミングで床をすり抜けてくる」といったギミックも簡単に作れます。


    メリットと応用

    PlatformDropper を「床抜け専用コンポーネント」として分離しておくと、

    • プレイヤーの移動スクリプトが「移動とジャンプ」だけに集中できる
    • 床抜けロジックを変えたいとき(長押しにしたい、一定高さ以上では禁止したいなど)に、このコンポーネントだけを差し替えればよい
    • プレハブとして管理しやすく、「床抜け可能なプレイヤー」「床抜けしないプレイヤー」を簡単に作り分けられる
    • レベルデザイン時に、「このステージだけ床抜け時間を長くする」など、インスペクタからパラメータをいじるだけで調整できる

    たとえば、動く床にも応用できます。
    動く床オブジェクトに PlatformEffector2D を付けて OneWayPlatform レイヤーにしておけば、
    プレイヤー側は同じ PlatformDropper で「動く床からも下抜け」できるようになります。

    改造案:落下中は床抜けを禁止する

    「すでに落下しているときに、さらに床をすり抜けるのは危険なので禁止したい」という場合は、
    TryDropThroughPlatform() の前に「接地中かどうか」をチェックするメソッドを追加してもいいですね。

    
    /// <summary>
    /// 足元のサークル判定で「今、床の上に立っているか」をざっくり判定する
    /// </summary>
    private bool IsGrounded()
    {
        Vector2 checkPosition = (Vector2)transform.position + groundCheckOffset;
        Collider2D hit = Physics2D.OverlapCircle(
            checkPosition,
            groundCheckRadius,
            platformLayerMask
        );
        return hit != null;
    }
    

    そして Update() 内の条件を

    
    if (!isDropping && IsGrounded() && IsDownInput() && IsJumpPressed())
    {
        TryDropThroughPlatform();
    }
    

    のように変えると、「床の上にいるときだけ床抜けを許可する」挙動にできます。
    こうした小さな改造をコンポーネント単位で積み重ねていくと、プロジェクト全体がかなり見通しやすくなりますね。