主なコンテンツ

〜主なコンテンツ〜

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

2022年12月10日土曜日

27. Polishing the Menu Subsystem

 このビデオへようこそ。


いくつかのことを磨いて、メニュー システムを完成させます。


ホスティングが常に可能であることを確認したいと思います。


そのため、メニューが何を行っているかを見て、それが事実であることを確認できるようにします.


また、簡単にゲームを終了できるように、クイック ゲーム ボタンをメニューに追加します。 また、メニュー ボタンをクリックしたら無効にして、セッションが作成される前に複数のセッションを作成しようとしないようにします。


それでは始めましょう。


まず、セッションが既に作成されている場合に、セッションをホストすることに注意しましょう。


いくつかのテストを行っている場合は、小さな問題に気付いたかもしれません。


別のマシンでセッションを実行していて、このマシンから参加をクリックして参加するとします。


そして今、私はこのゲームセッションにいます。


ここで、ホスト プレイヤーがゲームを終了することですべてのプレイヤーをキックすることを決定したとしましょう。


そのため、他のマシンのテスターはゲームを終了し、メイン メニューに戻ります。


だから私はメインメニューに戻ってきました。


自分のゲーム セッションをホストできるようにしたいのですが、ホスト ボタンをクリックすると、ゲーム セッションの作成に失敗したことがわかります。


しかし、ここに面白いことがあります。


ほんの数秒待ってからもう一度ホストをクリックすると、ホスティングされます。


それで、ここで何が起こっているのですか?


それでは、コードを見てみましょう。


私は multiplayerSessionSubsystem.cpp にいます。 下にスクロールして createSession に移動します。 既存のセッションがあるかどうかを確認していることがわかります。存在する場合は、destroySession を呼び出します。


しかし問題は、destroy session を呼び出した直後に、create session を呼び出すことです。


Destroy Session が呼び出されましたが、その情報がネットワークを介してサーバーに移動し、そのセッションが破棄されるまでには時間がかかります。


そのため、Create Session をすぐに呼び出すと、セッションがまだ破棄されていない可能性があります。


その場合、セッションの作成に失敗します。


したがって、新しいセッションを作成する前に、セッションを破棄するアクションが完了していることを確認する必要があります。


幸いなことに、サブシステムでのセッションの破棄を含め、これらすべてのセッション アクションに対していくつかの関数とコールバックを作成しました。


現在使用していない destroySession 関数を作成しました。 また、sessionInterface にデリゲート リストを追加できるデリゲートもあります。


この destroySession 関数は、実際にはデリゲートにバインドされています。


コンストラクターに戻ると、destroySessionCcompleteDelegate があり、onDestroySessionComplete 関数がこれにバインドされていることがわかります。


そのため、セッション インターフェイスに直接アクセスして destroy session を呼び出すのではなく、destroy session 関数を実装し、create session で使用する必要があります。


サブシステム関数 destroySession でこの呼び出しを処理する必要があります。


これが完了したら、onDestroySessionComplete コールバック関数で反応し、そこで関数を作成できます。


したがって、新しいセッションを作成していて、セッションが既に存在する場合、ここでサブシステム関数で createSession を呼び出すと、失敗するセッションを作成することがわかります。


失敗することがわかっているので、セッションとコールバック onDestroySessionComplete を作成できます。


ただし、セッションをホストしていることがわかっている場合にのみ、そのコールバックでセッションを作成したいと考えています。


したがって、いくつかの変数を作成することで、これを簡単に行うことができます。


multiplayerSessionSubsystem.h に戻り、ここの一番下にいくつかのプライベート変数を追加します。


まず、ブール値を作成したいと思います。これを bCreateSessionOnDestroy と呼びます。


ここでは false で初期化します。


そして、セッションが破棄されたときに、これとコールバックを確認します。


真であれば、新しいセッションを作成します。


ここで、セッションが既に存在するためにセッションの作成が失敗することがわかっている場合は、新しいセッションに関する情報を保存する必要があります。


