【Unity】移動スクリプトサンプルコード

ネット上や書籍の参考となるコードを見つけても、自分のコードに活かすには文法やコーディングに関するスキルが必要になります
一見、コピー・貼り付けで問題ないように見えても、調整したい箇所や修正したい箇所を見つけた時に自在に処理できるようにするコツを掴むことが大事です

コードの理解と更新の考え方

自分で作成したコードに活かせそうなサンプルコードを見つけた際は、まず全体構造と各部分がどのように機能するかを理解しましょう。重要なのは、そのコードがどの問題を解決しようとしているか、どのような原理で動作しているかを把握することです。次に、そのコードを自分のプロジェクトやニーズに合わせてカスタマイズする方法を考えます。この過程で、新しい技術や手法を学び、最新のプラクティスやライブラリに更新していくことが重要です。また、パフォーマンスの改善やセキュリティの強化も考慮に入れ、継続的にコードをリファクタリングし、更新していきましょう。

今回参考とするオリジナルコード

このコードはUnityのCharacterControllerコンポーネントを使用したプレイヤーコントローラーの実装です。プレイヤーの移動速度(moveSpeed)とジャンプ力(jumpPower)を設定可能にしており、地上にいるか空中にいるかをログで表示します。水平及び垂直方向の入力に応じて移動し、向きを変更します。地上にいる時にジャンプボタンを押すと、ジャンプ力に基づいて上方向に移動します。空中にいる場合は、重力に従って下方向に加速します。プレイヤーの移動は、CharacterController.Moveメソッドを使用して適用されます。

using UnityEngine;

// CharacterControllerコンポーネントが必要であることを指定
[RequireComponent(typeof(CharacterController))]
public class RefacterPlayerController : MonoBehaviour
{
    [SerializeField] private float moveSpeed = 3; // 移動速度
    [SerializeField] private float jumpPower = 3; // ジャンプ力
    private CharacterController _characterController; // キャラクターコントローラーへの参照
    private Transform _transform; // トランスフォームへの参照
    private Vector3 _moveVelocity; // 移動速度ベクトル

    private void Start()
    {
        // コンポーネントの取得
        _characterController = GetComponent<CharacterController>();
        _transform = transform;
    }

    void Update()
    {
        // キャラクターが地上にいるか空中にいるかをログに出力
        Debug.Log(_characterController.isGrounded ? "地上にいます" : "空中です");

        // 水平および垂直の入力に基づいて移動速度を計算
        _moveVelocity.x = Input.GetAxis("Horizontal") * moveSpeed;
        _moveVelocity.z = Input.GetAxis("Vertical") * moveSpeed;

        // キャラクターが移動方向を向くようにする
        transform.LookAt(_transform.position + new Vector3(_moveVelocity.x, 0, _moveVelocity.z));

        if (_characterController.isGrounded)
        {
            // 地上にいる場合、ジャンプの入力を受け付ける
            if (Input.GetButtonDown("Jump"))
            {
                Debug.Log("ジャンプ!");
                _moveVelocity.y = jumpPower; // ジャンプ力を適用
            }
        }
        else
        {
            // 空中にいる場合、重力を適用して落下させる
            _moveVelocity.y += Physics.gravity.y * Time.deltaTime;
        }
        // 計算した移動速度でキャラクターを移動させる
        _characterController.Move(_moveVelocity * Time.deltaTime);
    }
}

リファクタリングされたコード

コードのリファクタリングにおいて、主要な機能をメソッドに分割することは、コードの可読性と再利用性を向上させます。以下のコードでは、移動、向きの変更、ジャンプといった機能を個別のメソッドに分けています。これにより、各機能が独立しているため、理解しやすく、修正や拡張がしやすくなります。

using UnityEngine;

// CharacterControllerコンポーネントが必要です
[RequireComponent(typeof(CharacterController))]
public class PlayerController : MonoBehaviour
{
    [SerializeField] private float moveSpeed = 3; // 移動速度
    [SerializeField] private float jumpPower = 3; // ジャンプ力
    private CharacterController _characterController;
    private Vector3 _moveVelocity; // 移動方向と速度を管理するベクトル

    private void Start()
    {
        _characterController = GetComponent<CharacterController>(); // コンポーネントの取得
    }

    void Update()
    {
        ProcessMovement(); // 移動処理を実行
    }

