主なコンテンツ

〜主なコンテンツ〜

1. Unityで製作したゲームと製作Tips
  1. 三月精チャレンジ(東方Project二次創作)
    1. 作り方
  2. 英語学習2D(オリジナルスマホアプリ)
2. UE4
3. ゲームアプリ見学
4. Bitbucket & SourceTreeでの一連の流れ
  1. 前半
  2. 後半
5. Tips
  1. UnityのTips
  5. SQL文のTips
  6. Final IK
  7. GearVR+Unity

2023年3月12日日曜日

43. Network Role

 いらっしゃいませ。


このレクチャーでは、ロールの概念と、Unreal Engine のマルチプレイヤー ゲームでロールがどのように使用されるかについて説明します。 これには、ローカル ロールとリモート ロールの概念と、この 2 つの違いが含まれます。


そして、キャラクターの上に表示して現在の役割を表示できる特別なウィジェットを作成して、役割とその使用方法をよりよく把握できるようにします。


それでは、ネットワークの役割について少しお話しましょう。マルチプレイヤー ゲームでは、プレイヤーが制御する特定のキャラクターの複数のバージョンが存在します。


あなたがサーバーに接続しているクライアントの場合、現在操作しているキャラクターは [あなたのマシン] に存在します。


しかし、あなたのキャラクターのバージョンも [サーバー] に存在します。


たとえば、ゲームに 3 人以上のプレイヤーがいる場合は、他のクライアントがサーバーに接続されています。 (その後) [そのクライアントのマシン] にあなたのキャラクターのバージョンがあります。


したがって、3 人用のゲームでは、各マシンに 1 つずつ、合計 3 つのキャラクターのコピーがあります。 そのため、コード内で扱っている CHARACTER のバージョンを区別する方法を知ることが重要になります。 これを整理するために、Unreal Engine にはロールの概念があります。


ENetRole と呼ばれる列挙型が存在します。これには、特定のキャラクターまたはポーンの役割を識別するために使用できるいくつかの列挙型定数があり、その役割には役割権限 enum 定数があり、この役割はサーバー マシン上に存在するポーンに割り当てられます。


Unreal Engine は権限のあるサーバー モデルを使用するため、サーバー上に存在するすべてのポーンが権限の役割を持つと見なされます。


その役割には、ポーンに関しては [シミュレートされたプロキシ] の役割もあります。 これらは、そのポーンを制御していないマシン上に存在する任意のキャラクターまたはポーンのバージョンです。


現在マシン上で制御しているキャラクターまたはポーンには、[自律プロキシ] の役割があります。


ここで、ポーンまたはキャラクターには複数のバージョンがあり、1 つは自分のマシンにあり、もう 1 つは画面に表示される他のマシンにあることに注意してください。


したがって、[自分のマシン] でポーンを制御している場合、ポーンのそのバージョンには [自律プロキシ] の役割があります。


あなたがサーバーではないと仮定すると、あなたがサーバーである場合、ポーンはサーバー上に存在するため、役割権限を持っています。


他のプレイヤーと遊んでいるときに、自分のマシン上のキャラクターまたはポーンのバージョンが、ネットワークを介して別のマシン上の他の誰かによって制御されている場合、そのキャラクターまたはポーンのシミュレートされたプロキシを見ていることになります。 ポーンは別のクライアントによって制御されているか、サーバーによって制御されています。


ENetRole には、ロールが定義されていないアクター用のロール [none] もあります。


それでは、エディタに戻り、新しい C++ クラスを作成しましょう。 これは、キャラクターの頭の上に表示できるウィジェット用です。


[C++ クラス > Blaster] フォルダーにいます。 右クリックして新しい C++ クラスを作成し、すべてのクラスを選択します。 ユーザー ウィジェットに基づいて C++ クラスを作成したいと考えています。そうすれば、ユーザー ウィジェットの C++ 変数を操作できます。


それでは、このウィジェットを作成しましょう。 [次へ] をクリックしましょう。これを独自のフォルダに貼り付けたいと思います。


このフォルダを単に HUD と呼びます。 キャラクターの頭上に表示されるので「頭上ウィジェット」と名付けます。


それでは、クラスの作成をクリックしましょう。

わかった。


したがって、このおなじみのメッセージにノーを押して、ここにオーバーヘッド ウィジェットを用意します。 もちろん、C++ クラスでは、インクルードの HUD 部分を削除して、コンパイラ エラーを取り除くことができます。


これで UUserWidget ができました。これを、キャラクターの頭上にロールを表示するために使用するウィジェット ブループリントの親クラスとして使用します。


さて、このウィジェットは単純にテキスト ブロックを持つことになるので、このテキスト ブロックのタイプは you text block になります。


ここに公開セクションを作成しましょう。


