🙆‍♀️

コントローラーを作る

2024/05/29に公開

入力機能を作る

ゲームやアプリケーションに入力機能を作成することは、ユーザーとのインタラクションを実現するために非常に重要です。入力デバイスは様々な種類があり、それぞれの特性に合わせたプログラムを作成する必要があります。ここでは、一般的な入力デバイスについて説明し、Unityでの実装方法について詳しく見ていきます。

キーボード&マウス

概要

キーボードとマウスは、最も一般的な入力デバイスです。キーボードは文字入力や特定のキーの押下によるコマンド入力に使用され、マウスはポイント&クリックやドラッグ&ドロップ操作に使用されます。

Unityでの実装

Unityでは、Inputクラスを使用してキーボードやマウスからの入力を検出します。例えば、特定のキーが押されたかどうかを確認するには以下のようにします:

void Update()
{
    if (Input.GetKeyDown(KeyCode.Space))
    {
        Debug.Log("Space key was pressed.");
    }

    if (Input.GetMouseButtonDown(0))
    {
        Debug.Log("Left mouse button was clicked.");
    }
}

ゲームパッド

概要

ゲームパッドは、特にコンソールゲームやPCゲームで広く使用される入力デバイスです。複数のボタンやアナログスティックを備えており、多様な操作が可能です。

Unityでの実装

Unityでは、ゲームパッドの入力もInputクラスを使用して検出します。以下のように、ゲームパッドのボタンやスティックの入力を処理します:

void Update()
{
    float horizontal = Input.GetAxis("Horizontal");
    float vertical = Input.GetAxis("Vertical");

    if (Input.GetButtonDown("Jump"))
    {
        Debug.Log("Jump button was pressed.");
    }
}

タッチパネル

概要

タッチパネルは、スマートフォンやタブレットなどのデバイスで使用される入力方式です。マルチタッチ対応で、複数の指での操作が可能です。

Unityでの実装

Unityでは、タッチパネルの入力はInputクラスのtouchesプロパティを使用して検出します。例えば、タッチの位置や状態を取得するには以下のようにします:

void Update()
{
    if (Input.touchCount > 0)
    {
        Touch touch = Input.GetTouch(0);

        if (touch.phase == TouchPhase.Began)
        {
            Debug.Log("Touch started at position: " + touch.position);
        }
    }
}

VRゴーグル

概要

VRゴーグルは、仮想現実環境に没入するためのデバイスです。ヘッドトラッキングやハンドコントローラーを使用して、ユーザーの動きを検出します。

Unityでの実装

Unityでは、VRデバイスの入力を処理するために、XRプラグインや特定のSDKを使用します。例えば、OculusやSteamVRなどのプラットフォームに対応したSDKをインポートして使用します。基本的なヘッドトラッキングは以下のように行います:

using UnityEngine.XR;

void Update()
{
    Vector3 headPosition = InputTracking.GetLocalPosition(XRNode.Head);
    Quaternion headRotation = InputTracking.GetLocalRotation(XRNode.Head);

    Debug.Log("Head Position: " + headPosition);
    Debug.Log("Head Rotation: " + headRotation);
}

各入力デバイスには独自の特性と実装方法がありますが、基本的な考え方はユーザーからの入力を検出し、それに応じた処理を行うことです。Unityを使用することで、これらの入力デバイスを簡単に統合し、豊かなユーザーエクスペリエンスを提供することが可能です。

Character Controller

Character Controllerは、Unityでキャラクターの動きを制御するためのコンポーネントです。特に3Dゲームにおいて、プレイヤーキャラクターの移動や衝突判定を簡単に実装するために使用されます。ここでは、Character Controllerの導入方法と、導入のメリットおよびデメリットについて説明します。

導入

