主なコンテンツ

〜主なコンテンツ〜

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年4月10日月曜日

2023年3月21日火曜日

47. Variable Replication

 いらっしゃいませ。


このレクチャーでは、クライアントにウィジェットを表示する方法と、これに変数の複製がどのように関係するかについて説明します。


ここで、変数がいつレプリケートされたかを知りたいと思います。 そのため、変数が複製されたときにクライアントで呼び出される関数を作成する方法を学びます。 このタイプの機能は REP 通知として知られています。


それでは始めましょう。


BlasterCharacter.h に戻り、現在オーバーラップしている武器を格納できる変数が必要です。 それでは、新しいプライベート変数を作成しましょう。 クラス AWeapon を前方宣言します。 この変数はoverlapingWeaponと呼ばれます。


ここで、この変数をレプリケートする必要があります。つまり、サーバーで変更されたときに、すべてのクライアントで変更する必要があります。 そして、武器へのポインターを複製できます。 これを行うには、UProperty を追加する必要があります。 ここで指定したレプリケートされた UProperty を追加します。


これにより、OverlapWeapon がレプリケートされた変数になるように指定されます。 さて、変数を複製するために必要なことはこれだけではありません。 パブリック関数になる関数をオーバーライドする必要があります。 したがって、ここの public セクションでオーバーライドします。


ここで、変数をレプリケートする予定のクラスでオーバーライドする必要がある関数は [virtual void] 関数であり、getLifetimeReplicatedProps と呼ばれます。


現在、この関数は FLifetimeProperties の TArray を取ります。 そして、この理論は参照によって渡され、outLifetimeProps と呼ばれます。


現在、この関数は [const] 関数です。そしてそれを 上書きする予定です。そして、この関数内で、レプリケートする変数を登録します。


それでは、getLifetimeReplicatedProps の関数定義を作成しましょう。 ここで、この関数定義を移動したいと思います。 頂上付近まで。 コンストラクタのすぐ下。 この関数を上書きしているので、super::getLifetimeReplicatedProps を呼び出す必要があります。 そのため、スーパー バージョンを呼び出して、その入力パラメーター outLifetimeProps を渡します。


[overlappingWeapon] 変数をレプリケートするために登録する必要があるのはここです。 これを行うには、doRepLifetime というマクロを使用します。 そして、doRepLifetime で ABlasterCharacter クラスを指定します。 これは、レプリケートされた変数を持つクラスです。 また、複製された変数も指定します。これは、overlapingWeapon になります。

doRepLifetime が定義されていないため、赤い波線が表示されることに注意してください。 そして、そのマクロは、変数をレプリケートするたびに含める必要があるヘッダーで定義されます。 インクルードするヘッダーは [net/unrealNetwork.h] です。 そのヘッダーを含めると、doRepLifetime が定義されます。 この変数は、初期化された時点で開始されます。 設定するまで null になります。


次に、武器クラスとオーバーラップ関数 onSphereOverlap から設定したいと思います。つまり、この変数のパブリック セッターが必要になるということです。 だから、ブラスターのキャラクターの一番下に 1 つ貼り付けます。


インラインにしたいので、forceInLine マクロを使用します。これは [VOID] になります。 これを setOverlappingWeapon と呼びます。


そして、AWeapon ポインターを受け取ります。 単純に【武器】と呼ぼう。 [overlappingWeapon = Weapon] を設定します。 したがって、この変数を設定できます。


そして、overlapingWeapon の値が変更されるとすぐに複製されます。つまり、すべてのクライアント (= blasterCharacters) で変数が設定されます。


現在、レプリケーションは変数が変更された場合にのみ機能します。 したがって、たとえば、すべてのフレームやすべてのネット更新を複製するわけではありません。 重複する武器がサーバー上で実際に変更された場合にのみ、クライアント上で変更されます。 [weapon.cpp] と onSphereOverlap でこれを設定できます。


では、ここで設定して、武器に onSphereOverlap を設定しましょう。 otherActor を BlasterCharacter にキャストしたので、ここでアクセスできる BlasterCharacter を取得します。 そして、ここ onSphereOverlap では、[this] を渡して setOverlappingWeapon を呼び出します。 したがって、武器はこの関数 (= setOverlappingWeapon) を呼び出します。 そして、私たちの blasterCharacter クラスでは [overlappingWeapon] が設定されます。


つまり、ピックアップ ウィジェットがここで有効かどうかを確認する必要がなくなりました。 つまり、BlasterCharacter にoverlapingWeapon を設定するだけです。 BlasterCharacter では、いつそのウィジェットを表示する必要があるかを知る必要があります。 そのウィジェットを表示するための便利な関数をここで武器クラスに作成したいと思います。


そこで [weapon.h] に公開セクションを追加します。 TICKのすぐ下。 こちらは【無効】となります。 これを showPickupWidget と呼びます。 これで、BShowWidget というブール値の入力パラメーターを与えることができます。 このようにして、ピックアップ ウィジェットを表示するか非表示にするかを true または false で渡すことができます。


それでは、showPickupWidget を定義しましょう。 ここにウィジェットを表示できます。 これからは武器を整理整頓しておきたいと思います。 だから私はTicKを動かすつもりです、ここでプレイ開始のすぐ下に。


showPickWidget では、pickupWidget->setVisibility を取得するだけです。 まず、pickupWidget が有効であることを確認します。 そうであれば、pickupWidget を取得し、bShowWidget を渡して setVisibility を呼び出します。 したがって、この関数を呼び出すときに渡したブーレンは、pickupWidget を表示するか非表示にするかを決定します。


問題は、いつこの関数を呼び出すかです。 我々は BlasterCharacter で OverlapWeapon が null であるかどうかを知っているだけであり、その情報を使用してウィジェットを表示または非表示にすることができます。


さて、このオーバーラップする武器変数が複製されたときに呼び出される関数があれば素晴らしいと思います。 しかし、私たちはまだそれを学んでいません。 したがって、あまり最適ではない単純なアプローチを取ることができますが、より良い解決策を学ぶまでは、今のところは機能します。


それでは、ティック関数に行きましょう。 繰り返しになりますが、これらを整理しておきたいと思います。 [BlasterCharacter.cpp]で。 そこで、TICK 関数を beginPlay のすぐ下に移動します。

ここでは、重複する武器が有効かどうかを確認するだけです。 もしそうなら、その重なっている武器にピックアップ ウィジェットを表示できることがわかります。


というわけで【if(OverlappingWeapon)】と言います。 それが有効であれば、overlapingWeapon を取得し、その showPickupWidget 関数を呼び出して True を渡します。


ここでは、overlapingWeapon を使用しています。つまり、そのヘッダー ファイルをインクルードする必要があります。 BlasterCharacter の上部に include を追加します。武器は Weapon クラスに含まれています。つまり、インクルードされた [blaster/Weapon/weapon.h] を追加する必要があります。 [weapon.h] をインクルードしているので、変数の型が定義されています。


ここ TICK では、overlapingWeapon がサーバーで設定されるとすぐに、すべてのクライアントでその pickupWidget を表示し、このブール値をフレームごとに true に設定します。 これもあまり最適ではありませんが、すぐにより良い方法を学びます。

これをコンパイルしてみましょう。 ここでエディターに戻り、これをプレイテストしてみましょう。 そして、これがどのように機能するかを見てください。


最初にクライアントの 1 つを制御しようとしていますが、ここでクライアントに pickupWidget が設定されていることがわかります。 実際、すべてのクライアントに設定されています。 これは、ここの tick 関数で、重複する武器がすべてのクライアントにレプリケートされるためです。 サーバー上で値が変更されるとすぐにこの変数がレプリケートされるように監視(mark)すると、レプリケートはこのように機能します。 その後、すべてのクライアントの BlasterCharacter のバージョンに設定されます。 この変数をここでレプリケートするように登録し、LifetimeReplicatedProps を取得します。

このキャラクターを所有しているクライアントにウィジェットを表示することだけが本当に意味があります。 クライアント マシンのシミュレートされたプロキシで武器をオーバーラップするように設定する必要はありません。 つまり、他の誰かが武器と重なっている場合、その武器の上にマシンのピックアップ ウィジェットを表示する必要はありません。

さて、私達はどのクライアントがこの変数をレプリケートするかを制御できます。これを行うには、この変数を複製するように登録するときに条件を追加して、複製条件を使用します。


別のマクロを使用します。 DOREPLIFETIME_CONDITION を使用します。 クラスと変数を指定するだけでなく、条件も指定します。 現在、条件を指定するために使用できる列挙定数があります。


