主なコンテンツ

〜主なコンテンツ〜

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


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


よくやった。


また後で。

2023年3月4日土曜日

41. Animation Blueprint

 おかえり。


これでキャラクターの動きができましたが、キャラクターはアニメーション化されていないため、アニメーション インスタンスが必要になります。


これは、アニメーション ブループリントのベースとなる C++ クラスです。


ここで、C++ クラスが必要な理由は、C++ からこれにアクセスしてそのプロパティを設定できるようにするためです。


ここで、このコース全体を通して、アニメーション ブループリントであるアニメーション インスタンスを使用します。


しかし今のところは、プロジェクトを走り回ってテストできるように、アイドル ウォーク、ラン、ジャンプなどの未装備のアニメーションをキャラクターに設定したいと思います。


それでは飛び込みましょう。


わかった。


これが私たちのプロジェクトで、私たちのキャラクターは非常に硬いので、アニメ インスタンスをセットアップします。


それでは、C++ クラスに入りましょう。このキャラクター フォルダーには、Blaster キャラクターがあります。


ここでアニメのインスタンスを作りたいので、右クリックで新しい C++ クラスを選択し、すべてのクラスを選択してインスタンスを検索します。


そして、ここにあります。


[次へ] をクリックすると、ここにパスが設定されていることがわかります。これをキャラクター フォルダーに配置します。


では、これを「ブラスター アニメーション インスタンス」と呼びましょう。


文字クラスを作成したときと同じ警告メッセージが表示されます。 繰り返しますが、それはキャラクター フォルダーに配置したためです。


いいえをクリックします。


ここ Visual Studio には、BlasterAnimInstance.h と BlasterAnimInstance.cpp があります。


  エラーの原因は次のとおりです。Unreal Engine は自動的にキャラクター フォルダをそこに配置します。 これを削除すると、コンパイルできるようになります。

これで、AnimInstance は非常に単純になり、駆動するいくつかの重要な変数を設定するだけで、非常に基本的な未実装のアニメーションになります。


そこで、パブリック セクションを追加し、いくつかの継承された関数をオーバーライドします。 したがって、これらは「仮想オーバーライド」になります。


1 つ目は NativeInitializeAnimation です。 これはBegin Playによく似ています。 これは、ゲームの最初だけでなく、Unreal Engine をロードしてコンパイルするときなど、さまざまなときに呼び出されます。


ここで、NativeUpdateAnimation という別の仮想関数もオーバーライドしたいと思います。これは Delta Time という float を取り、tick 関数と同じようにすべてのフレームで呼び出されます。


これもオーバーライドなので、先に進んでこれら 2 つの関数を実装できます。 そして、何よりも先に最初に行うことは、これらの関数の SUPER バージョンを呼び出すことです。


ですから、忘れないようにしましょう。


これで、super::NativeInitializeAnimation と super::NativeUpdateAnimation が得られました。 それにデルタ時間を渡します。 これで、アニメーション インスタンスには、アニメーション ブループリントに公開できる変数がいくつかあるはずです。


これらは非公開にすることができるので、非公開セクションを作成します。 最初に追加したい変数は、Blaster キャラクターを格納する変数です。 これは、このアニメーション インスタンスを使用するキャラクターになります。


そこで、ABlasterCharacter を前方宣言します。 このポインターは単純に BlasterCharacter と呼ばれます。


アニメーション ブループリントからこれにアクセスする必要があるため、これを UProperty で公開し、このブループリントを読み取り専用にします。


必要に応じて、これにカテゴリを指定できます。 CHARACTER というカテゴリにします。 また、このブループリントを読み取り専用にしているため、これはプライベート変数であるため、メタ指定子が必要です。


このメタ指定子を [allowPrivateAccess = true] に使用しない限り、プライベート変数で読み取り専用のブループリントを使用することはできません。


ここで、ここに保存したい他のいくつかの変数に対して同じことを行います。


1 つは速度のためなので、ここでは同じ UProperty を使用します。 MOVEMENT にできるのはカテゴリのみです。


速度に加えて、空中にいるかどうかも知りたいです。 そこで、bIsInAir という bool を作成します。 そして、これに同じ UProperty カテゴリ MOVEMENT を与えます。


そして、私は別のブールが必要です。 これを [bIsAccelerating] と呼ぶことにします。 その通り。 加速しているかどうかがわかるので、それを使用して、走っているかどうかなどのアニメーションを操作できます。


これら 4 つの変数があり、それらを設定する必要があります。


ここで、ネイティブ初期化アニメーションの早い段階でブラスター キャラクターについて説明します。 BlasterCharacter を TryGetPawnOwner の結果と等しく設定するだけです。


しかし、TryGetPawnOwner を呼び出すと、この関数は Pawn を返します。 これをブラスターキャラクターにキャストする必要があります。


というわけでキャストをやっていきます。 これを ABlasterCharacter にキャストします。 そしてもちろん、BlasterCharacter を「含める」必要があるので、ここの一番上に「含める」ことにします。 blasterCharacter.h になります。


