1.15 0から学ぶUnityゲーム開発 - ゲームUI

前の章では、設計のために十字線を制御するためにマウスをサポートする必要がある最後のタスクを残しました。その後、十字線は基本的に画面上に常に表示される画像になります。もちろん、3D オブジェクトを使用して、前の説明で、コンセプトをレンダリングするときに、画面がカメラのニアカット面であると述べましたが、3D オブジェクトをニアカット面に保持している限り、オブジェクトがそのように見えることを保証できます。常に画面上にあります。

しかし、これを行うには、非常に狭い領域 (多くの場合、切断に近い平面はわずか 0.1 以下です) にオブジェクトを配置する必要があり、非常に不便です。また、3D オブジェクト自体は、UI で必要なコンテンツに合わせて特別に処理されません。独自のホイールを作成する場合、Unity はゲーム内で UI を作成するのに便利な一連の組み込みソリューション、UGUI を提供します。

ウグイ

UGUI は実際には名前です。本質的に、Unity は特別なコンポーネントのセットを提供します。このコンポーネントのセットのレンダリング プロセスはメッシュ レンダラーではなく、画面上にレンダリングされるコンテンツに特化されています。UI 開発前に従来のソフトウェアを使用していた場合は、の場合、Qt、WPF、MFC などのフレームワークには、インターフェイスの構築を容易にし、迅速に行うためのボタン、ラベルなどの多くのコントロールが含まれます。当然、UGUI も同様のものを提供する必要があります。

次に、通常の 3D オブジェクトを作成するのと同じパスで UI コンテンツの作成を開始します。また、階層内で右クリックして UI->Image を選択すると、突然多くのものが表示されます。

ゆっくり説明しましょう:

  • キャンバスオブジェクト:

ここでオブジェクトと呼んでいるのは、このオブジェクトの中に Canvas というコンポーネントがあるためです。区別するために、階層内に存在する GameObject を Canvas オブジェクトと呼びます。このオブジェクトによってどのコンポーネントが自動的に作成されるかを分析します。

ここでの RectTransform コンポーネントは、実際には Transform コンポーネントを継承しています。ここでは、元の Transform コンポーネントパネルのデータ表示を Unity が覆い、直接 RectTransform を表示しています。Unity がデフォルトでいくつかの暗黙のルールを設けていると考えられます。ここでUIの3D座標データを編集しても意味がありません。

実際、RectTransform で設定されるパラメータは、従来のソフトウェア UI のアンカー ポイント、位置、ピクセル位置、サイズと似ていますが、ここで変更できない理由は、上部に Canvas コンポーネントを持つ親要素がなくなったためです。 Canvas オブジェクトのレイヤーです。キャンバス自体は全画面を表すため、ゲーム ウィンドウのサイズを調整しようとすると、内部の値もそれに応じて変更されます。子要素の場合は、ここで次のように変更できます。後で見てみましょう。

Canvas コンポーネントは Mesh Renderer コンポーネントと同様の機能で、オブジェクトの下のすべてのサブ要素に含まれる UI コンテンツのレンダリングを実行します。3D オブジェクトのレンダリングと比較して、Mesh やマテリアルなどを提供する必要があります。ここでは、Unityこれらの詳細を保護します。必要なのは、レンダリングする表示ターゲットなどの RenderMode を指定することだけです。ここで注意する必要があるのは一時的なレンダリング モードだけです。デフォルトでは、スクリーン スペース - オーバーレイになっています。人間の言葉で言うと、Unity は一連の暗黙のルール ロジックを使用して、UI コンテンツを個別に画面にレンダリングします。この暗黙のルールについては、他の RenderMode の使用方法については、高度なチュートリアルで詳しく説明します。ここで知っておく必要があるのは、このコンポーネントが UI 表示を実行するために使用されることだけです。

Canvas Scaler と Graphics Raycaster については、当面は無視してかまいません。それについては、会ったときに話します。

  • EventSystem、これは実際には、UI システムが使用する必要がある入力イベントを処理する方法のロジックです。これには 2 つのコンポーネントがありますが、今は注意する必要はありません。とにかく、知っておく必要があるのは、 UI では、クリックなどの操作に応答するためにこのコンポーネントが必要です。

  • 画像

最後が今回擬似ハートを描くために必要な画像です

