主なコンテンツ

〜主なコンテンツ〜

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月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.


よくやった。


次回の講義に続きます。


Welcome.


In this lecture, we're going to talk about how we can show the widget on clients and how this is going to involve replicating variables.


Now, we're going to want to know when a variable has been replicated. So we're going to learn how we can make a function that will be called on clients when the variable has been replicated. And this type of function is known as a REP notify.


So let's get started.


So I'm back in BlasterCharacter.h and I'd like to have a variable that can store the weapon that we're currently overlapping. So let's make a new private variable. I'm going to forward declare the class AWeapon. and this variable will be called overlappingWeapon.


Now we want this variable to be replicated, which means when it changes on the server, then we'd like it to change on all clients. and we can REPLICATE a pointer to an a weapon. To do this, we need to add a UProperty. And add the replicated UProperty specified here.


This will designate overlappingWeapon to be a replicated variable. Now, this isn't the only thing we need to do to replicate a variable. We have to override a function that's going to be a public function. So we're going to override it right here in the public section.


Now, the function we need to override in any class where we plan on replicating variables is a [virtual void] function, and it's called getLifetimeReplicatedProps.


Now this function takes a TArray of FLifetimeProperties. And this theory is passed and by reference and is called outLifetimeProps.


Now this function is a [const] function. and we're overwriting it.And inside this function is where we register variables to be replicated. 


So let's make a function definition for getLifetimeReplicatedProps. Now I'd like to move this function definition. Up near the top. just under the constructor. And since we're overwriting this function, we need to call super::getLifetimeReplicatedProps. So we'll call the super version and pass in that input parameter outLifetimeProps.


And here is where we need to register our [overlappingWeapon] variable to be replicated. Now to do that, we use the macro called doRepLifetime. And in doRepLifetime we specify the ABlasterCharacter class. That's the class that has the replicated variable. And we also specify the replicated variable, which will be overlappingWeapon.

Now notice we get the red squiggles because doRepLifetime is undefined. And that macro is defined in a header that we need to include whenever we replicate variables. That header that we're going to include is [net/unrealNetwork.h]. Once we include that header, now doRepLifetime will be defined. this variable starts off on initialized. It'll be null until we set it.


Now we'd like to set it from the weapon class and our overlap function onSphereOverlap, which means I'm going to need a public setter for this variable. So I'll stick one down here at the bottom of Blaster character.


Now I'd like it to be in line, so I'm going to use a forceInLine macro, and this will be [VOID]. And I'll call this setOverlappingWeapon.


And it'll take in AWeapon pointer. I'll simply call this [weapon]. and we'll just set [overlappingWeapon = weapon]. So we can set this variable.


And as soon as the value of overlappingWeapon changes, then it will replicate, which means the variable will be set on all client (= blasterCharacters).


Now, replication works only when the variable is changed. So it's not going to replicate every frame or every net update, for example. Only when overlapping weapon actually changes on the server does it change on the client. So we can set this in [weapon.cpp] and onSphereOverlap.


So let's set it here, and onSphereOverlap for the weapon. we're going to get BlasterCharacter, which we have access to here since we cast otherActor to a BlasterCharacter. And here in onSphereOverlap we're going to call setOverlappingWeapon passing in [this].  So the weapon will call this function (= setOverlappingWeapon). And in our blasterCharacter class [overlappingWeapon] will be set.


So this means we no longer need to check to see if pick up widget is valid here. So all we're doing is setting overlappingWeapon on BlasterCharacter. On BlasterCharacter, we need to know when we should show that widget. I'd like to create a convenient function here on the weapon class for showing that widget.


So here in [weapon.h], i'm going to add a public section up here. Just under TICK. This will be [VOID]. And I'll call this showPickupWidget. Now I can give a boolean input parameter called BShowWidget. And that way we can pass in true or false whether we want to show or hide the pickup widget.


So let's define showPickupWidget. And we can show the widget down here. Now, I'd like to keep my weapon that keep organized. So I'm going to move TicK, Up here just under begin play.


Now in showPickWidget, we simply need to get our pickupWidget->setVisibility. So first, we're going to check to make sure pickupWidget is valid. And if it is, we'll get pickupWidget and call setVisibility passing in bShowWidget. So when we call this function the boolen we pass and will determine whether we show or hide pickupWidget.