[自律のみ] という条件があることに注意してください。 これは、これを [自律プロキシ] に複製するだけです。


[所有者のみ] など、他にもいくつかあります。 ここで、所有権の概念について詳しく説明しますが、ポッドを制御しているのは誰でもあると考えてください。 マシンでポーンを制御している場合は、そのポッドの所有者です。 したがって、所有者のみを条件として指定すると、重複する武器は BlasterCharacter を所有するクライアントにのみ複製されます。

それでは、これをコンパイルして、ここでの動作を見てみましょう。 再生ボタンを押して、クライアントの 1 つで武器に近づきます。


ここで違いがあることに注意してください。 サーバー上では確認できますが、クライアントではこの pickupWidget のみが表示され、他のクライアントでは武器と重なって表示されますが、ここでは表示されません。 これは、変数がこのクライアントに複製されなかったためです。 武器の areaSphere とオーバーラップするポーンを所有するクライアントにのみ複製されます。


ここで、武器の onSphereOverlap で、overlapingWeapon を設定していることに注意してください。 そして、これはサーバー上でのみ発生します。 そのため、私達がsetOverlappingWeapon を呼び出すとすぐに、BlasterCharacter のここで、サーバー上に OverlappingWeapon を設定します。

サーバーに設定しているので、所有しているクライアントに複製しています。 getLifetimeReplicatedProps で指定しているように、所有者に複製するためだけです。


サーバーがオーバーラップしているポーンを制御していない限り、サーバー上で pickupWidget を確認する必要はありません。 つまり、そのポーンを所有しています。


したがって、 tick 関数で pickupWidget を表示するこの方法は機能しません。 削除する tick 関数で pickupWidget の可視性を設定する代わりに、変数の値が複製されるたびに呼び出される関数を設定できます。


このタイプの機能は、[rep notify] と呼ばれます。 設定方法は次のとおりです。 こちらと【BlasterCharacter.h】。 [rep notify] となる関数を作成できます。


これらは void 関数であり、規則では OnRep アンダースコアを使用し、その後に [レプリケートされる変数の名前] を続けます。 これは [overlappingWeapon] になります。 [rep notify] に意味のある値を渡すことはできません。これは、変数が複製されるときに自動的に呼び出されるためです。 これらも UFunction である必要があるため、これを使用するには UFunction マクロが必要です。


ここで、この [rep notify] を指定するために、overlapingWeapon がレプリケートされるたびに呼び出されるように、overlapingWeapon UProperty を取得し、[replicatedUsing] の指定を追加してから、[rep notify] を指定します。 つまり、OnRep_overlappingWeapon になります。 この UProperty を使用して、overlappingWeapon がそのクライアントにレプリケートされるときに、クライアントで onRep_OverlappingWeapon が呼び出されるように指定します。


では、この [REP notify] の定義を作成しましょう。 これが [REP 通知] です。 [rep notify] では、複製されたばかりなので、overlapingWeapon が変更されたことがわかります。 ここで pickupWidget を表示できます。


まず、overlapingWeapon が null でないかどうかを確認する必要があります。 null になる可能性があるためです。 OverlapingWeapon が以前は null ではなかったときにサーバーから null に設定されている場合、その変更はクライアントに複製されます。 したがって、OverlapWeapon が有効な場合、OverlapWeapon を取得して showPickupWidget を呼び出し、True を渡すことができます。


それでは、これをコンパイルして、これがどのように機能するかを見てみましょう。 というわけでプレイを叩きます。


そして今、クライアントと重なって、ここで何かに気付く。 武器と重なったクライアントのウィジェットのみが表示されます。 ここのサーバーには表示されません。 さて、これは注目すべき重要な詳細です。 [rep notifications] がサーバー上で呼び出されないため、サーバー上でウィジェットを取得しません。 それらは、変数が複製され、複製がサーバーからクライアントへの一方向でのみ機能する場合にのみ呼び出されます。 したがって、サーバーに複製されることはないため、サーバーは [rep notify] を呼び出すことはありません。


したがって、ここでサーバー上の武器とサーバーが制御するキャラクターとオーバーラップすると、別の問題が発生します。 ウィジェットを取得できません。これは、担当者通知でそのウィジェットのみを表示しているためです。 そのため、サーバー側でケースを処理する必要があります。

では、overlapingWeapon を設定する関数でこれを処理しましょう。 単純な getter ではなく、FORCEINLINE を削除し、この setter にさらにロジックを追加します。 したがって、このようにすべてを 1 行で定義するつもりはありません。


重複する武器と[blasterCharacter.cpp]のセットを定義します。 [rep notify] のすぐ上に設定します。 このセッターで最初に行うことは、setOverlappingWeapon です。 したがって、それを武器と同じに設定します。


次に、実際にポーンを制御しているキャラクターにのみウィジェットを表示するようにする必要があります。 これで、これがサーバー上でのみ呼び出されることがわかりました。 サーバー上でのみ呼び出される武器の onSphereOverlap から呼び出しているためです。


しかしここで、ポーンを制御しているキャラクターに実際に乗っているかどうかを知る必要があります。 isLocallyControlled というブール値を返す非常に便利な関数で確認できます。 isLocallyControlled は、実際に制御されているキャラクターでこの関数が呼び出された場合に true になります。


繰り返しますが、私たちはサーバー上にいることを知っています。 したがって、この if チェックの内部に到達すると、サーバー上でゲームをホストしているプレイヤーによって制御されているキャラクターにいることがわかります。 その場合、ここには所有者のみの条件があるため、overlapingWeapon が複製されないことがわかります。 また、このキャラクターはサーバー上でローカルに制御されるため、どのクライアントもこのキャラクターの所有者にはなりません。 したがって、この場合は、pickupWidget を表示するだけです。


ここでこのロジックを使用して、ここの if ステートメントに貼り付けます。 これで、サーバー制御のキャラクターがウィジェットとオーバーラップすると、サーバー上にウィジェットが表示されることがわかりました。


それでは、これをコンパイルしましょう。 エディターで、これをテストできます。


というわけで、ここに自分の【サーバー操作キャラ】を乗せて重ねていきます。 そして今、pickupWidget があることがわかります。 2 つのクライアントをここに戻すと、クライアントにウィジェットが表示されないことがわかります。


これはまさに私が欲しかったものです。なぜなら、キャラクターをコントロールしているプレイヤーのために、マシン上でその pickupWidget だけを見たいからです。


クライアント上でオーバーラップすると、ピックアップ ウィジェットが表示されます。 クライアントのみがオーバーラップし、サーバーはオーバーラップしない場合をテストできます。 したがって、クライアントとオーバーラップしても、サーバーには表示されません。


だから私たちは近づいています。 オーバーラップを停止しても、ウィジェットはまだ非表示になっていません。 ウィジェットはまだあります。 つまり、[オーバーラップの終了] 関数が必要です。


それでは、それを追加しましょう。 [wepon.h] でそれを行うことができます。 したがって、onSphereOverlap に加えて、[onSphereEndOverlap] が必要です。 [void onSphereEndOverlap] になるようにしましょう。


そして、このタイプのオン コンポーネント [オーバーラップ デリゲートの終了] のコールバックの場合。 [overlapComponent と呼ばれる UPrimitiveComponent]、[otherActor と呼ばれる AActor]、[otherComp と呼ばれる UPrimitiveComponent]、および [int32 otherBodyIndex] が必要です。


そこで、これらの入力パラメータをここに追加します。 また、これには UFunction マクロも必要です。 だから私はそれを追加するつもりです。 そして、この関数を定義できます。


[weapons.cpp] では、sphereOverlap で行ったのと同じことを行います。つまり、otherActor を取得して blasterCharacter にキャストします。 その blasterCharacter が有効な場合は、setOverlapping を呼び出します。 オーバーラップを終了しているので、これに設定する必要はありませんが、null に設定しています。 ここではヌルポインタを使います。


したがって、オーバーラップを終了すると、キャラクターの OverlapingWeapon は null になります。 そのため、そのウィジェットを非表示にする方法を知る必要があります。 null に設定しているためです。 したがって、null ポインターにアクセスして showPickupWidget を呼び出すことはできません。

さて、Blaster キャラクターに戻り、OnRep_overlappingWeapon を見てみましょう。 これがrepNotifyです。


ここで、入力パラメーターを渡すことができないと述べました。これは、onRepOverlappingWeapon をアクティブに呼び出していないためです。 変数がレプリケートされると自動的に呼び出されますね。


