主なコンテンツ

〜主なコンテンツ〜

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月3日土曜日

22. Join Sessions from the Menu

 いらっしゃいませ。


このレクチャーでは、新しいコールバック システムを使用して、メニューからセッションを見つけて参加します。


それでは、まずセッションの検索に取り組みましょう。


これが menu.cpp です。Join Button Collect と呼ばれる参加ボタンのコールバックに進みます。


今は単純にメッセージを画面に出力していますが、この結合されたボタンの収集が機能することがわかっているので、そのメッセージは必要ありません。


必要なのは、アクティブなゲーム セッションを見つけることです。


したがって、サブシステム関数 find sessions を呼び出す必要があります。


そのため、最初にマルチプレイヤー セッション サブシステムが有効かどうかを確認し、次に multiplayerSessionsSubsystem-> findSessions を呼び出します。


これには init32 が必要であることがわかります。


それは最大です。


の検索結果。


ここでも、80 の Steam 開発 ID を使用しています。


そのため、同じ開発能力を使用している他の人々によって作成された多くのセッションが行われている可能性があります。


したがって、10,000 などの高い値を使用します。


現在、おそらく 10,000 セッションは見つかりませんが、多数のセッションがある場合は、すべてのセッションを見つけることができます。


このようにして、これらのセッションのいずれかがゲームに有効かどうかを確認できます。


現在、multiplayerSessionsSubsystem->findSessions 関数は現在空です。


multiplayerSessionsSubsystem.cpp に戻り、セッション検索機能に進みましょう。


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


現在何もしていないので、findSessions が必要です。


ここで、キャラクター クラスに戻り、それをどのように行ったかを思い出します。


この menuSystemCharacter.cpp では、createGameSession を最小化して、joinGameSession 関数を確認できるようにします。


ここで、ゲーム セッションに参加するブループリント、呼び出し可能な関数を作成したことを思い出してください。次に、定義済みのセッションを呼び出します。ここで、これと同様のことを行います。


セッションインターフェイスを使用します。


したがって、そのポインターが有効であることを確認する必要があります。


ここでは、multiplayerSessions,Subsystem.cpp で、最初にセッション インターフェイスを確認します。


したがって、sessionInterface.IsValid ではないかどうかを判断します。


有効でない場合は戻ります。


キャラクター クラスに戻ると、findSessionsCompleteDelegate をセッション インターフェイス デリゲート リストに追加したことがわかります。


だから私たちはそれをしたいと思うでしょう。


サブシステムに戻りますが、sessionInterface->AddOnFindSessionsCompleteDelegate_Handle と言うと、パスして findSessionsCompleteDelegate になります。


これらを混同しないようにしましょう。


これは作成したデリゲートではありません。


これは、sessionInterface およびデリゲート リストのデリゲートです。


プライベート セクションまでスクロールすると、findSessionsCcompleteDelegate があることがわかります。


これは、オンライン セッション インターフェイスのデリゲート リストに追加するデリゲートです。


cpp ファイルに戻り、それを渡します。


繰り返しますが、これはハンドルを返し、デリゲートします。このためのハンドルを作成しました。


デリゲート ハンドル findSessionsCompleteDelegateHandle は十分にあります。


それを使用します。


したがって、セッション検索を追加すると、完全なデリゲート ハンドルは、セッション検索用のデリゲート ハンドルに入力するために使用できる値を返します。


このデリゲートをオンライン セッション インターフェイスのデリゲート リストに追加したので、find sessions を呼び出す準備ができました。


Character クラスに戻ると、findSessions を呼び出すには、onlineSessionSearch が必要であることがわかります。


これらはいくつかの searchSettings であり、characte でこの sessionSearch メンバー変数を作成したことを思い出してください。これは FOnlineSessionSearch をラップする TSharedPointer でした。


ここのサブシステムでそのための変数が必要になります。


そこで、multiplayerSessionSubsystem.h で、それをプライベート セクションに追加します。


TSharedPointer になり、FOnlineSessionSearch になります。


これをチェックして最後のセッション検索を確認できるため、これを lastSessionSearch と呼びます。


このメンバ変数は単なるポインタであり、新しいセッション検索を作成することで有効なデータを実際に入力できます。


したがって、lastSessionSearch = makeShareable とします。


そして、EFOnlineSessionSearch で NEW キーワードを使用して作成します。


Character クラスに戻ると、MaxSearchResults があることがわかります。


しかし、サブシステムの find sessions という関数では、これを入力パラメーターとして設定し、メニュー クラスから find sessions を呼び出したときにそれを渡したので、この値を使用して maxSearchResults を簡単に設定できます。


したがって、lastSessionSearch->MaxSearchResults = maxSearchResults とします。


だからMaxSearchResults。


Character クラスに戻ると、bIsLanQuery を設定し、SEARCH_PRESENCE を true に設定していることがわかります。


まず、bIsLanQuery です。 lastSessionSearch->bIsLanQuery とします。セッションを作成したときと同じように、null サブシステムを使用しているかどうかに基づいて、これを true または false に設定したいと思います。


Welcome.

In this lecture, we're going to find and join sessions from the menu using our new callback system.

So let's first work on finding sessions.

Now here's our menu.cpp, and we're going to go down to our callback for the join button that's called Join Button Collect.

Now we're simply printing a message to the screen, but we know now that this joined button collect works, so we don't need that message.

What we do want is to find an active game session.

So we're going to need to call our subsystem function find sessions.

So it will first check if multiplayer sessions subsystem is valid and then we'll call multiplayerSessionsSubsystem-> findSessions.

Now we see that this takes an init32.

That's for the max.

Search results.

Now again, we're using steam develop ID for 80.

So there may be lots of sessions going on out there created by other people using the same dev ability.

So we're going to use a high value such as, say, 10,000.

Now we probably won't find 10,000 sessions, but if there are lots of sessions, we'll find all of them.

And that way we can check to see if any of these sessions are valid for our game.

Now are multiplayerSessionsSubsystem->findSessions function is currently empty.

Let's go back to multiplayerSessionsSubsystem.cpp and go up to our find sessions function.

And here it is.

We're currently doing nothing, so we need to findSessions.

Now we can go back to our character class and remind ourselves how we did that.