開いている公開接続の数とマッチ タイプを作成したいと考えています。


この情報が必要な理由は、ここで Create Session を呼び出すと、開いているパブリック接続の数とマッチ タイプを渡す必要があるためです。


したがって、作成に失敗した最後のセッション用にこれらを保存します。


そこで、最後の NumPublicConnections という名前の int32 型のメンバー変数を作成します。 LastMatchType という名前の FString を作成します。 ここでこれらを CPP ファイルに設定し、セッションを作成できます。


したがって、Destroy Session を呼び出しているところでは、その情報を保存する必要があります。セッションが既に存在するため、セッションの作成に失敗するからです。


そして、destroy session コールバックで、Create Session を再度呼び出します。


ここで、既存のセッションが null ポインターでない場合は、最初にブール値の createSessionOnDestroy を true に設定します。 次に、パブリック接続の数と lastNumPubliCconnections を保存し、それを NumPublicConnections に等しく設定します。


したがって、numPublicConnections.

また、そのマッチ タイプも保存します。


したがって、lastMatchType は matchType と等しいと言えます。


ここで、セッション インターフェイスで直接 destroySession を呼び出しています。


しかし、ヘッダー ファイルで使用するサブシステムで定義した destroySession 関数を呼び出したいと思います。


上にスクロールすると、destroySession が入力パラメータを取らないことがわかります。


したがって、ここの createSession では、既存のセッションが既に存在する場合は、単純に DestroySession を呼び出します。


Welcome in this video.


We're going to finish up our menu system by polishing up a few things.


We'd like to make sure hosting is always possible.


So we're going to take a look at what our menu is doing so that we can make sure that's the case.


We're also going to add a quick game button to the menu so we can exit the game easily. and we're going to disable the menu buttons once we click them so that we're not trying to create multiple sessions before one is already created.


So let's get started.


First, let's take care of hosting a session when one has already been created.


Now, if you've been doing some testing, you may have noticed a little issue.


Let's say I have a session running on another machine and I click join from this machine to join it.


And now I'm in this game session.


Now, let's say that the hosting player decides to kick all the players by leaving the game.


So my tester on the other machine is going to exit the game and kick me back to the main menu.


So I've been kicked back to the main menu.


Now I'd like to be able to host my own game session, but if I click the host button, we'll see that we failed to create a game session.


But here's the funny thing.


If we wait just a few seconds and click host again, then we're hosting.


So what's going on here?


Well, let's take a look at our code.


I'm here in multiplayerSessionSubsystem.cpp. and if I scroll down to createSession. I see that I am checking if there is an existingSession and if there is, I call destroySession.


But the problem is immediately after calling destroy session, we then call create session.


So Destroy Session has been called, but it takes time for that information to travel across the network to the server to destroy that session.


So if we're calling Create Session immediately, the session might not have been destroyed yet.


And in that case, we fail to create the session.


So we need to make sure that the action of destroying the session has been completed before we go and create a new session.


Now, luckily, we have created some functions and callbacks for all of these session actions, including destroying a session here in our subsystem.


We created a destroySession function which we're not currently using. and we also have a delegate that we can add to the sessionInterface a delegate list.


This destroySession function is in fact bound to our delegate.


If we go back up to the constructor, we'll see that we have destroySessionCcompleteDelegate and our onDestroySessionComplete function is bound to this.


So we need to implement our destroy session function and use it here in create session rather than accessing the session interface directly and calling destroy session.


We should be handling this call in our subsystem function  destroySession.


Now, once we do that, we can react in our onDestroySessionComplete callback function, and then we can create a function there.


So if we're creating a new session and a session already exists, then we know that when we call createSession here in our subsystem function, create a session that's going to fail.


Since we know that's going to fail, we can create the session and our callback onDestroySessionComplete.


But we only want to create a session in that callback if we know we're hosting a session.


So we can do this easily by creating a couple of variables.


Let's go back to multiplayerSessionSubsystem.h and we'll add a couple of private variables down here at the bottom.