名前の前にそのキャラクター フォルダーを配置せずに、ここでこれを「含める」ことができる理由は、アニメーション インスタンスがキャラクター フォルダー内にあるからです。


これで、ブラスターのキャラクターができました。

現在、NativeUpdateAnimation はすべてのフレームで呼び出されます。 また、NativeInitializeAnimation で設定する前に BlasterCharacter にアクセスしようとしていないことを確認する必要があります。 簡単に確認できます。 「if BlasterCharacter」。 BlasterCharacter が null ポインターであるかどうかを確認したいのですが、そうであればすぐに取得して、NativeUpdateAnimation を呼び出す最初のフレームにアクセスして設定できるようにします。 BlasterCharacter が等しいと言えます。 ここでは、ポーンの所有者を取得して BlasterCharacter にキャストしようとする同じ行を使用します。 これで、Blaster キャラクターへのアクセスが処理されます。


次に、以下の行で BlasterCharacter にアクセスしたいと思います。「If BlasterCharacter」などの IF チェックですべてをラップするのではなく、単に「If BlasterCharacter が null であるか」を確認して戻ります。 その場合、BlasterCharacter が null の場合はこの行に到達しないため、この方法で BlasterCharacter に安全にアクセスできます。


次に設定したい変数は速度です。これにより、BlasterCharacter の速度を取得できます。 ここでは FVector の速度を指定し、単純に BlasterCharacter を使用して getVelocity を呼び出します。 そして、私の速度については、横方向の速度だけが本当に必要です。 速度の z コンポーネントは気にしないので、速度の Z をゼロにします。 したがって、velocity.z = 0.f とします。


次に、このベクトルのサイズを簡単に取得して、速度に等しく設定できます。 したがって、速度は「velocity.size」に等しいと言えます。 そして、私にはスピードがあります。


ここで、bIsInAir を設定したいと思います。 そのため、bIsInAir では、キャラクターの動きのコンポーネントを取得する必要があります。 つまり、もちろん、そのための適切なインクルードを追加する必要があります。 そして、その characterMovementComponent はゲーム フレームワークにあります。 [game/framework/characterMovementComponent.h] をインクルードします。 その型が使えるようになったので、BlasterCharacter から characterMovementComponent を取得します。 つまり、BlasterCharacter->getCharacterMovement になります。 characterMovementComponent から isFalling を呼び出すだけです。 ブール値を返します。 落下している場合は true になります。この場合、空中にいることがわかり、適切なアニメーションを再生できます。

最後に、bIsAccelerating を使用します。 速度ではなく、なぜこれを使用しているのか疑問に思われるかもしれません。 また、インスタンスの観点から加速について話すとき、速度の変化率について話しているのではありません。 物理学を知っていれば、それが加速度の技術的定義であることを知っています。


私が話しているのは、キーを押して移動する入力を追加するかどうかです。 ここでそれを設定します。 bIsAccelerating で、これもキャラクターの動きのコンポーネントから取得します。 BlasterCharacter->getCharacterMovement と言って、現在の加速度を取得します。 getCurrentAcceleration を使用して、このベクトルのサイズを取得します。 ゼロより大きい場合は、それを使用して加速を定義します。


ここで三項演算子を使用して、この数量が 0 より大きい場合は「?」を使用します。 そこで「f」を使います。 次に、ブール値を「true」に設定します。 そして、これが私の「:」であり、「false」はそれ以外の場合です。


というわけで簡単にレビュー。 三項演算子に慣れていない場合は、ここで最初の式がその式、値の真偽を評価します。 したがって、現在の加速度のサイズが 0 より大きい場合、この三項演算子式によって返されるのは、この 2 番目の量です。 最初の式が false と評価された場合、三項式はこの数量をここに返します。 つまり、「この量なら「これ」を返し、そうでなければ「あれ」を返す」と言っているのと同じです。


そのため、コード行を単純化するために、必要に応じて三項演算子を使用します。 そうでなければ、ここに FALSE STATEMENT があるだけです。


これで、"speed" と "isInAir" と "isAccelerating" ブール値を変数に設定しました。 私たちのブラスターキャラクターと同様に。 これで、アニメーション ブループリント内からこれらにアクセスできるようになりました。 それでは、このクラスに基づいてアニメーション ブループリントを作成しましょう。


このコードをコンパイルし、blueprints フォルダーに移動してキャラクターに入ります。 ここにアニメーション ブループリントを追加したいと思いますが、ここで Animation という新しいフォルダーを作成し、作成したアニメーション ブループリントとその他すべてをここに保持します。 これは、アニメーション モンタージュのようなアニメーション関連です。


右クリックして、新しいアニメーション ブループリントを作成します。 親クラスには AnimInstance を選択します。 次に、クラスの親を blasterAnimInstance に変更します。 [最高の成功設定] をこのように設定しました。