public セクションでは、UTextBlock を前方宣言します。これは UTextBlock ポインターであり、これを [DisplayText] と呼びます。 これで、ウィジェット ブループリントを作成するこのウィジェットの表示テキストを設定できます。


先に進み、そのウィジェットのブループリントを作成しましょう。 ここのブループリント フォルダに、HUD という名前の新しいフォルダを作成します。 HUD フォルダで、右クリックして [User Interface] と [Widget Blueprint] を選択します。これを WPP_overhead_widget と呼びます。


これは単純なウィジェットの設計図になります。 キャンバスパネルは必要ありません。 私が本当に欲しいのはテキストウィジェットだけです。 テキストを選択してドラッグし、テキストのサイズを変更します。


ここで、[overheadWidget.h] です。 [DisplayText] という変数があり、それは UTextBlock です。 したがって、ここのウィジェット ブループリントの変数は、その C++ 変数と同じ名前にする必要があります。 これを [DisplayText] と呼びます。


次に、フォントを太字から標準に設定し、両端揃えを中央に変更します。 そうすれば、それは素晴らしく、中心になります。


これで、このテキスト ブロックのテキストを C++ から制御できます。 その方法は、表示テキストに特定の指定を持つ UProperty を与えることです。 では、これに UProperty を与えましょう。それは [meta = ] として指定され、括弧内にウィジェットをバインドします。 これで、この C++ 変数がウィジェット ブループリントのテキスト ブロックに関連付けられます。


それでは、C++ コードをコンパイルしましょう。 コードをコンパイルして、ウィジェット BLUEPRINT に戻り、BLUEPRINT の親を変更して、その親 C++ クラスがオーバーヘッド ウィジェット C++ クラスになるようにします。


これを行うには、グラフ タブに移動し、クラス設定を選択します。 そして、このクラス オプションの下に、親クラスが表示されます。 これで、ユーザー ウィジェットに基づいてこれを作成しました。 ただし、ドロップダウンを開いて OverheadWidget を選択することで、これを変更できます。


このウィジェット ブループリントは OverheadWidget の C++ クラスの親になり、C++ クラスには DisplayText と呼ばれる UTextBlock 変数があり、BindWidget を指定するメタがあります。 この C++ 変数を変更すると、[DisplayText] TextBlock に影響します。 これを行う場合、WidgetBlueprint 内の TextBlock が C++ 変数とまったく同じ名前であることが非常に重要です。 そうしないと、これは機能しません。

これで、ウィジェットのサイズが変更されたことがわかりました。 このテキスト ブロックに必要なサイズに戻します。


(だから今) このウィジェットを動かしている C++ クラスにいくつかの機能を追加する必要があります。 したがって、ここの C++ クラスでは、いくつかの基本的な機能を追加します。


ここで、ユーザー ウィジェットによって継承された仮想関数をオーバーライドしたいと考えています。 これは保護された機能です。 そこで、保護セクションを追加します。


そして、これは OnLevelRemovedFromWorld と呼ばれる void を返す仮想関数です。 OnLevelRemovedFromWorld は、別のレベルに移行するか、現在のレベルを離れるときに呼び出されます。 これにより、このウィジェットをビューポートから削除できます。


OnLevelRemovedFromWorld は、ULevel ポインター = InLevel と UWorld ポインター = InWorld を受け取ります。 そして、これはオーバーライドです。


では、このための関数定義を作成しましょう。 ここと OnLevelRemovedFromWorld では、このウィジェットを取得してビューポートから削除する RemoveFromParent を呼び出すだけです。


そして、これのスーパーバージョンを呼び出す必要があります。 そのため、super:: OnLevelRemovedFromWorld を呼び出し、InLevel と InWorld を渡す必要があります。 これでウィジェットがクリーンアップされ、ビューポートから削除されます。 だから、まだそこにはありません。


今度は OverheadWidget.h です。 [DisplayText] テキスト ブロックのテキストを設定する関数を作成したいと思います。 そして、これは公開機能になる可能性があります。 これを setDisplayText という void 関数にします。 これは単純に FString を取り、これを textToDisplay と呼びます。 そして、その文字列を使用して、DisplayText を値に設定できます。


関数定義をしましょう。 ここで、[DisplayText] のテキストを設定したいと思います。 そこで、まず [DisplayText] が有効かどうかを簡単に確認します。 そうであれば、[DisplayText] を取り、関数セットのテキストを使用します。 この関数は、このテキスト ブロックのテキストを設定します。


ここでは、テキスト ブロック データ型を使用しているため、そのためのヘッダー ファイルを INCLUDE する必要があります。 また、テキスト ブロック ヘッダーは「components/TextBlock」にあります。 そして、それはヘッダーファイルです。


これで、setText を呼び出すことができます。 これは FText を取ります。 したがって、FString から FText に変換する必要があります。


これで、FromString という FText 静的関数を使用して、この textToDisplay を単純に渡すことができます。 これで、表示テキストを値に設定する FString を渡すことができる便利な関数ができました。

