【Unity】3Dオブジェクトをキャラクタコントローラで移動させる

2024年9月30日

3D空間で、プレイヤーを移動させるサンプルを作成してみましょう
様々な方法がありますが、今回はUnityエンジンのライブラリで用意されているCharacter Controllerクラス(コンポーネント)を使ってみます

どのようなサンプルか

次の動画で、イメージを確認しましょう

操作

キーボードで移動(上下左右キー、または、WASDキー)、マウスでカメラの移動になります

動画で確認

プレイヤーの作成

では、プレイヤーの作成から始めましょう
モックアップとして、カプセル型に鼻のようなCubeをくっつけたものを作ります
鼻はどちらを向いているかわからないためにつけておきます
実際の作成時においては、3Dモデルが実装できる状態の段階で差し替えを考えるといいでしょう
それまでの評価はこのようなもので進めると効率よく開発することができます

ゲームオブジェクトの作成

プレイヤーはプリミティブなCapsuleで作成します(標準の3Dオブジェクト)
前がわからないので、適当な形に加工したCubeを子オブジェクトとして取り付けておきます

Capsuleゲームオブジェクト等、デフォルトでアタッチされているコライダーの削除

CharacterControllerを使用する際に、そのコンポーネントを持つオブジェクトに既に別のコライダー(例えば、Capsule Colliderなど)がアタッチされている場合、一般的にはその他のコライダーを削除することが推奨されます。CharacterController自体がコライダーの役割を果たし、キャラクターの移動や地形との衝突検出を管理するためです。二つのコライダーが同一オブジェクトに存在すると、予期せぬ物理挙動や衝突検出の問題が発生する可能性があります。

Capsuleゲームオブジェクトにアタッチされているコライダー

Capsule Colliderコンポーネントの削除方法

同様に鼻の部分にあたるBox Colliderも削除しておきましょう

上記を参考に削除しておきましょう

キャラクタコントローラコンポーネントのアタッチ

『CharacterController』コンポーネントは、キャラクターの移動と衝突の検出を管理します。重力の自己管理、斜面の制御、及び障害物を越える機能を提供し、物理エンジンを用いずに動作するため、開発者はキャラクターの動きを細かくコントロールできます。このコンポーネントは「Slope Limit」で斜面の扱い、「Step Offset」で障害物越えを調整し、ゲーム内で自然なキャラクターの動きを実現します。プレイヤーやNPCの制御に適しており、特にアクションゲームでの使用に優れています。

プレイヤーにスクリプトをアタッチ

プレイヤーをコントロールするための2つのスクリプトを作成し、アタッチします

入力による移動用のスクリプト

「PlayerController」スクリプトは、キャラクターの前進速度と回転速度を制御するフィールド(公開変数)があり、CharacterController コンポーネントを使って移動と回転を実現しています。Updateメソッド内では、垂直(前後)と水平(左右)の入力を取得し、これをもとにキャラクターの前進・後退と左右の回転を行います。前進・後退はキャラクターの向きと入力値に基づいて計算され、CharacterController.Moveメソッドを使用して実際の移動を行い、回転はtransform.Rotateメソッドで実現されています。このスクリプトは、キーボードの矢印キーによるプレイヤーキャラクターの基本的な操作を可能にします。

using UnityEngine;

public class PlayerController : MonoBehaviour
{
    public float speed = 5.0f; // 前進の速度
    public float rotationSpeed = 240.0f; // 回転の速度(度/秒)

    private CharacterController characterController;

    void Start()
    {
        characterController = GetComponent<CharacterController>();
    }

    void Update()
    {
        float vertical = Input.GetAxis("Vertical"); // 上下の矢印キーの入力を取得
        float horizontal = Input.GetAxis("Horizontal"); // 左右の矢印キーの入力を取得

        // 前進または後退
        Vector3 forwardMovement = transform.forward * vertical * speed * Time.deltaTime;

        // キャラクターの移動
        characterController.Move(forwardMovement);

        // 左右の回転
        transform.Rotate(0, horizontal * rotationSpeed * Time.deltaTime, 0);
    }
}

斜め方向に移動すると、前後と左右の移動速度の合成で実際の速度が速くなってしまうため、それを修正するには、移動ベクトルの正規化を行います。つまり、入力の結果得られたベクトルの大きさを常に一定に保つようにすることで、移動速度が一定になるように調整します。

以下のように、forwardMovementsideMovementを合成した後に、ベクトルの長さを正規化してから移動させるように修正します。