    // 移動処理をまとめたメソッド
    private void ProcessMovement()
    {
        LogGroundStatus(); // 地上または空中の状態をログに出力

        CalculateMovement(); // 移動速度を計算
        ChangeDirection(); // 向きを変更
        ProcessJump(); // ジャンプ処理

        _characterController.Move(_moveVelocity * Time.deltaTime); // 実際の移動処理
    }

    // 地上または空中の状態をログに出力するメソッド
    private void LogGroundStatus()
    {
        Debug.Log(_characterController.isGrounded ? "地上にいます" : "空中です");
    }

    // 移動速度を計算するメソッド
    private void CalculateMovement()
    {
        _moveVelocity.x = Input.GetAxis("Horizontal") * moveSpeed;
        _moveVelocity.z = Input.GetAxis("Vertical") * moveSpeed;
    }

    // プレイヤーの向きを変更するメソッド
    private void ChangeDirection()
    {
        Vector3 direction = new Vector3(_moveVelocity.x, 0, _moveVelocity.z);
        if (direction != Vector3.zero) // 方向がゼロベクトルでない場合
        {
            transform.LookAt(transform.position + direction); // その方向を向く
        }
    }

    // ジャンプ処理を行うメソッド
    private void ProcessJump()
    {
        if (_characterController.isGrounded && Input.GetButtonDown("Jump")) // 地上にいて、ジャンプボタンが押された場合
        {
            Debug.Log("ジャンプ!");
            _moveVelocity.y = jumpPower; // 上向きに速度を加える
        }
        else if (!_characterController.isGrounded) // 空中にいる場合
        {
            _moveVelocity.y += Physics.gravity.y * Time.deltaTime; // 重力を加算
        }
    }
}

このリファクタリングでは、次のような変更を行いました

Update メソッド内の処理を ProcessMovement メソッドに統合しました。

地上状態のログ出力を LogGroundStatus に、移動量の計算を CalculateMovement に、向きの変更を ChangeDirection に、ジャンプ処理を ProcessJump にそれぞれ分割しました。

これにより、各機能がはっきりと分かれ、修正や拡張がしやすくなります。たとえば、ジャンプの挙動を変更したい場合、ProcessJump メソッドのみを修正すればよく、他の機能に影響を与えません。

気になるところの調整

キャラクタの方向転換が急すぎる

キャラクターの方向転換が急すぎる場合、その変更を滑らかにするためにQuaternion.Slerpを使用して、徐々に目的の方向への回転を行うように改善できます。Quaternion.Slerpは、二つの回転間を球面線形補間します。これにより、キャラクターの向き変更をより自然に見せることができます

変更されたChangeDirectionメソッドは以下のようになります。追加でrotationSpeedパラメータをクラスのフィールドとして定義し、これを使って回転の速さを調整します。

[SerializeField] private float rotationSpeed = 5f; // 回転速度

private void ChangeDirection()
{
    Vector3 direction = new Vector3(_moveVelocity.x, 0, _moveVelocity.z);
    if (direction != Vector3.zero) // 方向がゼロベクトルでない場合
    {
        Quaternion targetRotation = Quaternion.LookRotation(direction);
        transform.rotation = Quaternion.Slerp(transform.rotation, targetRotation, rotationSpeed * Time.deltaTime);
    }
}

このコード行は、キャラクターやオブジェクトの現在の回転を目標の回転に向けて滑らかに補間(徐々に変化させていく)するために使用されます。具体的には、Quaternion.Slerp関数を用いて実現されています

transform.rotation = Quaternion.Slerp(transform.rotation, targetRotation, rotationSpeed * Time.deltaTime);

transform.rotation:UnityのTransformコンポーネントにおけるオブジェクトの現在の回転を表します。

Quaternion.Slerp:2つの回転を指定した割合で球面線形補間(Slerp)します。このメソッドは、第1引数の回転から第2引数の回転へ滑らかに移行する新しいQuaternionを返します

transform.rotation(第1引数):補間の開始点としてのオブジェクトの現在の回転。

targetRotation(第2引数):補間の終点としてオブジェクトが向かうべき目標の回転。

rotationSpeed * Time.deltaTime(第3引数):補間の割合を決定します。rotationSpeedはオブジェクトがどれだけ早く回転するかを定義する値で、Time.deltaTimeは前のフレームからの経過時間を意味します。この積により、フレームレートに依存しない一貫した回転速度が実現されます

動作の説明