Now the question is when do we call this function. we only know in BlasterCharacter whether or not overlappingWeapon is null, and we can use that information to show or hide the widget.


Now, it'd be great if we had a function that could be called when this overlapping weapon variable gets replicated. But we haven't learned that yet. So we can take a naive approach which won't be very optimal, but it'll work for now until we learn a better solution.


So let's go to our tick function. And again, I'd like to keep these organized. in [BlasterCharacter.cpp]. So I'm going to move the TICK function up here towards the top just under beginPlay.

And here in tech, I'm simply going to check to see if overlapping weapon is valid. And if it is, we know that we can show the pickup widget on that overlapping weapon.


So I'm going to say [if (OverlappingWeapon)]. If that's valid, I'm going to take overlappingWeapon and call its showPickupWidget function and pass in True.


Now we're using overlappingWeapon here, which means we need to include its header file. So at the top of BlasterCharacter, I'm going to add an include and weapon is in the weapon class, which means we need to add the included [blaster/Weapon/weapon.h]. and now that we're including [weapon.h] that variable type is defined.


So here in TICK, as soon as overlappingWeapon becomes set on the server, then on all clients we'll be showing that pickupWidget and we'll be setting this boolean to true every frame. which again is not very optimal but we'll learn a better way very shortly.

Let's go ahead and compile this. Now back here in the Editor let's go ahead and playtest this. And see how this works.


I'm going to first gain control of one of my clients and now I see that the pickupWidget is set here on the client. In fact, it's set on all the clients. And that's because here in the tick function, overlapping weapon is replicated to all clients. That's how replication works when we mark this variable to be replicated as soon as its value changes on the server. It will then be set on the version of BlasterCharacter on all clients. Now we registered this variable to be replicated here and getLifetimeReplicatedProps.

it really only makes sense to show the widget on the client who owns this character. We don't need to set overlapping a weapon on the simulated proxies on client machines. In other words, when someone else overlaps with a weapon, we don't need to see the pickup widget on our machine above that weapon.

Well, we can control which clients have this variable replicated to them.

We can do this by adding a condition when we register this variable to be replicated, to use a replicating condition.


We use a different macro. We use DOREPLIFETIME_CONDITION. And in addition to specifying the class and the variable, we also specify a condition. Now there are enum constants that we can use to specify conditions. 


Notice we have conditions [autonomous only]. which will only replicate this, to [autonomous proxies]. 


We also have several others, including [owner only]. Now we're going to talk more about the concept of ownership, But think of it as whoever's controlling the pod. If you are controlling the pawn on your machine, you're the owner of that pod. So if we specify owner only as the condition, then overlapping weapon will only replicate to the client that owns the BlasterCharacter.

So let's compile this and see the behavior here. So I'm going to hit play and walk up to the weapon on one of the clients.


Now notice there's a difference here. We do see it on the server, but we only see this pickupWidget on the client that overlapped with the weapon on the other client here we don't see it. And that's because the variable did not replicate to this client. It only replicated to the client that owns the pawn that overlapped with the weapon's areaSphere.


Now notice here in weapon's onSphereOverlap that we're setting the overlappingWeapon. And this only happens on the server. So as soon as we call setOverlappingWeapon, then here in BlasterCharacter, we've set the overlappingWeapon on the server.

And since we're setting it on the server, we're replicating it to the owning client. as we're specifying in getLifetimeReplicatedProps, only to replicate it to the owner.


Now we don't need to see the pickupWidget on the server unless the server is controlling the pawn that's overlapping. In other words, it owns that pawn.


So this method of showing the pickupWidget in the tick function isn't going to work. So what we can do, instead of setting the visibility of the pickupWidget in the tick function I'm going to remove that is we can set up a function that will be called whenever the variables value gets replicated.


This type of function is called a [rep notify]. Here's how we set it up. here And [BlasterCharacter.h]. We can create a function that will be our [rep notify]. 