次に、スケルトンを sk_epic_character_skeleton に設定し、[OK] をクリックします。 これを BlasterAnimBP と呼びたいと思います。


そして、それを開いてみましょう。 さて、これが私の blasterAnimBP です。


最初に行うことは、クラスの設定に移動することです。これは、ここの上部のバーに表示されます。 クラス設定では、親クラスを BlasterAnimInstance に設定します。


BlasterAnimInstance からアニメーション ブループリントを派生させたので、C++ で宣言した変数 (加速、速度、Blaster キャラクターなど) にアクセスできるはずです。そのため、これらのブループリントを読み取り専用にして、このブループリント内からアクセスできるようにしました。


さて、このゲームの大部分で、キャラクターは武器を装備します。 しかし、その機能を実装するまでは、未装備用のアニメーション ステート マシンを使用する予定です。


それではステートマシンを作ってみましょう。


新しいステート マシンを追加して、出力ポーズに接続します。 そして、これを未装備と呼びます。

これは、キャラクターが武器を持っていないときにポーズに使用するステート マシンです。 そこで、ステート マシンに移動して、いくつかのステートを追加します。


さて、まず、アイドル ウォーク アラウンド状態が必要になります。 したがって、このアイドル ウォーク ランを簡単に呼び出すことができます。 そして、このための簡単なブレンド スペースを作成します。


また、キャラクターもジャンプできる必要があります。 そこで、ジャンプ用の状態をいくつか作成します。 最初のアニメーションを作成するためにジャンプ スタートを実行し、空中にいるときのアニメーション状態を作成します。 これを落下と呼ぶことにします。


そして落下から、ジャンプストップアニメへ。 それでは、ジャンプ停止状態を追加しましょう。 そして、ジャンプストップから、そのままアイドルウォーク、ランに戻ることができます。 繰り返しますが、これは簡単です。


「アイドルウォークランからジャンプスタートへ」、ダブルクリックしてそのトランジションに入りましょう。


これには isInAir を使用します。 isInAir が true の場合、ジャンプ スタート アニメーションに移行し、ジャンプ スタートから落下することができます。 これを自動移行にするだけです。 [シーケンス、プレーヤー、状態に基づく自動ルール] の横にあるチェックボックスをオンにできます。 つまり、このアニメーションが終了するとすぐに、次の状態 (落下アニメーション) に自動的に遷移します。


フォールバックからジャンプストップまで、再び isInAir を使用できます。 そこで、isInAir を使用します。 そしてもちろん、このブール値を false にする必要があります。 そこで、[NOT boolean] を使用します。 isInAir が false の場合は遷移し、アイドル ウォーク ランに戻ります。


このアニメーションが特定のポイントまで再生されたら、そのために残り時間ノードを使用します。これを行うには、そこにアニメーションが必要です。 ですから、これについてはすぐに説明します。


Jump Start では、Jump を検索し、Learning Kid Games Pack に付属のアニメーションをいくつか使用します。 そしてそれらは EPIC CHARACTER で始まります。 したがって、エピック キャラクター ジャンプ アップ スタートは、キャラクターが武器を持っていない場合のジャンプ スタート アニメーションです。 それをダブルクリックすると、このように見えることがわかります。 これをジャンプスタートと落下状態に使います。


EPIC CHARACTER ジャンプ アップ ループがあり、それがこのアニメーションです。 彼はちょうど落下ポーズです。 ジャンプストップには、EPIC CHARACTERのジャンプアップエンドがあります。 そして、これがそのアニメーションの外観です。 それを再生して、キャラクターが地面に (落下) 戻っていることを確認できます。


これで [jump stop] の中にアニメーションができました。 [残り時間の比率] を使用するこの遷移規則を完成させることができます。


[残り時間比率] は、比率が必要なアニメーションを知る必要があるノードです。 だからこそ、あのアニメーションが必要だったのです。


これはジャンプアップとアニメーションのためのもので、残り時間が特定の値以下かどうかを確認したいだけで、0.1 などを使用できます。 したがって、アニメーションが 90% 再生され、そのアニメーションの残り時間の比率が 0.1 になると、IdleWalkRun に戻ります。

IdleWalkRun では、ブレンド スペースを使用して、アイドリングから実行中へのブレンドを行います。 それでは、そのブレンドスペースを作りましょう。


アニメーション フォルダに戻り、右クリックしてアニメーションを選択し、単純な [ブレンド スペース 1D] を使用します。 SK_epic_character_skeleton を選択します。 そして、これを未装備の IdleWalkRun と呼ぶことにします。


そして、これは簡単です。 EpicCharacter_Idle アニメーションを作成します。 ここにあります。 そして、彼はただそこに立って周りを見回しています。


(今) EpicCharacter_Walk があります。 これを 2 番目の場所で設定し、最後に EpicCharacter_Run を使用できます。 今度は横軸が速度になります。 [maximum] については、およそ 350 に達したら running_animation に入りたいと思います。