So down here in menuSystemCharacter.cpp, I'm going to minimize createGameSession so that we can see our joinGameSession function.

Now remember we made a join game session blueprint, callable function and then we call defined sessions and here so we're going to do something similar to this.

We're going to be using the session interface.

So we need to make sure that that pointer is valid.

So here in multiplayerSessions,Subsystem.cpp, we're going to first check our session interface.

So we're going to say if Not a sessionInterface.IsValid.

And we'll return if it's not valid.

Now back in our character class, we see that we added our findSessionsCompleteDelegate to the session interface delegate list.

So we're going to want to do that.

So back here in our subsystem, we're going to say sessionInterface->AddOnFindSessionsCompleteDelegate_Handle and will pass and are findSessionsCompleteDelegate.

Now let's not get these confused.

This is not the delegate that we created.

This is the delegate for the sessionInterface and delegate list.

So we can scroll all the way down to our private section and we'll see that we have findSessionsCcompleteDelegate.

This is the delegate we're adding to the online session interface delegate list.

So back in the cpp file, we're going to pass that in.

Now, again, this returns and delegate handle and we created one for this.

We have enough delegate handle  findSessionsCompleteDelegateHandle.

We're going to use that.

So add on find sessions, complete delegate handle will return us the value that we can use to fill in our delegate handle for finding sessions.

So now that we've added this delegate to the online session interface delegate list, we can now prepare to call find sessions.

Now back in the Character class, we see that to call findSessions, we needed an onlineSessionSearch.

These are some searchSettings and recall that we made this sessionSearch member variable on the characte, It was a TSharedPointer wrapping up FOnlineSessionSearch.

We're going to want a variable for that here on our subsystem.

So in multiplayerSessionSubsystem.h, We'll add that here to the private section.

It's going to be a TSharedPointer and it's going to be an FOnlineSessionSearch. 

And since we'll be able to check this to see the last Session Search, that we used will call this lastSessionSearch.

So now that we have this member variable, which is just a pointer, we can actually fill this in with valid data by creating a new session search.

So we're going to say lastSessionSearch = makeShareable.

And we'll use the NEW keyword with EFOnlineSessionSearch and we'll construct one.

Now going back to the Character class, we see that we have MaxSearchResults.

But here in our function called find sessions on our sub system, we have this set as an input parameter and we passed that in when we called find sessions from the menu class so we can simply set the maxSearchResults using this value.

So we'll say lastSessionSearch->MaxSearchResults = maxSearchResults.

So MaxSearchResults.

Now hopping back over on the Character class, we see that we're setting bIsLanQuery and then we're setting SEARCH_PRESENCE to true.

So first bIsLanQuery. We'll say lastSessionSearch->bIsLanQuery. And just like we did when we created a session, I'd like to set this to true or false based on whether or not we're using the null subsystem.

ここで、createSession 関数と sessionSettings までスクロールして戻ることができます。

これと同じことをしました。

IOnlineSubsystem を通じてサブシステム名を取得し、それが null かどうかを確認してから、三項演算子を使用してその結果に基づいて bIsLanMatch を true または false に設定します。

それをコピーして、findSession で使用します。

したがって、bIsLanQuery は、使用しているサブシステムに基づいて true または false になります。 null でない場合。

最後に Character クラスに戻ると、set 関数を使用して、equals オンライン比較演算子 (EOnlineComparisonOp:: Equals) を使用して SEARCH_PRESENCE を true に設定していることがわかります。

それでは、この関数呼び出しをコピーしてみましょう。

そして、ここで lastSessionSearch がクエリ設定にアクセスし、ここで設定されたコールをここでプレゼンスを有効にします。

再び Character クラスで、ローカル プレイヤーを取得し、細かいセッションを呼び出していることがわかります。この 2 行もコピーできます。

そして、ここに貼り付けます。

ここで、sessionInterfacePointer は単純に sessionInterface と呼ばれるため、これを変更する必要があります。 Character では、これを onlineSessionInterface と呼びます。ですから、それを変更します。

したがって、ここで localPlayer を取得し、FindSessions を呼び出して、優先する一意のネット ID を渡してから、sessionSearch を渡して、それを ToSharedRef に変換しています。

しかし、私たちはそれを sessionSearch とは呼びませんでした。これを lastSessionSearch と呼びました。そのため、これを lastSessionSearch に変更する必要があります。

ただし、ここで別の方法で行うことは、findSessions が bool を返すことに気付き、この findSessions が false を返すかどうかを確認することです。

したがって、これを IF チェック内に配置します。

if と言って、この関数呼び出し全体を IF 内に配置します。

ここで否定演算子を使用して、この検索セッションが false を返すかどうかを確認します。

false が返された場合は、2 つのことを行い、セッション インターフェイスのデリゲート リストからデリゲートをクリアし、カスタム デリゲートをメニューにブロードキャストします。

そのため、メニューはファイン セッションが失敗したという情報を受け取ります。

したがって、最初にデリゲートをクリアします。

したがって、sessionInterface->ClearOnFindSessionsCompleteDelegate_Handle と言って、デリゲート ハンドルを渡す必要があります。

ここで、findSessionsCompleteDelegateHandle と呼ばれることがわかります。

では、それを渡します。

そして今、そのデリゲートをデリゲート リストからクリアしています。

これで、デリゲートをブロードキャストできます。

作成したデリゲートを使用します。

public セクションまでスクロールすると、multiplayerOnlineSessionsComplete という名前が付けられていることがわかります。

では、これをブロードキャストしましょう。

したがって、multiplayerOnFineSessionsComplete.Broadcast を使用します。

次に、findSessions について説明します。

メニューのコールバックには 2 つの入力があり、1 つは onlineSessionSearchResults の TArray で、もう 1 つはブール値です。

したがって、この時点で正常なセッションが失敗したことがわかっているので、このタイプの空の TArray を渡すことで、この入力パラメーターを満たすことができます。

ここで FOnlineSessionSearchResult と言って括弧を使用し、bWasSuccessful に false を渡すので、空の配列と false を渡します。

したがって、メニューがこのブロードキャストを受信して​​ false になった場合、その配列が空になることがわかります。

したがって、これは失敗のケースを処理します。

しかし、セッションが見つかった場合はどうでしょうか?