RectTransform コンポーネントの値を変更でき、内部のデータを変更して位置、サイズ、レイアウトを調整できることがわかります。

Canvas Renderer コンポーネントは、親要素の Canvas コンポーネントに、レンダリングする必要がある UI 要素があることを伝えます。

Image コンポーネントは、レンダリングする必要があるコンテンツを提供します。ここでの Source Image は、表示したい画像が割り当てられている場所でなければならないことが直感的にわかります。Color は、画像に色を重ね合わせます。ここではデフォルトは白ですこれは、割り当てられた画像も白で表示されることを意味します。他のパラメータは無視しましょう。

デフォルトで作成されるコンポーネントについて説明しましたが、実はここでの Canvas と EventSystem は Image を表示するために必要なもので、Unity はこの 2 つがシーン内に存在しないことを確認してデフォルトで作成します。画像の場合、新しい画像のみが表示され、追加の Canvas や EventSystem は作成されません。

それでは、シーン内の現象を見てみましょう。

巨大な白いブロックがシーンに表示され、ゲームの左下隅にも白いブロックが表示されていることがわかります。これは実際に画像によって表示された効果です。

では、どうやって編集すればよいのでしょうか?マウス ホイールをスライドして、シーンの視点をズームアウトしてみましょう。

UIで表示されている部分(白いワイヤーフレーム)と白い画像で表示されている位置が確認できますが、CubeとWallは遠すぎてよく見えません。これは実は Unity がデフォルトで与えている効果で、RenderMode が Screen Sapce -Overlay の場合、UI 表示内容を運ぶための巨大なスクリーンが座標原点の位置に生成されます。 window. とゲームウィンドウがそのまま画面に表示されます。

これを知ると、UI 編集は 3D シーン オブジェクトの編集と何ら変わらないことがわかります。また、座標軸の矢印をドラッグして画像を移動することもできます。もちろん、3D パースでの透視投影も可能です。グラフィック デザインを行う場合は、 , そのため、Scene ウィンドウの上部にあるツールバーに 2D ボタンがあり、これを押すと直接 2D モードに切り替わり、UI の調整が容易になります。

十字線の写真を作成する

もちろん、現在必要なのは白いブロックではなく十字線を作成することなので、十字線の画像を見つける必要があります。ただ検索するだけです。

http://static.fotor.com.cn/assets/stickers/freelancer_ls_20180125_26/ba9b50fb-1efd-4854-928b-0b40ae26e36f_medium_thumb.jpg http://static.fotor.com.cn/assets/stickers/freelancer_ls_20180125_26/ba9b50fb-1ef d -4854-928b-0b40ae26e36f_medium_thumb.jpg

このリンクは jpg 画像です。自分で保存し、プロジェクトの Assets フォルダー内の任意の場所に置きます。

ここで目的を達成するために名前を変更し、それを選択して、[インスペクター] パネルでこの画像の目的を指定します。

[スプライト (2D および UI)] を選択します。このタイプは、画像コンポーネントのソース画像に必要なタイプです。この選択を有効にするために [適用] をクリックすることを忘れないでください。有効になると、画像には通常、透明な背景。

次に、この画像をドラッグ アンド ドロップして Image コンポーネントに割り当てます。

ドラッグ アンド ドロップによる割り当てが失敗した場合は、前の手順が正しく行われていないかどうかを確認してください。タイプが間違っている場合、割り当ては許可されません。

割り当てが成功すると、左側のゲーム ウィンドウに十字線の画像が表示されます。

目立ちにくいと思われる場合は、他の画像を自分で変更したり、Image コンポーネントの Color パラメータを赤に変更して重ねてみると、より鮮やかになります。

UI要素の位置を動的に変更する

上で述べたように、十字線の位置を変更するには RectTransform の PosX または PosY を変更するだけで済みますが、必要なのは、実行中のマウスの位置に応じて十字線の位置を動的に調整することです。

もちろんロジックに合わせて動的に調整する必要があるので、新しいコンポーネントを作成してImageに配置してコードで調整する必要があるのですが、すぐにGPTさんに書いてもらうことにしました。

using UnityEngine;

public class AimController : MonoBehaviour
{
    private RectTransform rectTransform;

    private void Start()
    {
        rectTransform = GetComponent<RectTransform>();
    }

