【Unity 6対応】3Dキャラクターをキーボードで移動・回転させる方法を解説!【後編】
© Unity Technologies Japan/UCL
- (前回の記事)【Unity 6対応】3Dキャラクターに歩行アニメーションを設定する方法を解説!【前編】
- (今回の記事)【Unity 6対応】3Dキャラクターをキーボードで移動・回転させる方法を解説!【後編】
この記事は
【Unity 6対応】3Dキャラクターに歩行アニメーションを設定する方法を解説!【前編】
の続きの内容になります。
まだ前編の記事内容を実装していない場合は、まず前編から始めてください。
前回の記事では、Unityでキャラクターを用意して、Idle(待機)とWalking(歩行)のアニメーションを切り替えられるところまで設定しました。
ここまでできたら、次はいよいよ「自分でキャラクターを操作する」段階です。
WASDキーを押すとキャラクターが歩き出し、進む方向に向きを変え、止まるとIdleに戻る。
ここから一気に「Unityでゲームを作っている感」が強くなってきます。
今回は、前回作ったAnimatorとUnity 6の新Input Systemをつないで、キャラクターを実際に操作できるようにしていきます。
完成すると、次の操作ができるようになります。
- Wキー:前に進む
- Sキー:後ろに進む
- Aキー:左へ移動する
- Dキー:右へ移動する
- 移動した方向へキャラクターが向く
- 移動中はWalkingアニメーションになる
- 停止するとIdleアニメーションに戻る
そして今回もう一つ大切なのが、Unity 6で推奨される新しいInput Systemの基本的な使い方です。
以前のUnityでは、次のようなコードでキーボード入力を取得する方法がよく使われていました。
// 旧Input System
Input.GetAxis("Horizontal");
Input.GetAxis("Vertical");
しかし今回は、この旧Input APIではなく、Input ActionとPlayerInputを使った新Input Systemで実装します。
新Input Systemを理解すると、WASDだけでなく、矢印キーやゲームパッドなどの入力も同じ仕組みで扱いやすくなります。
今回の処理の流れは、次のようになります。
// 新Input System
WASDを入力
↓
Input System
↓
Move Action
↓
PlayerInput
↓
OnMove()で入力を受け取る
↓
moveInputへ保存
↓
FixedUpdate()
↓
moveDirectionを作る
↓
Move() / Rotate() / UpdateAnimation()
↓
移動・回転・アニメーションに反映
少し長く見えますが、実際には「入力を受け取る」「移動する」「向きを変える」「アニメーションを変える」という4つの処理を順番につないでいるだけです。
この記事では、完成コードをそのまま書くだけではなく、OnMove()とMove()の違いや、FixedUpdate()を使う理由なども一つずつ確認しながら進めます。
前回作ったキャラクターが、今回ついに自分の操作で歩き始めます。
手元に前回のプロジェクトを用意して、一緒に実装していきましょう!

前回のプロジェクトを開こう!
今回は前回作成したプロジェクトの続きから進めます。
まだキャラクターやAnimatorの設定が終わっていない場合は、先に前回の記事を進めておきましょう。

今回の記事では、Unity Editor 6000.3.23f1を使用して解説します。
Unityのバージョンによって画面表示が多少異なる場合がありますが、基本的な考え方は同じです。
キャラクターが倒れないように設定しよう!
まず、キャラクターが移動中に横倒しにならないようにしておきます。
HierarchyからSD_unitychan_humanoidを選択してください。
InspectorにあるRigidbody → Constraintsを開きます。
その中のFreeze Rotationを、次のように設定してください。
// Freeze Rotation
X:ON
Y:OFF
Z:ON