ここで、キャラクターのネットワーク ロールを把握する別の関数を作成したいと思います。


それでは、「overheadWidget.h」に行き、別のパブリック関数を作成しましょう。 そして、これは void 関数になります。 これを単に showPlayerNetRole と呼びます。 InPawn と呼ばれるタイプ APawn の入力パラメータをこれに追加します。


そうすれば、キャラクターなどのどこからでもこの関数を呼び出して、キャラクターへのポインターを渡すことができ、ここでネットの役割を把握できます。


それでは、この関数を定義しましょう。 そして、ここ showPlayerNetRole で、プレイヤーのロールである InPawn のローカル ロールを列挙値として取得したいと考えています。 その列挙型は EMetRole です。


そこで、LocalRole というローカル変数を作成し、それを InPawn->getLocalRole と等しくなるように設定します。 したがって、ポーンから呼び出すことができる関数としてローカル ロールを取得し、そのローカル ロールが何であるかを確認します。 もちろん、このローカルの役割は、呼び出し元のマシンによって異なります。


ここで、この情報を使用して、setDisplayText 関数を呼び出してその役割を表示したいと思います。 そこで、ローカルの FString 変数を作成し、このロールを呼び出し、ローカルのロールに応じてロールの値を設定して、ここで switch ステートメントを使用できるようにします。 そこで、ローカルの役割をオンにして、ここでいくつかのケースを取り上げます。


さて、最初のケースは、localRole が ENetRole 権限の場合です。 ここでコロンを 2 つ押すとすぐに、ENetRole が使用できるさまざまな列挙型定数が表示されます。


[権限]、[自律プロキシ] があります。ロール MAX があります。これは、ほとんどのアニメでデフォルトの最大値として表示されるものです。 ロール [なし] と [シミュレートされたプロキシ] があります。


ロール [権限] を選択します。 また、ロールが [authority] の場合、ROLE (文字列) を値「authority」を持つ FString に設定します。 次のケースにカスケードしないように、ここに break ステートメントを追加します。


さて、ここではいくつかのケースがあります。 これを貼り付けて、ロール [authority] からロール [autonomous proxy] に変更します。 この場合、ロールを [autonomous proxy] という文字列に設定します。


ここで別のケースを追加しましょう。 [自律プロキシ] の代わりに、[シミュレートされたプロキシ] を使用します。 この場合、ロール文字列を [simulated proxy] に設定します。


ポーンの役割を [none] にするべきではありませんが、とにかくそのケースを処理します。 そのため、ロールがたまたま [none] の場合は、文字列を [none] に設定します。


したがって、この switch ステートメントの後、ローカル ロールの値に応じて FString ロールが設定されます。


今度は文字列をフォーマットしたいので、LocalRoleString という名前の新しい FString を作成し、FString::printf を使用します。ここでは、フォーマットされた文字列のテキストとして、単に [local role %s ] で、その文字列に役割を渡します。


もちろん、これは FString なので、アスタリスクのオーバーロードを使用して C スタイルの文字列を取得する必要があります。 これで、localRoleString という文字列ができました。この文字列値を持つように displayText を設定します。


そこで、このテキストを設定するために、FString を受け取る setDisplayText という便利な関数を作成しました。 したがって、setDisplayText を呼び出して localRoleString を渡すだけです。


したがって、このタイプのウィジェットがあり、showPlayerNetRole を呼び出す場合、役割を表示したいポーンを渡すだけです。 それは素晴らしいことです。 このウィジェットをキャラクターに追加するだけです。


それでは、ブラスターのキャラクターに戻りましょう。 したがって、この Blaster キャラクターでは、いくつかの変数を追加して、これらのoverheadWidgets の 1 つを作成できるようにします。


ですから、これらをここのプライベート セクションに貼り付けます。 これにはウィジェットコンポーネントを使用したいと思います。 そこで、OverheadWidget という UWidgetComponent を前方宣言し、これに UProperty を与え、この EditAnywhere を作成して、キャラクター ブループリント内から設定できるようにします。 これで、UWidgetComponent であるこのオーバーヘッド ウィジェットができました。


BlasterCharacter.cpp に戻り、コンストラクターでこれを作成しましょう。ここの一番下。 OverheadWidget と言って createDefaultSubobject を使用します。これは UWidgetComponent になり、[overheadWidget] という名前を付けます。 これをルート コンポーネントにアタッチするだけです。そこで、overheadWidget->setupAttachment のようにして、単純に rootComponent にアタッチします。

今は UWidgetComponent を使用しているので、そのヘッダー ファイルをインクルードする必要があります。 それでは一番上に行きましょう。これは [components/WidgetComponent] になります。 それで、そのヘッダーファイルを取得しました。 わかった。 これで、widgetComponent を BlasterCharacter に追加しました。