その場合、オンライン サブシステムのおかげでサブシステム コールバックが呼び出されることがわかっています。そのデリゲート リストを反復処理し、そのコールバックを起動して、findSessionsComplete で呼び出されたサブシステムのコールバックに移動できるようにします。

ここで、いくつかのことを行うことができます。

最初に、セッション インターフェイスのデリゲート リストからデリゲートをクリアできます。

したがって、if sessionInterface.

そして、セッション インターフェイスを取得して clearOnFindSessionsCompleteDelegate_Handle を呼び出し、findSessionsCompleteDelegateHandle と呼ぶデリゲート ハンドルを渡します。

そして、MultiplayerFindSessionsComplete と呼ばれるカスタム デリゲートを使用して、メニューにブロードキャストできます。

そのデリゲートにアクセスして、ブロードキャストを呼び出します。

これで、searchResults の有効な配列を渡すことができます。

これらは、lastSessionSearch と呼ばれる sessionSearch に存在するようになります。

onlineSessionSearch には searchResults があり、searchResults が FOonlineSessionSearchResults の TArray であることがわかります。

これらをメニューに渡し、bWasSuccessful を渡すことができます。

イベントでは、いくつかの検索結果が得られます。

しかし、何らかの理由でこの TArray は空です。有効な検索結果が見つからないことをメニューが認識できるように、単純にブロードキャストしたいと思います。

そのため、別の If チェックを配置して、lastSessionSearch かどうかを確認します。

Now, we can scroll back up to our createSession function here and on our sessionSettings.

We did this same thing.

We got the subsystem name through the IOnlineSubsystem and we check to see if it's null and then we set bIsLanMatch to true or false based on that result using the ternary operator?

So I'm simply going to copy that and use that down here in findSession.

So bIsLanQuery will be true or false based on the subsystem that we're using. And if it's not null.

Now finally, if we go back to the Character class, we'll see that we're using the set function to set SEARCH_PRESENCE to true using the equals online comparison operator (EOnlineComparisonOp:: Equals).

So let's go ahead and will copy this function call.

And here on lastSessionSearch will access the query settings and call set here enabling presence here.

Now again on the Character class we see that we're getting the local player and we're calling fine sessions. so we can copy these two lines as well.

And we'll paste them in here.

Now, we're going to need to change this because our sessionInterfacePointer is simply called sessionInterface. And on the Character, we called it onlineSessionInterface. So we'll change that.

So we're getting the localPlayer here and we're calling FindSessions passing in the preferred unique net ID and then we passed in sessionSearch, converting it to a ToSharedRef.

But we didn't call it sessionSearch. We called it lastSessionSearch. So we need to change that to say lastSessionSearch.

Now, something that we're going to be doing differently here, though, is we're going to notice that findSessions returns a bool and we're going to check to see if this findSessions returns false.

So we're going to place this inside of an IF check.

We're going to say if and place this whole function call inside of an IF.

And we're going to use the negation operator here to see if this find sessions returns false.

And if it does return false, we'll do two things will clear the delegate from the session interface delegate list and we'll broadcast our custom delegate to our menu.

So the menu receives the information that fine sessions was a failure.

So first will clear the delegate.

So we're going to say sessionInterface->ClearOnFindSessionsCompleteDelegate_Handle and we need to pass in the delegate handle.

So we'll see up here that it's called findSessionsCompleteDelegateHandle.

So we'll pass that in.

And now we're clearing that delegate from the delegate list.

Now we can broadcast our delegate.

So we're going to use the delegate that we created.

We'll scroll up to the public section and we'll see that we called it multiplayerOnlineSessionsComplete.

So let's Broadcast this.

So we'll use multiplayerOnFineSessionsComplete.Broadcast.

Now for on findSessions.

The callback on the menu has two inputs one's a TArray of onlineSessionSearchResults and the other is a boolean.

So since we know at this point that fine sessions was a failure, we can simply satisfy this input parameter by passing in an empty TArray of this type.

So we're going to say FOnlineSessionSearchResult and use parentheses here and then we'll pass in false for bWasSuccessful so we're passing an empty array and false.

So if the menu receives this broadcast and we get false, then we know that that array will be empty.

So this handles the failure case.

But what about when we've successfully found sessions?

Well, in that case, we know that our subsystem callback will be called thanks to the online subsystem, iterating through its delegate list and firing that callback so we can go over to our callback here on the subsystem called on findSessionsComplete and here it is.

So here we can do a couple of things.

We can first clear the delegate from the session interface delegate list.

So we're going to say if sessionInterface.

And we can get the session interface and call clearOnFindSessionsCompleteDelegate_Handle, passing and the delegate handle which we call it findSessionsCompleteDelegateHandle.

And then we can broadcast to our menu using our custom delegate, which we called MultiplayerFindSessionsComplete.

So we'll access that delegate and call broadcast.

And now we can pass and a valid array of searchResults.

Now, those are going to exist on our sessionSearch, which we called lastSessionSearch.

Our onlineSessionSearch has searchResults, and you'll notice that searchResults is a TArray of FOonlineSessionSearchResults.

So we're passing those over to the menu and then we can pass bWasSuccessful.

Now in the Event that we do get some search results.

But for some reason this TArray is empty, I'd like to simply broadcast so that the menu knows that we haven't found any valid search results.

So I'm going to place another If check and check to see if lastSessionSearch.

SearchResults.Num が 0 以下です。

これは TArray であるため、この num 関数があり、この配列が空かどうかを確認できます。

そうであれば、メニューに false をブロードキャストしたいと思います。

ここで、FindSessions 関数までスクロールして戻ります。ここにあるこのブロードキャストは、まさに私が話していることです。

したがって、配列が空の場合に onFindSessionsComplete に戻ると、単に空の配列がブロードキャストされ、bWasSuccessful の場合は false になります。

また、ここでブロードキャストを取得しないように、この時点でリターンしたいと思います。

というわけで追加チェックです。

これで、メニューから findSessions を呼び出します。

サブシステムでは、onlineSessionInterface の findfSessions 関数を呼び出しています。

いくつかのチェックを行っているだけで、デリゲート ハンドルをクリアしています。また、searchResults の配列と、これが成功したかどうかをメニューにブロードキャストしています。

成功しなかった場合は、false をブロードキャストします。

