1.11 Unity ゲーム開発を 0 から学ぶ - カメラを動かす

前回の記事では、Unity でユーザー入力を受け取る方法を紹介しました。Unity では、さまざまなデバイスの違いに対応する必要があるため、一連のパッケージが作成されています。最初は使いにくいかもしれません。今回の記事では、 wasd を使用します ゲーム内で視線を動かすには、FPS ゲームの操作効果に少し似ていますが、同時に、コードでオブジェクトを制御する方法も学びますコンポーネント自体が配置されているオブジェクトだけでなく、シーンも含めます。

コンポーネントは WASD 入力を受信します

CameraController という新しいコード リソース ファイルを作成します。

using UnityEngine;

public class CameraController : MonoBehaviour
{
    void Update()
    {
        if (Input.GetKey(KeyCode.W))
        {
            // Move camera forward
        }
        if (Input.GetKey(KeyCode.A))
        {
            // Move camera left
        }
        if (Input.GetKey(KeyCode.S))
        {
            // Move camera backward
        }
        if (Input.GetKey(KeyCode.D))
        {
            // Move camera right
        }
    }
}

前の記事で説明した Input クラスを使用して、現在のユーザーが押したボタンを取得していることがわかりますが、キーボードを使用していない場合、それがハンドルの場合はどうなるかということも述べました。次に、Axis 情報を使用してモバイル入力と同様の情報を表現し、コードの変更を GPT に簡単に支援してもらいましょう。

using UnityEngine;

public class CameraController : MonoBehaviour
{
    void Update()
    {
        float horizontal = Input.GetAxis("Horizontal");
        float vertical = Input.GetAxis("Vertical");
    }
}

「水平」と「垂直」は両方とも、InputManager 構成ファイルから対応する構成名を見つけることができます。明らかに、これら 2 つはそれぞれ水平方向と垂直方向の入力ボリュームを表し、float の値の範囲は [-1, 1] です。

しかし、注意深い人なら、InputManager がこれら 2 つの入力に対してキーボードのどのキーを設定していないことがわかるでしょう。どうして WASD になるのでしょうか? これは Unity の暗黙のルールとしか言えず、ドキュメントから学ぶことができます。 :

軸を読み取るには、 Input.GetAxis を 次のデフォルトの軸のいずれかで使用します。「水平」と「垂直」は、ジョイスティック、A、W、S、D および矢印キーにマップされます。「マウス X」と「マウス Y」はマウス デルタにマッピングされます。「Fire1」、「Fire2」、「Fire3」は、Ctrl、Alt、Cmd キーと 3 つのマウスまたはジョイスティック ボタンにマッピングされています。

コンポーネントをカメラに追加します

ここでカメラの位置を移動したいので、明らかにカメラの Transform コンポーネントの位置情報を変更する必要があります。最初に独自のコンポーネントをカメラに追加しましょう。

次に、コンポーネント内の同じ GameObject の下にある他のコンポーネントにアクセスしたい場合は、GetComponent メソッドを直接使用できます。

using UnityEngine;

public class CameraController : MonoBehaviour
{
    void Update()
    {
        float horizontal = Input.GetAxis("Horizontal");
        float vertical = Input.GetAxis("Vertical");

        Transform transform = GetComponent<Transform>();
    }
}

次に、コンポーネント内の値を変更します。

using UnityEngine;

public class CameraController : MonoBehaviour
{
    void Update()
    {
        float horizontal = Input.GetAxis("Horizontal");
        float vertical = Input.GetAxis("Vertical");

        Transform transform = GetComponent<Transform>();
        transform.position += new Vector3(horizontal, vertical, 0);
    }
}

ここでは、z 軸は変更せずに、座標の x 軸と y 軸に水平方向と垂直方向の入力を直接かつ激しく追加します。

Rider を IDE として使用する場合、変換が波線で描画されることがわかります。これは、すべての GameObjects に変換コンポーネントが必要なため、GetComponent 関数を使用せずに、変換メンバー変数を使用して直接アクセスできるためです。このようになります:

using UnityEngine;

public class CameraController : MonoBehaviour
{
    void Update()
    {
        float horizontal = Input.GetAxis("Horizontal");
        float vertical = Input.GetAxis("Vertical");

        transform.position += new Vector3(horizontal, vertical, 0);
    }
}