これによって、キャラクターが前後や左右に倒れる回転を制限できます。
Y軸はキャラクターが左右を向くために使うので、チェックを入れません。
Input Systemを設定しよう!
ここから今回の重要な部分です。
Unityでキーボードやゲームパッドなどの入力を扱う仕組みがInput Systemです。
以前のUnityでは、Input.GetAxis()やInput.GetKey()などを直接Scriptに書く方法がよく使われていました。
新Input Systemでは、次のように入力とゲーム側の処理を分けて管理します。
// 新Input System
WASD
↓
Move Action
↓
PlayerInput
↓
PlayerController
つまり、「どのキーを押したか」と「ゲーム上で何をするか」を分けて管理できます。
例えば後から、次のように入力方法を増やすこともできます。
Move
├─ WASD
├─ 矢印キー
└─ ゲームパッド左スティック
この場合でも、キャラクターを動かすScriptを大きく書き換える必要がありません。
Unity公式のInput Systemでも、PlayerInputはInput Actionをプレイヤーに結び付けるためのコンポーネントとして用意されています。
Unity Documentation|PlayerInput
Input Systemがインストールされているか確認しよう!
Unity6であれば、新Input Systemはすでに導入されている設定のはずですが、念のためインストール方法も記載しておきます。
Unity上部メニューからWindow → Pakege Management → Package Managerを開きます。

Package ManagerからInput Systemを検索してください。
Installedと表示されていればOKです。
まだ入っていない場合はInstallをクリックします。

新Input Systemを有効にしよう!
Unity6であれば、基本的には新Input Systemが有効になっているはずですが、念のため設定方法を記載しています。
次に、Edit → Project Settings → Playerを開きます。

Other Settingsの中にあるActive Input Handlingを確認してください。
今回はInput System Package (New)を使用します。
【画像挿入:Active Input HandlingをInput System Package (New)に設定した画面】


この状態では、旧方式のInput.GetAxis("Horizontal")などは使用しません。
旧Input APIを使うと、次のようなエラーが発生します。
InvalidOperationException:
You are trying to read Input using the UnityEngine.Input class,
but you have switched active Input handling to Input System package...
Input Actionsを作ろう!
Input ActionはUnity6では、すでにProject内にInputSystem_Actionsという名前のInput Actionがデフォルトで生成されていると思いますが、もし存在しない場合は以下の手順で作成してください。
Input Actionでは、「どの入力を、何という操作として扱うか」を設定します。
Projectウインドウで右クリック → Create → Input Actionsを選択します。
今回は名前をPlayerInputActionsとします。
すると、PlayerInputActions.inputactionsというファイルが作成されます。

PlayerというAction Mapを作ろう!
PlayerInputActions.inputactionsをダブルクリックします。
Input Actionsの編集画面が開いたら、Action Maps → +からPlayerというAction Mapを作ります。
Action Mapとは、入力をまとめて管理するグループです。
例えば今後ゲームを大きくしていくと、次のように整理できます。
Player
├─ Move
├─ Jump
└─ Attack
UI
├─ Navigate
└─ Submit
今回はプレイヤー操作だけなので、Playerを使用します。
Move Actionを作ろう!
Playerの中にMoveというActionを作ります。
設定は次のようにします。
Action Type:Value
Control Type:Vector2

なぜVector2なのでしょうか?
キャラクターの移動には、左右と前後という2方向の情報が必要だからです。
例えば、次のような2つの数値で入力方向を表します。
右 → (1, 0)
左 → (-1, 0)
前 → (0, 1)
後 → (0, -1)
WASDをMoveに登録しよう!
Move Actionに2D Vector Compositeを追加します。
そして、次のように設定します。
Up → W
Down → S
Left → A
Right → D

2D Vector Compositeは、上下左右のボタン入力を1つのVector2としてまとめてくれる仕組みです。
Unity Documentation|Vector2Composite
設定が終わったらSave Assetをクリックして保存しましょう。
これで、次の入力設定が完成しました。
WASD
↓
Move
↓
Vector2
PlayerControllerを作ろう!
次に、Input Systemから受け取った入力を使ってキャラクターを操作するScriptを作ります。
Projectウインドウから+ → MonoBehaviour Scriptを選択してください。

名前はPlayerControllerとします。

作成したらダブルクリックして開きましょう。