キャラクターのデフォルトの最大歩行速度はそれよりも高速です。 約 600 です。ゼロから最大歩行速度まで加速するので、スムーズな移行が得られるはずです。


これでブレンド スペースができたので、それを閉じます。 ここで UnequipledIdleWalkRun を取得し、それを PLUG して、速度変数 (C++ で宣言し、NativeUpdateAnimation で設定しています) を使用するだけです。


ですから、それをコンパイルして保存します。 これで、今のところのアニメーション グラフが完成しました。 キャラクター用の単純なステート マシンがあります。 武器なしで装備されていません。


それでは、キャラクター フォルダの BP_BlasterCharacter に戻り、メッシュを選択して新しいブループリントを割り当てます。


では、アニメーションモードを [use animation blueprint] にしましょう。


[アニメーション クラス] には BlasterAnimBP を使用します。アニメーション インスタンスではなく BP を選択してください。これは、BP がステート マシンを持つものであるためです。 コンパイルして再生すると、キャラクターがアイドル状態になっていることがわかります。


これで、まだコントローラーと一緒に回転しており、動きに向けられていません。これは、私たちが望むものです。 そこで、いくつかのことを行います。


まず、BlasterCharacter.CPP に戻り、C++ でいくつかの変数を設定します。


1 つ目は、コントローラーの rotationYaw を使用することです。 これを false に設定します。 少なくともまだ、コントローラーの回転に合わせてキャラクターを回転させたくないからです。 武器を装備し、エイム オフセットを使用していて、状況がさらに複雑になったら、それを使用したいと思うでしょう。 しかし今のところ、コントローラーの回転とは無関係にキャラクターを静止させ、代わりに独自の移動方向に向けます。


そのために、getCharacterMovement を使用してキャラクターの動きのコンポーネントを取得し、ブール値の bOrientRotationToMovement にアクセスします。 これを true に設定します。


ここで、キャラクターの移動コンポーネントを使用しています。 また、ヘッダーを含めたインスタンスでも使用していることを思い出してください。 これをコピーして、インクルードし、BlasterCharacter の上部に貼り付けることができます。 そして今、そこに含まれているエラーはありません。


これをコンパイルしますが、設定した C++ 変数とコンストラクターをブループリントがオーバーライドするため、ブループリントでこれらの変数を設定する必要もあります。


BlasterCharacter に戻ります。 BP_BlasterCharacter(self) を選択し、[use controller rotation Yaw] を検索します。 チェックを外します。 CharacterMovement を選択し、[回転を動きに合わせる] を検索し、チェックされていることを確認します。


コンパイルできるように見えるので、今再生すると、キャラクターは移動方向に向き、コントローラーを動かし、スプリングアームとカメラを拡張すると、キャラクターはそれに沿って回転しません.


これで、キャラクターが走り回ることができました。これは素晴らしい出発点です。 動きがあり、アニメーションがあり、ジャンプできます。 だから、これはよさそうだ。


ここで、ジャンプの最後でキャラクターの一種のグリッチが発生することに気付きました。 そこで、AnimBP の JumpStop 状態に戻り、ジャンプ エンドを選択し、ループ アニメーションのチェックを外してコンパイルします。 さて、戻ってジャンプすると、もうその不具合はありません。 そして今、私たちはジャンプしています。


すべてが良さそうです。


この講義では、アニメーション ブループリントをセットアップし、このアニメーション ブループリントの親として使用するアニメーション インスタンス C++ クラスを用意しました。


このようにして、C++ から変数を設定し、ブループリント内の変数にアクセスしてアニメーションを駆動できます。 キャラクターが走り回っており、今後のビデオでより複雑な機能を追加する予定です。 しかし、今のところ、基本的な動きがあります。


よくやった。


また後で。

2023年2月6日月曜日

40. Character Movement

 おかえり。


これで、スプリング アームとカメラ コンポーネントにキャラクターができました。


プロジェクトとプログラムの入力、キャラクターの移動機能を構成する必要があります。


始めましょう。


そのため、プロジェクトにはいくつかの入力が必要になります。 そのために、編集とプロジェクト設定に進みます。


ここから、入力を選択して、アクション マッピングと AXIS マッピングを展開します。


ここで、軸マッピングにいくつかのアクションが見られます。これらは、ラーニング キット ゲームからいくつかのアセットを移行したときに作成されました。


プロジェクト設定からこれらの軸マッピングをすべて削除します。 これをすべてゼロから行いましょう。


アクション マッピングでは、ジャンプできるようにしたいと考えています。

ジャンプ アクション マッピングを作成し、これをスペースバーにマッピングします。 このアイコンをクリックしてスペースバーを押すと、スペースバーをジャンプ アクション マッピングにリンクできます。


AXIS マッピングでは、これらのいくつかが必要になります。


1 つ追加して、これを MoveForward と呼びます。 そして、これを W キーにマッピングしたいと思います。 また、これを S キーにマップし、スケールを -1 にして後方に移動します。 これで、MoveForward ができました。