では、武器を NULL に設定した後で、重複する武器にアクセスするにはどうすればよいでしょうか? ここでも単純なアプローチは、lastOverlappingWeapon などと呼ばれる武器ポインター型の別の変数を作成することです。 また、重複する武器を変更する前にポインタを保存できます。


しかし、もっと簡単な方法があります。 おわかりのように、rep 通知には入力パラメーターを含めることができますが、レプリケートされる変数の型の入力パラメーターしか含めることができません。 つまり、onRep_overlappingWeapon は AWeapon ポインターのみを持つことができます。 これを lastWeapon と呼びます。


これは、repNotify に入力パラメーターを追加すると、変数のレプリケーションで呼び出されたときに何が渡されるのでしょうか? 複製が行われる前の最後の値になります。


つまり、オーバーラップする武器が NULL から始まるとしましょう。これは事実ですよね? サーバー上で変更されるとすぐに、新しい値がクライアントに複製されます。 ここに武器の入力パラメータがある場合、この関数内の lastWeapon の値は、変数が複製される前のOverlapWeaponの最後の値になります。


つまり、null の場合もあれば、設定前にオーバーラップしていた LastWeapon の場合もあります。 そのため、武器の変更が重複している場合は、変更前の最後の値を確認する必要があります。これには、lastWeapon を使用してアクセスできます。


その値が null でない場合、最後の武器にアクセスしてピックアップ ウィジェットを非表示にすることができます。 では、ここでそれを行います。


[if(lastWeapon.)]... とします。lastWeapon が null でない場合は、lastWeapon を取得して ShowPickupWidget を呼び出し、False を渡します。


このように、オーバーラップを終了すると、overlapingWeapon は null に設定されます。つまり、これはもちろん、チェックが失敗することを意味しますが、overlapingWeapon の最後の値には、オーバーラップを停止した武器へのポインターがまだ含まれています。 その場合、ピックアップ ウィジェットを非表示にすることができます。


これをコンパイルしてテストする前に、このコールバック onSphereEndOverlap を作成しました。 これを end コンポーネントにバインドし、areaSphere のデリゲートをオーバーラップする必要があります。


[weapon.cpp] で。 ここでこれを行うことができ、チェック hasAuthority で beginPlay を実行できるため、これがサーバー上でのみ発生するようにします。


そこで、areaSphere を使用して onComponentEndOverlap と addDynamic を使用し、[this] を AWeapon::OnSphereEndOverlap と共に使用します。


これで、AWeapon とのオーバーラップを停止すると、onSphereEndOverlap が呼び出されることがわかりました。

それでは、これをコンパイルしましょう。 そして、これをテストします。


私のクライアントの1人を連れて行くことで、私は重なります。 その pickupWidget があります。今、オーバーラップを終了し、pickupWidget は消えます。 OverlapWeapon が null に設定されていても、その値 lastWeapon は null ではないためです。 最後の値があるので、それにアクセスしてウィジェットを非表示にします。 したがって、これは両方のクライアントで機能します。 ここに私の他のクライアントがあり、ここで機能していることがわかります。

しかし、サーバーを使用してオーバーラップを終了すると、機能しません。 これは、repNotify でのみこれを設定しており、repNotify がサーバー上で呼び出されないためです。 したがって、私たちがサーバーである場合は、[ケース] を処理する必要があります。


BlasterCharacter でこれを行うことができます。 setOverlappingWeapon のセッターです。 ここで、overlapingWeapon の値を実際に設定しています。 したがって、この値を設定する前に、重複する武器が有効かどうかを確認し、pickupWidget を変更する前に非表示にすることができます。


したがって、重複する武器が有効かどうかを確認するこれらの行を使用して showPickupWidget を呼び出すと、ここで false を渡すことができます。


これをコンパイルして、これをテストしてサーバー上で再生し、pickupWidget と onEndOverlap を取得します。 ウィジェットを非表示にします。 これはまだクライアントで機能します。 そのクライアントがいて、これが私の別のクライアントです。 そのため、pickupWidget を表示して適切なタイミングで非表示にし、正しいプレーヤーに対して表示しています。


さて、ここでローカルに制御されているかどうかを確認せず、OverlappingWeapon を設定しても問題ありません。 考えてみると、これはサーバーでのみ呼び出されるため、そのオーバーラップ関数で呼び出されます。


また、サーバー上で何かとオーバーラップしている場合、ローカルで制御されているオーバーラップしているキャラクターのみがウィジェットを表示する必要があるため、サーバー上で他のキャラクターの pickupWidget を実際に表示する必要はありません。


これで、必要な動作が得られました。


この講義では、変数の複製について学び、overlapingWeapon という AWeapon へのポインタを複製しています。


また、レプリケーションはサーバーからクライアントへの一方向にしか機能しないことがわかりました。


また、[rep notifications] についても学びました。これは、変数が複製されるときに呼び出される関数です。


また、サーバーからのみ変数がサーバーにレプリケートされることはないため、repNotify はクライアントでのみ呼び出されることがわかりました。


そして、条件を使用して、キャラクターの所有者にのみ複製できるようにする方法を学びました。


したがって、変数値をすべてのクライアントにレプリケートするのではなく、現在 Pawn を制御しているクライアントにのみレプリケートします。


私たちの(場合)BlasterCharacter.


よくやった。


次回の講義に続きます。

2023年3月19日日曜日

46. Pickup Widget

 おかえり。


この講義では、武器クラスのピックアップ ウィジェットを作成します。 これにより、武器をピックアップできることがわかり、武器の状態に基づいて、このピックアップ ウィジェットの可視性をいつ設定するかを決定します。


HUD フォルダに移動し、右クリックして [User Interface] に移動し、[Widget Blueprint] を選択します。 これを WBP_pickup_widget と呼びます。


このウィジェットはシンプルです。 武器を拾うことができることを伝えるテキストが表示されるだけです。


[キャンバス パネル] を削除し、[テキスト ブロック] を追加します。この [テキスト ブロック] は、pickupText と呼ぶことができます。 テキストは「E-pickup」に設定します。 これにより、E キーを使用して武器を拾うことができることがわかります。


ここで、サイズを少し大きくして、たとえば約 48 にして、テキスト ウィジェットのサイズを変更します。 そして、書体をボールドからレギュラーに変更し、[位置揃え] を中央に設定したいと思います。


これで、コンパイルして保存できます。 ここで、ウィジェット コンポーネントを武器クラスに追加したいと思います。 [weapon.h] に、ピックアップ ウィジェット用のプライベート変数を追加します。 そこで、UWidgetComponent ポインターを前方宣言し、これを pickupWidget と呼びます。


次に、どこでも編集できるようにし、カテゴリ [武器のプロパティ] を指定します。 そして、これらの他の変数には [武器のプロパティ] がカテゴリとして含まれているため、そのカテゴリを WeaponState にも指定します。

あとは、このコンポーネントと [weapon.cpp] を作成するだけです。


コンストラクターの一番下までスクロールして、pickupWidget とします。 これを createDefaultSubObject で作成します。

さて、これは UWidgetComponent です。 そして、これを pickupWidget と呼びます。 これをルートにアタッチできます。 [pickupWidget->setupAttachment(rootComponent)] とします。


そしてもちろん、ウィジェット コンポーネントのインクルードを追加する必要があります。 この上部に、[components/widgetComponent.h] になるインクルードを追加します。


ウィジェットを作成したので、コンパイルしてエディターに戻り、武器フォルダーに移動して BP_weapon を開きます。 BP_weapon には、pickupWidget があり、[スペース] を [スクリーン] スペースに設定し、[ウィジェット クラス] を選択します。 BP_pickup_widgetになり、[希望のサイズで描画]にチェックを入れます。


ビューポートで、pickupWidget を上に移動し、weaponMesh のすぐ上にあることを確認します。 これでコンパイルできます。


再生ボタンを押すと、pickupWidget が武器の上にないように見えます。 原点はきっとあそこにある。 そのため、rootComponent が設定されていることを確認します。 これにはweaponMeshを選択します。 そこで setRouteComponent として、weaponMesh を使用します。 そのため、pickupWidget はかなり良さそうです。

次に、いつウィジェットを表示し、いつウィジェットを非表示にするかを決定する必要があります。 そしてそのエリアスフィアにキャラクターが重なったらすぐに見せたいです。