これで、関数がプレーヤーのネット ロールを表示します。これを、キャラクター クラスのブループリントから呼び出したいと思います。 これを Ufunction にして、blueprintCallable を追加します。


これをコンパイルしてテストする前に、Blaster キャラクターに戻ります。 また、ブループリントからウィジェットにアクセスするので、少なくとも blueprintReadOnly にする必要があります。 このプライベート変数を blueprintReadOnly にすると、「meta = ([allowPrivateAccess] = true)」が必要になります。


これが初めての場合、これはすべて、この変数をブループリントに公開していることを意味します。 このメタ指定を追加しない限り、C++ でプライベートな変数に blueprintReadOnly または blueprintReadWrite を使用することはできません。 したがって、C++ では非公開ですが、この変数で blueprintReadOnly を使用できるようになりました。


これを Blaster キャラクターでコンパイルできます。これが私のオーバーヘッド ウィジェットです。 それを選択し、ユーザー インターフェイス セクションでいくつかのプロパティを変更します。


[スペース] を [スクリーン] に変更し、[ウィジェット クラス] は [WBP_OverheadWidget] を選択し、[希望のサイズで描画] にチェックを入れたいと思います。 そうすれば、このウィジェットのサイズを手動で設定する必要がなくなります。


これで [オーバーヘッド ウィジェット] が設定されました。ここでブループリントの呼び出し可能な関数を呼び出して [beginPlay] を実行したいと思います。 そこで、overheadWidget を取得します。 そして、overheadWidget から getUserWidgetObject を呼び出します。


これは、ウィジェット コンポーネントが使用している実際のユーザー ウィジェットであり、このクラス WBP_overhead_widget です。 したがって、それにキャストできます。 キャストを使用して WBP_overhead_widget を選択し、このクラスとして、ブループリントの呼び出し可能な関数 show player net role を呼び出します。


これにはポーンが必要です。 私たちのキャラクターは実際に Pwan であるため、[自己への参照] を渡すだけです。 したがって、showPlayerNetRole は netRole を取得し、その netRole をオンにして、それに基づいてウィジェットのテキストを設定します。


それでは、コンパイルしてプレイして、何が起こるか見てみましょう。 ローカル ロール [権限] が表示されます。これは、1 人のプレーヤーだけでゲームをプレイしているためです。 その場合、私たちは [権限] であり、サーバー上にいます。 ビューポートに入り、overheadWidget をキャラクターの頭のすぐ上に移動します。


ここからが興味深いところです。 ここで実際に [3 つのドット (...)] まで来て、ネット モードがリッスン サーバーとして再生するように設定されていることを確認します。

このようにして、プレイヤーの 1 人が Listen サーバーであるゲームを実際にプレイしています。 つまり、彼らはサーバーとしてゲームをプレイしており、他のプレイヤーはクライアントであり、ここでプレイヤーの数を変更できます。


マルチプレイヤー ゲームをテストするときは、少なくとも 3 人のプレイヤーでテストすることをお勧めします。 ネットの役割に関しては、関連するすべてのケースが見られます。 プレイヤー数を 3 に設定した場合の意味を説明します。[プレイ] をクリックすると、3 つの個別のゲーム インスタンスが実行されます。


[ネット モード クライアント 1] と [ネット モード クライアント 2] の 2 つのウィンドウがトップ バーに表示されます。 したがって、ゲームのこれら 2 つのインスタンスは [クライアント] であり、このエディタの大きなウィンドウは [サーバー] です。


今、テキストはすべてごちゃごちゃしています。 ここで、サーバー上で少し先に進みます。


これをみて。 私たちのローカルでの役割は、これら 3 つすべてに対する権限であることがわかります。これは期待すべきことです。


サーバー上に存在するすべてのポーンは、ローカル ロール [権限] を持ちます。


ここで、クライアントをここに移動し、ウィンドウを少し拡大します。 ここで、私が操作しているキャラクターにはローカル ロール [自律プロキシ] があることに注意してください。 つまり、私のマシン上の私のキャラクターにはローカル ロール [自律プロキシ] があり、それは私がそれを制御しているためです。 だから私はここに走って、どちらが私か分かるようにします。 しかし、サーバー上では、ローカルの役割が [権限] であることがわかります。


ここで、他の 2 人のキャラクターを見てみましょう。どちらも [シミュレートされたプロキシ] であることがわかります。 それで、私は自分のマシンでこれらのキャラクターのバージョンを調べていますが、それらは別の場所で制御されているため、[シミュレートされたプロキシ] になります。


現在、ローカルの役割からは、サーバーによって制御されている役割を判断できません。 それを決定する方法については、コースの後半で説明します。