MoveRight も必要です。 そこで、MoveRight を追加して、これを D キーにマップします。 また、スケール -1 の A キーにマップしたいと思います。


これで左右に移動できるようになります。


ここで、前進と右への移動に加えて、マウスを使用してカメラを回転できるようにしたいと考えています。 そこで、さらに 2 つの軸マッピングを追加し、そのうちの 1 つを [Turn] と呼びます。


このために、マウスの x 軸をマップし、look up という別の軸を作成して、これをマウスの y 軸にマップします。

ここで、マウスの y 軸のスケールを -1 に設定します。


そうすれば、マウスを前方に動かすと、下ではなく上を向くようになります。


したがって、これらのアクションと軸のマッピングがあります。


文字クラスでこれらのマッピングにバインドする関数をいくつか作成できます。


これが BlasterCharacter.h で、動きを処理する関数をいくつか作成します。


ここで、子クラスからこれらにアクセスしたい場合に備えて、これらを保護されたセクションに貼り付けます。 BlasterCharacter から派生する可能性があります。


そして、float を取る MoveForward という関数を作成します。 この値を呼び出します。これは、軸のマッピングのための軸の値です。


MoveRight という void 関数も作成し、そのために value という float を追加します。


次に、Turn と LookUp が必要です。


そこで、value という float を持つ Turn という関数と、value という float を持つ LookUp という関数を作成します。


そして、これらは私たちの基本的な動きを処理します。


これらを定義していきましょう。


ここに関数の定義があります。

ところで、これは現時点でのレビューです。 以前にこれを行ったことがなく、私たちが行っていることについて少し理解していない場合は、他の C++ Unreal Engine コースの 1 つを受講することをお勧めします。ここでは、ここで行っていることをより詳細に説明します。 少なくとも一度はこれを行ったことがあると思います。


キャラクターを立ち上げて機能させるには、このレビュー資料のいくつかを通過する必要があります.


Unreal Engine のマルチプレイヤーに関連するいくつかの新しい機能に入る前に、関数の動きができたので、setupPlayerInputComponent 関数を取得して、beginPlay の下にカット アンド ペーストします。


こうすることで、ここで関数をアクセス マッピングにバインドしているため、プレーヤー入力コンポーネントの設定を確認できます。


これで、これらの移動関数は非常に単純になり、前進します。


文字コントローラーが null ではなく、値がゼロでないことを確認します。


したがって、コントローラーが null ポインターと等しくなく、値が 0.f と等しくない場合を言います。


そのため、Character クラスには、AController 型の controller と呼ばれる継承された変数があります。


ここで、この IF チェックを行う場合、どの方向が前方であるかを調べる必要があり、コントローラーの回転を使用してこの値を見つけます。


getActorForwardVector を使用して前方方向を取得しないのはなぜですか? これは、アクティブな前方ベクトルがキャラクターの前方方向であるためです。


より具体的には、カプセルであるルート コンポーネントの前方ベクトルです。 ただし、コントローラーの回転を変更します。


コントローラーはルート コンポーネントとは異なる回転を持つ可能性があり、キャラクターの前方方向ではなく、コントローラーの前方方向に移動したいと考えています。 そこで、コントローラーを前方に向けます。


ローカル変数を宣言しましょう。 const FRotator になります。 これを YawRotation と呼びます。 これは、ヨーのピッチがゼロの FRotator になります。 そこで、ヨーイングのピッチには 0.f を使用します。 しかしヨーに関しては、私はコントローラのヨーが好きなので、controller->getControlRotation と言って、その回転からヨーを取得します。


前述のように、ロールとピッチに 0.f を使用します。 したがって、この回転 F rotator はピッチとロールに対してゼロであり、コントローラーの回転ヨーです。


次に、この FRotator を使用して前方ベクトルを取得し、方向を取得します。これが FVector になります。 そこで、const FVector を作成します。 そして、これを単に「方向」と呼ぶことにします。


  FRotation 行列を使用してヨー回転から前方ベクトルを取得することで、これを初期化します。 そのため、回転を使用して FRotationMatrix を作成できます。


FRotationMatrix は、行列と呼ばれる単純な数学的構造です。 ローテーターから回転行列を作成でき、その回転行列には情報が含まれています。 FRotationMatrix で getUnitAxis という関数を使用できます。


ここで、getUnitAxis と getUnitAxes があることに注意してください。 getUnitAxis が必要で、これに値を渡します。値は EAxis::X になります。 EAxis は名前空間であり、この「列挙型」定数 X が含まれています。


私たちが行っているのは、x 軸を指定して、それを単位軸と呼んでいることだけです。 この背後にある数学を理解する必要はありません。 FRotator から FRotationMatrix を作成し、それを単位軸と呼び、FVector を返すことだけを知っておく必要があります。