Character ControllerをUnityプロジェクトに導入するための基本的な手順は以下の通りです:

  1. プロジェクトのセットアップ

    • Unity Hubを使用して新しい3Dプロジェクトを作成します。
    • 必要に応じて、ProBuilderをUnityのPackage Managerからインストールします。
  2. キャラクターモデルの作成

    • ProBuilderを使用して、シンプルなキャラクターモデルを作成します。例えば、カプセルや立方体を組み合わせてキャラクターを構築します。
    • 作成したモデルにCharacter Controllerコンポーネントを追加します。
  3. Character Controllerの設定

    • キャラクターモデルを選択し、Inspectorウィンドウで「Add Component」ボタンをクリックします。
    • 「Character Controller」を検索して追加します。
    • Character Controllerのパラメータ(例えば、高さや半径)を適切に設定します。
  4. スクリプトの作成

    • キャラクターの移動を制御するためのスクリプトを作成します。以下に簡単な例を示します:
using UnityEngine;

public class PlayerController : MonoBehaviour
{
    public float speed = 6.0f;
    private CharacterController controller;

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

    void Update()
    {
        float moveDirectionY = 0;
        Vector3 move = new Vector3(Input.GetAxis("Horizontal"), moveDirectionY, Input.GetAxis("Vertical"));
        controller.Move(move * Time.deltaTime * speed);
    }
}

メリット&デメリット

メリット

  1. 簡単な実装

    • Character Controllerは、キャラクターの移動や衝突処理を簡単に実装するための便利なコンポーネントです。
    • 物理エンジンに依存しないため、操作が直感的で制御しやすいです。
  2. 柔軟性

    • Character Controllerは、様々なキャラクターの動きを柔軟に制御できます。例えば、重力の影響を受けずに滑らかに移動することが可能です。
  3. ProBuilderとの統合

    • ProBuilderを使用することで、素早くプロトタイプを作成し、Character Controllerと組み合わせて動作を確認することができます。

デメリット

  1. 物理挙動の限界

    • Character Controllerは物理エンジンを完全には利用しないため、リアルな物理挙動を再現するのが難しい場合があります。例えば、坂道での滑りや複雑な衝突判定には制限があります。
  2. パフォーマンスの制約

    • Character Controllerは比較的単純な動作に適しており、複雑な動作や大規模な環境での使用にはパフォーマンスの問題が生じる可能性があります。
  3. カスタマイズの難しさ

    • 物理エンジンを直接操作する場合に比べて、Character Controllerの動作を細かくカスタマイズすることが難しい場合があります。

Character Controllerは、その簡便さと柔軟性から、特にプロトタイプ作成やシンプルなキャラクター制御に適しています。一方で、より複雑な物理挙動やパフォーマンスが求められる場合は、RigidBodyを使用した制御やカスタムの物理システムを検討する必要があります。

Inputクラス

Inputクラスは、Unityにおける標準的な入力管理クラスであり、キーボード、マウス、ゲームパッド、タッチ入力など、様々な入力デバイスからの入力を簡単に取得できます。ただし、Inputクラスはレガシー(旧式)とされており、Unityの新しいInput Systemに置き換えられつつあります。それでも、多くの既存プロジェクトやシンプルなプロトタイプでは依然として使用されており、その簡便さから初心者にも適しています。

Inputクラスを使用すると、以下のような操作が可能です:

  • キーボード入力の検出
  • マウスのクリックや位置の取得
  • ゲームパッドのボタン押下やアナログスティックの状態の取得
  • タッチデバイスからの入力の取得

導入コスト

メリット

  1. 簡単な実装:Inputクラスはシンプルで使いやすく、初心者でも直感的に使用できます。
  2. 幅広いデバイス対応:キーボード、マウス、ゲームパッド、タッチ入力など、多様な入力デバイスに対応しています。
  3. 既存のリソース:豊富なドキュメントやチュートリアルが存在するため、学習リソースが充実しています。

デメリット

  1. 拡張性の限界:レガシーであるため、複雑な入力処理やカスタマイズには制限があります。
  2. 新機能の欠如:新しいInput Systemに比べて機能が限られており、最新の入力デバイスや機能に対応していません。
  3. 移行コスト:将来的に新しいInput Systemへの移行が必要になる可能性があるため、長期的には移行コストが発生する可能性があります。

導入

Inputクラスの導入は非常に簡単で、スクリプト内で直接使用することができます。以下に、キーボード、マウス、ゲームパッド、タッチ入力を使用したサンプルコードを示します。

キーボード入力

キーボードのスペースキーが押されたことを検出する例です。