移動速度が異常すぎる!

OK、コードを保存して Unity に自動的にコンパイルしてもらい、ゲームを実行して試してみましょう。

マシンによっては、ボタンを押すと非常に遠くまで移動したり、非常に敏感になったり (四角形が見つからない場合は、ゲームを停止して再開できます)、または非常にゆっくりと移動したりする場合があります (そのような悪いマシンはありません)。 、それではどこに問題があるのでしょうか?

Update 関数が呼び出されるたびに位置を移動し、フレームごとに Update が呼び出されるため、マシン構成が優れており、実行中のフレーム レートが比較的高い場合、Update 呼び出しの数が非常に多くなり、距離が遠いほど、ゲーム ウィンドウの右上隅にある [統計] ボタンをクリックできます。

内部のグラフィックスの右側にある FPS 値を見てください。私の GTX1060+i7 8700 マシンでは、多くの場合 300 フレームまで実行できます。これは、Update が 1 秒あたり 300 回呼び出されることを意味します。ボタンを少し長く押すと、カメラの位置が変わります。移動は非常に遠いです。通常の状況では、移動速度がリアルタイムに基づいていることを強く望みます。1 秒間に 1 メートル移動する効果がある場合、移動の計算は現在のゲームに関連する必要があります。したがって、コードは次のように変更できます。

using UnityEngine;

public class CameraController : MonoBehaviour
{
    void Update()
    {
        float horizontal = Input.GetAxis("Horizontal");
        float vertical = Input.GetAxis("Vertical");

        transform.position += (new Vector3(horizontal, vertical, 0) * Time.deltaTime);
    }
}

各フレームの移動距離に、前のフレームからこのフレームまでに実際に経過した時間を表す Time.deltaTime を乗算していることがわかります。たとえば、300 フレームの場合、この時間は 1/ 300 秒、つまり 3.33 ミリ秒程度です。

概念的な理解に切り替えると、Time.deltaTime は 1 秒/フレーム レートになります。そして毎秒、非常に多くの動きデータ、つまり * フレーム レートを追加し、1 秒間の移動距離 = 移動距離 * フレーム レート * 1 秒 / フレーム レート、2 つのフレーム レートを取り消すと、結果は次のようになります。移動距離は、1 秒間にこれだけの距離を移動することに相当します。変更後に再度実行してみましょう。効果はより制御しやすくなります。

もちろん、この速度は少し遅いと思うかもしれないので、係数を掛けて速度と呼びましょう。

using UnityEngine;

public class CameraController : MonoBehaviour
{
    public float speed = 1.0f;
    
    void Update()
    {
        float horizontal = Input.GetAxis("Horizontal");
        float vertical = Input.GetAxis("Vertical");

        transform.position += (new Vector3(horizontal, vertical, 0) * (Time.deltaTime * speed));
    }
}

なぜここにメンバー変数を追加したのでしょうか? ゲーム開発ではプログラマーの思考ですべてのことを考えることはできないため、コンテンツをいかに迅速に作成するかを考える必要があることが多いため、エディターでリアルタイムに調整できるパラメーターが非常に多いと述べましたが、この速度パラメータは優れた例であり、ここでの速度はインスペクター パネルに表示されます。

この値を変更すると、エディターで通常実行中のゲームにリアルタイムで反映され、ゲームを停止する必要はありません (もちろん、コードを変更する場合は、ゲームを停止してコンパイルする前に行う必要があります)ゲームを開始します)、試してみることができます。

このようにして、移動速度の適切な値をすばやく直感的に見つけることができ、たとえば、手の感覚に合わせて 11 程度に調整します。

ゲーム実行中に調整したパラメータ値は、ゲームを停止するとゲーム停止前の値に戻りますが、ゲーム実行中に設定を保存する方法については、後の章で説明します。

シーンの観点からモバイルの動作を観察してください。

ゲームの実行中にシーン ウィンドウを開くこともできます。ゲーム タブまたはシーン タブを押したままドラッグして、ウィンドウのレイアウト モードを変更できます。ゲーム ウィンドウとシーン ウィンドウを同時に表示します。

その後、ゲームの実行中にカメラを移動すると、シーン ウィンドウの状況を観察できます。フォーカスを取得するには、ゲーム ウィンドウをクリックすることを忘れないでください。そうしないと、フォーカスがシーン ウィンドウにある可能性があります。画角がより直感的になります。ゲーム ウィンドウよりもカメラの動きを確認します。