このベクトルは、回転の方向を表します。 これで、ピッチとヨーの回転がゼロになります。 したがって、このベクトルは地面に平行で、コントローラーのヨーに対応する方向を指しています。

これが前進したい方向なので、キャラクターの継承関数 addMovementInput を呼び出し、方向と値を渡します。


ここで、方向の大きさは問題ではないことに注意することが重要です。 たとえば、方向を 2 倍することでこれをスケーリングできると考えていて、それが移動速度を 2 倍にする場合、それは機能しません。 AddMovementInput は単に方向と値を取り、その方向にキャラクターが移動する結果になります。


ただし、速度と加速度は characterMovementComponent から取得されます。 キャラクターをより速く動かしたい場合は、characterMovementComponent の変数を変更する必要があります。 したがって、これで先に進むことができます。


moveRight で同じコードを使用します。 回転行列から x 軸を取得するのではなく、重要な違いが 1 つあります。 y 軸を取得します。 これにより、ヨー回転に対応する前方ベクトルではなく、正しいベクトルが得られます。 他のすべては同じです。


回転と見上げがさらに簡単になりました。 turn 関数の addControllerYawInput を呼び出すだけで、マウスを左右に動かしながら値を渡します。 値は、マウスを動かしてコントローラーを追加する速度を表します。 ヨー入力は、コントローラーの回転にヨーを追加します。


LookUp は単純に addControllerPitchInput を呼び出し、ここに値を渡します。 したがって、基本的な移動関数があり、これらを軸マッピングにバインドする必要があります。


タイプ UInputComponent のオブジェクトにアクセスできるため、セットアップ プレーヤー入力コンポーネントでそれを行うことができます。 これが playerInputComponent です。 もちろん、playerInputComponent には bindAxis があります。 今 bindAxis は FName を取ります。


ここで実際の FName を渡すか、その中に文字列を含むテキスト マクロを渡すことができます。 または、単に通常の文字列を使用することもできます。リテラル バインド アクセスは、その軸名に対してこれらすべてを受け入れます。 これで moveForwards ができたので、それから始めることができます。


2 番目の入力は、このキャラクターになるユーザー オブジェクトです。


そして、ABlasterCharacter::MoveForward になるバインド先の関数のアドレスが必要です。


次に、moveRight に対して同様の方法で他の軸マッピングをバインドします。 文字列を moveRight に変更し、関数を moveRight 関数に変更できます。


「turn」と「lookUp」もあります。 それでは、それらも見つけてみましょう。 ターンにはターン軸マッピングが必要であり、ターン関数をバインドし、ルックアップまたはルックアップ軸マッピングにバインドし、ルックアップ関数を使用しています。


これで、ジャンプに関するアクション マッピングもできました。 今のところ、キャラクターの継承されたジャンプ機能を直接バインドするだけです。 これを一番上に挙げてみましょう。


playerInputComponent と言います。 bind アクションを使用します。 アクション名は「ジャンプ」。


EInputEvent があります。 これが、使用できるキー イベントです。 IE_Pressed ユーザー オブジェクトはこれで、関数は単純に ACharacter::Jump です。


後で、追加の機能が必要になる可能性があるため、ジャンプ機能をオーバーライドしますが、今のところは、キャラクターのジャンプ機能で十分です。 そのため、移動を設定し、移動関数を入力にバインドしました。


これをコンパイルしてみましょう。


エディターに戻って、キャラクター クラスをここに置いておきます。


プレーヤーの開始もまだ使用していません。


私はワールドに実際の BP_BlasterCharacter を持っています。最後のビデオでは、詳細パネルに移動し、自動所有プレイヤーをプレイヤー 0 に設定しました。


そうすれば、Play を押すと、実際にプレーヤーを所有していることになり、動き回ることができます。


もちろん、アニメーション ブループリントを使用していないため、キャラクターは固く、アニメーション ポーズすらありません。


それが私たちの次のステップになるでしょう。


キャラクターのセットアップの基本はほぼ終わりました。キャラクターを動かせたら、マルチプレイヤー プログラミングの概念を学び始めることができます。


そのため、この講義では、基本的なキャラクターの動きのアクションとアクセスのマッピングを設定し、それらの動き関数を作成してマッピングにバインドしました。


そして、実際にキャラクターとコントローラーを動かし、ひいてはスプリング アームとカメラを動かしています。


繰り返しますが、これはすべてあなたのためのレビューです。


キャラクター設定の基本はほぼ完了しており、すぐにマルチプレイヤー プログラミングに取りかかることができます。


また後で。

2023年1月18日水曜日

38. Camera and Spring Arm

 おかえり。


これで、キャラクター クラスとキャラクター ブループリントができました。


カメラ コンポーネントやスプリング アーム コンポーネントなどのコンポーネントの追加を開始します。


それでは飛び込みましょう。


ロビー マップに移動して、BP_Blaster_character をドラッグします。


まず、ゲームモードを設定していないため、自動的にキャラクターを所有することはありません。


