Unityを触り始めた頃にやりがちなのが、「とりあえず Update() の中に移動・ジャンプ・アニメーション・入力処理・UI更新…全部書いてしまう」スタイルですね。
動き始めるまでは早いのですが、少し機能が増えると
- 1つのスクリプトがどんどん巨大化する
- 別のキャラや敵にも同じ処理を使い回したいのに、コピペ地獄になる
- ちょっとした仕様変更で、あちこちのコードを書き換える羽目になる
といった問題が一気に表面化します。
そこでおすすめなのが、「入力」「移動」「攻撃」などの機能ごとに小さなコンポーネントに分解するやり方です。
この記事では、キーボード入力にだけ責務を絞ったコンポーネント 「KeyboardMover」 を作ってみましょう。
役割はシンプルに1つだけ:
矢印キーやWASD入力を監視し、親オブジェクトの Rigidbody の velocity をセットして移動させる コンポーネントです。
【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 を追加する
- 対象の GameObject を選択
- Add Component ボタンをクリック
- Rigidbody を検索して追加
ポイント:
- 「動く床」など、重力で落ちてほしくない場合は Use Gravity のチェックを外しましょう。
- 物理的な回転が不要なら Constraints > Freeze Rotation で回転を固定しておくと扱いやすいです。
手順③:KeyboardMover を追加して設定する
- 同じ GameObject に KeyboardMover スクリプトを追加
- インスペクターで以下を調整
- Move Speed: 1秒あたりの移動距離。
3〜8くらいから試すと良いです。 - Preserve Y Velocity:
- ジャンプや重力を別コンポーネントで扱うなら ON(デフォルト)
- 2Dライクに完全に平面で動かしたいなら OFF にして Y 速度も上書き
- Enable WASD / Enable Arrow Keys: どの入力を有効にするか切り替え
- Use Local Space:
- キャラの向きに対して「前後左右」で動かしたい → ON
- 常にワールドの X/Z 軸に沿って動かしたい → OFF
- Move Speed: 1秒あたりの移動距離。
この状態でゲームを再生すると、矢印キーまたは WASD でオブジェクトが移動するはずです。
手順④:具体例ごとのちょい足し
例1:プレイヤーキャラに使う
- プレイヤー用の GameObject(例:
Player)にRigidbodyとKeyboardMoverを付ける - カメラは
Playerを追従する別コンポーネント(簡単なFollowCameraなど)に任せる - 攻撃・ジャンプ・アニメーション制御もそれぞれ別スクリプトに分けると、かなり見通しが良くなります
例2:敵キャラのデバッグ用に使う
敵 AI を作る前に、まずは「この見た目のキャラをプレイヤーと同じように動かしたらどんな感じか」を確認したいことがあります。
そんなとき、一時的に敵キャラに KeyboardMover を付けて手動操作してみると、
- 移動速度のバランス感
- 当たり判定の大きさ
- 地形との相性
などをすぐにチェックできて便利です。
例3:プレイヤー操作の「動く床」や「リフト」に使う
例えば、プレイヤーが乗ったまま操作できるリフトを作りたい場合:
- 床オブジェクトに
Rigidbody+KeyboardMoverを付ける Rigidbodyの Use 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;
}
このように、小さな責務に絞ったコンポーネントにしておくと、
「ダッシュ」「スロー」「スタンで移動不可」などの拡張も、別スクリプトから簡単に行えるようになります。
ぜひ、自分のプロジェクトに合わせてカスタマイズしてみてください。