コンポーネントが他のオブジェクトにアクセスする

上記のコードと変更により、コンポーネントが配置されているゲームオブジェクトを制御できるようになりましたが、他のゲームオブジェクトを制御したい場合はどうすればよいでしょうか? より現実的な問題は RPG ゲームであり、モバイル コンポーネントを制御対象のキャラクターに配置する方が合理的ですが、このとき、コンポーネントはアクセス対象のトランスフォームを持つキャラクターに直接アクセスします。カメラにアクセスしますか?

まず、CameraController コンポーネントを Cube に配置しましょう。

  1. メイン カメラの CameraController コンポーネントの右側にある 3 つの点をクリックし、[コンポーネントの削除] を選択して、前に追加したコンポーネントを削除します。これにより、2 つの CameraController が同時にロジックに応答することがなくなります。
  2. CameraController コンポーネントを Cube オブジェクトに追加します。

次に、ゲームを直接実行すると、ゲーム ウィンドウではカメラがまだ移動しているように見えますが、シーン ウィンドウを通して、実際に移動しているのはカメラではなくブロック自体であることがわかります。動き方とシーン 中にはこのブロックしかなく、カメラが動いているように見えます。

次に、メイン カメラにアクセスするなど、Cube オブジェクト上のコンポーネントから他のシーンのオブジェクトにアクセスするには、Unity が提供するメソッドを使用できます。

using UnityEngine;

public class CameraController : MonoBehaviour
{
    public float speed = 1.0f;
    
    void Update()
    {
        float horizontal = Input.GetAxis("Horizontal");
        float vertical = Input.GetAxis("Vertical");

        GameObject mainCamera = GameObject.Find("Main Camera");
        mainCamera.transform.position += (new Vector3(horizontal, vertical, 0) * (Time.deltaTime * speed));
    }
}

GameObject の Find 関数は、どこでも呼び出すことができる静的関数です。パラメータは、検索するオブジェクトの名前である文字列を受け取ります。特定の API 手順については、公式ドキュメントを参照してください。

Unity - スクリプト API: GameObject.Find https://docs.unity3d.com/ScriptReference/GameObject.Find.html

コードも非常に単純で、シーン内で「Main Camera」というオブジェクトを見つけて、その変換の位置の値を変更するだけです。

コードを変更した後、それを実行して、結果に問題がないことを確認します。

でもそれでいいのでしょうか?

実際、公式ドキュメントでは、この Find メソッドは比較的遅く、すべてのフレームを呼び出すのには適していないため、次のように変更できることを強調しています。

using UnityEngine;

public class CameraController : MonoBehaviour
{
    public float speed = 1.0f;

    private GameObject mainCamera;

    private void Start()
    {
        mainCamera = GameObject.Find("Main Camera");
    }

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

        mainCamera.transform.position += (new Vector3(horizontal, vertical, 0) * (Time.deltaTime * speed));
    }
}

つまり、Start で検索する場合、Start は Update を初めて呼び出す前に 1 回だけ呼び出して、それをメンバー変数に格納します。これにより、Update は毎回メンバー変数を直接フェッチできるため、繰り返し すべてのフレームのパフォーマンス オーバーヘッドを検出します。

パフォーマンスの問題は解決されましたが、まだ新しい問題が残っています。一定の経験のある開発者は、ここで検索するためにオブジェクトの名前を直接使用していることに注意する必要があります。いつか名前が変わったらどうなるでしょうか? 明らかに、ここでは見つけることができないため、シーン内のコード ロジックとリソースの名前付けの間に結合関係が形成されます。

さらに、複数人でのコラボレーションの場合、シーンの編集はアーティストやプランナーが担当することが多く、コード内に特定の名前をハードコーディングする場合、またはシーンを編集できるすべての人に、これらの名前には特別な名前があることを伝えるようにトレーニングする必要があります。意味は、変更できない、または知らない人が変更するとロジックがおかしくなります。

明らかにこれらは受け入れられません。シーン内のオブジェクトを参照するために名前を直接使用することは避ける必要があるため、コードの変更を続けましょう。

参照を使用して他のオブジェクトをリンクする

using UnityEngine;