検索セッションの完了時にサブ システムでコールバックが成功した場合は、呼び出されてデリゲートをクリアし、検索結果の配列をブロードキャストします。もちろん、searchResults TArray が空の場合は false と空の配列を渡してブロードキャストします。


セッションを見つけたので、それらのセッションの 1 つを選択して参加します。

メニューに戻りましょう。参加ボタンをクリックすると、findSessions が呼び出され、サブシステムがセッションの検索を処理し、成功した場合は searchResults の TArray をメニューにブロードキャストします。

ブロードキャストによってコールバックが呼び出され、そのコールバックは onFindSessions と呼ばれます。

そして、それを見ていきます。

これが FOnlineSessionSearchResults の配列です。

ですから、それらの結果の 1 つを選んで参加したいと思います。

ここで、セッション インターフェイス デリゲートへのコールバックをバウンスしたときにこれを行ったので、再びキャラクター クラスを参照できます。

そして、これらのセッション検索結果を渡した場所がここにあります。

それで、それを行うつもりです。

このメニューでは、ここで検索結果を渡します。

ここでこれを行っている理由は、検索結果からセッションを選択する方法は、ゲームとそれをどのように設計したいかによって決まると思うからです.

検索結果をループして、正しい一致タイプを持つ最初の検索結果を検索し、JOIN します。

そして、これを改良して、後でより洗練されたものにすることができます。

しかし今のところ、これが機能していることを確認したいだけです。

したがって、sessionResults を単純にループします。

ここで for ループを作成しましょう。auto Results と sessionResults を使用します。

ここで、各結果について、マッチ タイプが正しいかどうかを確認したいと思います。

キャラクターに戻ると、結果を取得してそのセッションにアクセスすることで、ここでこれを行ったことがわかります。

セッションには sessionSettings があり、キーを指定して FString を渡す get 関数を呼び出すと、キー マッチ タイプのキーと値のペアがある場合、get 関数はその FString にこのセッションの値を入力します。 .

では、これを実行します。

それでは、この行をコピーしてここに貼り付けましょう。

ここで FString を渡しているだけで、menu.h にはマッチ タイプと呼ばれるプライベート メンバー変数があるため、マッチ タイプを使用できません。

そこで、FString ローカル変数を作成し、この settingsValue を呼び出します。

そして、それをここに渡します。

したがって、settingsValue には、このキーに対応する値が入力されます。

検索結果に実際にこのキーと値のペアが含まれている場合、それが正しいマッチ タイプかどうかを確認できます。

したがって、if settingsValue == と言って、それをローカル変数の matchType と照合します。

したがって、これが matchType と等しい場合、その特定のセッションが正しい matchType を使用しているため、有効な searchResult が得られます。

これは、参加したいセッションが見つかったことを意味します。

これは、サブシステム関数の参加セッションを呼び出すことができることを意味します。

サブシステムにジャンプして joinSession を見ると、これが単純に searchResult を受け取ることがわかります。

メニューに戻り、サブシステムで joinSession を呼び出します。

さて、何かを行う前に、サブシステム ポインターが null でないことを確認したいと思います。

したがって、multiplayerSessionsSubsystem = nullptr の場合は RETURN となり、検索結果を解析する必要はありません。

ただし、null でない場合は、searchResults を調べて、正しい matchType を持つものを見つけ、jointSession を呼び出します。

そこで、multiplayerSessionsSubsystem->joinSession を呼び出して、この FOnlineSessionSearchResult を渡します。

それが私たちがここにいる現在の結果です。

それでは、最終結果を渡します。

SearchResults.Num is less than or equal to zero.

So this being a TArray, we have this num function and we can see if this array is empty.

And if it is, then I'd like to broadcast false to the menu.

Now scrolling back up to our FindSessions function, this broadcast here is exactly what I'm talking about.

So back down here in onFindSessionsComplete if the array is empty will simply broadcast an empty array and false for bWasSuccessful.

And I'd also like to RETURN at this point so we don't get that broadcast down here.

So this is an additional check.

So now we're calling findSessions from the menu.

And here in our subsystem we're calling the onlineSessionInterface's  function of findfSessions here.

Only we're making some checks and we're clearing the delegate handle and we're also broadcasting to the menu the array of searchResults and whether or not this was successful.

If it's not successful, will broadcast false.

If it is successful than our callback here in the sub system on find sessions complete, we'll get called and then we clear the delegate here and we broadcast the array of search results. and of course we broadcast passing false and an empty array if that searchResults TArray is empty.


So now that we're finding sessions, it's time to pick one of those sessions and join it.

So let's go back to the menu and we see that when we click the join button we're calling findSessions and our subsystem will handle finding the sessions and broadcast back to the menu a TArray of searchResults if it was successful.

Now that broadcast will result in our callback being called and that callback is called onFindSessions.

And we'll see that.

Here's our array of FOnlineSessionSearchResults.

So we'd like to pick one of those results and join.

Now we can refer back to our character class again because we did this here when we bounced a callback to the session interface delegate and we call this here in the Character class onFindSessionsComplete.

And here is where we passed those session search results.

So we're going to do that.

So here in our menu, we're going to pass the search results here.

Now, the reason I'm doing this here is because I think how we choose to pick a session from our search results is going to sort of depend on the game and how you'd like to design it.

I'm simply going to loop through the search results and find the first one that has the correct match type, and JOIN.

And we can refine this and make it more sophisticated later.

But for now, we just want to make sure that this is working.

So we're going to simply loop through our sessionResults.

So let's make a for loop here and I'm going to say auto Results and sessionResults.

Now for each result, we'd like to check to see if we have the correct match type.

And if we got back over to the character, we'll see that we did this here by getting the result and accessing its session.

And on the session we have the sessionSettings and then we called the get function specifying a key and passing in an FString and the get function will fill in that FString with the value for this session if it has the key value pair with the key match type.

So we're going to do this.

So let's copy this line and we'll paste it in here.

Only we're passing in an FString here and we can't use match type because menu.h has a private member variable called match type.

So what I'm going to do is create an FString local variable and call this settingsValue.

And we'll pass that in here.

So the settingsValue will be filled in with the value corresponding to this key.

If the search result indeed has this key value pair, and then we can check to see if it's the correct match type.

So we'll say if settingsValue == and we'll check it against our local variable matchType.