しかし今のところ、自分のマシンで実際に制御していないすべてのポーンがローカル ロール [シミュレートされたプロキシ] を持っていることは明らかです。 ここにいるもう 1 つのクライアント、つまりクライアント 1 に移動すると、同じことが表示されるはずです。 自分のマシンで制御しているポーンにはローカル ロール [自律プロキシ] があり、他のすべてのポーンにはローカル ロール [シミュレートされたプロキシ] があることがわかります。


これで、localRole の感触がつかめました。


それでは、remoteRole を見てみましょう。 ここで、showPlayerNetRole 関数のoverheadWidget.CPP に戻り、ローカル ロールの代わりにリモート ロールを表示したいと思います。

そこで、このローカル変数の名前をローカル ロールからリモート ロールに変更します。


また、ポーンから getLocalRole を呼び出す代わりに、getRemoteRole を呼び出したいと思います。 そして、この関数はまさにそのように機能します。 これは EnetRole を返しますが、今回は remoteRole を提供します。


したがって、localRoll で切り替える代わりに、remoteRoll で切り替えて、そのリモート ロールが何であるかに応じて文字列を設定します。


ここで、この FString の名前をリモート ロール文字列に変更します。本文では、ローカル ロールではなく、リモート ロールと表記します。 setDisplayText への関数呼び出しでは、ここでリモート ロール文字列を渡します。


それでは、これをコンパイルしましょう。 エディターに戻り、再生をクリックします。 これで、ウィジェットにリモート ロールが表示されます。


では、こちらをご覧ください。 私はクライアント 2 を使用しており、リモート ロールは [権限] です。 したがって、このキャラクターのローカル ロールが何であるかがわかります。


私はその[自律プロキシ]を制御しており、そこにある他の2つは[シミュレートされたプロキシ]です。 しかし、サーバー上にいない場合は、リモート ロールがサーバー上のロールになります。 したがって、これらすべての[権限]になります。 ここで、他のクライアントにも同じことが当てはまることに注意してください。 3つともリモートの役割は【権限】です。


次に、サーバーを見てみましょう。 ここのサーバーでは、ポートの状況が少し異なります。 私がコントロールしているリモート ロールは [自律プロキシ] であり、ここでの他の 2 人のリモート ロールは [シミュレートされたプロキシ] です。 これは、サーバー上で注意すべき微妙な違いです。


リモートの役割は、サーバーで制御されるキャラクターの [自律プロキシ] と、他の 2 つのキャラクターの [シミュレートされたプロキシ] になります。


そのため、常にこの情報を使用して、リモート ロールと C++ コードをチェックし、サーバー上かクライアント上かにかかわらず、自分がどのマシン上にいるかを知ることができます。



そのため、このレクチャーでは、ネットワーク ロールの概念と、ポーンのローカル ロールがどのように [権限] になるかについて説明しました。つまり、サーバー上に存在するポーン、または [自律プロキシ] を検討していることになります。 これは、クライアント マシンで現在制御しているポーンのローカル ロールです。


また、[シミュレートされたプロキシ] があることも確認しました。これは、制御していないクライアント マシン上のポーンのローカル ロールになります。


また、リモート ロールの概念と、クライアント マシンから見た場合のすべてのポーンのリモート ロールが [権限] になる方法、およびサーバー上で現在制御されているポーンのリモート ロールがautonomousProxy である方法についても説明しました。 サーバーがアクティブに制御していないサーバー上の他のポーンのsimulatedProxyです。


したがって、このコース全体でこのロールの概念を使用して、コード内でどのマシンを使用しているかを判断します。


よくやった。


また後で。


Welcome.


In this lecture, we're going to talk about the concept of a role and how role is used in Unreal Engine multiplayer games. This includes the concept of local role and remote role and how the two are different.


And we're going to create a special widget that we can display above our character to show its current role so we can get a better grasp of role and how we can use it.


So let's talk about network role for a minute.In a multiplayer game, there are multiple versions of any given character controlled by a player. 


If you're a client connected to a server, then the character that you're currently controlling exists on [ your machine]. 


But there also exists a version of your character on [the server]. 


And if there are more than two players in your game, for instance, you have other clients connected to the server. (Then) there's a version of your character on [that client's machine].


So in a three player game, there are three copies of your character, one on each machine. So it becomes important that we know how to distinguish which version of the CHARACTER we're dealing with in the code. To sort this out, Unreal Engine has the concept of role. 


and there exists an enum called ENetRole that has several enum constants that we can use to identify the role of any given character or pawn in that role has the role authority enum Constant, and this role is assigned to pawns that exist on the server machine.


Since Unreal Engine uses the authoritative server model, we consider any pawns that exist on the server to have the role of authority.


in that role also has a role of [simulated proxy] when it comes to pawns. These are the versions of any character or pown that exist on a machine that is not controlling that pawn. 


your character or pawn that you're currently controlling on your machine has the role of [autonomous proxy].


Now, keep in mind that there are multiple versions of your pawn or character, one on your machine and one on each other machine that shows you on the screen.