RigidbodyとAnimatorをScriptから使えるようにしよう!
まずは次のように書きます。
using UnityEngine;
using UnityEngine.InputSystem; // 書き加える
public class PlayerController : MonoBehaviour
{
private Rigidbody rb;
private Animator animator;
private Vector2 moveInput;
private float moveSpeed = 1f;
private float rotateSpeed = 10f;
void Awake()
{
rb = GetComponent<Rigidbody>();
animator = GetComponent<Animator>();
}
void Update()
{
}
}
順番に見てみましょう。
Rigidbodyを入れておく変数
private Rigidbody rb;
キャラクターのRigidbodyをプログラムから操作するための変数です。
あとでrb.MovePosition()などを使ってキャラクターを移動させます。
Animatorを入れておく変数
private Animator animator;
キャラクターのAnimationを操作するために使います。
あとでanimator.SetBool()を使ってIdleとWalkingを切り替えます。
入力を保存する変数
private Vector2 moveInput;
ここにはInput Systemから受け取った左右・前後の入力が入ります。
例えばWキーなら(0, 1)、Dキーなら(1, 0)といった値です。
Awakeでコンポーネントを取得する
void Awake()
{
rb = GetComponent<Rigidbody>();
animator = GetComponent<Animator>();
}
GetComponent<Rigidbody>()によって、このScriptを付けたGameObjectにあるRigidbodyを取得します。
同様にGetComponent<Animator>()でAnimatorを取得しています。
今回はtransformを自分で取得する必要はありません。
MonoBehaviourではtransformをそのまま使用できるためです。
PlayerControllerをキャラクターに付けよう!
作成したPlayerController.csをSD_unitychan_humanoidへドラッグ&ドロップします。

InspectorにPlayer Controller (Script)が表示されていればOKです。

Input SystemとPlayerControllerを関連付けよう!
まだInput ActionsとPlayerControllerはつながっていません。
ここで使うのがPlayer Inputというコンポーネントです。
SD_unitychan_humanoidを選択して、Add Component → Player Inputを追加してください。

Player Inputは、Input ActionsとPlayer GameObject、Scriptをつなぐ役割を持っています。
Player Inputを設定しよう!
Player InputのActionsに、デフォルトで用意されているInputSystem_Actions(新しく作った人は、先ほど作ったPlayerInputActions.inputactions)をドラッグ&ドロップします。
Default MapはPlayer、BehaviorはInvoke Unity Eventsに設定してください。

Invoke Unity Eventsを使うと、Input Actionごとに「どのメソッドを呼ぶか」をInspector上から設定できます。
各イベントはInputAction.CallbackContextを渡せるため、入力値をScript側で取得できます。
Unity Documentation|PlayerInput.ActionEvent
Move入力をScriptで受け取ろう!
いよいよInput Systemから入力を受け取ります。
PlayerControllerに次のメソッドを追加してください。
public void OnMove(InputAction.CallbackContext context)
{
moveInput = context.ReadValue<Vector2>();
}
全体では次のようになります。
using UnityEngine;
using UnityEngine.InputSystem;
public class PlayerController : MonoBehaviour
{
private Rigidbody rb;
private Animator animator;
private Vector2 moveInput;
private float moveSpeed = 1f;
private float rotateSpeed = 10f;
void Awake()
{
rb = GetComponent<Rigidbody>();
animator = GetComponent<Animator>();
}
public void OnMove(InputAction.CallbackContext context)
{
moveInput = context.ReadValue<Vector2>();
}
}
OnMoveって何?
ここは非常に重要です。
public void OnMove(InputAction.CallbackContext context)は、Input SystemからMove入力が送られてきたときに呼び出すメソッドです。
例えばWを押すと、次の順番で処理されます。
Wキー
↓
Move Action
↓
PlayerInput
↓
OnMove()
context.ReadValue<Vector2>()によってMove Actionの入力値を取得し、その値をmoveInputに保存しています。
Player InputとOnMoveをつなごう!
Player InputのEvents → Player → Moveを開きます。
「+」を押して、SD_unitychan_humanoid自身を登録します。

次に関数としてPlayerController → OnMoveを選択します。

Inspectorは次のようになります。