So if it is equal to matchType, then we have a valid searchResult because that particular session is using the correct matchType.

So this means we've found a session that we would like to join.

This means we can call our subsystem function join session.

If we jump over to the subsystem and we look at joinSession, we see that this simply takes a searchResult.

So back here in menu, what we're going to do is call joinSession on our subsystem.

Now, before we do anything, I'd like to make sure that our subsystem pointer is not null.

So we're going to say if multiplayerSessionsSubsystem = nullptr then will RETURN and there's no need to parse the search results.

But if it's not null, then we will look through the searchResults, find one that has the correct matchType and call jointSession.

So we're going to call multiplayerSessionsSubsystem->joinSession and pass in this FOnlineSessionSearchResult.

That's the current result that we're in here.

So we're going to pass end result.

有効な検索結果が見つかった場合は、これらを繰り返しループして、それ以上の join session を呼び出す必要はありません。

ですから、この時点で戻るだけです。

そのため、サブシステムで joinSession を呼び出しています。

しかし、サブシステムに戻って、joinSession を見てみましょう。

上にスクロールすると、joinSession が何もしないことがわかります。

実際にセッション インターフェイス関数 joinSession を呼び出す必要があります。

では、ここでそれを行います。

これには、すべての代表者の世話も含まれます。

さて、ここで混乱するのは簡単です。

オンライン セッション インターフェイスのデリゲート リストのデリゲートと、独自のカスタム デリゲートがあります。

それらは私たちのメニューのために特別に作られています。

したがって、それらを順番に保つようにする必要があります。

ここで、最初に行うことは、セッション インターフェイスを確認することです。

そうでない場合、セッション インターフェイス ドットは有効です。

したがって、セッション インターフェイスが有効でない場合は、戻ります。

しかし、何かが正しくないことがわかるように、メニューにもブロードキャストします。

そこで、進行中のマルチプレイヤー セッションを取得し、委任を完了してブロードキャストします。

そして、このデリゲートは EOnJoinSessionCompleteResult::type 型の値でブロードキャストします。

したがって、EOnJoinSessionCompleteResult と言うことにします。

タイプという単語を入力する前に、これらの異なるタイプの結果があることがわかります。

そして、この F チェックでは、セッション インターフェイスが有効ではありません。

したがって、報告するエラーがあり、unknownError を選択するだけです。

そのため、セッションへの参加を続行できない場合に備えて、メニューはこの情報を受け取ります。

わかった。

これで、セッション インターフェイス ポインターのチェックが処理されます。


ここで、いくつかのことを行う必要があります。

まず、セッション インターフェイスで join session を呼び出します。これは、デリゲートを追加することを意味します。

デリゲート リストに追加し、デリゲート ハンドルに格納します。

したがって、sessionInterface->addOnJoinSessionCompleteDelegate_Handle と言って、カスタム デリゲートではなく、セッション インターフェイス デリゲート リストのデリゲートであるデリゲートを渡します。

これはここではプライベート変数であり、参加セッション完了デリゲートと呼ばれます。

ここでそれを渡し、これを joinSessionCompleteDelegateHandle と呼ばれるデリゲート ハンドルに格納します。

というわけで、ここに保存します。

これで、デリゲートをセッション インターフェイスのデリゲート リストに追加しました。

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

ここで、セッション インターフェイスを使用して joinSession を呼び出すと、一意のネット ID、sessionName、および sessionSearchResult が必要であることがわかります。

さて、ここに sessionSearchResult があります。

メニューからこの関数を呼び出したときに渡されました。

ただし、一意のネット ID が必要です。つまり、コントローラーから最初のローカル プレーヤーを取得する必要があります。これにより、ここまでスクロールしてセッションを見つけ、取得した場所にこの行をコピーできます。

では、ここでそれを行います。

したがって、joinSession を呼び出す前に、ローカル プレーヤーを取得してから、優先する一意のネット ID を渡します。

そのため、セッション名に対して localPlayer->getPreferredUniqueNetID を逆参照します。

いつものように、NAME_GAME_SESSION を使用しています。

また、sessionSearchResults には、メニューが joinSession を呼び出したときに渡された有効な searchResult を渡しています。これは sessionResult と呼ばれます。

ここで、これまで行ってきたように、joinSession が true を返すかどうかを確認します。

したがって、これを否定演算子を使用して IF チェック内に配置し、それが false を返すかどうかを確認できるようにします。これは、joinSession が失敗したことを意味します。

その場合、デリゲートをクリアします。

メニューにブロードキャストして、これが失敗したことをメニューに知らせます。

したがって、最初にデリゲートをクリアします。

sessionInterface->clearOnJoinSessionCompleteDelegate_handle と言って、パスします。

デリゲート ハンドルです。これがデリゲート ハンドルです。

そして、メニューへのブロードキャストを行うことができます。

つまり、multiplayerOnJoinSessionComplete.broadcast とします。

そして、これはセッションへの参加に失敗した場合です。そのため、EOnJoinSessionCompleteResults を使用します。ここでも、何か問題が発生したため、unknownError を使用します。

ここで、セッションへの参加が成功した場合、ここでデリゲート リストにデリゲートを追加したため、サブシステムにデリゲートがあります。つまり、このサブシステムのコールバックに入ることができます。 onJoinSessionComplete と呼ばれます。

したがって、ここでできることは 2 つだけです。

デリゲートの処理が完了したので、デリゲートをクリアし、メニューにブロードキャストして、セッションに参加したことをメニューに通知できます。

最初に、セッション インターフェイスを確認します。そしてデリケートをクリアしていきます。

継承インターフェース。 ClearOnJoinSessionCompleteDelegate_handle は、joinSessionCompleteDelegateHandle であるデリゲート ハンドルを渡します。

それでは、それを渡しましょう。そうすれば、簡単にブロードキャストできます。

And if we found a valid search result, there's no need to continue looping through these and calling join session on any more of them.

So we're just going to return at this point.

So now we're calling joinSession on our sub system.

But let's go back to the sub system and look at joinSession.

If we scroll up, we'll see that joinSession does nothing.

We need to actually call the session interface function joinSession.

So we'll do that here.

And this is also going to involve taking care of all of our delegates.

Now, it's easy to get confused here.