So when you're controlling your pawn on [your machine], that version of your pawn has the role [autonomous proxy].


Assuming you're not the server, if you are the server, then your pawn has role authority as it exists on the server.


Now if you're playing with other players and you see a version of the character or pawn on your machine controlled by someone else on another machine across the network, then you're looking at the simulated proxy of that character or pawn, whether that pawn is controlled by another client or controlled by the server.


Now ENetRole also has a role [none], for actors that don't have any defined role.


So let's jump back into the editor and create a new C++ class. And this will be for a widget that we can display above the head of our character.


I'm here in the [C++ classes > Blaster] folder. I'm going to right click and make a new C++ class and I'm going to choose all classes. I'd like to make a C++ class based on user widget, and that way we can drive the C++ variables of our user widget.


So let's create this widget. Let's click next and I'd like to stick this in its own folder.


I'll simply call this folder HUD. and I'm going to name is "overhead widget" as it will be displayed over the head of the character.


So let's click create class.

Okay.


So we'll hit no to this familiar message and we have our overhead widget here. Now, of course in the C++ class I can remove that HUD part of the include to get rid of the compiler error.


Now we have this UUserWidget and we're going to use this as the parent class for the widget blueprint we're going to use to show the role above our character's head.


Now, this widget is simply going to have a text block, so the type for this text block is going to be you text block.


Now let's create a public section here.


And in the public section, I'm going to forward declare UTextBlock That's a UTextBlock pointer, and I'm going to call this [DisplayText]. Now we can set the display text for this widget that will create a widget blueprint for.


Let's go ahead and create that widget blueprint. So here in my blueprints folder, I'm going to make a new folder called HUD. And in the HUD folder, I'm going to right click, select [User Interface] and [Widget Blueprint].And I'm going to call this WPP_overhead_widget. 


And this is going to be a simple widget blueprint. I don't need a canvas panel. All I really want is a text widget. So I'm going to select text and drag it in and resize this text.


Now here, an [overheadWidget.h]. I have a variable called [DisplayText] and it's UTextBlock. So my variable here in the widget blueprint has to have the same name as that C++ variable. So I'm going to call this [DisplayText].


Now I'm going to take the font and set it from bold to regular and change the justification to center. So that way it's nice and centered.


Now we can control the text for this text block from C++. And the way to do that is to give our display text a UProperty with a particular specify. So let's give this a UProperty, and that's specified as [meta = ] and in parentheses bind widget. Now what this does is it associates this C++ variable with that text block in the widget blueprint.


So let's compile our C++ code. And with our code compiled, back here in our widget BLUEPRINT, we can reparent the BLUEPRINT so that its parent C++ class is our overhead widget C++ class.


We do that by going to the graph tab and selecting class settings. And here under class options, we see parent class. Now we just created this based on user widget. But we can change this by opening the dropdown and selecting OverheadWidget.


Now that this widget blueprint is parented to the OverheadWidget, C++ class and our C++ class has a UTextBlock variable called DisplayText with the meta to specify our BindWidget. Any changes we make to this C++ variable will affect our [DisplayText] TextBlock. Now, if you're going to do this, it's very important that the TextBlock here in the WidgetBlueprint has the same exact name as the C++ variable. Otherwise this won't work.


Now I see that my widget has resize. I'm going to size it back to roughly the size that I need for this text block.


(So now) we just need to add some functionality to the C++ class that's driving this widget. So here in the C++ class, we're just going to add some basic functionality.


Now I'd like to override a virtual function inherited by user widget. This is a protected function. So I'm going to add a protected section.


And this is a virtual function returning void called OnLevelRemovedFromWorld. Now OnLevelRemovedFromWorld is going to be called when we transition to a different level or leave the current level. And this allows us to remove this widget from the viewport.


Now OnLevelRemovedFromWorld takes a ULevel pointer = InLevel, and a UWorld pointer = InWorld. And this is an override.


So let's go ahead and make a function definition for this. And here and OnLevelRemovedFromWorld, we can simply call RemoveFromParent, which will take this widget and remove it from the viewport.


And we do need to call the super version of this. So we're going to call super:: OnLevelRemovedFromWorld and we need to pass it InLevel and InWorld. So that'll clean up our widget and remove it from the viewport. So it's not still there.


Now an OverheadWidget.h. I'd like to make a function to set the text for our [DisplayText] text block. And this can be a public function. I'm going to make this a void function called setDisplayText. And this will simply take an FString and I'll call this textToDisplay. And then we can use that string to set our DisplayText to value.


Let's make a function definition. And here is where I'd like to set the text for [DisplayText]. So I'm first going to make a quick check to see if [DisplayText] is valid. And if it is, I'm going to take [DisplayText] and use the function set text. This function will set the text for this text block.


