Unityを触り始めた頃にやりがちなのが、「とりあえず Update() の中に移動・ジャンプ・アニメーション・入力処理・UI更新…全部書いてしまう」スタイルですね。
動き始めるまでは早いのですが、少し機能が増えると

  • 1つのスクリプトがどんどん巨大化する
  • 別のキャラや敵にも同じ処理を使い回したいのに、コピペ地獄になる
  • ちょっとした仕様変更で、あちこちのコードを書き換える羽目になる

といった問題が一気に表面化します。

そこでおすすめなのが、「入力」「移動」「攻撃」などの機能ごとに小さなコンポーネントに分解するやり方です。
この記事では、キーボード入力にだけ責務を絞ったコンポーネント 「KeyboardMover」 を作ってみましょう。

役割はシンプルに1つだけ:
矢印キーやWASD入力を監視し、親オブジェクトの Rigidbodyvelocity をセットして移動させる コンポーネントです。


【Unity】キーボードだけでサクッと移動!「KeyboardMover」コンポーネント

まずは完成形のコードから載せます。コピペしてそのまま使える形になっています。


using UnityEngine;

/// <summary>
/// キーボード入力(矢印キー / WASD)を監視して、
/// アタッチされたオブジェクトの Rigidbody の velocity を直接書き換えるコンポーネント。
/// 
/// ・責務は「入力 → 移動ベクトル算出 → Rigidbody.velocity 反映」のみに限定
/// ・カメラ追従やアニメーションなどは別コンポーネントに分離する前提
/// </summary>
[RequireComponent(typeof(Rigidbody))]
public class KeyboardMover : MonoBehaviour
{
    // --- 設定項目(インスペクターから調整) ---

    [Header("移動設定")]
    [SerializeField]
    [Tooltip("1秒あたりの移動速度(m/s)")]
    private float moveSpeed = 5f;

    [SerializeField]
    [Tooltip("Y方向の速度を維持するかどうか(重力やジャンプを別コンポーネントで扱う場合に便利)")]
    private bool preserveYVelocity = true;

    [Header("入力設定")]
    [SerializeField]
    [Tooltip("WASD キーを有効にするか")]
    private bool enableWASD = true;

    [SerializeField]
    [Tooltip("矢印キーを有効にするか")]
    private bool enableArrowKeys = true;

    [SerializeField]
    [Tooltip("対角移動時に速度を正規化する(斜め移動が速くなりすぎないようにする)")]
    private bool normalizeDiagonal = true;

    [Header("ローカル・ワールド切り替え")]
    [SerializeField]
    [Tooltip("true: ローカル座標系で移動(キャラの向きに対して前後左右)\nfalse: ワールド座標系で移動(常にグローバルX/Z方向)")]
    private bool useLocalSpace = false;

    // --- 内部キャッシュ ---

    private Rigidbody rb;

    // 入力から算出した「移動方向(正規化前)」を一時的に保持
    private Vector3 inputDirection = Vector3.zero;

    private void Awake()
    {
        // RequireComponent により、必ず Rigidbody が存在する前提
        rb = GetComponent<Rigidbody>();

        // 回転を物理演算で制御したくない場合は、ここで固定しておくのも手
        // (キャラクターがコロコロ転がるのを防止)
        // rb.freezeRotation = true;
    }

    private void Update()
    {
        // 1. キーボード入力を読む
        ReadKeyboardInput();
    }

    private void FixedUpdate()
    {
        // 2. 物理更新タイミングで Rigidbody.velocity を書き換える
        ApplyVelocity();
    }