We have delegates for the online session interface delegate list and we have our own custom delegates.

Those are made specifically for our MENU.

So we need to make sure to keep those in order.

Now, the first thing we're going to do is check the session interface.

So if not, session interface dot is valid.

So if the session interface is not valid, then we're going to return.

But we're also going to broadcast to our menu so it knows that something isn't right.

So we're going to take our multiplayer ongoing session, complete delegate and broadcast it.

And this delegate broadcasts with a value of type EOnJoinSessionCompleteResult::type.

So we're going to say EOnJoinSessionCompleteResult.

And before we type out the word type, we'll see that there are these different types of results.

And here in this F check, the session interface is not valid.

So we have an error to report and we can simply choose unknownError.

So that way the menu will receive this information in the case that we cannot continue joining the session.

Okay.

So that takes care of checking our session interface pointer.


Now we need to do a couple of things.

First, we're going to call join session on the session interface, which means we want to add our delegate

to its delegate list and store it in a delegate handle.

So we're going to say sessionInterface->addOnJoinSessionCompleteDelegate_Handle and pass in our delegate, which is not our custom delegate, the delegate for the session interface delegate list.

This one is a private variable here and it's called join session complete delegate.

So we're going to pass that in here and then we're going to store this in a delegate handle that we call  joinSessionCompleteDelegateHandle.

So we're going to store that here.

So now we've added the delegate to the session interface delegate list.

So now we can call joinSession.

Now, if we take our session interface and call joinSession, we'll see that we need the unique Net ID the sessionName and a sessionSearchResult.

Well, we have the sessionSearchResult here.

It was passed in when we called this function from the menu.

But we do need the unique net ID, which means we need to get the first local player from controller so we can scroll right up here to find sessions and copy this line where we get that.

So we're going to do that down here.

So before we call joinSession, we'll get the local player and then we'll pass in the preferred unique net ID.

So we'll dereference localPlayer->getPreferredUniqueNetID now for the session name.

As always, we're using NAME_GAME_SESSION.

And for the sessionSearchResults we're passing in that valid searchResult that the menu passed in when it called joinSession and that's called a sessionResult.

Now, just like we've been doing, we're going to check to see if joinSession returns true or not.

So we'll place this inside an IF check with a negation operator here so we can check to see if it returns false, which means joinSession has failed.

In that case, it will clear the delegate.

And we'll broadcast to the menu to let the menu know that this has failed.

So first will clear the delegate.

We're going to say sessionInterface->clearOnJoinSessionCompleteDelegate_handle and pass.

And our delegate handle and that's this delegate handle right here.

And then we can do the broadcast to the menu.

So we're going to say multiplayerOnJoinSessionComplete.broadcast.

And this is the case where joining the session has failed. So we're going to use EOnJoinSessionCompleteResults. And again, we'll use unknownError here because something went wrong.

Now, in the case where joining a session has succeeded, well, we have our delegate here on our subsystem since we added it to the delegate list here, which means we can then go into our callback for this, which here on this subsystem is called onJoinSessionComplete.

So here we can do just two things.

We can clear the delegate since we're done with it, and we can broadcast to the menu to inform the menu that we have joined the session.

So first, we'll check the session interface. And we'll clear the delicate.

succession interface. ClearOnJoinSessionCompleteDelegate_handle passing in our delegate handle which is joinSessionCompleteDelegateHandle.

So let's pass that in and then we can simply broadcast.

ブロードキャストします。ブロードキャストするのは、onJoinSessionComplete にある結果です。

これは、デリゲート リストを調べてこれらのイベントを発生させたときに、セッション インターフェイスが渡したものです。

そして、それらの議論の 1 つがこの結果です。

そのため、結果を渡すことができ、それがメニューに渡されます。

この時点でセッションに参加し、メニューが応答できるようになりました。これは、このブロードキャストによってメニューのコールバックが呼び出されるためです。

メニューに戻りましょう。これに対するコールバックは進行中のセッションです。

そのため、進行中の joinSession では、セッションに参加したいだけです。つまり、正しい住所を取得し、正しい住所を取得するために client travel を使用してその場所に移動する必要があります。

キャラクターに戻ると、参加セッションのコールバックが完了し、セッション インターフェイスを使用して getResolvedConnectString を呼び出してそのアドレスを取得することがわかります。

そのため、ここでオンライン セッション インターフェイスにアクセスする必要があります。

これで、メニューのここから取得できます。それで問題ありません。

ここでオンライン セッション インターフェイスに直接アクセスしている場合でも、一方向の依存関係は解消されません。

メニューは、サブシステムと同様に、オンライン セッション インターフェイスに依存できます。

オンライン セッション インターフェイスがメニューに依存せず、サブシステムもメニューに依存しない限り、問題ありません。

それでは、そのセッション インターフェイスを取得しましょう。

multiplayerSessionSubsystem に戻り、一番上までスクロールしてコンストラクターを表示すると、セッション インターフェイスを取得できることがわかります。

最初に IOnlineSubsystem::get でサブシステムを取得してから、subsystem->getSessionInterface を呼び出します。

これらをここにコピーし、メニューに戻って貼り付けます。

これは、IOnlineSubsystem をインクルードする必要があることを意味します。これは、ここでサブシステムにインクルードした単に onlineSubsystem.h であることがわかります。

メニューに戻って、これをここの一番上の onlineSubsystem.h に貼り付けましょう。サブシステムからこれをコピーしたので、インクルード エラーに対処しました。メンバであったこのセッション インターフェイスにアクセスしていることがわかります。 multiplayerSessionSubsystem の変数ですが、ここにはそのような変数はありません。

ここのサブシステムとプライベート セクションは、セッション インターフェイス メンバー変数であり、オンライン セッション ポインターのような型であることを思い出してください。

メニューに戻って、これをローカル変数にすることができます。

したがって、IOnlineSessionPtr sessionInterface と言って、ここで設定します。

インターフェイスができたので、アドレスまたは解決された接続文字列を取得できます。

まず、sessionInterface.IsValid かどうかを確認します。

そして、resolvedConnectString を呼び出します。

キャラクターに戻ると、最初に address というローカル FString を作成し、それを getResolvedConnectString に渡す必要があることがわかります。

では、ここでそれを行います。

address という FString を作成し、セッション インターフェイスを取得します。