    private void Update()
    {
        rectTransform.anchoredPosition = Input.mousePosition;
    }
}

ここで意味するのは、RectTransform は Transform から継承しますが、position を直接使用しないということです。この座標は 3D 空間の座標であり、UI システムの座標ではないためです。代わりに、Vector2 であるアンカー位置を使用します。それはたまたま画面座標 X と Y であり、それらにマウス座標を直接割り当てます。

見に走って?

奇妙なことに、なぜ十字線とマウスの間に一定の距離があるのでしょうか? このanchoredPositionの説明を見てみましょう。

https://docs.unity3d.com/ScriptReference/RectTransform-anchoredPosition.html https://docs.unity3d.com/ScriptReference/RectTransform-anchoredPosition.html

アンカー参照点を基準としたこの RectTransform のピボットの位置。

つまり、この座標は、アンカー ポイントを基準としたこのコンポーネントのピボット ポイントの座標です。

英語を話す:

ピボットはいわゆる中心点です。移動する場合でも回転する場合でも、画像には中心点が必要です。その後、これらはこの中心点に基づいて計算されます。画像自体は形状とサイズを持っているため、中心点を比較できます形状の中心を調整します。

アンカーは非常に神秘的に感じられます。実際、アンカーは親要素のレイアウトに関連しています。内部のこの場所をクリックすると、いくつかのレイアウト方法を直感的に選択できます。

詳細な定義については、公式ドキュメントを参照してください。

https://docs.unity3d.com/Packages/[email protected]/manual/UIBasicLayout.html https://docs.unity3d.com/Packages/[email protected]/manual/UIBasicLayout.html

デフォルトのレイアウトは親要素を基準にして中央に配置されていることがわかりますが、Input.mousePosition は座標原点として左下隅から開始するため、ここでは左下隅を基準にしてマウスのオフセットをオフセットするのと同等です。そうすれば、マウスは常に十字線から画面の半分の距離にあることになります。

次に、左下隅の配置を選択するだけでアンカーを変更できます。

変更後、ゲームを再度実行して、Zhuoxin がそれに従ったかどうかを確認します。

弾丸を発射する

さて、十字線ができたので、あとは弾を発射するだけです。十字線に照準を合わせて弾丸を発射したい場合、照準距離が当たると予想される位置に十字線がなければならないと想像してください。つまり、照準が照準対象物の上に隠れている状態であり、端的に言えば、目から照準線までの線の延長線が照準対象物と交差する可能性があります。

目の位置が実はカメラの位置になっていてわかりやすいですね。

しかし、私たちの目的は画面上にあるのでしょうか?それは問題ではありません。前に述べたように、画面は近接平面であり、画面上の任意の点は 3D 空間座標に変換できます。

この共通のアルゴリズムは、Unity によってカプセル化されています。

https://docs.unity3d.com/ScriptReference/Camera.ScreenToWorldPoint.html https://docs.unity3d.com/ScriptReference/Camera.ScreenToWorldPoint.html

画面座標 xy を渡す必要があることがわかりましたが、もう 1 つの z は何でしょうか? これはカメラからの距離です (実際、画面上の 1 つの点は 3D 世界の無数の点をカバーすることができ、これらの点はカメラからの距離によって決まります)。前のカメラコンポーネント。

Camera は使用する MainCamera なので、スクリプトを変更しましょう。

AimController では、十字線に対するカメラの方向を計算し、それを前の発射制御コードに渡します。同様に、従来のコード設計でインスタンスを直接渡すことに加えて、他の機能モジュールを参照する必要がある場合、クラス自体は MonoBehaviour です。その後、それをパブリック メンバーとして設定することで、エディターでドラッグ アンド ドロップ割り当てを提供できます (ただし、プログラマにとってはあまりフレンドリーではありません)。

using UnityEngine;

public class AimController : MonoBehaviour
{
    public Camera mainCamera;
    public FireController fireController;

    private RectTransform rectTransform;

    private void Start()
    {
        rectTransform = GetComponent<RectTransform>();
    }