    /// <summary>
    /// キーボード入力(WASD / 矢印キー)から移動方向ベクトルを作成する
    /// </summary>
    private void ReadKeyboardInput()
    {
        float horizontal = 0f;
        float vertical = 0f;

        // --- WASD 入力 ---
        if (enableWASD)
        {
            if (Input.GetKey(KeyCode.A)) horizontal -= 1f;
            if (Input.GetKey(KeyCode.D)) horizontal += 1f;
            if (Input.GetKey(KeyCode.S)) vertical  -= 1f;
            if (Input.GetKey(KeyCode.W)) vertical  += 1f;
        }

        // --- 矢印キー入力 ---
        if (enableArrowKeys)
        {
            if (Input.GetKey(KeyCode.LeftArrow))  horizontal -= 1f;
            if (Input.GetKey(KeyCode.RightArrow)) horizontal += 1f;
            if (Input.GetKey(KeyCode.DownArrow))  vertical  -= 1f;
            if (Input.GetKey(KeyCode.UpArrow))    vertical  += 1f;
        }

        // XZ 平面での移動方向を作る
        Vector3 rawDir = new Vector3(horizontal, 0f, vertical);

        // 対角移動の速度補正
        if (normalizeDiagonal && rawDir.sqrMagnitude > 1f)
        {
            rawDir.Normalize();
        }

        // ローカル座標 or ワールド座標で扱うかを切り替え
        if (useLocalSpace && rawDir != Vector3.zero)
        {
            // transform の向きを基準にローカル → ワールドへ変換
            inputDirection = transform.TransformDirection(rawDir);
        }
        else
        {
            inputDirection = rawDir;
        }
    }

    /// <summary>
    /// Rigidbody.velocity に対して、入力に応じた移動速度を反映する
    /// </summary>
    private void ApplyVelocity()
    {
        // 現在の速度を取得
        Vector3 currentVelocity = rb.velocity;

        // XZ 平面の速度を入力に応じて更新
        Vector3 newVelocity = inputDirection * moveSpeed;

        if (preserveYVelocity)
        {
            // Y 方向の速度(重力・ジャンプなど)は維持
            newVelocity.y = currentVelocity.y;
        }

        rb.velocity = newVelocity;
    }

    // --- 便利な補助メソッド(任意で使用) ---

    /// <summary>
    /// 外部から移動速度を変更したいとき用のメソッド
    /// 例: ステージギミックで一時的にスローにする など
    /// </summary>
    /// <param name="speed">新しい移動速度(0以上)</param>
    public void SetMoveSpeed(float speed)
    {
        moveSpeed = Mathf.Max(0f, speed);
    }

    /// <summary>
    /// 現在の移動速度を取得する
    /// </summary>
    public float GetMoveSpeed()
    {
        return moveSpeed;
    }
}

使い方の手順

ここでは「プレイヤーキャラをキーボードで動かす」例をベースに、敵や動く床などへの応用も交えて手順を説明します。

手順①:移動させたいオブジェクトを用意する

  • 例1:プレイヤーキャラ(3Dモデルやカプセルなど)
  • 例2:敵キャラ(テスト用に、まずはプレイヤーと同じ挙動で動かしてみたい場合)
  • 例3:動く床(プレイヤー操作で動かせるリフトなど)

いずれの場合も、まずはシーン上に GameObject を1つ配置します。

手順②:Rigidbody を追加する

  1. 対象の GameObject を選択
  2. Add Component ボタンをクリック
  3. Rigidbody を検索して追加

ポイント:

  • 「動く床」など、重力で落ちてほしくない場合は Use Gravity のチェックを外しましょう。
  • 物理的な回転が不要なら Constraints > Freeze Rotation で回転を固定しておくと扱いやすいです。

手順③:KeyboardMover を追加して設定する

  1. 同じ GameObject に KeyboardMover スクリプトを追加
  2. インスペクターで以下を調整
    • Move Speed: 1秒あたりの移動距離。3〜8 くらいから試すと良いです。
    • Preserve Y Velocity:
      • ジャンプや重力を別コンポーネントで扱うなら ON(デフォルト)
      • 2Dライクに完全に平面で動かしたいなら OFF にして Y 速度も上書き
    • Enable WASD / Enable Arrow Keys: どの入力を有効にするか切り替え
    • Use Local Space:
      • キャラの向きに対して「前後左右」で動かしたい → ON
      • 常にワールドの X/Z 軸に沿って動かしたい → OFF

この状態でゲームを再生すると、矢印キーまたは WASD でオブジェクトが移動するはずです。

手順④:具体例ごとのちょい足し

例1:プレイヤーキャラに使う
  • プレイヤー用の GameObject(例:Player)に RigidbodyKeyboardMover を付ける
  • カメラは Player を追従する別コンポーネント(簡単な FollowCamera など)に任せる
  • 攻撃・ジャンプ・アニメーション制御もそれぞれ別スクリプトに分けると、かなり見通しが良くなります