First, I'd like to create a boolean and I'm going to call this bCreateSessionOnDestroy.


And I'll initialize it here with false.


And we're going to check this and our callback when a session has been destroyed.


And if it's true, we're going to create a new session.


Now, if we know that creating a session will fail because one already exists, we're going to want to store some information about the new session.


We'd like to create the number of open public connections and the match type.


And the reason we need that information is because once we call Create Session here, we have to pass in the number of open public connections and the match type.


So we're going to store these for the last session we failed to create.


So we're going to make a member variable of type int32 called last NumPublicConnections. and we'll make an FString called LastMatchType. and we can set these here in the CPP file and create session.


So right here where we're calling Destroy Session, we need to store that information because we're going to fail creating a session as it already exists.


And in our destroy session callback we're going to call Create Session again.


So here, if existing session is not null pointer, we're going to first set our Boolean createSessionOnDestroy to true. and we're then going to store the number of public connections and lastNumPubliCconnections and we'll set that equal to NumPublicConnections.


So numPublicConnections.

And we're also going to store that match type.


So we're going to say lastMatchType equals matchType.


Now we're calling destroySession here directly on the session interface.


But I'd like to call our destroySession function that we defined here in the subsystem that we should be using back in the header file.


If we scroll up, we'll see that destroySession here takes no input parameters.


So here in createSession, if there already is an existing session, we're simply going to call, DestroySession.


Now, we haven't implemented this function yet.


So let's scroll down to Destroy Session.


And here is where we'd actually like to call the session interface function, destroy session.

また、デリゲートの管理など、他のことも処理できます。

したがって、最初に行うことは、セッション インターフェイスが有効であることを確認することです。

したがって、sessionInterface.IsValid でない場合は、戻ります。

それだけでなく、MultiplayerOnDestroySessionComplete という、作成したカスタム デリゲートをブロードキャストします。

したがって、ブロードキャストを呼び出し、メニューでこれにバインドしたコールバックがセッションの破棄が成功したかどうかを判断するブール値を受け取るため、false をブロードキャストします。

セッション インターフェイスが有効でない場合、その値は false になります。

これで、マルチプレイヤー セッション サブシステムにデリゲートができました。これをセッション インターフェイスのデリゲート リストに追加します。

したがって、sessionInterface->addOnDestroySessionCompleteDelegate_handle を呼び出して、そのデリゲートを渡します。ヘッダー ファイルに戻ると、ここのプライベート セクションに DestroySessionCompleteDelegate という名前が表示されます。

それが今から渡すものです。

また、これを格納できるデリゲート ハンドルもあるので、後でセッション インターフェイスのデリゲート リストからこのデリゲートを削除できます。

それで、それを取得します。

これは destroySessionCompleteDelegate_handle と呼ばれ、この関数からの戻り値を取得して保存します。

そしてデリゲート ハンドル。

これで、このデリゲートのデリゲート ハンドルができました。

これで、destroySession を呼び出すことができます。

したがって、sessionInterface->destroySession とします。

これにはセッション名が必要で、name_gameSession を使用します。

しかし、他のインターフェイス関数と同様に、これは bool を返し、その bool の値をチェックします。

これを IF チェックでラップして、これが false を返すかどうかを確認します。

ここでは否定演算子を使います。

したがって、これが false を返す場合、セッションの破棄が失敗したことがわかります。

そのため、デリゲートをクリアし、false 値でメニューにブロードキャストします。

まず、デリゲートをクリアします。

sessionInterface->ClearOnDestroySessionCompleteDelegate_handle とします。

これにはデリゲート ハンドルが必要で、ここではその値を設定するだけです。

そこで、ここでデリゲート ハンドルを渡します。

これで、メニューに false をブロードキャストできます。これは、この if チェックで行ったことです。

それをコピーしてそこに貼り付けます。

そのため、DestroySession を呼び出しています。

セッションを破棄するアクションが完了すると、マルチプレイヤー セッション サブシステムのコールバック関数が呼び出されます。これは、ここでセッション インターフェイス デリゲート リストに追加したためです。