using UnityEngine;

public class PlayerController : MonoBehaviour
{
    public float speed = 5.0f; // 前進の速度
    public float rotationSpeed = 240.0f; // 回転の速度(度/秒)

    private CharacterController characterController;

    void Start()
    {
        characterController = GetComponent<CharacterController>();
    }

    void Update()
    {
        float vertical = Input.GetAxis("Vertical"); // 上下の矢印キーの入力を取得
        float horizontal = Input.GetAxis("Horizontal"); // 左右の矢印キーの入力を取得

        // 前後と左右の動きを合成した移動ベクトルを作成
        Vector3 moveDirection = (transform.forward * vertical) + (transform.right * horizontal);

        // ベクトルを正規化して、移動速度を一定にする
        if (moveDirection.magnitude > 1)
        {
            moveDirection.Normalize();
        }

        // 移動ベクトルにスピードと時間を掛けて移動
        characterController.Move(moveDirection * speed * Time.deltaTime);

        // 左右の回転
        transform.Rotate(0, horizontal * rotationSpeed * Time.deltaTime, 0);
    }
}

変更点:

  • moveDirectionというベクトルに前進と左右の動きを合成。
  • ベクトルの長さ (magnitude) が1を超える場合、Normalize()を使ってベクトルを正規化し、斜め移動でも速度が速くならないように調整。

重力を適用したキャラクター移動を実装するコード

「CharacterGravityController」スクリプトは、キャラクターに重力を適用しています。CharacterControllerコンポーネントを利用し、キャラクターが地面にいるかどうかを判定しています。地面に接触している場合、垂直方向の速度をリセットし、空中にいる場合は重力加速度を垂直速度に加えてキャラクターを下向きに動かします。Updateメソッド内でこれらの処理を毎フレーム実行し、Moveメソッドでキャラクターを移動させています。

using UnityEngine;

public class GravityMovement : MonoBehaviour
{
    public float gravity = -9.81f; // 重力加速度
    private Vector3 velocity; // キャラクターの速度 

    CharacterController controller;

    private void Start()
    {
        controller = GetComponent<CharacterController>();
    }

    void Update()
    {
        if (controller.isGrounded)
        {
            velocity.y = 0f; // 地面にいる場合は垂直方向の速度をリセット
        }
        else
        {
            velocity.y += gravity * Time.deltaTime; // 空中にいる場合は重力を適用
        }
        controller.Move(velocity * Time.deltaTime); // 速度に基づいて移動
    }
}

360度自由な視点移動を提供するカメラの追加

CinemachineのFreeLookカメラは、プレイヤーがキャラクター周囲を360度自由に見渡せるカメラシステムを実装するための強力な機能です。三つの異なる軌道(上、中、下)を設定し、プレイヤーの入力に応じて滑らかに移動やズームができるように調整されます。これにより、第三人称視点ゲームでの没入感を高め、環境の探索やキャラクターの観察を直感的に行えるようになります。

Cinemachineのインストール

UnityのPackageManagerからCinemachineを検索し、「Install」をクリックして追加します
画面は、Unity2023になります

WindowメニューからPackage Managerを選択

Unity Registoryからcinemachineをインストール

Unity2023-
-Unity2022

Unity Registoryをプルダウンメニューから選択

検索(絞り込み)してインストール

ヒエラルキービューからフリールックカメラを作成

FreeLookの動き

FreeLook Cameraゲームオブジェクトを選択すると、移動ガイドがシーンビューに赤色の円で表示されます
上から、Top Ring, Middle Ring, Bottom Ringになります

設定

次を参考に調整してみましょう
Follow, Look Atに追いかけるプレイヤーを登録します
カメラの動きについては、今回は3つのリングの直径と高さを調整しています

移動で基本的な登録は終わりです

試してみよう

Terrainで緩やかな起伏を設けてみたりして移動してみましょう
スロープ(Cubeを変形させて坂を作る)の登り降りにも挑戦してみましょう
どの角度まで登れるか、インスペクターの数値調整でできる機能がCharacter Controllerコンポーネントには備わっています

カメラが壁を突き抜けないようにしたい

プレイヤーとカメラの距離が一定のため、迷路などの場合、壁をカメラが突き抜けて具合が悪いですね
カメラにコライダーのような機能を持たせ、あたかも映画のカメラマンの存在のように見せることでめり込まないようにする設定が可能です

突き抜ける様子

突き抜けない場合

設定

Unity

Posted by hidepon