また、プレーヤーの役割を表示するために、このオーバーヘッド ウィジェットはもう必要ありません。 後でキャラクターの上にプレイヤー名などを表示するために使用できるので、オーバーヘッド ウィジェットは残しておきます。 そのため、ウィジェットを CHARACTER クラスから削除するのではなく、オーバーヘッド ウィジェット BLUEPRINT に移動して、テキスト ブロックを取得し、そのテキストを空の文字列に設定することができます。


さて、ウィジェットを表示したりウィジェットを非表示にしたりできるように、areaSphere のオーバーラップ イベントを設定する必要があります。 これは、オーバーラップ関数が必要であることを意味します。 これを[保護されたセクション]に追加します。


そうすれば、必要に応じてそれと子クラスをオーバーライドできます。 仮想関数を作成します. これは VOID になります. これを単に OnSphereOverlap と呼びます.


以前にオーバーラップ関数を作成し、コンポーネント beginOverlap のコンポーネントにバインドしたことがある場合、この関数には特定の署名が必要であることがわかります。


最初の入力パラメーターは、overlappedComponent と呼ばれる UPrimitiveComponent である必要があります。


次に、OtherActor という AActor があり、その後に otherComp という別のプリミティブ コンポーネントが続きます。


そして、otherBodyIndex という名前の int32 があります。


次は bFromSweep という bool です。


最後に、const FHitResult があります。これは、sweepResult と呼ばれる const 参照です。


そして、これらの入力パラメーターが必要な情報をどこで見つけることができるかを簡単に説明できます。


これをコンポーネントの開始オーバーラップのデリゲートにバインドします。 したがって、それは UFunction でなければなりません。 したがって、この関数の定義を作成できます。 それを定義する前に、areaSphere のオーバーラップを開始するコンポーネントにバインドしたいと思います。


繰り返しますが、サーバー上でオーバーラップ イベントが発生することだけを望んでいるので、if has authority チェック内でこのオーバーラップ関数を実際にバインドできます。 したがって、areaSphere を取得して OnComponentBeginOverlap にアクセスし、addDynamic を使用できます。 最初の入力はユーザー オブジェクトで、2 番目の入力はコールバック関数です。 そこで、オペレーターのアドレスを使用します。

関数は AWeapon::onSphereOverlap です。


今。 これらの入力パラメーターを見つける方法を簡単に説明すると約束しましたが、これはコンポーネントの beginOverlap を調べることで実行できます。


定義を見てみましょう。これは FComponentBeganOverlapSignature 型のデリゲートであることがわかります。 この定義をピークにすると、[primitiveComponent.h] で、declare_dynamic_multicast_sparse_delegate_six_params を使用してこのデリゲートが作成されていることがわかります。


ここにデリゲート型名が表示され、その後に入力パラメーターのリストが続きます。 そして、このマクロは、これらの入力パラメーターを持つ関数用に設計されたデリゲートを構築します。 このようにして、このタイプのデリゲートにバインドできるようにするには、オーバーラップ関数にこれらの入力パラメーターが必要であることがわかります。


onSphereOverlap 関数をバインドしていますが、[権限] を持つ武器のサーバーでのみ実行しています。 これを行っているので、onSphereOverlap 関数で、ピックアップ ウィジェットの可視性を設定するなどの操作を行うことができます。


ここで、他のアクターが blasterCharacter である場合にのみ可視性を設定したいと考えています。 そこで、BlasterCharacter のヘッダー ファイルをインクルードします。 これが [blaster/character/blasterCharacter.h] になります。


これをインクルードしたので、otherActor を blasterCharacter にキャストできます。 だから私は「ABlasterCharacter、そして単にこれを BlasterCharacter と呼びます。ABlasterCharacter にキャストします」と言うことができます。


そして、私はotherActorをキャストしています。 したがって、BlasterCharacter が有効である場合、キャストは成功し、BlasterCharacter は areaSphere とオーバーラップしています。 その場合は、pickupWidget の可視性を設定できます。 それでは、pickupWidget を取得して setVisibility を呼び出し、true を渡しましょう。


今、pickupWidget にアクセスしています。 したがって、それが null ポインターでないことも確認する必要があります。 そのため、if ステートメントに追加して追加し、これらの両方を同時にチェックしてみましょう。


ここで、この pickupWidget の可視性を最初から false にしたいと思います。 そこで、beginPlay に移動して、pickupWidget が有効かどうかを確認するチェックを追加し、有効な場合は可視性を false に設定します。


それでは、コンパイルしてみましょう。 これをテストするとどうなるか見てみましょう。


まず、サーバー制御のキャラクターで武器に近づきます。ピックアップ ウィジェットが表示されます。


さて、クライアントに表示される内容を見てみましょう。 ピックアップ ウィジェットは表示されません。 そして、これが私の他のクライアントで、ピックアップウィジェットが表示されません。 そして、クライアント上で重なっても、クライアント上ではまだ見えません。


クライアントが武器とオーバーラップした場合、サーバーで何が起こるか見てみましょう。 私はクライアントでオーバーラップするつもりで、サーバーでそれを確認します。 ピックアップ ウィジェットが表示されるようになりました。 これは、サーバー上でオーバーラップ イベントのみを生成しているためです。 また、オーバーラップ イベントが発生した場合は、可視性を設定し、サーバー上でのみ呼び出される sphereOverlap を設定します。


これまでのところ、すべてが期待どおりに機能しています。 ただし、クライアントの可視性も設定する必要があります。


サーバーがウィジェットの可視性を表示することを制御する権限の部分があります。 その後、キャラクターが武器を拾えるようにします。 ただし、ウィジェットが重なっている場合は、クライアントにウィジェットを表示していません。 そのため、それを行う方法を理解する必要があります。 これにはレプリケーションが含まれますが、これについては次に説明します。


このレクチャーでは、ピックアップ ウィジェットを武器クラスに追加し、オーバーラップ関数を作成して、areaSphere のオーバーラップを開始するコンポーネントにバインドしました。 ただし、サーバー上でのみ行いました。


したがって、サーバーは、ピックアップ ウィジェットを表示できるかどうか、および講義が来るかどうかを制御できます。 サーバーからクライアントに変数をレプリケートする作業を行って、ピックアップ ウィジェットの可視性をいつ設定するかをクライアントが認識できるようにします。 これで、ウィジェットのテキストが画面に表示されます。


素晴らしい仕事。


また後で。

45. Weapon Class

 おかえり。


さて、キャラクターは別として、ゲームで最も重要なクラスの 1 つは武器クラスです。 それでは、武器クラスを作成します。 この講義では、すべての武器に必要ないくつかの基本的なコンポーネントと、武器の状態の列挙型を追加します。 だから私たちは武器を扱う方法を知っています。 その状態次第です。


それでは始めましょう。 そのため、武器クラスが必要になります。 右クリックして [新しい C++ クラス] を追加します。 そして、私たちの武器は単に ACTOR にすることができます。 だから私はACTORを選ぶつもりです。 次へをクリックします。


そして、武器クラスと [Weapon] という独自のフォルダーを保持したいと思います。 後で、Weapon からさらに多くのクラスを派生させるときに、そのようにします。 これらすべての武器クラスを武器フォルダに貼り付けることができます。 さて、このクラスは「武器」と呼ばれます。 それでは[クラスを作成]をクリックしましょう。 したがって、このポップアップ メッセージで [いいえ] を押すことができます。


ここ Visual Studio には、武器クラスがあります。 今のところ、残りのタブを開く必要はありません。


[ Weapon.h] を右クリックして [これ以外をすべて閉じる] を選択します。 [Ctrl + K または O] を使用して、武器のヘッダー ファイルから [weapon.cpp] を開くことができます。


武器は武器フォルダにあるので、これまで見てきたように、ここではこの武器パーツは必要ありません。 そのため、[weapon.h] だけを含めるようにインクルードを変更すると、すべてのコンパイラ エラーが修正されます。

それでは、[weapon.h] ファイルに向かいましょう。ここには、コンストラクター beginPlay と tick 関数があります。 CHARACTER クラスで行ったのと同じように、これらのコメントは今は必要ないので削除します。


そして、すべての武器に必要な基本コンポーネントを追加したいと思います。 そのため、これらのコンポーネント変数用に [プライベート セクション] をここに作成できます。


先に進み、TICK をここの [public section] の Weapon コンストラクターのすぐ下に移動します。


[プライベート セクション] では、武器とすべてのアセットのメッシュが必要です。 ASSETS フォルダに移動して、[militaryWeaponsSilver] と [weapons] を確認します。 これらはすべて SKELETAL メッシュであることがわかります。 したがって、私の武器クラスには確実に SKELETAL メッシュ コンポーネントが必要です。