このコードは、オブジェクトをtargetRotationに向けて、指定されたrotationSpeedで滑らかに回転させるために使用されます。Quaternion.Slerpによる球面線形補間は、直線的な補間(Lerp)と比べてより自然な回転の変化を提供し、特に3D空間内でのオブジェクトの回転に適しています。

rotationSpeedを調整することで、回転の速さを制御でき、Time.deltaTimeを掛けることで、異なるフレームレートを持つデバイス上でも一貫した動作を保証します。結果として、このコードを使用すると、オブジェクトは滑らかにかつ一定の速度で目標の方向に回転するようになります

rotationSpeedの値を調整することで、キャラクターの回転の速さを変えることができます。値が大きいほど早く回転し、小さいほどゆっくり回転します。この方法により、キャラクターの方向転換を自然に感じさせることができるでしょう

このコードは、キャラクターが新しい方向に向かう際に滑らかに回転するようにします。rotationSpeedを適切に設定することで、急な方向転換ではなく、より自然でプレイヤーが予期しやすい動きを実現できます。

斜めの移動が前後・左右方向移動より速い

キャラクターの斜め移動が速くなる問題は、斜め方向に移動する際、移動ベクトルの大きさが横移動や縦移動の時よりも大きくなるために起こります。これを修正するには、移動ベクトルを正規化してから移動速度を掛けることで、どの方向に移動しても速度が一定になるようにします。

以下の変更をCalculateMovementメソッドに適用します。この変更により、斜め移動時の速度が他の方向の移動時と同じになります。

private void CalculateMovement()
{
    Vector3 direction = new Vector3(Input.GetAxis("Horizontal"), 0, Input.GetAxis("Vertical")).normalized;
    _moveVelocity.x = direction.x * moveSpeed;
    _moveVelocity.z = direction.z * moveSpeed;
}

新しいVector3方向ベクトルを作成し、Input.GetAxisで取得した水平方向("Horizontal")と垂直方向("Vertical")の入力を使用します。このベクトルは、キャラクターの移動方向を表します。

.normalizedを使用して方向ベクトルを正規化します。これにより、ベクトルの大きさが1になり、どの方向に移動しても速度が均一になります。

正規化された方向ベクトルのxz成分をそれぞれ移動速度moveSpeedと掛け合わせて、_moveVelocity.x_moveVelocity.zを設定します。

参考

このコードから得られるスキル

このコードから学べるスキルは、UnityとC#開発において非常に重要な基礎知識と概念を幅広くカバーしています。特に、以下の領域での能力が向上するでしょう:

  1. Unity特有のコンポーネントシステムへの理解
    • CharacterControllerコンポーネントの利用方法について学びます。これはUnityでキャラクター移動を実装する際によく使われるコンポーネントです。
    • [RequireComponent(typeof(CharacterController))] アトリビュートを使用して、RefacterPlayerControllerスクリプトがアタッチされたGameObjectにCharacterControllerコンポーネントが必要であることを指定しています。これにより、依存関係が明確になり、エラーの可能性が減少します。
  2. 基本的なプログラミング概念とC#の特徴
    • クラス、変数、メソッドの宣言と初期化について学びます。
    • シリアライズフィールド([SerializeField])を使って、Unityエディター内でプライベート変数の値を調整できるようにする方法を理解します。これはゲーム開発において、コードを変更せずにパラメータを調整する便利な方法です。
  3. 物理とゲームのフレームワークへの適用
    • プレイヤーの移動とジャンプメカニズムを実装する方法を学びます。ここでは、ユーザーの入力を取得し、それに基づいてキャラクターを動かす方法を示しています。
    • 物理法則(特に重力)をゲームのキャラクターに適用する方法を理解します。キャラクターが空中にいる間、重力を加算して自然な落下を再現します。
  4. デバッグと状態管理
    • Debug.Logを使用してゲームの実行中に情報をログ出力する方法を学びます。これはデバッグや開発中に何が起こっているかを理解するのに役立ちます。
  5. トランスフォームとベクトル演算
    • UnityのTransformコンポーネントを使用してゲームオブジェクトの位置、回転、スケールを操作する基本的な方法を理解します。
    • ベクトル演算を使用してキャラクターの移動と向きを計算し、操作する方法を学びます。これには、位置ベクトルの加算や、キャラクターが向いている方向の計算が含まれます。

他の移動サンプル

Unity

Posted by hidepon