そして、getResolvedConnectString を呼び出します。

ここで使用しているオーバーロードには、セッション名と文字列が必要です。

NAME_GAME_SESSION とアドレスを使用します。

これで ADDRESS が得られました。

この時点で、単純に clientTravel を呼び出すことができます。

ゲーム インスタンスからプレイヤー コントローラーを最初に取得したキャラクターで、どのようにそれを行ったかを見ることができます。

ここで、メニュー クラスには getPlayerController 関数がないため、ゲーム インスタンスを使用していることは非常に重要です。

キャラクターでもポーンでもないので、ゲーム インスタンスから取得する必要があります。

これらの行をここにコピーして、ここに貼り付けましょう。

呼び出してプレーヤー コントローラーを取得し、ゲーム インスタンスを取得し、最初のローカル プレーヤー コントローラーを取得してから、travel の絶対 travelType を使用してアドレスを渡す clientTravel を呼び出します。

わかった。

したがって、これは多くのことを考慮する必要があり、私たちのシステムには多くの複雑さが伴います。

セッション インターフェイスによって起動されるデリゲートがあります。

サブシステムによって起動されるデリゲートがあります。

しかし、今では堅牢なシステムがあり、新しいメニュー クラスなどの新しいクラスを簡単に作成し、ゲームに応じてさまざまな方法で応答する独自のコールバックを持つことができます。

それでは、コンパイルしましょう。

これでまとめが終わりましたので、これで締めくくりたいと思います。

アンリアル エンジン エディタを閉じて、このプラグインのプロジェクト ファイルを再生成します。

それでは、プラグイン、マルチプレイヤーセッションに行き、バイナリと中間を削除して戻ってきましょう

Visual Studio プロジェクト ファイルを生成します。

それが完了したら、プロジェクトを開きます。

[はい] をクリックして、モジュールを再構築します。

そして、エディターに戻ります。

プロジェクトがコンパイルされました。

これで、システムをテストする準備が整いました。

複数のマシンでテストできると、これは非常にエキサイティングです。

そこで、プロジェクトをパッケージ化し、複数のコンピューターでテストすることに挑戦します。

So we're going to broadcast and what we'll broadcast is the result that we have here in onJoinSessionComplete.

This was what the session interface passed in when it looked through its delegate list and fired off these events.

And one of those arguments is this result.

So we can pass in result and that will be passed through to the menu.

So at this point we've joined the session and now the menu can respond because this broadcast will result in the menu's callback being called.

So let's go back to menu and the callback for this is ongoing session.

So in ongoing joinSession we just want to join the session, which means we need to get the correct address and travel to it using client travel now to get the correct address.

If we go back to our character, we'll see and it's callback on joins session completes and gets that address using the session interface and calling getResolvedConnectString.

So we need to access the online session interface here.

Now we can get that from here in the menu and that's okay.

If we're accessing the online session interface directly here, we're still not breaking the one way dependency.

Our menu can depend on the online session interface, just like our sub system does.

That's okay, as long as the online session interface does not depend on the menu and as long as our subsystem does not depend on the menu either.

So let's get that session interface.

Let's go back to multiplayerSessionSubsystem and scroll all the way up to the top to the constructor and we'll see that this is how we can get the session interface.

We first get the subsystem with IOnlineSubsystem::get and then we call subsystem->getSessionInterface.

So we'll copy these here and go back to the menu and we'll paste them in.

Now this means we need to include for the IOnlineSubsystem and we see that that's simply onlineSubsystem.h that we included here in our subsystem.

So back in menu, let's paste that up here at the top onlineSubsystem.h and now we've taken care of that include error now because we copied this from our subsystem, we see that we're accessing this session interface which was a member variable on our multiplayerSessionSubsystem, but we don't have such a variable here.

Remember on our subsystem and our private section here is our session interface member variable and it's type as I online session pointer.

Well back in our menu, we can simply make this a local variable here.

So we'll say IOnlineSessionPtr sessionInterface and we'll set it here.

Now that we have the interface, we can get the address or the resolved connect string.

So first we'll check if sessionInterface.IsValid.

And then we'll call resolvedConnectString.

If we hop back over to the character, we'll see that we first had to create a local FString called address and pass that and to getResolvedConnectString.

So we'll do that here.

We'll create an FString called address and we'll get our session interface.

And we'll call getResolvedConnectString.

Now the overload we're using will require a session name and the string.

So we're going to use NAME_GAME_SESSION and address.

So we now have the ADDRESS.

And at this point we can simply call clientTravel.

We can see how we did that in the character where we first got the player controller from the game instance.

Now, this is very important that we're using the game instance because in the menu class there is no getPlayerController function.

We're not on a character or a pawn, so we need to get it from the game instance.

So let's copy these lines here and we'll paste them in right here.

We're getting the player controller by calling, get game instance, get first local player controller and then we're calling clientTravel passing in the address using the travel, absolute travelType.

Okay.

So this is a lot to take in and our system has many complexities to it.

We have delegates that are fired off by the session interface.

We have delegates that are fired off by our subsystem.

But we now have a robust system and we can easily create new classes such as a new menu class and have its own callbacks that respond in various ways depending on the game.

So let's compile.

And now that we've compiled, I'd like to close out of this.

And with the Unreal Engine Editor Close, I'm going to regenerate project files for this plug in.

So let's go to plug ins, multiplayer sessions and delete the binaries and intermediate and come back

and generate Visual Studio project files.

And once that's done, we'll open our project.

We'll click Yes to rebuild the modules.

And now we're back in the editor.

Our project is compiled.

So we're ready to test out our system.

And this gets really exciting when you can test with multiple machines.

So I'm going to challenge you to package the project and test with multiple computers.

ここで、両方のコンピューターがそれぞれ異なる Steam アカウントをバックグラウンドで実行する必要があります。

さて、それは大丈夫です。

2 台のコンピューターまたは 2 つの Steam アカウントを持っていない場合は、私の生徒用の DISCORD に移動して、別の生徒を見つけることができます。

プロジェクトをパッケージ化して Google ドライブにアップロードし、お互いのプロジェクトをダウンロードします。

そのため、プロジェクトをダウンロードして実行することで、彼らがあなたを助けてくれます。