しかし、キャラクターを選択し、自動所有プレイヤーの詳細パネル検索に移動し、それを player_0 に設定した場合、プレイを押すと、このキャラクターが自動的に所有されます。


しかし、カメラはほとんどキャラクターの内部にあることがわかります。


そのため、キャラクターを所有しているときにビューを制御するには、カメラとスプリング アームが必要です。


したがって、これらのコンポーネントを C++ から追加します。


これが blasterCharacter.h と BlasterCharacter.cpp です。


いくつかの新しい変数を宣言します。


public セクションにコンストラクターがあります。 ここには、Tick と SetupPlayerInputComponent を含む 2 つ目のパブリック セクションがあります。


これらの公開機能をここの一番上にあるこの公開セクションに移動したいと思います。


また、この時点で、これらの関数が何をするかをすべて知っているはずなので、これらのコメントが必要だとは本当に思いません。 なので、それらのコメントは削除します。

ここでは、Tick と SetupPlayerInputComponent を [トップ パブリック セクション] に移動します。 および保護されたセクションの下。


プライベートセクションを希望します。


そこで一つ追加します。


この公開セクションはここに置いておきます。 この領域は、さまざまなメンバー変数の単純な getter と setter 用に予約します。


ここのプライベート セクションでは、ここにスプリング アームとカメラを追加します。

そこで、use spring arm コンポーネントを前方宣言します。


これを CameraBoom と呼ぶことにします。


さて、これには新しいプロパティ マクロが必要になります。


だから私はこれをどこでも見えるようにするつもりです。


そして、CameraBoom とカメラを 1 つのカテゴリに入れたいと思います。


これを CategoryCamera と呼びます。


そしてもちろん、実際のカメラ自体も必要です。これも前方宣言されます。


そして、これは UCameraComponent になります。


これを FollowCamera と呼ぶことができます。


さて、これもVisibleAnywhere、そしてCategoryCameraにします。 これで、変数を宣言しました。


しかし、これらを構築する必要があります。 これを行い、Blaster キャラクター コンストラクターを作成します。


現時点ではこれらのコメントは必要ないと思うので、ここでこれらのコメントを削除します。 彼らは初心者をスピードアップさせるためのものです. したがって、ここでコンストラクターでそれらを削除できると思います。


CameraBoom を作成しましょう。 だから私はカメラブームと言ってデフォルトのサブオブジェクトを作成します。


これは USpringArmComponent です。


テキスト マクロでは、これを CameraBoom と呼びます。


もちろん、ここではスプリング アーム コンポーネントを使用しているため、そのタイプのヘッダーを含める必要があります。 「GameFramework/SpringArmComponent.h」をインクルードします。


これで、アンリアル エンジンのドキュメントでいつでもクラスを検索して、このインクルードを確認できます。


通常、CameraBoom をルート コンポーネントに接続しますが、カメラ ブームをメッシュ コンポーネントに接続したいと思います。


これは、後でカプセル サイズを変更するしゃがみ機能を使用するためです。


カプセルは私たちのルートであり、スプリング アームがカプセルに取り付けられた状態でカプセル サイズを変更すると、スプリング アームが動きます。


ということで、代わりにスプリングアームをメッシュに取り付けます。

セットアップ アタッチメントを呼び出して、get mesh でアクセスできるメッシュを使用します。


次に、カメラ ブームのターゲット アームの長さを設定します。 それでは、cameraBoom->TargetArmLength としましょう。 これを 600 に設定します。


しかし、それを調整したいかどうか見ていきます。


現在、カメラ ブームにも bUsePownControlRotation があります。 これを true に設定して、マウス入力を追加するときにコントローラーと共にカメラ ブームを回転できるようにします。


これで cameraBoom ができました。 カメラを作成しましょう。 そのため、フォローカメラがあります。


createDefaultSubObject を使用します。 これは UCameraComponent です。 これを followCamera と呼びます。


setupAttachment を使用して、followCamera をカメラ ブームに取り付けることができるようになりました。


そして、use springArmComponent、静的メンバー変数ソケット名を使用して、カメラを cameraBoom の socketName ソケットに接続できます。


そして、カメラ コンポーネントを使用しています。 そのため、ヘッダーを INCLUDE する必要があります。 これは camera/cameraComponent.h にあります。


現在、フォロー カメラは cameraBoom にアタッチされているため、PownControlRotation を使用する必要はありません。 したがって、followCamera->beUseControlRotation = false と言えます。


それでは、先に進んでコンパイルしましょう。


カメラがあり、もちろん、カメラ ブームがメッシュに取り付けられていることがわかります。


下部にピボットポイントがあります。 ここでは、スプリング アームが下部に取り付けられていることがわかります。


さて、ここの BLUEPRINT を見ると、約 600 単位離れているように見えます。 私たちのターゲット アームはこの 600 のようなものだからです。

しかし、ここではそれがわかりません。 カメラが床に衝突する可能性が最も高いためです。