それでは、そのうちの 1 つを追加しましょう。 これは USkeletalMeshComponent になり、これを WeaponMesh と呼ぶことができます。


さて、weaponMesh はもちろん UProperty を取得しており、[visibleAnywhere] で作成します。 そして、ブループリントからコンポーネントにスケルタル メッシュを設定できるようになります。 では、これにカテゴリを付けて、そのカテゴリを [武器のプロパティ] と呼びます。


ここで、SKELETAL メッシュに加えて、キャラクターが近くにいるときにこの武器を装備できるように、ある種のオーバーラップ ボリュームが必要になります。


そこで、USphereComponent を前方宣言し、areaSphere という sphereComponent を使用します。 また、エリアスフィアは武器メッシュと同じプロパティを取得できます。


この武器を開発するにつれて、より多くのコンポーネントができあがります。 でも今のところ、これでよかったと思っています。 これらと [weapon.cpp] を作成してみましょう。


さて、これらのコンポーネントを作成する前に最初に行うことは、bCanEverTick を false に設定することです。 後で武器を作動させる必要があると判断した場合は、いつでもこれを変更できます。 しかし、今のところ false に設定します。


また、これらのコメントは実際には何の役にも立たないため、これらのコメントも削除します。


WeaponMesh を作成できます。 WeaponMesh = createDefaultSubObject として、USkeletalMeshComponent を使用します。 そして、これをweaponMeshと呼ぶことにします。


[weaponMesh->setupAttachment(rootComponent)] とします。

後で、この武器をドロップできるようにする予定です。ドロップするたびに、武器が壁の地面で跳ね返るようにしたいと思います。 そこで、ブロックするすべてのチャネルに衝突応答を設定します。 そのためには、weaponMesh->SetCollisionResponseToAllChannels と言うことができます。 衝突応答は ECollisionResponse::BLOCK になります。 したがって、完全にブロックするように設定されています。


ここで、ドロップしたら、ポーンを無視して、ポーンがそれをまたいで、衝突せずに通り抜けることができるようにします。 だから、weaponMesh->SetCollisionResponseToChannel と言って、その応答を ECollisionChannel に設定し、ポーンを選択します。 そして、その RESPONDS を ECollisionResponse::ignore に設定します。


もちろん、武器を落とすときはこれですべてですが、武器はその状態では開始されません。 そのまま歩いて持ち上げられる状態からスタートです。 そのため、最初から、weaponMesh の衝突を無効にしたいと考えています。 そのため、weaponMesh を取得し、setCollisionEnabled を ECollisionEnabled:: noCollision に呼び出すことでそれを行うことができます。


したがって、これらの衝突設定をそのまま維持できます。 そして、武器を実際に物と衝突させたい場合は、武器を落とすか投げるとします。次に、衝突を有効にして、物理のみを言うことができます。 またはAquarian Physicsまたはそのようなもの。

これで、areaSphere もできました。


エンジンのバージョンごとに変更される可能性のあるスケルタル メッシュ コンポーネント タイプを使用しても、エラーは発生しません。


そのため、インクルード エラーが発生した場合は、必ずそのヘッダーをインクルードしてください。


アクター クラスでこれを使用しなくても問題ありませんが、使用する sphereComponents を使用するには、sphereComponent ヘッダー ファイルをインクルードする必要があります。 それが [components/sphereComponent.h] にあることがわかっているので、それをインクルードして、areaSphere を作成します。 [areaSphere = createDefaultSubObject(USphere コンポーネント)] とします。 そして、これは単純にareaSphereという名前になります。


また、ルートにもareaSphereをアタッチしたいと思います。 [areaSphere->SetupAttachment(RootComponent)] ということになります。


さて、このエリアスフィアについて話しましょう。 この SPHERE は、CHARACTERS との重複を検出するために使用するものになります。 そしてそれらが重なり合ったら、武器を拾えるようにしたいと考えています。 ピックアップ ウィジェットのようなものを表示して、アイテムの状態などを設定できます。


しかし、[マルチプレイヤー ゲーム] と [マルチプレイヤー ゲーム内のシングル プレイヤー ゲーム] では、このタイプの違いがあります。 このような重要なことは、サーバー上でのみ実行することをお勧めします。


したがって、サーバー上にいる場合にのみ、areaSphere にオーバーラップ イベントを検出させたいと考えています。 したがって、areaSphere を取得して setCollisionResponseToAllChannels を呼び出したいと思います。 そして、ECollisionResponse::ignore を使用します。 そして、それはareaSphereが存在すらしないようなものです。


ここで、areaSphere とコンストラクターの衝突を無効にしてから、beginPlaying を実行したいと思います。 有効にしますが、サーバー上でのみ有効にします。


ここで、areaSphere->SetCollisionEnabled と言って ECollisionEnabled::NoCollision を渡します。 したがって、すべてのマシンで、これは衝突なしに設定されます。 しかし、サーバー マシンでは実際にコリジョンを有効にするように設定します。


先に進む前に、実際にはルートがないことに気付きました。 ルートをweaponMeshに設定します。 それでは、setRouteComponent を呼び出しましょう。 そしてweaponMeshを渡します。


サーバー上にいる場合は、衝突を有効にしたいと思います。 しかし、ここでコンストラクターでそれを行うのではなく、それを実行して beginPlay を開始します。 したがって、ここで権限があるかどうかを確認し、衝突を有効にすることができます。 後でオーバーラップ関数を追加するときに、それらをここにバインドして、サーバー上にある場合は beginPlay を実行できます。

ここで if チェックを入れます。 ここでできることは、getLocalRole を呼び出して、それが ENetRole::authority と等しいかどうかを確認することです。


その場合は、areaSphere で有効になっている衝突を呼び出すことができ、NoCollision の代わりに queryAndPhysics のようなものを使用できます。 また、オーバーラップさせたいチャネルに衝突応答を設定することもできます。これは、カプセルの衝突オブジェクト タイプであるため、ポーン衝突タイプになります。


そこで、areaSphere->setCollisionResponseToChannel と言います。 チャネルは ECollisionChannel になり、カプセルのコリジョン タイプであるポーン コリジョン タイプを選択します。 衝突応答は ECollisionResponse::Overlap になります。


だから私たちはこれを行うことができました。 しかし、私たちの役割が権威であるかどうかを確認する簡単な方法があります。 そして、それは hasAuthority 関数です。 これは同じことです。 ローカルの役割をチェックします。 そして、それが role [authority] と等しい場合、hasAuthority は True を返します。 これは、武器がレプリケート アクターである場合に当てはまります。 これは、サーバーがすべての武器オブジェクトを担当することを意味します。 したがって、私たちの武器はサーバー上でのみ権限を持ちます。 レプリケートするように設定する必要があり、コンストラクターでそれを行うことができます。 アクターには bReplicates ブール値があり、それを true に設定できます。

さて、このビデオを締めくくる前に最後に追加したいのは、武器の状態の列挙型です。 これを [weapon.h] のクラス宣言の直前に追加できます。


ここで、この列挙型の武器の状態を呼び出したいと思います。これはスコープ付きの列挙型になります。 つまり、EWeaponState という enum クラスを作成し、ここで uint8 を使用します。


したがって、enum 定数は符号なし 8 ビット整数であり、これもブループリント タイプになります。 そこで、blueprintType で UENUM を指定します。 そうすれば、この列挙型をブループリントの型として使用できます。


EWeaponsStates では、定数を列挙します。 EWeaponsState の前に EWS を付けます。


そして、初期状態が欲しいです。 これは、武器がワールドに置かれているときの状態です。 まだ拾ったことがなく、私たちのキャラクターは歩いて拾い上げて装備することができます.


EWS_initial 状態があり、これに UMETA マクロを指定して表示名を設定できます。これは表示名とブループリント用になります。 そして、これを【初期状態】と呼ぶことにします。


さて、イニシャルに加えて、実際に武器を装備したときの状態が欲しいです。 そこで、[装備] という別の列挙型定数を追加します。 その表示名は単に[装備]することができます。


そして、武器がドロップされたときの別の列挙型定数が必要です。 だから私はこれを[落とされた]と呼ぼう。 ここで、物理演算をオンにして、衝突をオンにして、武器を地面で跳ね返らせます。 だから私はこれに表示名を付けるつもりです[ドロップ]。