それでは、そのコールバックに行きます。

これを onDestroySessionComplete と呼びました。

ここに onDestroySessionComplete があります。

まず、デリゲート ハンドルを使用してデリゲートをクリアします。

セッション インターフェイスの場合は、セッション インターフェイスにアクセスして、clearOnDestroySessionCompleteDelegate_handle でデリゲートをクリアし、destroySessionCompleteDelegateHandle というデリゲート ハンドルを渡します。

これでデリゲートがクリアされました。

しかしここで、新しいセッションを作成する必要があるかどうかを確認する必要があります。

そのため、最初にブール値の bWasSuccessful を確認します。

その通り。

これは、セッションの破棄に成功した場合にのみ行います。

ここで、新しいセッションを作成できます。

しかし、作成した新しいブール値が true の場合にのみ、これを実行したいと考えています。

そこで、この if チェックに and を追加して、bCreateSessionOnDestroy の値をチェックします。

この値が true の場合、メニューから create session を呼び出したことが原因です。

そのため、新しいセッションを作成しようとしましたが、セッションは既に存在していました。

したがって、DestroySession を呼び出しました。

セッションの破棄に成功すると、ここまでになります。 この目的のために、サブシステムで destroySessionComplete コールバックを実行し、このブール値をチェックして、新しいセッションを作成する必要があるかどうかを確認します。

まず、このブール値 bCreateSessionOnDestroy を false にリセットします。

そうすれば、後でセッションを破棄しても、誤って新しいセッションを作成することはありません。

そして、create session 関数を呼び出すだけです。

これがサブシステムの関数です。

また、この関数には、開いているパブリック接続の数とマッチ タイプが必要です。

そのため、作成したばかりの新しいメンバー変数に使用した最後の値を保存しました。

それらは、last num public connections および last match type と呼ばれます。

したがって、セッションを作成しようとして失敗すると、セッションが既に存在するためにセッションが破棄されます。

そして、その破壊が完了すると、新しいものを作成します。

ここで、新しいものを作成したかどうかに関係なく、デリゲートをメニューにブロードキャストして、セッションが正常に破棄されたことを確認する必要があります。

そして、将来のゲームでは、その場合に何かをしたい場合は、メニューで行うことができます.
And we can also handle other things like managing our delegates.

So the first thing we're going to do is make sure our session interface is valid.

So we're going to say, if not sessionInterface.IsValid, then we're going to return.

But not only that, we're going to broadcast our custom delegate we created called MultiplayerOnDestroySessionComplete.

So we're going to call broadcast and we're going to broadcast false as our callback that we bound to this on the menu receives a boolean that determines whether or not destroying the session was successful.

And if our session interface is not valid, then that value will be false.

Now we have a delegate here on our multi-player session subsystem that we'd like to add to the session interface delegate list.

So we're going to call sessionInterface->addOnDestroySessionCompleteDelegate_handle, and we'll pass in that delegate, which if we go back to the header file, we'll see down here in the private section, it's called DestroySessionCompleteDelegate.

So that's what we're going to pass in now.

We also have a delegate handle that we can store this in so we can remove this delegate from the session interface delegate list later.

So we're going to get that.

It's called destroySessionCompleteDelegate_handle, and we'll take the return value from this function and store it.

And our delegate handle.

So now we have our delegate handle for this delegate.

Now we can call destroySession.

So we're going to say sessionInterface->destroySession.

And this requires a session name and we're going to use name_gameSession.

But like our other interface functions, this returns a bool and we're going to check the value of that bool.

So let's wrap this in an IF check and we're going to check to see if this returns false.

So we'll use the negation operator here.

So now if this returns false, we know that destroying the session has failed.

So we'll clear the delegate and we'll broadcast to our menu with a false value.

So first, we'll clear the delegate.

We're going to say sessionInterface->ClearOnDestroySessionCompleteDelegate_handle.

And this requires a delegate handle and we just set that value here.

