キャラクターを歩かせてジャンプさせたいなら、Unity標準の「Character Controller」コンポーネントがいちばん手軽です。この記事では、SDユニティちゃんを使った設定と移動・ジャンプのスクリプトに加えて、重力の付け方、Move と SimpleMove の違い、当たり判定のコツ、Unity 6 で動かない時の直し方まで分かります。
元は2020年に書いた記事で、2026年10月に Unity 6 の公式マニュアルを見ながら内容を足しました。当時のスクリプトと手順はそのまま残しています。
Character Controllerとは|Rigidbodyとの違い
スクリプトを作ってモデルを走らせたり、ジャンプさせるには、Character Controllerを使うと便利です。Character Controllerは、ColliderとRigidbodyを組み合わせたようなコンポーネントです。
階段を上る際など、物理的にやや複雑な動作等を上手く取り扱ってくれます。
Rigidbodyとのいちばんの違いは、物理演算に「押されない」ことです。公式マニュアルにも、Character Controllerは他のオブジェクトの物理の影響を受けないと書かれています。爆風で吹き飛ぶような動きは苦手ですが、そのぶん思ったとおりにキビキビ動かせるので、プレイヤーキャラにはこちらが向いていますね。
| Character Controller | Rigidbody | |
|---|---|---|
| 動かし方 | Move / SimpleMove を呼ぶ | 力(AddForce)や速度を与える |
| 重力 | Moveは自分で計算(SimpleMoveは自動) | 自動でかかる |
| 段差・坂 | Step Offset と Slope Limit で設定 | 自分で工夫が必要 |
| 他の物から押される | 押されない | 押される |
Character Controllerの使い方(モデル:SDユニティちゃん)
まずは、SDユニティちゃんをシーン上に配置します。SDユニティちゃんを選択した状態で、Add Componentメニューから「Character Controller」を追加します。

この状態だと分かりますが、ユニティちゃんの見た目とコライダの範囲が明らかに違います。かなり下の方までコライダが表示されているので、Character ControllerのCenterとHeightを調整します。
私の場合は、Centerを「0.5」にしてHeightを「1」に変更しました。
インスペクターの各項目の意味
CenterとHeight以外にも、動きに関わる項目がいくつかあります。Unity 6 の公式マニュアルの説明と目安をまとめると、次のとおりです。
- Slope Limit:登れる坂の最大角度(度)
- Step Offset:登れる段差の高さ。身長2mの人型なら0.1〜0.4が目安で、Heightより大きくするとエラーになる
- Skin Width:ほかのコライダーと少しだけ重なってよい幅。Radiusの10%くらい、最低でも0.01
- Min Move Distance:これより小さい移動は無視する。たいていは0でOK
- Center / Radius / Height:カプセル形の当たり判定の位置・太さ・高さ
最初はここでつまずきやすいです。キャラが壁や床に引っかかって動かなくなる時は、マニュアルによるとSkin Widthが小さすぎるのがいちばん多い原因だそうです。SDユニティちゃんのように小さいキャラは、HeightやRadiusを縮めたぶん、Skin WidthとStep Offsetも合わせて小さくしておくと安心ですね。
CharacterControllerで移動・ジャンプさせるスクリプト
SDユニティちゃんを制御する「UnitychanController」スクリプトを新規作成します。
using UnityEngine;
public class UnitychanController : MonoBehaviour
{
CharacterController controller;
Vector3 movedir = Vector3.zero;
void Start()
{
controller = GetComponent<CharacterController>();
}
void Update()
{
if (controller.isGrounded)
{
// 上下キーで前後に移動
movedir.z = Input.GetAxis("Vertical") * 3.0f;
// 左右キーで向きを変える
transform.Rotate(0, Input.GetAxis("Horizontal") * 3, 0);
// スペースキーでジャンプ
if (Input.GetButton("Jump"))
{
movedir.y = 10f;
}
}
// 重力(毎フレーム下向きの速さを足す)
movedir.y -= 20f * Time.deltaTime;
// ローカルの向きをワールドの向きに変換して移動
Vector3 globaldir = transform.TransformDirection(movedir);
controller.Move(globaldir * Time.deltaTime);
if (controller.isGrounded)
{
movedir.y = 0;
}
}
}
CharacterControllerの場合は、Rigidbodyとは違って重力は自動で計算されないので、重力の値を考慮しながらベクトルの計算を行います。
このスクリプトは、キーボードの入力に応じて、ユニティちゃんを前後左右に移動及びジャンプさせるような内容になっています。
「controller.isGrounded」でユニティちゃんが地面に接地しているか判断し、接地していればキーボードの入力を受け付けるようになります。
上下矢印キーで前後に移動し、左右キーで方向転換を行います。また、スペースキーを押すとジャンプするようにしています。
重力は、Time.deltaTimeの特性を活かして、毎フレームの重力のvelocityを下方向に加えます。
ユニティちゃんの移動方法は、TransformDirection関数で、ローカルの移動ベクトルをグローバルな値に変換して、CharacterControllerのMove関数に渡します。
CharacterControllerでの移動は、基本的にMove関数を使う事になります。
スクリプトを保存したら、ユニティちゃんにアタッチします。