もちろん、ほとんどの enum 定数にはデフォルトの最大定数があり、これは [EWS_MAX] と呼ばれます。 そして、表示名 [default Max] を付けます。 そうすれば、この列挙型に実際に含まれる定数の数を知る必要がある場合は、EWS_Max の数値を確認できます。


よし、武器はこの武器状態列挙型を定義する。 このタイプの変数を武器クラスに追加してみましょう。 そして、これはプライベートでも構いません。 ここに貼り付けます。


EWeaponState、これをweaponStateと呼びます。 これを UProperty にします。 そして、BLUEPRINTS から見えるように、これを visibleAnywhere にします。


しかし、私はこの変数をゲーム ロジックから設定したいと考えており、それはほとんど C++ で行われます。 後でそれをブループリントに公開することに決めた場合は、公開できます。 しかし、実際には、そうする必要はありません。


これで、コードをコンパイルできます。


エディタに戻って、新しい武器クラスに基づいてブループリントを作成できます。 そこで、私の blueprints フォルダーに移動して新しいフォルダーを作成し、これを [weapon] と呼びます。 そして、weapon フォルダで右クリックし、[Blueprint クラス] を作成します。 ここですべてのクラスが展開されているので、weapon を検索して [Weapon C++ クラス] を選択し、それを選択します。 ここで、これを BP_weapon と呼び、これをダブルクリックします。 そして、これが私たちの武器クラスです。


これで、武器メッシュと areaSphere が表示されました。 これで、SKELETAL メッシュの横にあるドロップダウンをクリックして、すぐに武器メッシュを設定できるようになりました。 そして、「アサルトライフル」を選択するだけです。 今のところそれを使用します。


私のアサルトライフルがありますが、少しずれていることがわかります。 そこで、areaSphere を武器の中心まで少し移動します。 そして、球の半径を少しだけ大きくしてみようと思います。 そうすれば、かなり近くにいて BP_weapons self を選択しているときに、このことを拾うことができます。

[武器の状態] があり、[初期状態] である最初の列挙型定数に自動的に設定されます。


これで武器ができました。 それをコンパイルして保存します。 そして、ここで 1 つをレベルにドラッグしてみませんか。 そして今、私たちは武器を持っています。 現在、これに対する機能をプログラムしていないため、これと重複しても実際には何もしません。 しかし、それがゲームのクライアント インスタンスに存在し、サーバー上に武器が存在することはまだわかります。


この講義では、武器クラスを作成し、それにいくつかのコンポーネントを追加しました。また、現在の状態に応じて、コードの観点から武器をどのように扱うかを決定するために使用できる [weapons state] 列挙型も追加しました。


ここで、ポップアップするピックアップ ウィジェットを表示して、武器と重なるとすぐに武器を拾うことができることを知らせたいと思います。 これについては、今後の講義で扱います。


素晴らしい仕事。


また後で。

44. Optional Challenge: Show the Player Name

 Player State で GetPlayerName() という関数を使用します。 OverheadWidget を使用して、プレーヤーの名前を頭上に表示できるかどうかを確認してください。

すべてのクライアントとサーバーで確認できますか? なぜですか、そうでないのですか?

Druid Mechanics Discord の ⚙|optional-challenges チャンネルで作品を共有してください。

2023年3月18日土曜日

42. Seamless Travel and Lobby

 アンリアル エンジンへようこそ。


マルチプレイヤー ゲームでは、何らかの方法で移動する必要があります。 このレクチャーでは、シームレスな移動がどのように機能するか、シームレスでない移動よりも優先される理由、およびそれが何を意味するかについて説明します。


遷移レベルを作成して、あるレベルから別のレベルに移動するたびにシームレスな移動を設定できるようにします。 十分な数のプレイヤーが集まったら、ロビーから実際のゲームに移動できるように、ロビー ゲーム モードを作成します。


それでは、まず非シームレス TRAVEL について話しましょう。 UNREAL ENGINE には、主に [シームレス] と [非シームレス] の 2 つの移動方法があります。


クライアントがシームレスに移動しない場合は常に、現在のサーバーから切断され、同じサーバーに再接続されます。 もちろん、これには切断と再接続に時間がかかり、異なるレベルに移動するたびに再接続するときに問題が発生する可能性があります. シームレスな移動を使用することをお勧めします。


初めてマップをロードするときに、シームレスでない移動が発生します。 サーバーに初めて接続するとき、およびマルチプレイヤー マッチまたはゲームが終了して新しいものが開始されるときも同様です。 これにはシームレスではない移動が含まれ、すべてのクライアントは切断してからサーバーに再接続して再度プレイする必要があります。


現在、シームレスな移動は、Unreal Engine での移動に好まれる方法です。 クライアントがサーバーから切断する必要がないため、よりスムーズなエクスペリエンスが得られます。


また、サーバーまたはサーバーが見つからない、突然プレイヤーが多すぎてプレイヤーが再参加できないなどの再接続の問題を回避するのにも役立ちます。


プロジェクトのシームレスな移動を可能にすることができます。 これはゲームモードで行います。 ゲーム モードには bUesSeamlessTravel というブール変数があり、これを true に設定してシームレスな移動を使用できます。

遷移マップまたは遷移レベルが必要です。 これは、あるマップから別のマップに移行するために使用できる単純な小さなレベルです。


遷移マップが必要な理由は、マップが特定の時点で常に読み込まれる必要があるためです。 したがって、遷移マップなしであるマップから別のマップに移動するには、最初のマップがまだロードされている間に 2 番目のマップをロードする必要があります。


そして、これには膨大な量のリソースが必要になる可能性があります。 そのため、遷移マップを使用して、既に存在していた元のマップを分解する前にそれをロードします。 移行マップに入ると、新しいマップをロードできます。両方の大きなマップを同時に存在させる必要はありません。


さて、[TRAVEL] とマルチプレイヤーについて説明してきました。 [TRAVEL]には複数の方法があります。 それらの方法の 1 つは、serverTravel を呼び出すことです。 これは、あなたの WORLD に属する機能です。 WorldServerTravel はサーバー専用です。 サーバー マシンは serverTravel を呼び出し、サーバーが serverTravel を呼び出すと、サーバーは新しいレベルにジャンプします。


接続されているすべてのクライアントは、サーバーが移動しているマップに従います。 サーバーは、接続されているすべてのプレーヤー コントローラーを取得し、プレーヤー コントローラー クラスに存在する関数 clientTravel を呼び出すことによってこれを行います。 clientTravel は、呼び出し元によって異なることを行います。


[クライアントから呼び出される] と、クライアントは [新しいサーバー] に移動します。 クライアントは、移動先の ADDRESS を提供する必要があります。 ただし、[サーバーから呼び出された]場合、これにより、プレーヤーはサーバーによって指定された[新しいマップ]に移動します。


次に、ロビー レベル用に作成するゲーム モードで serverTravel を使用します。 このようにして、十分な数のプレイヤーが参加すると、serverTravel を呼び出して、接続されているすべてのクライアントをサーバーに従ってゲーム マップに移動させることができます。


すでに 2 つのマップがあります。 gameStartupMap があります。再生を押すと、メニュー システム プラグインにあるメニュー ウィジェットが表示されます。


これまでのところ、プレイヤーはサーバーです。 マルチプレイヤーは存在せず、シングル プレイヤー ゲームでは唯一無二のプレイヤーがサーバーです。


ホストをクリックして Steam セッションを作成すると、最終的にプレイヤーはロビー レベルに移動し、他のプレイヤーが参加するのを待ちます。


これが私たちのロビーレベルです。 menuSystemPlugin は serverTravel を呼び出してこれを行います。 これでロビー レベルができたので、実際のゲーム レベルである新しいレベルに移動したいと思います。


そのために十分な数のプレイヤーが参加したら、参加者数をカウントできるロビー ゲーム モードを作成します。


それでは、新しいゲーム モード クラスを作成しましょう。 [C++ クラス] と [Blaster] に行きます。 ここで新しい C++ クラスを作成します。 それでは、右クリックして新しい C++ クラスを作成しましょう。[すべてのクラス] を選択し、ゲーム モードを検索します。


Unreal Engine には [game mode] と [game mode base] があります。 このプロジェクトでは [ゲーム モード] を使用します。 [ゲームモードベース] にはない追加機能があるためです。 そして、マルチプレイヤー ゲームを実装するときに、その追加機能についてさらに学習します。


[ゲームモード] を選択して、[次へ] を選択します。


ここで、このコース全体で複数のゲーム モードを作成しますが、それらはすべて独自のフォルダーに保存したいと考えています。