So we're going to pass in our delegate handle here.

And now we can broadcast false to the menu, which is what we did here in this if check.

So we're going to copy that and paste it there.

So now we're calling DestroySession.

And when the action of destroying a session is completed are multi-player session subsystem callback function for this will be called because we've added it to the session interface delegate list here.

So we're going to go to that callback.

We called it onDestroySessionComplete.

We have it down here now, An onDestroySessionComplete.

First, we're going to clear the delegate using the delegate handle.

So we're going to say if session interface, then we can access the session interface and clear the delegate with clearOnDestroySessionCompleteDelegate_handle and we'll pass in our delegate handle called destroySessionCompleteDelegateHandle.

And now we've cleared that delegate.

But now we need to see if we should create a new session.

So we're going to first check our boolean bWasSuccessful.

That way.

We'll only do this if we've successfully destroyed a session.

Now, in here, we can create a new session.

But we only want to do this if that new boolean we created is true.

So I'm going to add an and here to this if check, and check the value of bCreateSessionOnDestroy.

If this value is true, then what happened was we called create session from our menu.

So we were trying to create a new session, but a session already existed.

Therefore we called DestroySession.

Once we've successfully destroyed a session, we'll end up here. to this end, destroySessionComplete callback here on our subsystem, and we're checking this boolean to see if we should try to create a new session.

Now, the first thing I'm going to do is reset this boolean, bCreateSessionOnDestroy to false.

That way, if we destroy the session later, we won't accidentally create a new session.

And then we can simply call our create session function.

That's the function here on our subsystem.

And this function requires the number of open public connections and the match type.

That's why we stored the last values we used in our new member variables we just created.

They're called last num public connections and last match type.

So now if we're trying to create a session and it fails because one already existed will destroy a session.

And once that destruction is completed, will create a new one.

Now, regardless of whether we just created a new one or not, we should broadcast our delegate to the menu to make sure it knows that a session has successfully been destroyed.

And then in any future games, if we'd like to do something in that case, we can on the menu.
したがって、multiplayerOnDestroySessionComplete.broadcast と言って、bWasSuccessful の値を単純にブロードキャストします。

これで、私たちが参加したセッションをホストしているプレーヤーが退出してすべてのプレーヤーをキックすることを決定した場合、プレーヤーは [セッションの作成] をクリックできるようになります。

そのセッションがまだ存在する場合は、それを破棄します。

また、そのセッションが正常に破棄されるまで、新しいセッションは作成されません。

サブシステムでこのコールバックに到達した時点で、開いているパブリック接続の最後の数と、保存した値のタイプを使用して、Create Session を再度呼び出します。

それでは、これをコンパイルしてみましょう。

わかった。

そのため、別のマシンでゲームを実行し、セッションを再度ホストします。

ここで、新しいプロジェクトを右クリックし、[ゲームを起動] をクリックして、プロジェクトを起動します。

現在、私の別のマシンがゲームをホストしています。

ゲームに参加した今、私はそのゲームに参加するつもりです。

他のマシンでテスターを使用して、ゲームを終了し、メニューに戻ることができます。

さて、メニューに戻りました。

ホストをもう一度クリックすると、セッションの作成に失敗し、そのセッションを破棄したことがわかります。

そして、on destroy セッション コールバックで、Create Session を再度呼び出し、最初にセッションを作成しようとしたときに使用したのと同じ値を渡します。

したがって、セッションが既に存在する場合は、ホスト セッションをクリックするだけで新しいセッションをホストすることができ、それが機能します。

わかった。

次に、メニューから簡単に終了できるように、クイック ゲーム ボタンを追加します。

クイック ゲーム ボタンを追加するために、WBP_menu があるマルチプレイヤー セッション コンテンツ フォルダーに移動します。

このメニュー ウィジェットを開きましょう。

ここでメニュー ウィジェットに、単純に終了ボタンを追加します。

パレットからボタンをクリックし、ここの右上隅にドラッグして再調整します。

次に、アンカーをクリックして、これを右上に固定します。