void Update()
{
    if (Input.GetKeyDown(KeyCode.Space))
    {
        Debug.Log("Space key was pressed.");
    }
}

マウス入力

マウスの左クリックを検出し、クリック位置を取得する例です。

void Update()
{
    if (Input.GetMouseButtonDown(0))
    {
        Vector3 mousePosition = Input.mousePosition;
        Debug.Log("Left mouse button was clicked at position: " + mousePosition);
    }
}

ゲームパッド入力

ゲームパッドのジャンプボタン(デフォルトでは"A"ボタン)が押されたことを検出する例です。

void Update()
{
    if (Input.GetButtonDown("Jump"))
    {
        Debug.Log("Jump button was pressed.");
    }

    float horizontal = Input.GetAxis("Horizontal");
    float vertical = Input.GetAxis("Vertical");
    Debug.Log("Horizontal axis: " + horizontal + ", Vertical axis: " + vertical);
}

タッチ入力

タッチスクリーンのタッチを検出し、タッチ位置を取得する例です。

void Update()
{
    if (Input.touchCount > 0)
    {
        Touch touch = Input.GetTouch(0);

        if (touch.phase == TouchPhase.Began)
        {
            Vector2 touchPosition = touch.position;
            Debug.Log("Touch started at position: " + touchPosition);
        }
    }
}

以上のように、Inputクラスは簡単に導入でき、幅広い入力デバイスに対応しています。特にプロトタイプ作成やシンプルなプロジェクトにおいては、その手軽さと汎用性が大きなメリットとなります。

サンプルコード

以下は、UnityのInputクラスを使用してキャラクターを移動および回転させるサンプルコードです。このコードはCharacter Controllerコンポーネントを使用して、キャラクターの動きを管理します。

using UnityEngine;

[RequireComponent(typeof(CharacterController))]
public class CharactorControllerDemo : MonoBehaviour
{
    public float speed = 3.0F; // キャラクターの移動速度
    public float rotateSpeed = 3.0F; // キャラクターの回転速度
    public CharacterController controller; // CharacterControllerコンポーネントへの参照

    private void Start()
    {
        // GameObjectにアタッチされているCharacterControllerコンポーネントを取得します。
        controller = GetComponent<CharacterController>();
    }

    void Update()
    {
        // 水平方向の入力に基づいてキャラクターをY軸周りに回転させます。
        transform.Rotate(0, Input.GetAxis("Horizontal") * rotateSpeed, 0);

        // キャラクターの前方向を決定します。
        Vector3 forward = transform.TransformDirection(Vector3.forward);

        // 垂直方向の入力に基づいて現在の速度を計算します。
        float curSpeed = speed * Input.GetAxis("Vertical");

        // 現在の速度に基づいてキャラクターを前後に移動させます。
        controller.SimpleMove(forward * curSpeed);
    }
}

解説

  1. スクリプトの構成

    • CharactorControllerDemoクラスは、MonoBehaviourを継承しています。これにより、Unityのゲームオブジェクトとして機能します。
    • [RequireComponent(typeof(CharacterController))]属性は、スクリプトがアタッチされたオブジェクトにCharacterControllerコンポーネントが必須であることを示します。これにより、コンポーネントが自動的に追加されるか、存在しない場合はエラーが発生します。
  2. メンバ変数の定義

    • public float speed = 3.0F;:キャラクターの前後移動速度を設定します。
    • public float rotateSpeed = 3.0F;:キャラクターの回転速度を設定します。
    • public CharacterController controller;:CharacterControllerコンポーネントへの参照を保持します。
  3. Startメソッド

    • controller = GetComponent<CharacterController>();:現在のゲームオブジェクトにアタッチされているCharacterControllerコンポーネントを取得し、controller変数に格納します。
  4. Updateメソッド

    • 毎フレーム実行されるメソッドで、ユーザーの入力に基づいてキャラクターの動きと回転を更新します。
    • transform.Rotate(0, Input.GetAxis("Horizontal") * rotateSpeed, 0);
      • 水平方向の入力(デフォルトでは左右矢印キーやA/Dキー)に基づいてキャラクターをY軸周りに回転させます。
    • Vector3 forward = transform.TransformDirection(Vector3.forward);
      • キャラクターの前方向を取得します。この方向はキャラクターのローカル座標系での前方向を示します。
    • float curSpeed = speed * Input.GetAxis("Vertical");
      • 垂直方向の入力(デフォルトでは上下矢印キーやW/Sキー)に基づいて現在の移動速度を計算します。
    • controller.SimpleMove(forward * curSpeed);
      • 現在の速度に基づいてキャラクターを前後に移動させます。このメソッドは、物理エンジンを使用せずに簡単な移動を実現します。