These are void functions and the convention is to use OnRep underscore followed by [the name of the variable that's being replicated]. So this is going to be [overlappingWeapon]. Now we can't pass any meaningful values in to a [rep notify], as these are called automatically when the variable gets replicated. Now these also need to be UFunctions, so we have to have a UFunction macro in order to use this.


Now, in order to designate this [rep notify], to be called Whenever overlappingWeapon gets replicated, we take our overlappingWeapon  UProperty and we add the specify our [replicatedUsing] and then we specify the [rep notify]. So it's going to be OnRep_overlappingWeapon. Now with this UProperty specify our we will now have onRep_OverlappingWeapon be called on the client when overlappingWeapon replicates to that client.


So let's make a definition for this [REP notify]. So here's our [REP notify]. and in the [rep notify] We know that overlappingWeapon has changed, as it has just been replicated. so we can show the pickupWidget in here.


Now we first need to check to see if overlappingWeapon is not null. because it could be null. if overlappingWeapon is set to null from the server when it wasn't null before, then that change will replicate down to the client. So if overlappingWeapon is valid, we can take overlappingWeapon and call showPickupWidget and pass in True.


So let's compile this and see how this works. So I'm going to hit play.


And now I'm going to overlap with my client and notice something here. We only see the widget on the client that overlapped with the weapon. We don't see it on the server here. Now, this is an important detail to take note of. We don't get the widget on the server because [rep notifies] do not get called on the server. They only get called when the variable replicates and replication only works one way from server to client. So since it'll never be replicated to the server, the server will never get the [rep notify] called.


So this introduces another issue when we overlap with the weapon on the server here with the server controlled character. We don't get the widget and that's because we're only showing that widget in a rep notify. So we need to handle the case when we are the server.

So let's handle this in our function where we're setting the overlappingWeapon. now rather than having a simple getter, I'm going to remove the FORCEINLINE now, and add more logic to this setter. So I'm not going to have the definition all in one line like this.


I'm going to define a set overlapping weapon and [blasterCharacter.cpp]. So I'm going to set it right here just above the [rep notify]. Now in this setter, the first thing I'm going to do is setOverlappingWeapon. So we're going to set it equal to weapon.


And then we need to make sure that we only show the widget on the character that's actually controlling the pawn. Now we know that this is only called on the server. because we're calling it from the weapon's onSphereOverlap, which only gets called on the server.


But now we need to know if we're actually on the character that's controlling the pawn. we can check with a very handy function which returns a boolean called isLocallyControlled. isLocallyControlled will be true if this function is called on the character that's actually being controlled. 


So again, we know we're on the server. So if we reach the inside of this if check, we know that we're on the character being controlled by the player who is hosting the game on the server. Now if that's the case, we know that overlappingWeapon will not be replicated because we have our condition up here owner only. and none of the clients will be owners of this character because it's locally controlled on the server. So in this case, all we need to do is show the pickupWidget.


So I'm going to take this logic here and paste it into our if statement here. Now we know that we'll see the widget on the server when the server controlled character overlaps with it.


So let's compile this. And in the editor, we can test this out.


So I'm going to take my [server controlled character] here and overlap. And now we see that we have the pickupWidget. Now, if I get my two clients back here, I see that we do not see the widget on the clients.


This is exactly what I wanted because I only want to see that pickupWidget on the machine for the player who is controlling the character.


Now on the client, if we overlap, Then we see the pickup widget. and we can test the case where only the client overlaps and the server does not. So if I overlap with a client, we don't see it on the server.


So we're getting closer. we still aren't hiding the widget If I stop overlapping. the widget is still there. That means we need an [end of overlap] function.


So let's add that. We can do that here in [wepon.h]. So in addition to onSphereOverlap, we want an [onSphereEndOverlap]. So let's make that it's going to be [void onSphereEndOverlap].


And for this type of callback for an on component [end overlap delegate]. we need a [UPrimitiveComponent called overlapComponent], [AActor called otherActor], [UPrimitiveComponent called otherComp], and [int32 otherBodyIndex].


So I'm going to add those input parameters here. And this also has to have the UFunction macro. So I'm going to add that. And I can define this function.


So here in [weapons.cpp], I'm going to do the same thing I did in on sphereOverlap, namely take otherActor and cast to a blasterCharacter. And if that blasterCharacter's valid, call setOverlapping. and weapon only since we're ending overlap, we don't need to set it to this, but rather we're setting it to null. So I'm going to use null pointer here.


So now if we end overlap, overlappingWeapon on the character will be null. So now we need to know how we can hide that widget. because we're setting it to null. So it's not like we can access a null pointer to call showPickupWidget.

Well, let's go back to Blaster character and look at our OnRep_overlappingWeapon. This is our repNotify.


Now, I mentioned that we can't pass any input parameters in. because we're not actively calling onRepOverlappingWeapon. it gets called automatically when the variable is replicated, Right.


So how can we access overlapping a weapon after we've already set it to NULL? Now again, a naive approach would be to create another variable of a weapon pointer type called lastOverlappingWeapon or something like that. And we could store a pointer to the overlapping weapon before we changed it.


But there's an easier way. You see, rep notifies can have input parameters, but it can only have an input parameter of the type of the variable being replicated. In other words, onRep_overlappingWeapon can only have an AWeapon pointer. Now we're going to call this lastWeapon.


And that's because when you add an input parameter to a repNotify, then what gets passed in when it gets called upon variable replication? Well, it's going to be the last value before the replication happened.


So in other words, let's say overlapping weapons starts off NULL, which is the case, right? Well, as soon as it changes on the server, then the new value replicates down to clients. And if we have this a weapon input parameter here, then the value inside this function for the lastWeapon will be the last value of overlappingWeapon before the variable was replicated.


So that could be null or it could simply be the LastWeapon we were overlapping before it was set. So if overlapping weapon changes, we should check its last value before the change, which we'll have access to here with lastWeapon.


And if that value is not null, then we can access that last weapon and hide the pickup widget. So we'll do that down here.


We'll say [if(lastWeapon.)]..., And if lastWeapon is not null, then we can take lastWeapon and call ShowPickupWidget And pass and false.


So this way, if we end overlap, then overlappingWeapon will be set to null, which means this if check will fail, of course, but our last value for overlappingWeapon will still have the pointer to that weapon that we just stopped overlapping. And in that case, we can hide the pickup widget.


Now, before we compile this and test it out, we created this callback onSphereEndOverlap. We need to bind this to the end component and overlap delegate for the areaSphere.


So here in [weapon.cpp]. We can do this here, and beginPlay with our check hasAuthority so we only want this happening on the server.


So we're going to take areaSphere and use onComponentEndOverlap and addDynamic and we'll use [this] along with AWeapon::OnSphereEndOverlap.


And now we know that when we stop overlapping with AWeapon, onSphereEndOverlap will be called.

So let's compile this. And I'm going to test this out.


By taking one of my clients and I'll overlap. There's that pickupWidget and now I'll end overlap and the pickupWidget goes away. because that value lastWeapon is not null, even though overlappingWeapon is set to null. Since we have that last value, we're accessing it to hide the widget. So this will work for both clients. Here's my other client, and I see that it's working here.

But when I take my server, and I end overlap, it doesn't work. And that's because, again, we're setting this only in the repNotify and the repNotify will not be called on the server. So we need to take care of [the case] when we are the server.


we can do this here in BlasterCharacter. and our setter for setOverlappingWeapon. Here's where we're actually setting the value of overlappingWeapon. So before we set this value, we can check to see if overlapping weapon is valid and we can hide the pickupWidget before we change it. 


So if we take these lines here where we check if overlapping weapon is valid and then call showPickupWidget, we can pass in false here.


So let's compile this and we can play test this and on the server and get the pickupWidget and onEndOverlap. We hide the widget. and this still works with clients. There's that client, and here's my other client. So we're now showing the pickupWidget and hiding it at the appropriate time, and we're showing it for the correct player.


Now, that's okay that we're not checking if we're locally controlled here and setOverlappingWeapon. Because if you think about it, this will only be called in the server as it's called in that overlap function.


And if we're on the server and overlapping with something, then we don't really need to show the pickupWidget on the server for any other characters as only the locally controlled character overlapping should see the widget anyway.


So we now have the behavior that we want.


So in this lecture we learned about variable replication and we're replicating a pointer to an AWeapon called overlappingWeapon.


And we saw that replication only works one way from server to client.


And we learned about [rep notifies], which are functions that get called when a variable gets replicated.


And we saw that the repNotify only gets called on clients as variables never get replicated to the server only from the server.


And we learned how we can use a condition to allow us to only replicate to the owner of a character.


So rather than replicating a variables value to all clients, we only replicate to the client that's currently controlling the Pawn. 


In our (case) BlasterCharacter.


Great job.


We'll continue in the next lecture.

0 件のコメント:

コメントを投稿