そして、このボタンの名前を終了ボタンに変更します。

そして、このボタンにテキストを追加します。

テキストをボタンに直接ドラッグし、フォントを太字から標準に変更し、テキストを終了に変更します。

これで終了ボタンができました。

これで、C++ でこれを処理するという問題を解決できます。

ただし、ゲームを終了するなどの単純な機能の場合は、実際には必要ありません。

ボタンを選択した状態で詳細パネルを下にスクロールし、クリック時に横にあるプラス ボタンをクリックするだけです。

これにより、一部の機能をエンド クリックにバインドし、そのボタンを委任することができます。あとは、ゲームを終了するだけです。

そこで、quit game と入力してドラッグし、ここで簡単なゲーム ノートを使用して、[コンパイル] をクリックして保存します。

プロジェクトを右クリックしてゲームの起動を選択してゲームを起動すると、終了ボタンが表示されます。

終了をクリックすると、ゲームを終了します。

最後に、メニュー ボタンを無効にします。

そのようにクリックすると、ボタンをスパムして呼び出したり、セッションを作成したり、セッションを複数回検索したりすることはできなくなります。

メニューボタンを無効にするのは簡単です。

menu.cpp に移動し、HostButtonClicked と joinButtonClicked の関数まで下にスクロールするだけです。

これらのボタンをクリックしたらすぐに無効にしたいのですが、ボタン自体にアクセスすることで無効にできます。

ここでは、ホスト ボタンにアクセスし、関数 SetIsEnabled を呼び出しています。

false を渡すと、ボタンが無効になり、うまくいきます。

ホスト ボタンをクリックするとすぐにこれを呼び出し、参加ボタンをクリックしたときにもここで呼び出します。

したがって、これには joinButton を使用し、joinButton->SetIsEnabled を呼び出して false を渡します。

HOST または JOIN に失敗した場合は、最終的にこれらのボタンを有効にして、参加またはホストを再試行できるようにする必要があります。

なんらかの理由でホスト ボタンをクリックしてセッションの作成に失敗した場合は、ここで onCreateSession コールバックを呼び出すことができ、ここで bWasSuccessful をチェックしていることがわかります。

セッションの作成に失敗した場合、このセッション作成失敗メッセージを画面に表示します。

この時点で、ボタンを再度有効にできます。

したがって、hostButton->setIsEnabled に true を渡すとします。

次に、ホスト ボタンを再度有効にします。

ただし、参加ボタンをクリックしても、セッションが見つからない場合があります。

これの最も明白な理由は、現在誰もセッションをホストしていないことです。

ここで onFindSessions までスクロールします。

ここの一番下で、bWasSuccessful が false かどうかを確認します。

そこで、否定演算子を使用して、if (!bWasSuccessful) とします。

ただし、セッションが正常に見つかったが、何らかの理由でセッション検索結果の配列が空である場合は、ボタンを再度有効にする必要があるため、ここで注文したいと思います。

So we're going to say multiplayerOnDestroySessionComplete.broadcast and we'll simply broadcast, the value of bWasSuccessful.

So now if a player's hosting a session that we've joined and they decide to leave and kick all the players, then the players should be able to click Create Session.

And if that session still exists, will destroy it.

And we won't create a new one until that session has successfully been destroyed.

And we reach this callback here in our subsystem, at which point we'll call Create Session again using the last number of open public connections and match type of values that we stored.

So let's go ahead and compile this now.

Okay.

So I'm going to have my other machine run the game and host a session again.

And I'm going to launch my project here by right clicking the new project and clicking launch game.

Now my other machine is hosting a game.

I'm going to join that game now that I've joined the game.

I can have the tester on my other machine, exit the game and kick me back to the menu.

Okay, so I've been kicked back to the menu.

Now I can click host again and we see that we failed to create a session and then we destroyed that session.

And in our on destroy session callback we called Create Session again, passing in those same values that we used when we tried to create the session the first time.

So now, if a session already exists, we'll get to host a new session by simply clicking host session, and it'll work.