これで、入力からスクリプトまでがつながりました。
WASD
↓
Move
↓
PlayerInput
↓
OnMove()
↓
moveInput
キャラクターを実際に動かそう!
ここまででは入力を受け取っただけなので、まだキャラクターは動きません。
次はmoveInputを使って実際にキャラクターを移動させます。
FixedUpdateを作ろう!
次のコードを追加します。もともとあるUpdate関数をFixedUpdateに変更しています。
void FixedUpdate()
{
Vector3 moveDirection =
new Vector3(moveInput.x, 0f, moveInput.y);
if (moveDirection.sqrMagnitude > 1f)
{
moveDirection.Normalize();
}
Move(moveDirection);
}
Vector2をVector3へ変換する
Input Systemから受け取ったのはVector2 moveInputです。
しかし3Dゲームでは、X・Y・Zの3方向の座標を扱います。
そこで、次のコードで2D入力を3D空間の移動方向へ変換します。
Vector3 moveDirection = new Vector3(moveInput.x, 0f, moveInput.y);
対応関係は、InputのX → Unity空間のX、InputのY → Unity空間のZです。
例えばWなら、次のようになります。
moveInput
(0, 1)
↓
moveDirection
(0, 0, 1)
つまり、Z方向へ前進します。
斜め移動が速くならないようにする
続いて、次の処理を追加しています。
if (moveDirection.sqrMagnitude > 1f)
{
moveDirection.Normalize();
}
WとDを同時に押すと(1, 0, 1)という方向になります。
そのままだと直進より斜め移動の方が速くなるため、Normalize()によって方向ベクトルの大きさを揃えています。
Moveメソッドを作ろう!
次に実際の移動処理を作ります。
private void Move(Vector3 direction)
{
Vector3 nextPosition =
rb.position
+ direction * moveSpeed * Time.fixedDeltaTime;
rb.MovePosition(nextPosition);
}