このスクリプトにより、キーボードの入力に基づいてキャラクターを前後に移動させ、左右に回転させることができます。SimpleMoveメソッドは物理エンジンを使用しないため、より簡単な制御が可能です。

InputSystem

導入コスト

メリット

  1. 拡張性:新しいInput Systemは、複数の入力デバイスや複雑な入力シナリオに対応するために設計されており、キーボード、マウス、ゲームパッド、タッチデバイス、VRデバイスなど、様々なデバイスを一元的に管理できます。
  2. 柔軟性:デバイスごとに異なる入力マッピングを簡単に設定できるため、ゲームの入力要件に応じたカスタマイズが容易です。
  3. イベントベース:新しいInput Systemはイベントベースのアプローチを採用しており、入力イベントの処理がシンプルで直感的です。
  4. プラットフォーム対応:クロスプラットフォームで一貫した入力処理が可能であり、PC、コンソール、モバイルなどの異なるプラットフォームでの開発が容易です。

デメリット

  1. 学習コスト:新しいInput Systemは機能が豊富で柔軟性が高い反面、Inputクラスに比べて習得するための学習コストが高いです。特に、既存のInputクラスからの移行には時間と労力が必要です。
  2. 複雑性:設定やスクリプトの記述が複雑になる場合があり、シンプルなプロジェクトにはオーバーヘッドが大きく感じられることがあります。
  3. 移行コスト:既存のプロジェクトでInputクラスを使用している場合、新しいInput Systemに移行するためにはコードの大幅な変更が必要となる場合があります。

導入

新しいInput Systemを導入する手順と、キーボードおよびマウスのコントローラーの実装例を以下に示します。

ステップ1:Input Systemパッケージのインストール

  1. Unity Editorを開きます。
  2. Window > Package Managerを選択します。
  3. Package Managerで、Input Systemを検索し、インストールします。
  4. インストール後、プロジェクトを再起動する必要がある場合があります。

ステップ2:Input Systemの設定

  1. プロジェクト設定で、Edit > Project Settings > Playerを開きます。
  2. Active Input HandlingBothまたはInput System (New)に設定します。

ステップ3:Input Actionsの作成

  1. Assetsフォルダで右クリックし、Create > Input Actionsを選択します。
  2. 新しいInput Actionsアセットを作成し、名前を付けます(例:PlayerInputActions)。
  3. アセットをダブルクリックして、Input Actionsエディターを開きます。
  4. 必要なアクションマップ(例:Player)とアクション(例:MoveLookFire)を追加します。

ステップ4:スクリプトの実装

以下は、キーボードとマウスの入力を処理するスクリプトの例です。

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.InputSystem;

public class PlayerController : MonoBehaviour
{
    public float moveSpeed = 5f;
    public float lookSpeed = 10f;

    private PlayerInputActions inputActions;
    private CharacterController characterController;
    private Vector2 moveInput;
    private Vector2 lookInput;

    private void Awake()
    {
        inputActions = new PlayerInputActions();
        characterController = GetComponent<CharacterController>();

        inputActions.Player.Move.performed += ctx => moveInput = ctx.ReadValue<Vector2>();
        inputActions.Player.Look.performed += ctx => lookInput = ctx.ReadValue<Vector2>();

        inputActions.Player.Move.canceled += ctx => moveInput = Vector2.zero;
        inputActions.Player.Look.canceled += ctx => lookInput = Vector2.zero;
    }

    private void OnEnable()
    {
        inputActions.Enable();
    }

    private void OnDisable()
    {
        inputActions.Disable();
    }