それでは、Blaster に Game Mode という名前の新しいフォルダを作成しましょう。 このゲームモードはロビー用です。


それでは、このロビー ゲーム モードを呼び出して、[クラスの作成] をクリックします。

クラスと独自のフォルダーを作成するときに見た、おなじみのメッセージが表示されます。 いいえをクリックするだけです。


これがロビー ゲーム モードです。 ゲーム モード フォルダにあることがわかります。


そして、robyGameModes.cpp のインクルードから「gameMode」を削除することで、コンパイラ エラーを取り除くことができることがわかりました。


ここで、lobbyGameMode は 1 つのことを行う必要があります。 ロビー レベルに接続または移動したプレイヤーの数を確認する必要があります。 一定の数が入力されると、これから作成する実際のゲーム マップに移動できます。


gameMode を継承した関数 postLogin を使用して、参加するプレイヤーを追跡できます。 この関数をオーバーライドします。


[lobbyGameMode.h] に public セクションを作成します。 そして、仮想関数 postLogin をオーバーライドします。 ログイン後はバーチャルボイドになります。


この関数は、NewPlayer というポインターである APlayerController を受け取ります。これはもちろんオーバーライドです。


それでは、PostLogin の関数定義を作成しましょう。 これが postLogin 関数です。


これまで見てきたように、これらの関数をオーバーライドするときは [super] を呼び出す必要があります。 [super::postLogin] を呼び出して、NewPlayer を渡します。

postLogin は、ゲームに参加したばかりの playerController に安全にアクセスできる最初の場所です。


現在、Game State と呼ばれるクラスが存在します。 実際、ゲーム モードには Game State という変数があります。 gameState 変数は gameStateBase を保持し、この Game State にはゲームに参加したプレイヤーの配列があります。 具体的には、playerStates の配列です。


ここで、このコースでの [gameStates] と [playerStates] の役割について説明します。 しかし今のところ、知っておく必要があるのは、ゲームに参加した playerState の数だけです。 これで、ロビーにいるプレイヤーの数がわかります。


ここで Game State にカーソルを合わせると、それが TObjectPointer であることがわかります。 実際、右クリックして gameState 変数の定義を選択すると、gameState が AGameStateBase をラップする TObjectPointer であることがわかります。


TObjectPointer は Unreal Engine 5 で導入された新しいタイプのラッパーで、これらのエンジン クラスの変数の一部に使用されます。 これで、開発中のエディターとエディターにいくつかの機能が提供されます。 Unreal Engine は、「これによりアクセス トラッキングが提供されるため、オブジェクトがいつ使用されているかを実際に検出できる」と述べています。


しかし、この目的のために知っておく必要があるのは、gameState がこの型 TObjectPointer を持ち、get 関数を使用して TObjectPointer によって格納された gameStateBase ポインタにアクセスできることだけです。


したがって、gameState 変数を取得して [.Get] を呼び出すと、AGameStateBase ポインターが返されます。


ここで、gameStateBased クラスを使用します。つまり、そのヘッダー ファイルをインクルードする必要があります。 また、gameStateBase は gameFramework に存在します。 それは gameStateBase.h と呼ばれます。


gameState にアクセスしたら、そのプレーヤー配列を取得できます。 これは単に playerArray と呼ばれる TArray です。 カーソルを合わせると、playerStates を保持するこれらの TObjectPointers の TArray であることがわかります。


この配列には、ゲームに参加した各プレイヤーの playerState が含まれています。 TArray であるため、配列内の要素数を int32 として返す TArray.Num 関数があります。 これは、ゲームに参加しているプレイヤーの数を示しています。

これで、numberOfPlayers という名前の int32 を作成し、この配列の長さと等しくなるように設定できます。


これで、誰かがゲームに参加するたびに、ポスト ログインが呼び出され、この整数のプレイヤー数が参加したプレイヤーの数を示すゲーム モードが発生することがわかります。


これで、この数をチェックして、ゲームに参加しているプレイヤーの数を確認できます。 プレイヤーの数が一定数に達すると、新しいレベルに移動することを決定できます。 それが私たちがやりたいことです。


そのため、プレーヤーの数が特定の数に等しいかどうかを確認します。 今のところ、[2] はハード コーディングされた [2] を使用します。 しかし後で、これに変数を使用します。 しかし、今のところ、これがどのように機能するかを確認したいと思います。


serverTravel を呼び出して、接続しているすべてのクライアントを特定のマップに移動できることがわかっています。 MAP に移動するには、移動先の MAP が必要です。


それでは、エディターに戻って、maps フォルダーに戻りましょう。 そして、移動先の新しいマップを作成したいと考えています。 それでは、新しいレベルを作成しましょう。


アセットには learningKitGames があり、maps フォルダーにあることがわかっています。 これが LearningKit_games_showcase です。 さて、私がこのレベルに進む理由は、多くのアセットで満たされているためです。これらのアセットを使用して、実際のゲームの見栄えの良いマップを作成できます。


ですから、このレベルを複製します。 このレベルをマップ フォルダにドラッグし、[ここにコピー] を選択します。 Maps フォルダには、LearningKit_Games_Showcase があります。


これで改名します。 これが、ゲームを開始するときに移動するマップであることがわかっています。 これを [blasterMap] と呼ぶことにします。 これで、移動先のマップができました。


ここで、robyGameMode に戻ります。プレイヤーの数が 2 人に達したら、すぐにそのマップに移動したいと思います。 これは、新しいワールド クラスの serverTravel 関数を使用して実行できます。


それでは、UWorld と言うワールドを取得しましょう。 それはポインタです。 このワールドを呼び出し、get world 関数を使用します。 そして、ワールドが有効であれば、単純にワールドを取得して serverTravel を呼び出すことができます。


ここで、serverTravel は移動先の ADDRESS を取ります。 これをサーバーから呼び出す場合、後で説明するように、ゲーム モードはサーバー上にのみ存在します。 したがって、私たちは間違いなくサーバー上にいます。


ゲーム モード クラスの場合、移動したいレベルへのパスを渡すだけで、接続されているすべてのクライアントがそのレベルに移動します。 これは FString なので、FString を作成し、その文字列を BlasterMap へのパスに渡します。


さて、このエディターで、そのパスが何であるかを知ることができます。 本当に、これは [maps] フォルダへのパスです。 [コンテンツ] の代わりに [ゲーム] という単語を使用します。 したがって、ここの FString では、「/game/maps/(レベル名)」と入力します。 これは blasterMap になりますが、このレベルに移動してリッスン サーバーとして指定する必要があります。 したがって、疑問符 (?) を使用して、アドレスにオプションを追加します。 そして、必要なオプションは「LISTEN」です。 これにより、この blasterMap がクライアントの接続先のリッスン サーバーとして開かれることが指定されます。

というわけで、プレイヤー数が 2 人に達したらすぐにブラスター マップに移動します。


ここで、シームレスに移動したいと考えています。bUseSeamlessTravel というゲーム モード クラスに存在するブール変数を設定することで、シームレスに移動できます。これを true に設定できます。


それでは、ここでコードをコンパイルしてみましょう。 これで、エディターに戻ることができます。 エディターで、これを設定するためにいくつかのことを行う必要があります。


まず、ロビー レベルに戻りましょう。生の C++ クラスを使用するのではなく、ロビー レベルのゲーム モードを設定する必要があります。 このためのブループリントを作成したいと思います。


それでは、blueprints に移動して新しいフォルダーを作成し、これを [gameModes] と呼びましょう。 [gameModes] では、新しい設計図を作成します。


右クリックして新しいブループリント クラスを作成し、[すべてのクラス] を選択して、robbyGameMode を選択します。 そして、[選択] をクリックします。 これを BP_lobbyGameMode と呼びます。


そして、lobbyGameMode で、それを開いてみましょう。 [デフォルトのポーン クラス] を選択できます。 BP_BlasterCharacter を選択します。 これで、BlasterCharacter としてロビーを走り回ることができます。


それをコンパイルして保存しましょう。 ロビー ゲーム モードを閉じます。 それをロビーのゲームモードとして設定します。


それでは【ワールド設定】に行きましょう。 [game mode override] ではドロップダウンを選択し、BP_lobbyGameMode を選択します。 ここで、[デフォルトのポーン クラス] が BP_BlasterCharacter であることがわかります。


ゲームモードをオーバーライドしたので、ワールドに実際のキャラクターは必要ありません。 [プレイヤースタート]があるので。 そして、このレベルに移動するプレイヤーは、プレイヤーの開始時にここにスポーンされます。 実際、私はプレーを打つことができます。 そして、私はここの真ん中にいます。