rb.positionは現在の位置です。
そこへdirection * moveSpeed * Time.fixedDeltaTimeを加えて、次の位置を計算しています。
そしてrb.MovePosition(nextPosition)でRigidbodyを移動させています。
Rigidbodyを用いた継続的な物理処理はFixedUpdate()と組み合わせて扱うのが基本です。
OnMoveとMoveは何が違うの?
ここで、よく混乱するポイントを整理しておきましょう。
コードにはOnMove()とMove()という似た名前のメソッドがありますが、役割はまったく違います。
| メソッド | 役割 |
|---|---|
OnMove() |
Input Systemから入力を受け取る |
Move() |
Rigidbodyを実際に移動させる |
処理の順番は次の通りです。
Wを押す
↓
OnMove()
↓
moveInputに保存
↓
FixedUpdate()
↓
Move()
↓
キャラクターが動く
OnMove()は、「移動したい」という入力を受け付ける担当です。
一方、Move()は、実際にキャラクターを移動させる担当です。
入力と実際のゲーム処理を分けておくことで、プログラムが整理しやすくなります。
キャラクターを移動方向へ向かせよう!
この状態でゲームを実行すると、キャラクターは移動します。
しかし、モデルはずっと同じ方向を向いたままです。
そこで、右に進めば右、左に進めば左、後ろに進めば後ろを向くようにしてみましょう。
Rotateメソッドを作ろう!
次のメソッドを追加します。
private void Rotate(Vector3 direction)
{
if (direction.sqrMagnitude < 0.01f)
{
return;
}
Quaternion targetRotation = Quaternion.LookRotation(direction);
Quaternion newRotation =
Quaternion.Slerp(
rb.rotation,
targetRotation,
rotateSpeed * Time.fixedDeltaTime
);
rb.MoveRotation(newRotation);
}
そしてFixedUpdate()を、次のように変更します。
void FixedUpdate()
{
Vector3 moveDirection =
new Vector3(moveInput.x, 0f, moveInput.y);
if (moveDirection.sqrMagnitude > 1f)
{
moveDirection.Normalize();
}
Move(moveDirection);
Rotate(moveDirection);
}
RotateはInput Systemのイベントではない
ここも大切です。
今回のRotate()はInput Systemから直接呼び出されるイベントではありません。
今回はMove入力から移動方向を計算し、その方向を向く仕組みにしています。
Move入力
↓
移動方向を計算
↓
その方向を向く
そのため、Input System側にはMove Actionだけあれば、移動と回転の両方を処理できます。
マウス操作などで「移動方向とは別に向きを変えるゲーム」を作る場合は、Input System側にLookやRotate Actionを別途作ることもできます。
今回は必要ありません。
入力がないときは回転しない
if (direction.sqrMagnitude < 0.01f)
{
return;
}
では、ほとんど移動入力がない場合にRotate処理を終了しています。
入力がない状態でQuaternion.LookRotation(Vector3.zero)を実行しないためにも必要な処理です。
LookRotationで進行方向を向く
Quaternion targetRotation =
Quaternion.LookRotation(direction);
によって、移動方向を正面に向けるための回転を作っています。
Slerpで滑らかに回転する
いきなり方向を変えるのではなく、Quaternion.Slerp()を使って現在の向きから目標の向きへ滑らかに近づけます。
Quaternion newRotation =
Quaternion.Slerp(
rb.rotation,
targetRotation,
rotateSpeed * Time.fixedDeltaTime
);
rotateSpeedを大きくすると素早く向きを変え、小さくするとゆっくり回転します。
最後にrb.MoveRotation(newRotation)でRigidbodyの向きを変更します。
歩いているときだけWalkingにしよう!
Animatorと連動させるので、Animatorの設定を確認しておきましょう。
Idle状態とWalking状態を、isWalkingというBool型のパラーメータで切り替えられるようにしておく必要があります。
詳しくは前回の記事の【Animatorを設定しよう!】を参照してください。
UpdateAnimationを作ろう!
PlayerControllerに次のメソッドを追加します。
private void UpdateAnimation(Vector3 direction)
{
bool isWalking =
direction.sqrMagnitude > 0.01f;
animator.SetBool("isWalking", isWalking);
}
FixedUpdate()からも呼び出します。
void FixedUpdate()
{
Vector3 moveDirection =
new Vector3(moveInput.x, 0f, moveInput.y);
if (moveDirection.sqrMagnitude > 1f)
{
moveDirection.Normalize();
}
Move(moveDirection);
Rotate(moveDirection);
UpdateAnimation(moveDirection);
}
歩いているかどうかを調べる
direction.sqrMagnitude > 0.01fによって、移動入力があるかどうかを調べています。
移動入力があればtrue、なければfalseになります。
そしてanimator.SetBool("isWalking", isWalking)によって、AnimatorのBool Parameterを書き換えます。
Unity Documentation|Animator.SetBool
完成したPlayerControllerを確認しよう!
最終的なScriptはこちらです。
using UnityEngine;
using UnityEngine.InputSystem;
public class PlayerController : MonoBehaviour
{
private Rigidbody rb;
private Animator animator;
private Vector2 moveInput;
private float moveSpeed = 1f;
private float rotateSpeed = 10f;
void Awake()
{
rb = GetComponent<Rigidbody>();
animator = GetComponent<Animator>();
}
// Input SystemからMove入力を受け取る
public void OnMove(InputAction.CallbackContext context)
{
moveInput = context.ReadValue<Vector2>();
}
void FixedUpdate()
{
// 2D入力を3D空間の移動方向へ変換
Vector3 moveDirection =
new Vector3(moveInput.x, 0f, moveInput.y);
// 斜め移動が速くなりすぎないようにする
if (moveDirection.sqrMagnitude > 1f)
{
moveDirection.Normalize();
}
Move(moveDirection);
Rotate(moveDirection);
UpdateAnimation(moveDirection);
}
// キャラクターを移動させる
private void Move(Vector3 direction)
{
Vector3 nextPosition =
rb.position
+ direction * moveSpeed * Time.fixedDeltaTime;
rb.MovePosition(nextPosition);
}
// キャラクターを移動方向へ向かせる
private void Rotate(Vector3 direction)
{
// 入力がなければ回転しない
if (direction.sqrMagnitude < 0.01f)
{
return;
}
Quaternion targetRotation =
Quaternion.LookRotation(direction);
Quaternion newRotation =
Quaternion.Slerp(
rb.rotation,
targetRotation,
rotateSpeed * Time.fixedDeltaTime
);
rb.MoveRotation(newRotation);
}
// 移動状態に合わせてAnimationを変更する
private void UpdateAnimation(Vector3 direction)
{
bool isWalking =
direction.sqrMagnitude > 0.01f;
animator.SetBool("isWalking", isWalking);
}
}
プログラム全体の流れを理解しよう!
今回のプログラムで一番重要なのは、それぞれのメソッドに役割があることです。