実行して、動作を確認してみましょう。
アニメーションの実装を行っていないので、若干不自然ですが今回はCharacterControllerの使い方に関しての紹介でしたので、基本的なキャラクターの制御の仕方が分かってもらえればと思います。
isGroundedが正しく取れない時
isGroundedは「直前の Move(または SimpleMove)で地面に触れていたか」を返すプロパティです。公式のスクリプトリファレンスでもそう説明されています。つまり、Moveを呼ぶ前に読むと1フレーム前の結果になり、下向きの移動がゼロのフレームでは地面から離れた扱いになることがあるんです。
上のスクリプトで、接地中も毎フレーム重力を足してからMoveしているのはそのためです。地面に向かって押し付け続けることで、isGroundedが安定してtrueになります。
MoveとSimpleMoveの違い
CharacterControllerには移動の関数が2つあります。
Moveは重力を使いません。渡した移動量(距離)のとおりに動かし、ぶつかった方向を CollisionFlags(None / Sides / Above / Below)で返します。ジャンプしたいなら、上のスクリプトのように縦方向の速さを自分で管理します。
SimpleMoveは重力が自動でかかります。そのかわり、渡した速度のY成分は無視されるのでジャンプには使えません。速度は「1秒あたり」の値で渡し、戻り値は接地しているかどうかです。
// SimpleMoveなら重力の計算はいらない(ジャンプはできない)
Vector3 forward = transform.TransformDirection(Vector3.forward);
float speed = Input.GetAxis("Vertical") * 3.0f;
controller.SimpleMove(forward * speed); // Time.deltaTime は掛けない
歩くだけのNPCならSimpleMove、ジャンプのあるプレイヤーならMove、という使い分けが分かりやすいと思います。なお、公式リファレンスでは MoveとSimpleMoveは1フレームに1回だけ呼ぶよう勧めています。両方を同じフレームで呼ぶのは避けましょう。
当たり判定でRigidbodyの箱を押す
CharacterControllerは物理に押されませんが、逆に「箱を押す」ことも、そのままではできません。ぶつかった時に呼ばれる OnControllerColliderHit で、相手のRigidbodyに速度を与えてあげます。
public float pushPower = 2.0f; // 押す強さ
void OnControllerColliderHit(ControllerColliderHit hit)
{
Rigidbody body = hit.collider.attachedRigidbody;
// Rigidbodyがない、またはKinematicなら押さない
if (body == null || body.isKinematic) return;
// 足元の物(下向きにぶつかった物)は押さない
if (hit.moveDirection.y < -0.3f) return;
// 横方向にだけ押す
Vector3 pushDir = new Vector3(hit.moveDirection.x, 0, hit.moveDirection.z);
body.linearVelocity = pushDir * pushPower;
}
Unity 6 では Rigidbody の velocity が linearVelocity という名前に変わっています。古い記事のコードをそのまま貼ってエラーになったら、ここを疑ってください。
Unity 6でCharacterControllerが動かない時の対処
Input.GetAxisでエラーが出る
Unity 6で新しく作ったプロジェクトは、入力が新しいInput Systemになっていることが多く、上のスクリプトの Input.GetAxis を使うと InvalidOperationException が出て動きません。
Edit > Project Settings > Player > Other Settings の Active Input Handling を「Both」にすると、古い書き方のまま動かせます。
transform.positionで動かすと元の位置に戻る
リスポーンなどで transform.position を直接書き換えると、次のMoveでキャラが元の場所に戻ってしまうことがあります。物理側がTransformの変更をまだ知らないためで、書き換えた直後に Physics.SyncTransforms() を呼ぶと、変更が物理側に反映されます。
transform.position = respawnPoint.position; // リスポーン地点へ
Physics.SyncTransforms(); // 位置の変更を物理側に伝える
同じ現象はセーブデータから位置を読み込む時にも起きます。Easy Saveでセーブポイントを作った記事でも、この問題に触れています。
自作が大変ならアセットも検討する
CharacterControllerだけでも移動とジャンプは作れますが、動く床に乗る・カメラを回り込ませる・アニメーションと合わせる、まで作ると手間が一気に増えます。
Unity公式の無料アセット「Starter Assets」は、CharacterControllerを使った一人称・三人称の操作がまるごと入っていて、スクリプトのお手本にもなります。導入方法をこちらにまとめました。
坂道や段差の処理まで作り込まれたものが欲しい人は、無料・有料のキャラクターコントローラーを比べた記事も参考にしてください。
有料アセットは、毎月のAsset Storeセール情報で値下がりを確認してから買うのがお得です。
まとめ
CharacterControllerは、Add Componentで付けて、Moveに移動量を渡すだけでキャラを動かせます。つまずきやすいのは、重力を自分で足す必要があること、isGroundedが直前のMoveの結果であること、そしてSkin Widthの小ささによる引っかかり。この3つを覚えておけば、たいていのトラブルは自分で直せるはずです。
ほかの定番アセットも眺めてみたい人は、人気アセット40選もどうぞ。