ロビーゲームモードには「シームレストラベルを使用」があります。 ここで、ログイン後にこれを true に設定しましたが、必要に応じてここで true に設定することもできます。 これは、すでに true に設定されていることを意味します。


これで、シームレスな移動の準備がほぼ整いました。 しかし、transitionLevel について話したことを思い出してください。 そのうちの 1 つが必要になります。


マップに戻り、遷移レベルを作成します。 ファイルの新しいレベルに行きましょう。 そして、私たちの遷移レベルは実際には何も必要としません。 実際、小さくてシンプルであればあるほど良いのです。 空のレベルを選択するだけです。 ここで [選択項目を保存] をクリックすると、まったく何もない完全に空のレベルが表示されます。 これは移行レベルに最適です。 ファイルをクリックして現在のレベルを名前を付けて保存し、マップに保存します。 これを [transition map] と呼び、[保存] をクリックします。 これで遷移レベルができました。

それを transitionLevel として使用したいことを Unreal Engine が認識していることを確認する必要があるだけです。 [編集] と [プロジェクト設定] に移動して、[プロジェクト設定] でこれを確認できます。


[遷移] を検索します。 [default maps] の下に表示されます。 [トランジションマップ]。 ドロップダウンを開いて、transitionMap を選択できます。


これで、Unreal Engine はトランジション マップをトランジション レベルとして使用することを認識します。 これで、シームレスな移動を使用するための設定がすべて完了しました。2 人のプレイヤーがロビーに参加するとすぐに、サーバーはポストのロビー ゲーム モードからサーバー移動を呼び出し、ログインすると、全員が blasterMap に移動します。


これを試してみたいと思いますが、その前に、あなたに挑戦があります。 現在、いくつかのマップがありますが、どれも退屈です。 また、Learning Games アセット パックには多くのアセットがあることもわかっています。 挑戦として、私たちのレベルをもう少し面白くしてほしいです。


まず、ゲームの startStartupMap にいくつかのアセットを追加します。 これは、プレイヤーがゲームを開いたときに表示されるマップです。 いくつかのアセットを使用して、見栄えを良くしてください。


ホスト ボタンと参加ボタンのあるメニュー ウィジェットのみが表示されることはわかっています。 そのため、少し創造性を働かせて、ゲームで表示される内容のプレビューをプレーヤーに少しだけ示します。


次に、ロビー マップにいくつかのアセットを追加していただきたいと思います。 さて、ロビーは通常、人が走り回れる小さなエリアです。それほど大きくはありません。 本当にあまりありません。 走ってジャンプして、他のプレイヤーが参加するのを待つだけのエリアです。


最後に、これはおそらく最も野心的な BlasterMap を作成してもらいたいものです。 これは、プレイヤーが実際にゲームをプレイするマップになります。これは、プレイヤーが互いに撃ち合うマルチプレイヤー マッチメイキング タイプのゲームになります。 いくつかの障害物をプレイして、ピックアップや武器、その他のゲームプレイの仕組みをどこに配置するかを考えてください。 高さの変化を試してみてください。 いくつかの隠れ場所を置きます。


全力で行く。 これには、好きなだけ努力してください。 それでは、ビデオを一時停止して、これらのマップを構築して見栄えを良くしてください。 そして、ゲームをパッケージ化してテストします。

わかった。 これらのレベルの作成を楽しんでいただければ幸いです。


ここにロビー レベルがあり、learningKitGames のアセットをいくつか使用しています。 これらの浮島を拡大縮小してここに配置し、Learning Kid Games マップにある花粉などのさまざまな効果を追加しました。 そして、浮島のいくつかを引きずり出し、そこに見えるようにしました。


ゲームのスタートアップ マップでは、キャラクターをここに配置しました。 これはキャラクターの BLUEPRINT ではなく、単なる SKELETAL メッシュです。 そして、詳細パネルを見ると、ここでは別のアニメーション、epic_character_Idle を使用していることがわかります。


したがって、プレイをクリックすると、これがプレイヤーがゲームを最初にロードしたときに表示されるものです。 メニューはまだありますが、キャラクターの素敵な背景ができたので、見栄えがよく、まとめるのにそれほど難しくはありませんでした.


ブラスター マップにも力を入れています。ブラスター マップには、learningKitGames レベルのすべてのアセットが含まれています。 私は単純にすべてのメッシュを取得し、それらを拡大してさまざまな方法で配置し、素敵なシューティング マップを作成しました。


うまくいけば、これがマルチプレイヤー メカニクスを実装するための素晴らしい環境を提供してくれることを願っています。このマップでは多くのプレイヤーが走り回り、互いに撃ち合うことができます。 素晴らしい。


これでテストできます。 これで、パッケージ ビルドでテストする準備が整いました。


さて、この blasterMap にはゲーム モードがないため、BlasterMap の gameModeBlueprint を作成します。 このマップのゲーム モード C++ クラスを作成します。 しかし、私たちはそれに到達します。 今のところ、必要なのは [ゲーム モード] だけなので、[デフォルトのポーン クラス] があります。


[BLUEPRINTS] と [gameModes] に行きましょう。 [Create a new blueprint class] を右クリックして、gameMode を検索します。 そして【ゲームモード】を選んでこちらを選択。 これを BP_blasterGameMode と呼びます。


BlasterGameMode で行う必要があるのは、[デフォルトのポーン クラス] を設定することだけです。 BP_BlasterCharacter を選択します。 playerStart がある場合は、ここにドラッグできます。このレベルのゲーム モードを設定するとすぐに、ここにスポーンします。


[ワールド設定] で、[ゲーム モード オーバーライド] に BP_BlasterGameMode を選択します。 そして、今プレイすると、このレベルで走り回るキャラクターができます。


それでは、このゲームをパッケージ化してテストしてみましょう。 これが私の Unreal Engine Project フォルダです。私が作成したビルド フォルダがあります。 先に進み、ここに存在する古いビルドをすべて削除しました。ここに新しいビルドを配置します。


[プラットフォーム]、[Windows]、[パッケージ プロジェクト] に移動します。 ビルドフォルダーを選択します。 それでは、これをパッケージ化するのに少し時間を割いてみましょう。


さて、パッケージングが完了し、ここに Windows フォルダーがあり、その中に [blaster.exe] があります。 これで、これを zip フォルダーに送信できます。 圧縮が完了したら、Google ドライブにアップロードできます。 さて、これが私がパッケージ化した最後のビルドです。 右クリックしてそれを削除し、圧縮した新しいパッケージ プロジェクトを Google ドライブにドラッグするだけです。


それが終わったらすぐに、別のマシンにダウンロードして、別のマシンで別の Steam アカウントにログインして、このゲームをテストできるようにします。 わかった。 プロジェクトのアップロードが完了し、別のマシンに移動してこの zip フォルダーをダウンロードし、展開しました。


もう 1 台のマシンでゲームを起動しました。別のテスターが参加する準備ができているので、自分のマシンでゲームを起動できます。 Windows フォルダに移動して [Blaster.exe] を実行し、ホストをクリックするだけです。


ここでは、ロビー レベルにいます。ロビー ゲーム モードは、ゲーム状態でそのプレーヤー配列にアクセスできるため、入ってくるプレーヤーの数をカウントしています。


そこで、テスターに他のマシンにログインして参加するように指示します。 そして、他のプレイヤーがここからロビー レベルに移動するとすぐに、ロビー ゲーム モードのポスト ログイン関数は、ゲーム内に 2 人のプレイヤーが存在することを確認し、その時点で serverTravel を呼び出し、2 人とも blasterMap に移動します。 わかった。 プレイヤーが参加し、ここでブラスター マップにシームレスに移行しました。すべてが見栄えがします。 これで、レベル内を走り回り、ゲームをプレイすることができます。


そのため、この講義では多くのことを取り上げました。 シームレスな移動と、それを実現する方法、および移行レベルが必要な方法について説明しました。


次に、実際のゲーム用のブラスター マップと、2 人のプレイヤーがロビーに参加するとすぐにサーバー トラベルを呼び出すロビー ゲーム モードを作成しました。 これで、Blaster マップに移動する準備が整いました。


十分なプレイヤーがゲームに参加するとすぐに、ゲーム自体にいくつかのマルチプレイヤー機能を実装できるようになります。


よくやった。


また後で。

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です。


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


よくやった。


また後で。