それぞれの役割をまとめると、次のようになります。
| メソッド | 役割 |
|---|---|
Awake() |
RigidbodyとAnimatorを取得 |
OnMove() |
Input Systemから入力を受け取る |
FixedUpdate() |
一定間隔の物理更新タイミングで処理を実行 |
Move() |
キャラクターを移動 |
Rotate() |
移動方向へキャラクターを回転 |
UpdateAnimation() |
Animatorの移動状態を更新 |
特にOnMove()とMove()は名前が似ていますが、役割が違うことを覚えておきましょう。
OnMove()は入力を受け取り、Move()は実際にキャラクターを動かします。
実際に動かしてみよう!
Unityに戻ってゲームを実行します。
W・A・S・Dを押してみましょう。
W → 前進
S → 後退
A → 左へ移動
D → 右へ移動
さらに、進む方向へキャラクターが滑らかに回転し、移動中はWalking、停止するとIdleになっていることも確認してください。

これで、キーボードからキャラクターを操作できるようになりました!
うまく動かないときに確認しよう!
Input.GetAxisのエラーが出る
次のようなエラーが出る場合があります。
InvalidOperationException:
You are trying to read Input using the UnityEngine.Input class,
but you have switched active Input handling to Input System package...
これは新Input Systemを使用しているのに、Input.GetAxis()などの旧Input System用コードが残っている場合に発生します。
今回の記事ではpublic void OnMove(InputAction.CallbackContext context)を使うので、Input.GetAxis()は必要ありません。
WASDを押しても動かない
次を確認してください。
- Player Inputの
Actionsが設定されている Default MapがPlayerになっているBehaviorがInvoke Unity Eventsになっている- Player InputのMoveイベントに
PlayerController.OnMoveが設定されている - Input ActionsのMoveが
Vector2になっている - WASDが
2D Vector Compositeに設定されている
キャラクターが倒れる
RigidbodyのConstraints → Freeze Rotationで、XとZにチェックが入っているか確認してください。
歩いてもAnimationが変わらない
AnimatorのParameter名を確認します。
Scriptではanimator.SetBool("isWalking", isWalking)としているため、Animator側のParameter名もisWalkingでなければなりません。
大文字・小文字も区別されるため、walkingとisWalkingは別の名前です。
まとめ
今回はUnity 6と新Input Systemを使って、キャラクターをWASDで操作するプログラムを作りました!
今回覚えてほしい流れは、次の通りです。
① Input Systemを設定する
↓
② PlayerInputでScriptとつなぐ
↓
③ OnMove()で入力を受け取る
↓
④ Move()で移動する
↓
⑤ Rotate()で移動方向を向く
↓
⑥ UpdateAnimation()でAnimatorを更新する
単に完成したコードを覚えるのではなく、「入力」「移動」「回転」「アニメーション」を別々の処理として考えることがポイントです。
今回作った仕組みにJump、Run、Attack、Coin、Score、Cameraなどを追加していけば、少しずつ本格的な3Dゲームへ発展させられます!
テックチャンスでは無料のプログラミング体験を実施しています!
プログラミング教室テックチャンスでは、子どもたちが実際に作品を作りながらプログラミングを学べる授業を行っています。
Scratchを使ったプログラミングから、Minecraft、そして今回紹介したようなUnityを使った本格的なゲーム開発まで、経験に合わせてステップアップできます。
「ゲームを遊ぶだけではなく、自分で作ってみたい」
「プログラミングに興味はあるけれど、何から始めればいいか分からない」
という方は、ぜひ一度無料体験に参加してみてください!