1 人がホストし、もう 1 人が参加して、もう 1 人のホストと参加でテストできます。

両方とも同じ Steam リージョンにいて、両方ともバックグラウンドで Steam を実行していることを確認してください。

したがって、彼らがあなたのゲームをダウンロードしてテストしたら、先に進んでゲームをダウンロードし、彼らのゲームをテストすることで彼らを助けてください.

そうすれば、インターネット経由で Steam 経由で相互に接続していることと、メニュー システム プラグインが正しく機能していることを確認できます。

また、テストしている相手が同じプラットフォームを使用していることも確認してください。

したがって、Windows マシンを使用している場合は、Windows 用にパッケージ化しているため、それらが Windows マシン上にあることを確認してください。

あなたが Mac を使用していて、Mac 用のパッケージを使用している場合は、他の生徒が Mac を使用していることを確認してください。

ビデオを一時停止して、別の生徒や別のコンピューターでゲームをテストしてみてください。

わかった。

これで、このプロジェクトをパッケージ化する準備が整いました。

Platforms Windows Package Project に行きます。

これが私のビルドフォルダーです。

以前のビルドを削除して、このフォルダーを選択します。

そして今、私はゲームをパッケージ化しています。

よし、ゲームをパッケージ化して圧縮し、Google ドライブに貼り付けた。

そして、私のDiscordから何人かの生徒にプロジェクトを自分のマシンにダウンロードして解凍してもらいました

それとゲームを起動します。

だから私はゲームをホストしています。

ゲームをロードしてクリックしました。

ホスト そして今、これらの学生のそれぞれがゲームをロードして参加をクリックしています。

また、生徒の 1 人が参加するたびに、見つかったサブシステム ストリーム文字列が左上に出力されていることがわかります。

ここには一握りの学生がいて、みんなただぶらぶらしています。

いくつかは走ったり飛び跳ねたりしていて、かなりまともなパフォーマンスをしているようです。

みんなの動きはかなり滑らかに見えます。

これがなぜなのか、次のセクションのいくつかについて説明します。

実際にこのプラグインをゲーム プロジェクトに追加してゲームの作成を開始すると、マルチプレイヤー機能が向上します。

しかし、これは素晴らしい出発点であり、このプラグインをどのプロジェクトにも追加して、多くのプレイヤーが参加し、全員が一緒にゲームをプレイできるような動作を実現できるようになりました。

次のレクチャーでは、入ってくるプレーヤーを数えます。プレーヤーがいつ参加したか、いつ参加したかを表示し、プレーヤーの名前を取得して、誰がいつ参加したかを確認できるかどうかを確認します。彼らは参加します。

このレクチャーでは、セッションを見つけて参加しましたが、キャラクター クラスからではなく、メニューからこれを行っています。

また、プラグインのコア機能が完成したので、満足しています。

これで、まだ使用していないデリゲートと関数がいくつかあります。実際にゲームの作成を開始する必要があるため、すべてのプレイヤーがロビーに参加したら何をすべきかがわかります。

通常、プレイヤーの数を数えてからタイマーを開始し、すべてのプレイヤーを試合に移行して、各プレイヤーが互いに戦ったり、ある種の協力ゲームで一緒に戦ったりできるようにします。

これで、ほとんどの場合、プラグインは完了です。

いくつかの機能を追加してから、マルチプレイヤー ゲームのゲームプレイを作成する方法について実際に学習を開始する準備が整います。

これは非常にエキサイティングです。

背中をたたいてください。

おめでとう。

これは大きなマイルストーンです。

次のビデオでお会いしましょう。

Now, both computers must each have a different steam account running in the background.

Now, that's okay.

If you don't have two computers or two steam accounts, you can go on to the DISCORD for my students and find another student.

And what you're going to do is package your projects, upload them to a Google drive, and download each other's projects.

So they're going to help you out by downloading your project and then running.

And one of you can host and the other can join and then you can test with the other hosting and joining.

Make sure you're both in the same steam region as well and that you're both running steam in the background.

So once they've downloaded and tested your game, go ahead and download their game and help them by testing out theirs.

That way, you can both verify that you're connecting to each other via steam across the internet and your menu system plug in is working properly.

Now also make sure that whoever you're testing with has the same platform.

So if you're on a Windows machine, make sure they are on a Windows machine because you're packaging for Windows.

If you're on a mac and your packaging for Mac, make sure that the other student is on a mac and so on.

So pause the video and go ahead and try testing out your game with another student or with another computer.

Okay.

So I'm ready to package this project.

I'm going to go to Platforms Windows Package Project.

Now here's my build folder.

I'm going to delete the previous build and select this folder.

And now I'm packaging the game.

All right, so I've package the game and zipped it up and stuck it on my Google Drive.

And then I had several of my students from my Discord download the project onto their machines and unzip

it and launch the game.

So I'm hosting the game.

I loaded up the game and clicked.

Host And now each of these students is loading up the game and clicking join.

And you can see that the found subsystem steam string is printed at the top left every time one of the students joins in.

So I have a handful of students in here and they're all just kind of hanging out.

Some of them are running and jumping around and it looks like we have some pretty decent performance.

Everyone's movement looks pretty smooth.

Now we're going to get into why this is and some of the following sections, and we're going to be creating

multiplayer functionality when we actually add this plug in to a game project and start making the game.

But this is a great starting point and we now have this plug in that we can add into any project and get this sort of behavior where we have lots of players joining in and we're all able to play the game together.

So in the next lecture, we're going to count the players coming in and we're going to see if we can display when a player joins and leaves and even get the name of the player so that we can see who's joining in when they join.

So in this lecture, we found and joined sessions and we're doing this from the menu rather than from the Character class.

And we're also happy now because we've finished the core functionality of our plug in.

Now we have some delegates and functions that we didn't use yet because we're going to need to actually start to create a game so we know what to do once all the players have joined the lobby.

Usually we count the number of players and then start a timer and migrate all the players over to a match, allowing each player to start battling each other or fighting together in some sort of co-op game.

So for the most part, our plug in is done.

We'll still add a couple of features and after that we're ready to actually start learning about how to create the gameplay for a multiplayer game.

So this is very exciting.

Pat yourself on the back.

Congratulations.

This is a huge milestone.

I'll see you in the next video.

0 件のコメント:

コメントを投稿