では、カメラ ブームを上に動かしてみましょう。実際には地面から取り付けたくないからです。


カメラ ブームを取り、Z 方向の位置を 88 に設定します。そうすれば、ここの中央に取り付けられます。 カメラ ブームを少し上に移動することもできます。 それはあなた次第です。


コンパイルして再生ボタンを押すと、キャラクターから正しい距離にいることがわかります。


もちろん、まだ動き回ることはできません。


これは、その機能をプログラムしていないためです。


それが次のステップになります。


この講義では、カメラ コンポーネントとスプリング アームを追加し、それらのアタッチメントをセットアップして、カプセルであるルート コンポーネントではなく、メッシュにスプリング アームを取り付けました。


そうすれば、後でしゃがんでカプセルのサイズを変更しても、スプリング アームの高さには影響しません。


これで、移動機能をプログラムする準備が整いました。次にそれを行います。


また後で。


37. Blaster Character

 いらっしゃいませ。


キャラクタークラスの作成を開始する準備ができました。


そのため、このビデオでそれを行い、C++ クラスのフォルダー構造を設定して、プロジェクトを整理します。


それでは始めましょう。


これが私たちのプロジェクトです。


これまでのところ、C++ クラス フォルダーには BlasterGameModeBase クラスがあります。

新しい文字クラスを作成します。 そのため、右クリックして [新しい C++ クラス] を選択することで、C++ クラスと Blaster 内からそれを行うことができます。


次に、キャラクターに基づいてクラスを作成します。


それでは、文字を選択して次へをクリックしましょう。


このプロジェクトでは、キャラクター クラスに [ブラスター キャラクター] という名前を付けます。このプロジェクト用にかなりの数のクラスを作成します。


そのため、それらを独自のフォルダーに整理したいと思います。

Character というフォルダーに Blaster クラスを作成します。


パブリック フォルダー構造とプライベート フォルダー構造を使用するかどうかは、あなた次第です。


個人フォルダは使わなくてもいいので、そのままにして、BlasterCharacter と専用のキャラクター フォルダを追加するだけにします。 では、クラスの作成をクリックしましょう。


ここで、キャラクター クラスと独自のキャラクター フォルダーを追加することを選択したため、Blaster モジュールを再コンパイルする必要があるというメッセージが表示されます。これは、自動コンパイラ エラーが発生するためです。 私の言いたいことをお見せしましょう。 [いいえ]をクリックします。


Visual Studio は、プロジェクトをリロードするかどうかを尋ねてきます。


[すべて再読み込み] をクリックします。


これがプロジェクトです。 BlasterCharacter.h と .cp を含むソース フォルダーの Blaster の下に、このキャラクター フォルダーがあることがわかっています。


BlasterCharacter.cpp には、インクルード エラーがあります。 キャラクター フォルダー内から BlasterCharacter.h をインクルードしようとしていますが、CP ファイルは既にキャラクター フォルダーにあるためです。


したがって、include ステートメントでこの Character を削除するだけで、エラーを取り除くことができます。


これでコンパイルできるはずです。


コンパイルが成功したので、エディターに戻り、新しい Blaster キャラクター クラスに基づいてキャラクター ブループリントを作成します。


次に、コンテンツを右クリックして、新しいフォルダーを作成します。


これは、このプロジェクトのために作成するすべてのブループリントに適用されます。


このフォルダを blueprints と呼び、blueprints フォルダ内に character フォルダを作成します。


ということで、これをフォルダキャラクターと呼ぶことにします。


そして、BlasterCharacter に基づいて新しい C++ クラスを作成できるように、ここにキャラクターのブループリントを配置します。

では、そうしましょう。


そして、Blueprint のキャラクターに貼り付けます。


ここで、この BP_BlasterCharacter に名前を付けます。


そうすれば、どれがブループリント クラスで、どれが C++ クラスであるかがわかります。


それでは、Create Blueprint Class をクリックしましょう。


これが CharacterBlueprint です。


これで、すぐにメッシュを選択できます。


メッシュを選択します。


骨格メッシュには、Learning Kit ゲームからキャラクターを選択します。


今、このように見えるエピックキャラクターがいます.


Y 方向を向いていることがわかり、X 方向に向けたいと思います。


というわけで、90度回転させます。


現在、その足はカプセルの中心にあり、カプセルの半分の高さは 88 です。


したがって、メッシュを 88 単位下に移動できます。


その位置 Z を取得して、-88 に設定しましょう。


これで、キャラクターが地面に置かれます。


というわけで、コンパイルして保存します。


これで、キャラクター クラスとブループリントが完成しました。


これでキャラクター クラスが作成され、このクラスに基づく設計図ができました。このクラスをキャラクター フォルダーに貼り付けました。


したがって、C++ ソリューションは整理されています。


今後のビデオでは、カメラとスプリング アームを追加し、いくつかの入力とプログラム動作機能を追加します。


また後で。