Now, since we're using the text block data type, we need to INCLUDE the header file for that. And the text block header is in "components/TextBlock". And that's a header file.


So now we can call setText. and this takes an FText. So we need to convert from FString to FText.


Now we can use the FText static function called FromString and simply pass in this textToDisplay. So now we have a handy function that we can pass an FString into that will set our display text to value.

Now I'd like to make another function that will figure out the network role of our character.


So let's go to "overheadWidget.h" and make another public function. And this will be a void function. And I'm simply going to call this showPlayerNetRole. and I'll add an input parameter to this of type APawn called InPawn.


And that way we can call this function from anywhere such as on our character and pass in a pointer to the character and then we can figure out the net role here.


So let's define this function. And here in showPlayerNetRole is where I'd like to get the local role of InPawn, a player's role as an enum value. And that enum is EMetRole.


So we're going to make a local variable called LocalRole and we're going to set it equal to InPawn->getLocalRole. So get local role as a function that we can call from pawn to see what its local role is. And of course this local role is going to be different depending on the machine we're calling it from.


Now, using this information, I'd like to call our setDisplayText function to show that role. So I'm going to make a local FString variable and I'm going to call this role and I'll set the value of role depending on our local role so I can use a switch statement here. So I'm going to switch on local role and I'll have several cases here.


Now, the first case is going to be when a localRole is ENetRole authority. And as soon as I hit the double colons here I see the different enum constants that ENetRole can take on.


We have [authority], [autonomous proxy], there's the role MAX which is something you'll see in most animes that default max. and we have role [none] and [simulated proxy].


So we're going to choose role [authority]. And if our role is [authority], we're going to set the ROLE (string) equal to an FString with the value "authority". I'm going to add my break statement here so we don't cascade into the next case.


Now, we're going to have several cases here. I'm going to paste this and change it from role [authority] to role [autonomous proxy]. And in this case, I'm going to set role to the string [autonomous proxy].


Let's add another case here. And instead of [autonomous proxy], I'm going to use [simulated proxy]. And in this case, I'm going to set the role string to [simulated proxy].


And a pawn shouldn't have its role be [none], but we're going to handle that case anyway. So if the role happens to be [none], we're going to set the string to [none].


So after this switch statement, our FString role will be set according to the value of local role.


Now I'd like to format a string, so I'm going to create a new FString called LocalRoleString and use FString::printf and here for the text for the formatted string, I'm simply going to say [local role %s] and for that string I'm going to pass role.


And of course this is an FString, so we need to use that asterisk overload to get a C style string. And now I have a string called localRoleString and I'm going to set our displayText to have this string value.


So we created a nice function to set this text called setDisplayText, which takes an FString. So we can simply call that we're going to call setDisplayText and pass in localRoleString.


So now if we have a widget of this type and we call showPlayerNetRole, we just need to pass in the pawn that we'd like to display the role for. So that's great. We just need to add this widget to our character.


So let's go back to Blaster character. So here in Blaster character, I'm going to add a couple of variables so that we could create one of these overheadWidgets.


So I'll stick these down here in the private section. and I'd like to use a widget component for this. So I'm going to forward declare a UWidgetComponent called OverheadWidget, and I'll give this a UProperty and I'll make this EditAnywhere so we can set it from within our character blueprint. So now we have this overhead widget, which is a UWidgetComponent.


Let's go ahead and go back to BlasterCharacter.cpp and create this in the constructor. down here at the bottom. I'm going to say overheadWidget and use createDefaultSubobject and this will be a UWidgetComponent and I'll give it a name [overheadWidget]. and I can simply attach this to the root component. So I'm going to say overheadWidget->setupAttachment and simply Attach to rootComponent.

Now we're using UWidgetComponent, so we need to include that header file. So let's go to the top and this is going to be an [components/WidgetComponent]. So we got that header file. All right. So we've added our widgetComponent to the BlasterCharacter.

Now our function show player net role, I'd like to call this from blueprints in the character class. So let's make this a Ufunction and I'm going to add blueprintCallable.


Now before we compile and test this out, I'm going to head back to Blaster character and since we're going to call this. And since we're going to access our widget from blueprints we should at least make it blueprintReadOnly. if we make this private variable blueprintReadOnly, That means, we need "meta = ([allowPrivateAccess] = true)".


And if this is new to you, all this means is that we're exposing this variable to blueprints. We can't use blueprintReadOnly or blueprintReadWrite on a variable that's private in C++ unless we add this meta specify. So now we can use blueprintReadOnly on this variable, even though it's private in C++.


So we can compile this now here in Blaster character, here's my overhead widget. I'm going to select that and change a few properties in the user interface section.


I'm going to change [space] to [screen] and for the [widget class], I'm going to choose [WBP_OverheadWidget], and I'd like to check [draw a desired size]. That way, we don't have to manually set the size for this widget.