    private void Update()
    {
        // 移動処理
        Vector3 move = new Vector3(moveInput.x, 0, moveInput.y);
        characterController.Move(move * Time.deltaTime * moveSpeed);

        // 回転処理
        Vector2 look = new Vector2(lookInput.x, lookInput.y) * lookSpeed * Time.deltaTime;
        transform.Rotate(0, look.x, 0);
    }
}

スクリプト解説

  1. PlayerInputActionsの生成

    • inputActions = new PlayerInputActions();:事前に設定したInput Actionsアセットを基に、PlayerInputActionsクラスのインスタンスを生成します。
  2. 入力の受け取り

    • inputActions.Player.Move.performed += ctx => moveInput = ctx.ReadValue<Vector2>();Moveアクションが実行されたときに、入力された値をmoveInput変数に格納します。
    • inputActions.Player.Look.performed += ctx => lookInput = ctx.ReadValue<Vector2>();Lookアクションが実行されたときに、入力された値をlookInput変数に格納します。
  3. OnEnableおよびOnDisableメソッド

    • inputActions.Enable();:スクリプトが有効になるときにInput Systemを有効にします。
    • inputActions.Disable();:スクリプトが無効になるときにInput Systemを無効にします。
  4. Updateメソッド

    • Vector3 move = new Vector3(moveInput.x, 0, moveInput.y);:入力された移動値を基に移動ベクトルを計算します。
    • characterController.Move(move * Time.deltaTime * moveSpeed);:計算した移動ベクトルを使用してキャラクターを移動させます。
    • Vector2 look = new Vector2(lookInput.x, lookInput.y) * lookSpeed * Time.deltaTime;:入力された回転値を基に回転ベクトルを計算します。
    • transform.Rotate(0, look.x, 0);:計算した回転ベクトルを使用してキャラクターを回転させます。

新しいInput Systemを使用することで、複雑な入力処理を簡単に管理でき、将来的に拡張する際の柔軟性も高まります。これにより、より洗練されたユーザーエクスペリエンスを提供することが可能となります。

ジャンプを実装する

以下に、2段ジャンプの機能をbool値で切り替えられるようにしたサンプルコードを示します。このコードでは、allowDoubleJumpというbool変数を使用して2段ジャンプの有効化・無効化を制御します。

using UnityEngine;

[RequireComponent(typeof(CharacterController))]
public class CharactorControllerDemo : MonoBehaviour
{
    public float speed = 3.0F; // キャラクターの移動速度
    public float rotateSpeed = 3.0F; // キャラクターの回転速度
    public float jumpSpeed = 8.0F; // ジャンプの速度
    public float gravity = 20.0F; // 重力の強さ
    public bool allowDoubleJump = true; // 2段ジャンプを許可するかどうか
    public int maxJumpCount = 2; // 最大ジャンプ回数(2段ジャンプ用)

    private int jumpCount = 0; // 現在のジャンプ回数
    private Vector3 moveDirection = Vector3.zero; // 現在の移動方向
    private CharacterController controller; // CharacterControllerコンポーネントへの参照

    private void Start()
    {
        // GameObjectにアタッチされているCharacterControllerコンポーネントを取得します。
        controller = GetComponent<CharacterController>();
    }

    private void Update()
    {
        HandleMovement();
        HandleJump();

        // 重力を適用します。
        moveDirection.y -= gravity * Time.deltaTime;

        // CharacterControllerを使用してキャラクターを移動させます。
        controller.Move(moveDirection * Time.deltaTime);
    }

    private void HandleMovement()
    {
        // 水平方向の入力に基づいてキャラクターをY軸周りに回転させます。
        transform.Rotate(0, Input.GetAxis("Horizontal") * rotateSpeed, 0);

        // キャラクターの前方向を決定します。
        Vector3 forward = transform.TransformDirection(Vector3.forward);

        // 垂直方向の入力に基づいて現在の速度を計算します。
        float curSpeed = speed * Input.GetAxis("Vertical");

        // 現在の速度に基づいてキャラクターを前後に移動させます。
        moveDirection = forward * curSpeed;
    }