    private void Update()
    {
        rectTransform.anchoredPosition = Input.mousePosition;
        // 获取屏幕上当前鼠标位置(也就是准心位置)所在的3D空间位置
        Vector3 aimWorldPosition = mainCamera.ScreenToWorldPoint(new Vector3(Input.mousePosition.x,
            Input.mousePosition.y, mainCamera.nearClipPlane));
        // 通过坐标相减可以得到方向向量
        Vector3 fireDirection = aimWorldPosition - mainCamera.transform.position;
        // 归一化后传递给开火控制脚本
        fireController.SetDirection(fireDirection.normalized);
    }
}

ここでは、SetDirection が呼び出されるエディターで値を割り当てることができるように、新しいパブリック メンバーが追加されていることに注意してください。そのため、前に作成した FireController オブジェクトを画像の AimController にドラッグして割り当てる必要があります。はい、ここでドラッグ アンド ドロップできるのは、オブジェクトが FireController と呼ばれているからではなく、GameObject に FireController クラスのコンポーネントがあるためです。これは、GameObject を割り当てるだけでなく、コンポーネント自体を直接割り当てることもできることも意味します。それもOKでさらにオススメです。

OK、次のステップは FireController を書き直すことです。以前は発射方向を指定せずに単に弾丸を作成しました。照準関数から方向を受け取ったので、弾丸の作成時に飛行方向を設定する必要があります。

using UnityEngine;

public class FireController : MonoBehaviour
{
    private bool isMouseDown = false;
    private float lastFireTime = 0f;
    private Vector3 fireDirection;
    public float fireInterval = 0.1f;
    public AddVelocity bullet;

    void Update()
    {
        if (Input.GetButton("Fire1"))
        {
            if (!isMouseDown)
            {
                isMouseDown = true;
                lastFireTime = Time.time;
                Fire();
            }
            else if (Time.time - lastFireTime > fireInterval)
            {
                lastFireTime = Time.time;
                Fire();
            }
        }
        else
        {
            isMouseDown = false;
        }
    }

    void Fire()
    {
        // 在这里实现每次触发的逻辑

        // 创建新的子弹,每次都是从模板bullet复制一个出来
        AddVelocity newBullet = Object.Instantiate(bullet);
        newBullet.SetDirection(fireDirection);
    }

    public void SetDirection(Vector3 direction)
    {
        fireDirection = direction;
    }
}

SetDirection 関数の実装を追加し、値をメンバー変数として保存したことがわかります。その後、新しく作成された各弾丸にこの速度を渡す必要があるため、弾丸にもインターフェイスが必要ですが、次のメソッドを使用しました。 GameObject を使用して弾丸を直接作成するには、ここでは単に怠惰で、上で書いた唯一のコンポーネント クラス AddVelocity を直接使用します。弾丸メンバーが元の GameObject クラスから AddVelocity に変更されていることに注意してください。これにより、インスタンス化できるようになります。 GameObject を AddVelocity 型で直接呼び出し、戻り値は AddVelocity です。これは、GameObject をインスタンス化し、GetComponent を使用して上記の AddVelocity コンポーネントを取得するのと同等ですが、記述方法はよりエレガントで効率的です (GetComponent への各動的呼び出しは比較的低速です)。 )。

したがって、最後に行う必要があるのは、AddVelocity に SetDirection を実装して、弾丸に飛行方向を与えることです。

using UnityEngine;

public class AddVelocity : MonoBehaviour
{
    public float speed; // 初始速度
    public float lifeTime = 5.0f;
    private float lifeStartTime;
    private Vector3 fireDirection;

    void Start()
    {
        Rigidbody rb = GetComponent<Rigidbody>();
        if (rb != null)
        {
            rb.velocity = fireDirection * speed;
        }

        lifeStartTime = Time.time;
    }

    void Update()
    {
        if (Time.time - lifeStartTime > lifeTime)
        {
            Destroy(gameObject);
        }
    }

    public void SetDirection(Vector3 direction)
    {
        fireDirection = direction;
    }
}

方向を保存することに加えて、以前の初速度 Vector3 を float 型の速度変数に変更しました。これは、速度方向を手動で指定する必要がなくなり、代わりに初速度の強さを調整する必要があるためです。係数が乗算されるため、最終的に初期速度は fireDirection * 速度になります。

メンバーが変更されるため、Bullet プレハブの速度値をより快適な値 (10 など) に変更する必要もあります。