Okay.

So the next thing we'd like to do is add a quick game button so that we can exit from the menu easily.

Now to add a quick game button, we're going to go into our multiplayer sessions content folder where we have WBP_menu.

Let's open up this menu widget.

Now here in the menu widget, I simply want to add a quit button.

So I'm going to click on a button from the palette and drag it in to the top right corner here and readjust it.

Now I'm going to click on anchors and anchor this to the top right.

And I'm going to rename this button, quit button.

And I'm going to add some text to this button.

So I'll drag in a text directly onto the button, and I'm going to change the font from bold to regular and change the text to quit.

So now we have a quit button.

Now we can go through the trouble of handling this in C++.

But for a simple function like quitting the game, there's really no need.

I can simply scroll down in the details panel with our button selected and click the plus button next to on click.

And this will allow us to bind some functionality to the end clicks, delegate for that button, and all we want to do is quit the game.

So I'm going to drag off type quit game and use a quick game note here so I can click compile and save.

Now if I launch my game by right clicking my project and selecting launch game, now there's a quit button.

And if I click quit, then we quit the game.

Now the last thing we're going to handle is disabling the menu buttons.

Once we've clicked them that way, we can't spam the buttons and attempt to call, create session or find sessions multiple times.

Disabling menu buttons is easy.

All we need to do is go into menu.cpp and scroll down to our functions which HostButtonClicked and joinButtonClicked.

As soon as we've clicked these buttons we want to disable them and we can do that by accessing the buttons themselves.

Here I'm accessing host button and calling the function SetIsEnabled.

If we pass in false then the button will be disabled and it'll be great out.

We're going to call this as soon as we've clicked host button and we're also going to call it here when we've click the join button.

So we'll use the joinButton for this one and we'll call joinButton->SetIsEnabled and we'll pass in false.

Now if we failed to HOST or JOIN, then we should eventually enable these buttons so we can attempt to join or host again.

So if for any reason we've clicked the host button and failed to create a session, then we can come up here to our onCreateSession callback and we see that we're checking bWasSuccessful here.

Now if we fail to create a session, we're printing this fail to create session message to the screen.

And at this point we can enable the button again.

So we're going to say hostButton->setIsEnabled passing in true.

So then we're re enabling the host button.

Now if we click the join button, however, we may not find any sessions.

The most obvious reason for this would be that no one is currently hosting a session.

So we can scroll up here to our onFindSessions.

So down here at the bottom, I'm going to make a check to see if bWasSuccessful is false.

So I'll use the negation operator and say, if (!bWasSuccessful).

But I'd also like to place an order here because if we've successfully found sessions, but for some reason the array of session search results is empty, then we should enable the button again.

したがって、sessionResults.num == 0 とします。

したがって、bWasSuccessful が false であるかどうか、または sessionResults TArray が空であるかどうかを確認すると、この内部に到達します。

その場合は、joinedButton をもう一度有効にしたいと思います。

したがって、joinButton->setIsEnabled と言って、これに true を渡します。

さて、これに加えて、[Sessions の検索が成功し、配列にセッションがあるが、それらに参加できない] という可能性もあります。

その場合、参加セッションに到達し、ここでの結果は成功しないため、それを確認する必要があります。

そのため、on join セッションの下部に if チェックを配置します。

[if (Result != )] と言い、EOnJoinSessionCompleteResult を使用し、成功タイプをチェックします。

したがって、onJoinsSession コールバックに到達しても、セッションへの参加に失敗した場合は、参加ボタンを再度有効に設定して、クリックしてセッションへの参加を再試行できるようにします。

これは、セッションがまだ存在し、誰かがそのセッションを破棄せずに終了した場合に発生する可能性があります。

したがって、destroy session が呼び出されるまで、セッションはまだそこに存在しますが、移動先の有効なアドレスがないため、セッションに参加できません。 そして、ゲームをより適切に終了し、後でゲームを終了するときに常にセッションを破棄するようにします。