例2:敵キャラのデバッグ用に使う

敵 AI を作る前に、まずは「この見た目のキャラをプレイヤーと同じように動かしたらどんな感じか」を確認したいことがあります。
そんなとき、一時的に敵キャラに KeyboardMover を付けて手動操作してみると、

  • 移動速度のバランス感
  • 当たり判定の大きさ
  • 地形との相性

などをすぐにチェックできて便利です。

例3:プレイヤー操作の「動く床」や「リフト」に使う

例えば、プレイヤーが乗ったまま操作できるリフトを作りたい場合:

  • 床オブジェクトに Rigidbody + KeyboardMover を付ける
  • RigidbodyUse Gravity を OFF にして、重力で落ちないようにする
  • 速度は低め(例:1.5f など)にしておく

こうすると、プレイヤーがその床の上に乗ったまま、キーボードで床を動かせるようになります。
パズルゲームのギミックとしても面白いですね。


メリットと応用

KeyboardMover のように「キーボード入力で Rigidbody の velocity を操作するだけ」という小さな責務に切り出すことで、次のようなメリットがあります。

1. プレハブ管理が楽になる

  • 「キーボードで動くオブジェクト」という性質を、そのままプレハブ化できる
  • プレイヤー、デバッグ用の敵、テスト用のギミックなど、複数のプレハブで同じコンポーネントを再利用できる
  • 移動速度の調整も、各プレハブのインスペクターから変えるだけで済む

例えば、

  • Player.prefab: KeyboardMover + プレイヤー用の見た目 + プレイヤー専用の入力コンポーネント
  • DebugEnemy.prefab: KeyboardMover + 敵の見た目(AIは後から追加)
  • MovablePlatform.prefab: KeyboardMover + BoxCollider(床判定)

のようにしておくと、シーンにドラッグ&ドロップするだけで簡単に試せます。

2. レベルデザインがやりやすくなる

レベルデザイナー視点だと、「このオブジェクト、ちょっと動かしてみたいな」という場面が頻繁にあります。
そんなとき、

  • とりあえず KeyboardMover を付けて、実際に動かしながら距離感やスピード感を確認
  • 値が固まったら、後からスクリプトを AI や自動移動用のコンポーネントに差し替える

といったフローが取りやすくなります。
「まずは人間が動かしてみて、気持ちいいパラメータを探す」ためのツールとしても優秀です。

3. 入力と移動の責務を分離しやすい

今回のコンポーネントはあえて「キーボード入力 + 移動」を1つにまとめていますが、
将来的には

  • 入力を抽象化するコンポーネント(移動の意図だけを出す)
  • 物理移動を担当するコンポーネント(Rigidbody を操作する)

に分割することもできます。そのときも、今の KeyboardMover の構造をベースにすれば、リファクタリングしやすいはずです。


改造案:一時的な「ダッシュ」機能を足してみる

最後に、ちょっとした改造案として「一定時間だけ移動速度を2倍にするダッシュ機能」を追加する例を載せておきます。
以下のメソッドを KeyboardMover の中に追加し、他のスクリプトから呼び出せるようにします。


    /// <summary>
    /// 一定時間だけ移動速度を倍率付きで変更する簡易ダッシュ。
    /// 例: StartCoroutine(TemporaryBoost(2f, 1.0f));  // 1秒間だけ2倍速
    /// </summary>
    /// <param name="multiplier">速度倍率(例: 2f で2倍)</param>
    /// <param name="duration">効果時間(秒)</param>
    public System.Collections.IEnumerator TemporaryBoost(float multiplier, float duration)
    {
        float originalSpeed = moveSpeed;
        moveSpeed *= multiplier;

        float timer = 0f;
        while (timer < duration)
        {
            timer += Time.deltaTime;
            yield return null;
        }

        moveSpeed = originalSpeed;
    }

このように、小さな責務に絞ったコンポーネントにしておくと、
「ダッシュ」「スロー」「スタンで移動不可」などの拡張も、別スクリプトから簡単に行えるようになります。
ぜひ、自分のプロジェクトに合わせてカスタマイズしてみてください。