public class CameraController : MonoBehaviour
{
    public float speed = 1.0f;
    public GameObject mainCamera;

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

        mainCamera.transform.position += (new Vector3(horizontal, vertical, 0) * (Time.deltaTime * speed));
    }
}

今回は、Start 関数の Find のロジックを削除し、mainCamera メンバーを public に変更しました。データのシリアル化について話したときに、パブリック メンバー変数はデフォルトでシリアル化され、インスペクター パネルに表示されることがわかりました。 GameObject タイプはデフォルトでシリアル化をサポートします。

GameObject オブジェクトの場合、ここに入力ボックスのようなものが表示されますが、手動で入力することはできません。右側の小さな円をクリックすると、新しいウィンドウが表示されます。

「Assets」と「Scene」という 2 つのタブがあることがわかります。シーンの下にタブが表示され、現在のシーンの下にあるすべてのゲームオブジェクトが表示されます。MainCamera をダブルクリックして選択すると、インスペクターに表示されます。次のようになります:

この入力ボックスは非常に特殊であることがわかります。ドロップダウン選択ボックスに似た、GameObject のみを入力できます。ここで、ゲームを再度実行してみましょう。Cube 上のコンポーネントを使用してメイン カメラの動きを制御できることがわかります。

小さな円をクリックしてゲームオブジェクトを割り当てるだけでなく、[階層] ウィンドウ内のゲームオブジェクトをクリックしてこの入力ボックスに直接ドラッグして割り当てることもできます。

そして、この編集処理というのは、実は通常はコンポーネントのメンバ変数を通じて外部リソースを参照する方法なのですが、GameObjectだけでなく、コンポーネントタイプに置き換えることもできますし、自分で書いたコンポーネントクラスもシリアライズして使うことができます。このような割り当ては表示パネル上で行うことができます。

この利点は明らかです。名前ではなくオブジェクト自体を参照します。メイン カメラの名前をどのように変更しても、参照関係は依然として正しいです。

ゲームオブジェクト参照の深い理解

関数を理解する最善の方法は、その原理を深く理解することです。以前は、一般的な変数がどのようにシリアル化され、シーン ファイルにどのように格納されるかを深く分析しましたが、それは当然 GameObject でも同じです。シーン ファイルをテキスト ファイルとして再度開き、Cube の下にコンポーネントを見つけます。

この GameObject のシリアル化メソッドは fileID であり、これは明らかに参照であるため、この ID 値を直接検索すると、シーン内で見つけることができます。

Unity がプレフィックスとして「--- !u!1 &」を使用し、その後にファイル ID の値を使用して、シーン内のデータ内のゲームオブジェクトの始まりを示していることがわかります。 GameObject クラスは、Said Main Camera を使用してきたものです。

このことから、Unity がシーン内のすべてのゲームオブジェクトに一意の ID を割り当てていることがわかります。このゲームオブジェクトを参照する必要があるコンポーネントにメンバー変数がある場合、この一意の ID が埋められます。コンポーネントが逆シリアル化されるとき、作成時実際のクラスの場合は、GameObject を見つけて、この一意の ID を介して割り当て直します。

考える質問

カメラを上に移動すると、X 座標と Y 座標が直接変更されます。実際、これには問題があります。左右に移動する場合、実際にはカメラの向きと関係がありますが、デフォルトのカメラは正面を向いています。 Z 軸: 左右の方向が X 軸の正と負の軸であるため、現在のカメラの左または右の方向を使用して、X と Z をどの程度変更する必要があるかを計算する必要があります。は考える問題として残されています。

次の章

この章では、カメラを移動する必要性から始まり、入力応答の実装方法と実装プロセスで遭遇する実際的な問題について詳しく説明し、他のゲームオブジェクト コンポーネントを参照する方法を紹介します。ロジックを理解し、シーンを編集する担当者に不必要な精神的負担を与えないようにします。

ゲームを構成する基本要素はやはり画面のプレゼンテーションです。次の章では、ゲーム レンダリングの基本的な内容を最も簡単に理解します。レンダリング部分は非常に大きく、敷居が比較的高いため、ここでは一部のコンポーネントの説明のみを説明します。一般的なプロセスと特定のレンダリングの詳細な説明は、将来の特別な章で説明されます。

おすすめ

転載: blog.csdn.net/z175269158/article/details/129832622