    private void HandleJump()
    {
        if (controller.isGrounded)
        {
            // 地面に着地している場合、ジャンプ回数をリセットします。
            jumpCount = 0;

            // ジャンプボタンが押された場合、ジャンプします。
            if (Input.GetButtonDown("Jump"))
            {
                moveDirection.y = jumpSpeed;
                jumpCount++;
            }
        }
        else
        {
            // 空中にいる場合、追加のジャンプを許可します(2段ジャンプが有効な場合)。
            if (allowDoubleJump && Input.GetButtonDown("Jump") && jumpCount < maxJumpCount)
            {
                moveDirection.y = jumpSpeed;
                jumpCount++;
            }
        }
    }
}

解説

  1. 2段ジャンプの制御:

    • public bool allowDoubleJump = true;:2段ジャンプを許可するかどうかを制御するためのbool変数を定義します。この変数をtrueに設定すると、2段ジャンプが有効になります。
  2. HandleJumpメソッドの修正:

    • 地面に着地している場合、ジャンプ回数をリセットし、ジャンプボタンが押された場合にジャンプします。
    • 空中にいる場合、2段ジャンプが許可されていて、ジャンプボタンが押され、現在のジャンプ回数が最大ジャンプ回数未満の場合に追加のジャンプを行います。

このコードにより、allowDoubleJump変数をtrueまたはfalseに設定することで、2段ジャンプの有効化・無効化を簡単に切り替えることができます。

InputSystemとInputクラスの比較

InputSystemのメリット

  1. デバイスの統合管理:

    • Input Systemは、キーボード、マウス、ゲームパッド、タッチスクリーン、VRデバイスなど、複数の入力デバイスを一元的に管理できます。これにより、異なるデバイス間の入力処理が統合され、クロスプラットフォームの開発が容易になります。
  2. 柔軟な入力マッピング:

    • 入力アクションをデバイスごとに簡単にマッピングできます。Input Actionsアセットを使用することで、複数の入力デバイスに対する入力マッピングを視覚的に設定できます。
  3. イベント駆動型の入力処理:

    • Input Systemはイベントベースで入力を処理するため、入力イベントの処理が直感的でシンプルです。特定の入力イベントが発生したときにコールバック関数を実行する仕組みは、ゲームのリアルタイム入力処理に非常に適しています。
  4. モダンなアーキテクチャ:

    • 新しいInput Systemは、よりモダンなコードベースと設計原則に基づいて構築されており、拡張性とメンテナンス性に優れています。特に大型プロジェクトや複雑な入力シナリオに対して有効です。
  5. カスタマイズ可能な入力フィルター:

    • 入力に対してカスタムフィルターや処理を簡単に適用できるため、特定のゲームプレイ要件に応じた高度な入力処理が可能です。

InputSystemを使う必要がないケース

InputSystemが強力なツールである一方、すべてのプロジェクトで必ずしも必要とは限りません。以下のようなケースでは、従来のInputクラスが適している場合もあります。

  1. シンプルなプロジェクト:

    • 単純なゲームやプロトタイプで、複雑な入力処理が不要な場合。基本的なキーボードやマウス入力のみを処理する場合、Inputクラスの方が設定や実装が簡単です。
  2. 学習リソースやスキルの制約:

    • 開発チームが新しいInput Systemに不慣れであり、既存のInputクラスで十分な場合。Input Systemは学習コストが高いため、チームが既に習熟しているInputクラスを使う方が効率的です。
  3. レガシープロジェクト:

    • 既にInputクラスを使用して構築された大規模なプロジェクトでは、Input Systemに移行するためのコストが高いため、現状のInputクラスを維持する方が現実的です。
  4. パフォーマンスが重要なプロジェクト:

    • 非常に高いパフォーマンスが求められるプロジェクトでは、Inputクラスのシンプルさが有利になる場合があります。Input Systemの柔軟性と機能性はオーバーヘッドを伴うため、特定のパフォーマンス要件を満たすためにInputクラスを選択することもあります。

無理に使わない

新しいInput Systemは、特に複雑な入力要件や複数の入力デバイスを扱う場合に強力なツールとなります。
一方、シンプルなプロジェクトや既存のInputクラスで十分な場合には、従来のInputクラスの方が適していることもあります。プロジェクトの規模や要件に応じて、適切な入力管理ツールを選択することが重要です。

Discussion