それでは、コンパイルして、もう一度プロジェクトを起動します。

今回はゲームセッションがないので、ゲームセッションがないときに参加ボタンをクリックします。

そして、私がそうするとすぐに、セッションを見つけようとしている間、ボタンはほんの 1 秒間グレー表示されます。

次に、find sessions で呼び出されるメニューのコールバックが呼び出されます。これは、その joinButton を再度有効にするセッションが見つからないためです。

ホスト ボタンをクリックするとすぐに、ホスト ボタンがグレー表示され、実際にロビーに移動する前にもう一度クリックすることはできません。

データがネットワークを介して急速に送信されたため、非常に迅速に発生しましたが、わずか 1 秒間グレー表示されたため、クリックできませんでした。 新しいセッションを作成している最中でした。

要約すると、メニューシステムにいくつかの洗練されたタッチを追加しました。

現在、セッション破棄でセッションを作成しています。

したがって、セッションが既に存在する場合に新しいセッションを作成しようとすると、multiplayerSessionSubsystem 関数の destroy session が呼び出され、インターフェイスにアクセスして destroySession 関数が呼び出されます。

次に、サブシステム コールバックが呼び出されると、セッションがまだ存在するために sessionCreation が失敗したため、セッションを作成する必要があるかどうかを確認してから、Create Session を再度呼び出します。

メニューから終了できるように終了ボタンも追加しました。

最後に、メニュー ボタンをクリックしたら無効にし、セッションが見つからない場合や作成に失敗した場合に再度有効にします。

このコースの実際のゲームを作成するときに、さらにメニュー要素を追加します。

しかし今では、ゲーム セッションを処理するために設計された堅牢なメニュー システムがあります。

よくやった。

また後で。

So we're going to say sessionResults.num == 0.

So we'll reach the inside of this if check if bWasSuccessful is false or if the sessionResults TArray is empty.

If that's the case, I'd like to enable the joinedButton again.

So I'm going to say joinButton->setIsEnabled and passing true for this.

Now, in addition to this, there's also a possibility that [finding Sessions is successful and there are sessions in the array, but we can't join them].

In that case, we would get to on join session and the result here would not be a success, So we need to check that.

So we're going to place an if check at the bottom of on join session.

We're going to say [if (Result != )] and we're going to use EOnJoinSessionCompleteResult, and we'll check against the success type.

So if we've made it to the onJoinsSession callback, but joining the session was not successful, then we're going to set our join button to enable again so we can click and try to join sessions again.

This can happen if a session still exists and someone exited without destroying that session.

So until destroy session is called, a session still exists out there, but we can't join it as there is no valid address to travel to. and we'll take care of exiting the game more gracefully and making sure that we always destroy a session when we exit from a game, later on.

So let's compile and I'm going to once again launch my project.

In this time there are no game sessions, so I'm going to click the join button when no game sessions exist.

And as soon as I do, the button is grayed out for just a second while we're trying to find sessions.

Then our callback in the menu called on find sessions will be called and because we found no sessions where enabling that joinButton again.

Now as soon as I click the host button, we should see the host button grayed out so we can't click it again for just a moment before we actually travel to the lobby, so if I click it.

It happened very quickly because the data was sent across the network rapidly, but it was grayed out for just a second, so I couldn't click it. While we were in the process of creating a new session.

So in summary, we've added a few polishing touches to our menu system.

We're creating a session on session destroy now.

So if we attempt to create a new session when a session already exists, we're calling our multiplayerSessionSubsystem function destroy session, which will then access the interface to call its destroySession function.

Then when our subsystem callback is called, we check to see if we should create a session because our sessionCreation failed as a session still existed, and then we call Create Session again.

We also added a quit button so that we can quit from the menu.

And finally we disabled the menu buttons once we click them and then re-enable them in the event that we failed to find a session or failed to create one.

We'll be adding more menu elements when we create the actual game for this course.

But now we have a robust menu system designed for handling game sessions.

Great job.

I'll see you soon.

0 件のコメント:

コメントを投稿