FireController で Bullet メンバーの型を変更したため、エディターでのドラッグ アンド ドロップ割り当ての内容が GameObject 型にバインドされていたため、コードを変更すると、ここでの割り当てが無効になったことに注意してください。 Unity It will clear it for we. Bullet プレハブをもう一度ドラッグして、FireController の Bullet メンバーに割り当てることを忘れないでください。

さて、ゲームを実行して見てみましょう?

いいですね、十字線と弾丸は別々です、弾丸の方向は変わっていません、それではどこに問題があるのでしょうか?前の章で述べたことを思い出してください。デフォルトでは、新しく作成されたゲームオブジェクトの位置が原点になります (もちろん、プレハブ自体には位置情報がすでにあるので、それについては別途説明します)。その場合、方向をどのように変更しても、起動は行われません。実際には原点から始まりますが、全くそうではありません。私たちの目は撃たれているので、この起動の開始点を変更する必要があるため、現在 2 つの方法があります。

  1. 現在のカメラの位置情報を見て、バレットで作成したコードに手書きで値を代入するのは、将来のメンテナンスを容易にするという目的からは決して外れます。その立場、それは終わります。
  2. カメラの位置情報を渡す

明らかに 2 番目の方が優れているため、FireController に新しい起動ポイントを追加する必要があるのは当然です。

using UnityEngine;

public class FireController : MonoBehaviour
{
    private bool isMouseDown = false;
    private float lastFireTime = 0f;
    private Vector3 fireDirection;
    public float fireInterval = 0.1f;
    public AddVelocity bullet;
    public Transform fireBeginPosition;

    void Update()
    {
        if (Input.GetButton("Fire1"))
        {
            if (!isMouseDown)
            {
                isMouseDown = true;
                lastFireTime = Time.time;
                Fire();
            }
            else if (Time.time - lastFireTime > fireInterval)
            {
                lastFireTime = Time.time;
                Fire();
            }
        }
        else
        {
            isMouseDown = false;
        }
    }

    void Fire()
    {
        // 在这里实现每次触发的逻辑

        // 创建新的子弹,每次都是从模板bullet复制一个出来
        AddVelocity newBullet = Object.Instantiate(bullet);
        newBullet.transform.position = fireBeginPosition.position;
        newBullet.SetDirection(fireDirection);
    }

    public void SetDirection(Vector3 direction)
    {
        fireDirection = direction;
    }
}

fireBeginPosition 変数を追加しましたが、これは Transform タイプです。Transform コンポーネントはオブジェクトの位置情報を保存すると常に言ってきました。カメラを割り当てる必要はありません。必要なのは位置だけです。

位置を取得した後、新しく作成した弾丸に newBullet.transform.position = fireBeginPosition.position; を割り当てて、弾丸の作成時の初期位置を変更します。

コードを変更した後、エディターで Main Camera を FireController の fireBeginPosition メンバーにドラッグして値を割り当てます。

それでは、ゲームを実行して見てみましょう?

1.15 0から学ぶUnityゲーム開発 - ゲームUI

弾丸ではなく投石機が発射されたように見えますが、とにかく少なくとも照準は機能しています。

考える質問

  1. 上で述べたように、弾丸は命中する場所を狙うという直感にもっと一致させるために、実際には目から発射されるべきですが、実際には弾丸は銃身から発射されるべきであり、銃身の位置は決してそうではありません。目の位置を考慮して、銃身から弾丸を発射することは可能ですか。可能であれば、目は物体を狙うことができますが、銃身が壁で妨げられている場合はどうすればよいですか?
  2. 最終的には、予想した方向に弾を発射することに成功しましたが、速度が少し遅かったです。弾がより真っすぐに飛ぶように弾丸設定の速度パラメータを上げようとしましたが、弾が通り抜けてしまったことがわかりました。なぜ衝突しないのか、反発するのか?の解き方?

次の章

この章では、Unity で UI 制作を開始する方法を詳細に説明し、その後、照準と射撃ロジックのセット全体を完成させ、シーン内の複数のロジック間で情報を通信および転送する方法を明確にしました。

次章ではキャラクターの動きを追加していきますが、キャラクターのスケルトンアニメーションなど複雑なことは行わない前提で、3人視点の操作モードとキャラクターの動きの制御方法をシンプルに実装することで、人物撮影デモ。

おすすめ

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