So our [overhead widget] is set, and I'd like to call our blueprint callable function here and [beginPlay]. So I'm going to get overheadWidget. and from overheadWidget, I'm going to call getUserWidgetObject. 


That's the actual user widget that our widget component is using, which is this class WBP_overhead_widget. So we can cast to that. I'm going to use cast to and choose WBP_overhead_widget and as this class I'm going to call our blueprint callable function show player net role.


this takes Pawn. and I'm simply going to pass in a [reference to self] for this as our character is indeed a Pwan. so showPlayerNetRole is going to get the netRole, switch on that netRole and set the text for our widget based on that.


So let's compile and hit play and see what happens. we see local role [authority] and that's because we're playing a game with only one player. And in that case we are the [authority], we are on the server. Now I'm going to go into the viewport and take my overheadWidget and move it up just above the head of my character.


Now here's where it gets interesting. We can actually come up here to the [three dots (...)], and let's make sure that our net mode is set to play as listen server.

And that way we're actually playing a game where one of the players is the Listen server. So they're playing a game as the server and the other players are clients and we can change the number of players here. 


Now, when testing multiplayer games, it's a good idea to test with at least three players that way. We see all of the cases involved when it comes to net role. I'll show you what I mean with the number of players set to three, I'm going to hit play and now I have three individual game instances running.


Now we see on the top bar for these two windows [net mode client one] and [net mode client two]. So these two instances of the game are [clients] and the big window here in our editor is the [server].


Now the text is all jumbled up. So I'm going to move ahead a little bit now here on the server.


Take a look at this. We see that our local role is authority for all three of these, which is what we should expect.


All pawns that exist here on the server are going to have local role [authority].


Now I'm going to get my client to here and I'm going to expand the window a bit. And notice here the character I'm controlling has local role [autonomous proxy]. So my character here on my machine has local role [autonomous proxy], and that's because I'm controlling it. So I'm going to run over here so we know which one is me. But on the server I see that it's local role is [authority].


Now I'm going to run over here to the other two characters and notice they are both [simulated proxies]. So I'm looking at the versions of these characters on my machine and those are going to be [simulated proxies] because they're controlled elsewhere.


Now, we can't tell from the local role which one is controlled by the server. We're going to find out later in the course how to determine that.


But for now, it's very clear that all of the pawns that I'm not actually controlling on my machine have local role [simulated proxy]. And I can go to my other client here, client one and I should see the same thing. I should see that the pawn I'm controlling on my machine has local role [autonomous proxy] and all the other pawns have the local role [simulated proxy]. 


So now we have a feel for localRole. 


So let's take a look at remoteRole. Now I'm going to go back to overheadWidget.CPP in our showPlayerNetRole function and instead of local role, I'd like to show remote role.


So I'm going to change the name of this local variable from local role to remote role.


And instead of getLocalRole from the pawn, I'd like to call getRemoteRole. And this function does exactly what it sounds like. It returns EnetRole, only this time it's going to provide us with the remoteRole.


So instead of switching by localRoll, we're now going to switch by remoteRoll and set the string accordingly based on what that remote role is.


Now this FString down here, I'm going to rename to remote role string and in the text, instead of saying local role, I'm going to say remote role. And for our function call to setDisplayText, I'm going to pass in remote role string now.


So let's compile this. And back here in the editor, I'm going to hit play. And now our widgets are showing us the remote role.


Now, check this out right here. I'm on client two and the remote role is [authority]. So we know what the local role is for this character.


I'm controlling its[ autonomous proxy] and for those other two over there, it's [simulated proxy]. But the remote role is going to be the role on the server if we're not on the server. So it's going to be [authority] for all of these. Notice the same goes for the other client here. Remote role is [authority] for all three.


Now let's look at the server. here on the server, Things are a little different for the port. I'm controlling the remote role is [autonomous proxy] and the remote role for these other two characters here is [simulated proxy]. So that's a little nuance to be aware of on the server.


The remote role is going to be [autonomous proxy] for the character controlled on the server and [simulated proxy] for the other two characters.


So we can always use this information to check the remote role and our C++ code to know which machine we're on, whether we're on the server or on a client.



So in this lecture, we discussed the concept of network role and how a pawns local role can be [authority], which means that we're looking at the pawn that exists on the server, or [autonomous proxy]. That's the local role for a pawn that we're currently controlling on a client machine.


We also saw that we have a [simulated proxy], which is going to be the local role for any pawns on our client machine that we're not controlling.


We also saw the concept of remote role and how the remote role for All Pawns is going to be [authority] if we're looking at it from a client machine and how the remote role for the currently controlled pawn on the server is autonomousProxy and it's simulatedProxy for the other pawns on the server that the server is not actively controlling.


So we're going to use this concept of role throughout this course to determine which machine we're on in our code.


Great job.


I'll see you soon.

0 件のコメント:

コメントを投稿