photon勉強会(クライアントサイド)2015/8/4 発表資料

Post on 21-Aug-2015

340 Views

Category:

Business

0 Downloads

Preview:

Click to see full reader

TRANSCRIPT

Effective PUNPhotonを使って簡単マルチプレイ

Photon 運営事務局

山本昇平 2015/08/04

自己紹介

• 氏名: 山本昇平(Syohei Yamamoto)

• 所属: GMOクラウド株式会社

• 役割: 各種ソリューションの技術担当

• VRとUnityと料理が得意1988年生まれのエンジニア

• 最近ハマってるゲーム: 某イカゲー– 特にオンラインマルチプレイが楽しい!

– スマホもCSもマルチに盛り上がりを見せている

2

皆さん

ネットワーク対戦ゲームを

フルスクラッチで作れますか?

簡単に、かつ高速に作りたい…

マッチング専用のサーバを用意するのは手間が掛かる…

そう、Photonならこのすべてを実現します

アジェンダ

• Photonとは

• HELLO, Photon World!!

– Photonの機能について簡単に紹介します

• Effective PUN

–一歩進んだPUNの使い方を解説します

Photonとは

1ページでPhotonを紹介すると…

• ネットワークゲームを作るための機能を提供したサービス&ミドルウェア

• すべて無料から利用可能

• クラウド型とミドルウェア型がある

Photonのラインナップ

サーバー構築・運用が不要なクラウドサービス

クラウド系基本サービス、迷ったらまずはこれ

ターンベース、低トラフィック向け

Unity向け、サーバー・機能はRealtimeと全く同等

チャットに特化

サーバーにインストールして使うミドルウェアサービス

10

Photonの主な機能

• ロビー

– 名前付ロビー

– マッチメイキング

– プレイヤー検索

• ルーム

– 人数制御・表示制御

– カスタムプロパティ

• 同期関連

– オブジェクト同期

– イベント通知

– RPC (PUN)

• WebHooks/WebRPC

• オフラインモード

11

Photonの特徴

• サーバー経由なので接続性が高い

• 独自の技術による信頼性と低レイテンシーを確保

• 異なるプラットフォーム間でも利用可能

• UnityはAsset StoreからDLするだけで利用可能

12

Photonの開発メンバー

• 開発元はドイツ・ハンブルクの Exitgames社

• トップもエンジニアのエンジニア集団

• World Golf TourのノウハウをPhotonに活用

13

Photon採用事例

大手パブリッシャーさまにも、ご採用頂いております

14

Photonを利用することで

• 手軽に低コストでネットワーク対戦ゲームを実現

• クラウド型ならネットワークやインフラをアウトソーシングする事が可能、クライアント開発に集中できる

• 要件に合わない場合はミドルウェア型を使って、Photon自体のカスタマイズが可能

15

HELLO, Photon World!!

今回はクライアントサイドの話

• PhotonのクライアントSDKは2種類ある

– Photon Native SDK

• ExitGames社からダウンロードできるもの

– Photon Unity Networking

• Asset Storeからダウンロードできるもの

• Native SDKをUnityで使いやすくしたもの

• Unity Networking(旧)と互換性があり、移行が容易

17

今回はクライアントサイドの話

• PhotonのクライアントSDKは2種類ある

– Photon Native SDK

• ExitGames社からダウンロードできるもの

– Photon Unity Networking

• Asset Storeからダウンロードできるもの

• Native SDKをUnityで使いやすくしたもの

• Unity Networking(旧)と互換性があり、移行が容易

18

今回はこっち

Photon Native SDK

• 従来のPhoton SDK

• C++, .NET, Android NDK, iOSなど様々なプラットフォームで利用することが可能

• クロスプラットフォームでどの言語も同様のAPI

• Photonのすべての機能が使える

–ロビー、マッチメイキング、ルーム作成・入室

– イベント発生、受信処理

19

Photon Unity Networking(PUN)

• Native SDKをベースにUnityで使いやすくしたもの

– Native SDKの上位SDKの位置づけ

• 旧Unity Networkingと互換性があり、移行が容易

• Asset Storeからインポートするだけで利用可能

– 無料ですべての機能をご利用頂けます!

• UnityでPhotonを使うならPUNがオススメ!

20

PUNの特徴 / 接続処理

• 接続処理はStaticクラスのメソッドを呼び出すだけ

– PhotonNetwork.ConnectUsingSettings();

• Photon Unity Networking/Resources/PhotonServerSettingsを見てPhotonへ接続しロビーに入る

• ちなみに、PhotonServerSettingsはResources直下であれば、別のフォルダに移動しても可

21

PUNの特徴 / ルーム接続

• Photonではロビー→ルームの順に接続を行う

–病院の待合室(ロビー)と診察室(ルーム)

–実際のゲームはルーム(診察室)で行われる

• PhotonNetwork.JoinOrCreateRoom();

–ロビーに居るとき実行可能

–ルームに入室、ルームが無ければ作成を行う

22

PUNの特徴 / イベント通知(RPC)

• イベント通知機能もあるが、RPCを使うとより簡単

• メソッドに[PunRPC]属性を付けるだけで他のプレイヤーを指示することが可能

23

PUNの特徴 / オブジェクト同期

• PrefabにPhotonViewコンポーネントを追加するだけ

– Transform / Rigidbody / 作成したScriptを同期できる

– Transformを同期するにはPhotonTransformView, Animatorを同期するにはPhotonAnimatorViewを利用するとより簡単に同期できる

– コーディングなしでネットワーク同期が可能

• シリアライズのコードを書けばそれ以外のデータも同期可能

24

PUNで簡単にオンラインゲーム

• ネットワーク対戦ゲームに必要な機能を網羅

–キャラクタの位置・状態 : オブジェクト同期

– イベント情報 : イベント通知、RPC

–ゲーム全体の状態 : ルームのカスタムプロパティ

–ロビーやマッチメイキングもあります

25

プログラムを見てみましょう

サンプルプログラムについて

• 基本的な機能の紹介します

1. Photonへ接続

2. ルームへの入室• 無い場合は作成します

3. RPCの送信

27

28

1public class Example : Photon.MonoBehaviour

2{

3 [SerializeField] private Button connecToPhoton, joinRoom, rpc;

4 [SerializeField] private Text log;

5 private string state;

6

7 void Start()

8 {

9 connecToPhoton.onClick.AddListener(() =>

10 {

11 PhotonNetwork.ConnectUsingSettings("0");

12 });

13

14 joinRoom.onClick.AddListener(() =>

15 {

16 PhotonNetwork.JoinOrCreateRoom("TestRoom", new RoomOptions(), new TypedLobby());

17 });

18

19 rpc.onClick.AddListener(() =>

20 {

21 photonView.RPC("RPCTest", PhotonTargets.All, "Hello, Photon World!!");

22 });

23 }

29

25 void Update()

26 {

27 if (state != PhotonNetwork.connectionStateDetailed.ToString())

28 {

29 log.text += string.Format("[{0}] {1}¥n", System.DateTime.Now, state);

30 }

31

32 state = PhotonNetwork.connectionStateDetailed.ToString();

33 }

34

35 [PunRPC]

36 public void RPCTest(string message)

37 {

38 log.text += string.Format("[{0}] {1}¥n", System.DateTime.Now, message);

39 }

40}

30

1public class Example : Photon.MonoBehaviour

2{

3 [SerializeField] private Button connecToPhoton, joinRoom, rpc;

4 [SerializeField] private Text log;

5 private string state;

6

7 void Start()

8 {

9 connecToPhoton.onClick.AddListener(() =>

10 {

11 PhotonNetwork.ConnectUsingSettings("0");

12 });

13

14 joinRoom.onClick.AddListener(() =>

15 {

16 PhotonNetwork.JoinOrCreateRoom("TestRoom", new RoomOptions(), new TypedLobby());

17 });

18

19 rpc.onClick.AddListener(() =>

20 {

21 photonView.RPC("RPCTest", PhotonTargets.All, "Hello, Photon World!!");

22 });

23 }

接続ボタンが押されたタイミングでPhotonに接続を行う。PhotonNetwork.ConnectUsingSettings()を使えば、PhotonServerSettingsファイルに事前に設定しておいた接続先に接続を行う事が可能です

31

1public class Example : Photon.MonoBehaviour

2{

3 [SerializeField] private Button connecToPhoton, joinRoom, rpc;

4 [SerializeField] private Text log;

5 private string state;

6

7 void Start()

8 {

9 connecToPhoton.onClick.AddListener(() =>

10 {

11 PhotonNetwork.ConnectUsingSettings("0");

12 });

13

14 joinRoom.onClick.AddListener(() =>

15 {

16 PhotonNetwork.JoinOrCreateRoom("TestRoom", new RoomOptions(), new TypedLobby());

17 });

18

19 rpc.onClick.AddListener(() =>

20 {

21 photonView.RPC("RPCTest", PhotonTargets.All, "Hello, Photon World!!");

22 });

23 }

入室ボタンが押されたタイミングでルームに入室、ルームが無い場合は作成します。余計なコールバックを書かずに済むPhotonNetwork.JoinOrCreateRoom()が便利です。

32

1public class Example : Photon.MonoBehaviour

2{

3 [SerializeField] private Button connecToPhoton, joinRoom, rpc;

4 [SerializeField] private Text log;

5 private string state;

6

7 void Start()

8 {

9 connecToPhoton.onClick.AddListener(() =>

10 {

11 PhotonNetwork.ConnectUsingSettings("0");

12 });

13

14 joinRoom.onClick.AddListener(() =>

15 {

16 PhotonNetwork.JoinOrCreateRoom("TestRoom", new RoomOptions(), new TypedLobby());

17 });

18

19 rpc.onClick.AddListener(() =>

20 {

21 photonView.RPC("RPCTest", PhotonTargets.All, "Hello, Photon World!!");

22 });

23 }

RPCボタンが押されたタイミングでRPC Testというリモートプロシージャ(メソッド)を呼び出します。このとき”Hello, Photon World!!”というパラメータを持たせています。送信先はプレイヤー全員です(後述)。

33

1public class Example : Photon.MonoBehaviour

2{

3 [SerializeField] private Button connecToPhoton, joinRoom, rpc;

4 [SerializeField] private Text log;

5 private string state;

6

7 void Start()

8 {

9 connecToPhoton.onClick.AddListener(() =>

10 {

11 PhotonNetwork.ConnectUsingSettings("0");

12 });

13

14 joinRoom.onClick.AddListener(() =>

15 {

16 PhotonNetwork.JoinOrCreateRoom("TestRoom", new RoomOptions(), new TypedLobby());

17 });

18

19 rpc.onClick.AddListener(() =>

20 {

21 photonView.RPC("RPCTest", PhotonTargets.All, "Hello, Photon World!!");

22 });

23 }

RPCを利用するにはPhotonViewを使う必要がある。Photon.MonoBehaviorかPhotonNetwork.Get(GameObejct)を使うと取得可能です。

photonViewインスタンスを取得するにはPhotn.MonoBehaviorを継承する必要がある。

34

25 void Update()

26 {

27 if (state != PhotonNetwork.connectionStateDetailed.ToString())

28 {

29 log.text += string.Format("[{0}] {1}¥n", System.DateTime.Now, state);

30 }

31

32 state = PhotonNetwork.connectionStateDetailed.ToString();

33 }

34

35 [PunRPC]

36 public void RPCTest(string message)

37 {

38 log.text += string.Format("[{0}] {1}¥n", System.DateTime.Now, message);

39 }

40}

RPCで呼び出されるRPCTestメソッドを定義しています。このとき[PunRPC]属性を指定すると、Photonで呼ばれます。RPCでは引数を設定し、引き渡すことも可能です。

デモ

いかがでしたでしょうか

ここまでが基礎編

ここからが本題

Effective PUN• RPCを極める• マスタークライアントの切替• オフラインモードを使ってみる• WebRPCを使ってみる• マッチメイキングハック• ルームリストの取得を高速化する• Photonのコールバックと仲良くなる• PhotonでUniRxを使ってみる

38

Effective PUN• RPCを極める• マスタークライアントの切替• オフラインモードを使ってみる• WebRPCを使ってみる• マッチメイキングハック• ルームリストの取得を高速化する• Photonのコールバックと仲良くなる• PhotonでUniRxを使ってみる

39

間に合わなかった

RPCを極める

RPCのパラメータについて

• RPCは用途によって使い分けられることが可能

– 自分自身が実行するかどうか

– バッファリングして入室時に実行するかどうか

– オフラインモードで実行するかどうか

• photonView.RPC("RPCTest", PhotonTargets.All, "Hello");

41

RPCのパラメータについて

• RPCは用途によって使い分けられることが可能

– 自分自身が実行するかどうか

– バッファリングして入室時に実行するかどうか

– オフラインモードで実行するかどうか

• photonView.RPC("RPCTest", PhotonTargets.All, "Hello");

42

このパラメータ!

PhotonTargetsについて

43

All Others MasterClient

自分自身 サーバ経由せずに実行 実行しない

他のプレイヤー サーバ経由後に実行する サーバ経由後に実行する

後から入ったプレイヤー 実行しない 実行しない

マスタークライアント マスタークライアントのみ実行

オフラインモードの場合 自分自身のみ実行する 実行しない マスタークライアントのみ実行

AllBuffered OthersBuffered AllViaServer AllBufferedViaServer

自分自身 サーバ経由せずに実行 実行しない サーバ経由後に実行する サーバ経由後に実行する

他のプレイヤー サーバ経由後に実行する サーバ経由後に実行する サーバ経由後に実行する サーバ経由後に実行する

後から入ったプレイヤー ルーム入室後に実行する ルーム入室後に実行する 実行しない ルーム入室後に実行する

マスタークライアント

オフラインモードの場合 自分自身のみ実行する 実行しない 自分自身のみ実行する 自分自身のみ実行する

デモソースコード

RPCのパラメータについて

• 一例として– マスタークライアントに対してのみメッセージを送信したい場合

• photonView.RPC("RPCTest", PhotonTargets.MasterClient, “To MasteClient");

– 自分以外の他のプレイヤーと、ルームに後から入室した人に対してメッセージを送りたい(バッファしたい)場合• photonView.RPC(“RPCTest”, PhotonTargets.OthersBuffered, “他のプレイヤーとバッファ");

• 用途に応じて使い分けが可能、柔軟性が高く使いやすい

44

マスタークライアントの切替

マスタークライアントとは

• Photonではホストでは無いが、ルームを代表するプレイヤーとしてマスタークライアントというものがある

• 基本的には一番最初に入室したプレイヤーが設定される

• PhotonNetwork.isMasterClientを利用することで、マスタークライアントに代表して何らか処理を実行させることが可能– 例えば、ルーム情報や、ゲームデータの集計など…

• PUNの最近のバージョンでマスタークライアントの切替処理が実装されたのでご紹介します

46

1 rpc.onClick.AddListener(() =>

2 {

3 photonView.RPC("RPCTest", PhotonTargets.MasterClient, "Received a message for Master Client.");

4 });

5

6 master.onClick.AddListener(() =>

7 {

8 PhotonPlayer[] players = PhotonNetwork.playerList;

9 foreach(PhotonPlayer player in players)

10 {

11 if(masterName.text == player.name)

12 {

13 PhotonNetwork.SetMasterClient(player);

14 }

15 }

16 });

47

マスタークライアント変更ボタンが押された場合、プレイヤーリストを取得し入力した変更したいプレイヤー名にマスタークライアントの権限を付与する

RPCボタンが押された場合、マスタークライアントに対してRPCを送信

48

1 void OnJoinedRoom()

2 {

3 state = "Master Client: " + PhotonNetwork.isMasterClient;

4 }

5

6 void OnMasterClientSwitched(PhotonPlayer newMasterClient)

7 {

8 state = newMasterClient.name + " is the Master Client.";

9 }

10

11 [PunRPC]

12 public void RPCTest(string message)

13 {

14 log.text += string.Format("[{0}] {1}¥n", System.DateTime.Now, message);

15 }

マスタークライアントが変更された時のコールバックが用意されているパラメータに新しいプレイヤーの情報を含む

49

実行結果

デモ

オフラインモードを使ってみる

オフラインモードとは

• Photonのプログラムをオフラインで利用する機能

• シングルプレイヤーモードを実装する際にコードを使い回すことが可能!

– 例えば、プレイヤーが落ちてマルチの継続が不可能なときに、シングルプレイヤーモードとして継続する

51

オフラインモードの利用方法

• Photonと接続がされていない状態で…

1. PhotonNetwork.offlineMode = true;

オフラインモードに入ります

2. PhotonNetwork.JoinRoom(“RoomName”);オフラインモードの状態でルームに入室します

3. オフライン状態でPhotonNetwork.Instantiate()やphotonView.RPC()といった操作が可能になる。

52

53

実行結果

デモソースコード

WebRPCを使ってみる

WebRPCとは

• クライアント側からRESTで外部APIを呼び出す機能

• クラウド型サービスでも外部サービスと連携することが可能

• 現在 Photon Turnbasedのみでの提供– Photon Turnbasedとは2014年6月より開始した新しいサービス

– Webhook/WebRPCやクラウドセーブといった機能を搭載

• 詳細は Photon Turnbased公式サイトをご覧ください– https://www.photonengine.com/ja/Turnbased

55

受口となるWebアプリを作る

• Photon側から実際に呼び出すWeb側のアプリを作る

• 今回は webscript.ioというWebサービスを利用しました

56

webscriptとは

• webscriptとはブラウザ上でプログラムを記入し、すぐに実行することができるWebサービス

• 無料枠だとスクリプトが7日間保持される

• 簡単なテストを行いたいときに便利です!

• 詳細についてはwebscript公式サイトをご覧ください– https://webscript.io/

57

58

1 local jsonRequest = json.parse(request.body)

2 local _name = jsonRequest.name

3 local _age = jsonRequest.age

4

5 if _name ~= nil and _age ~= nil then

6 return {

7 ResultCode = 0,

8 Data = {

9 name = _name,

10 age = _age

11 }

12 }

13 else

14 return { ResultCode = 9 }

15 end

JSONをパースしてnameとageを返すだけの簡単なプログラムです(言語はLua)。

https://gist.github.com/syyama/ac5516bcfaa4fea14a9f

ダッシュボードの設定

• WebRPCの接続先の設定はダッシュボード上から行います

• Photon Turnbasedのダッシュボード→[詳細へ]を押下

• 最下部の[Webhook]ボタンを押下

59

ダッシュボードの設定

• 外部URLの設定を行います

• BaseURLにWebRPCで用いるURLを指定

• 例)http://photon-tb.webscript.io/webhookの赤字の部分

60

クライアント側からリクエストを行う

• Webscriptで作ったプログラムをPUNで呼び出します

• PhotonNetwork.WebRpc()の第1引数にダッシュボードで指定したBaseURL以降のURLを指定します

• 例)http://photon-tb.webscript.io/webhookの青字の部分

61

62

1 // WebRPC

2 webRpc.onClick.AddListener(() =>

3 {

4 Dictionary<string, object> parameters = new Dictionary<string, object>();

5 parameters.Add("name", "Nico Yazawa");

6 parameters.Add("age", "17");

7

8 PhotonNetwork.WebRpc("webhook", parameters);

9 });

第1引数にwebhookと指定。第2引数にPOST時のリクエストパラメータを指定します。型はDictionary型です。第2引数のDictionaryはJSONに変換されPOSTされます。

クライアント側でレスポンスを受ける

• WebRPCのレスポンスを受け取るにはOnWebRpcResponseメソッドを利用します

• RPCのレスポンスが返ってきたタイミングで呼ばれる

63

1 void OnWebRpcResponse(OperationResponse operationResponse)

2 {

3 if (operationResponse.ReturnCode != 0)

4 {

5 state = "WebRPCに失敗しました. Response: " + operationResponse.ToStringFull();

6 return;

7 }

8

9 WebRpcResponse webRpcResponse = new WebRpcResponse(operationResponse);

10

11 if (webRpcResponse.ReturnCode != 0)

12 {

13 state = "WebRPC '" + webRpcResponse.Name + "' に失敗しました. Error: "

14 + webRpcResponse.ReturnCode + " Message: " + webRpcResponse.DebugMessage;

15 return;

16 }

17

18 Dictionary<string, object> parameters = webRpcResponse.Parameters;

19

20 foreach (KeyValuePair<string, object> pair in parameters)

21 {

22 log.text += string.Format("[{0}] Key : {1} / Value : {2}¥n",

23 System.DateTime.Now, pair.Key, pair.Value);

24 }

25 } 64

1 void OnWebRpcResponse(OperationResponse operationResponse)

2 {

3 if (operationResponse.ReturnCode != 0)

4 {

5 state = "WebRPCに失敗しました. Response: " + operationResponse.ToStringFull();

6 return;

7 }

8

9 WebRpcResponse webRpcResponse = new WebRpcResponse(operationResponse);

10

11 if (webRpcResponse.ReturnCode != 0)

12 {

13 state = "WebRPC '" + webRpcResponse.Name + "' に失敗しました. Error: "

14 + webRpcResponse.ReturnCode + " Message: " + webRpcResponse.DebugMessage;

15 return;

16 }

17

18 Dictionary<string, object> parameters = webRpcResponse.Parameters;

19

20 foreach (KeyValuePair<string, object> pair in parameters)

21 {

22 log.text += string.Format("[{0}] Key : {1} / Value : {2}¥n",

23 System.DateTime.Now, pair.Key, pair.Value);

24 }

25 } 65

レスポンスが正しく返ってきているか判定正しく返ってきた場合は OperationResponseのReturnCodeに 0 が返ってくる

1 void OnWebRpcResponse(OperationResponse operationResponse)

2 {

3 if (operationResponse.ReturnCode != 0)

4 {

5 state = "WebRPCに失敗しました. Response: " + operationResponse.ToStringFull();

6 return;

7 }

8

9 WebRpcResponse webRpcResponse = new WebRpcResponse(operationResponse);

10

11 if (webRpcResponse.ReturnCode != 0)

12 {

13 state = "WebRPC '" + webRpcResponse.Name + "' に失敗しました. Error: "

14 + webRpcResponse.ReturnCode + " Message: " + webRpcResponse.DebugMessage;

15 return;

16 }

17

18 Dictionary<string, object> parameters = webRpcResponse.Parameters;

19

20 foreach (KeyValuePair<string, object> pair in parameters)

21 {

22 log.text += string.Format("[{0}] Key : {1} / Value : {2}¥n",

23 System.DateTime.Now, pair.Key, pair.Value);

24 }

25 } 66

WebRPCの結果を読み出すWebRpCResponseクラスの

インスタンスを生成し、レスポンスとして返されたデータを利用

WebRPCのレスポンスが正しいか判定

1 void OnWebRpcResponse(OperationResponse operationResponse)

2 {

3 if (operationResponse.ReturnCode != 0)

4 {

5 state = "WebRPCに失敗しました. Response: " + operationResponse.ToStringFull();

6 return;

7 }

8

9 WebRpcResponse webRpcResponse = new WebRpcResponse(operationResponse);

10

11 if (webRpcResponse.ReturnCode != 0)

12 {

13 state = "WebRPC '" + webRpcResponse.Name + "' に失敗しました. Error: "

14 + webRpcResponse.ReturnCode + " Message: " + webRpcResponse.DebugMessage;

15 return;

16 }

17

18 Dictionary<string, object> parameters = webRpcResponse.Parameters;

19

20 foreach (KeyValuePair<string, object> pair in parameters)

21 {

22 log.text += string.Format("[{0}] Key : {1} / Value : {2}¥n",

23 System.DateTime.Now, pair.Key, pair.Value);

24 }

25 } 67

レスポンスはKey-Value形式のDictionary型で取得可能そのままstring文字列として利用できます

68

実行結果

デモ

WebRPCを使うメリット

• 独自に立てたゲームサーバからルームリストを取得することで、プレイヤー情報を取得したり、より高度なマッチングを実装することが可能!

• マルチプレイの開始・終了時にゲームデータをやりとりするといったこともできる

• Photon自体のカスタマイズを検討する前に、WebRPCで実現できないか検討してみるのもアリ

69

マッチメイキングハック

PUNのマッチングはイカしてない

• PhotonNetwork.JoinRoom()

– ルーム名を指定して入室しマッチングする

• PhotonNetwork.JoinRandomRoom()

– ランダムでルームに入室しマッチングする

• PUNではこの2つしか用意されていない!

71

近いレベルごとでマッチングするには

• カスタムプロパティを利用する方法1. レベルをグルーピングし、適当なランクを設定する

• 例えば0~20: Eランク、21~40: Dランク…など

2. ランクをプレイヤーのPhotonの機能であるカスタムプロパティにセット

3. 「同じランクのみ入室」という条件を付けたルームを作成

4. ランクが一致したプレイヤーが入室する

• 他にもTypedLobbyを使う方法などがある

72

73

1 string LevelToMatchRank(int level)

2 {

3 if (level <= 20)

4 return "E";

5 if (level <= 40)

6 return "D";

7 if (level <= 60)

8 return "C";

9 if (level <= 80)

10 return "B";

11 if (level <= 99)

12 return "A";

13 if (level == 100)

14 return "S";

15 return "";

16 }

1 rank = this.LevelToMatchRank((int)levelValue);

1 GUILayout.Label("レベル: " + ((int)levelValue).ToString(), GUILayout.Width(80));

2 levelValue = GUILayout.HorizontalSlider(levelValue, 0.0f, 100.0f);

旧GUIで申し訳ないです

適当にランクをセット

74

1 PhotonNetwork.playerName = userName;

2 propeties = new Hashtable() { { "UserName", userName }, { "Level", ((int)levelValue).ToString() }, { "Rank", rank }, { "RoomName", roomName } };

3 PhotonNetwork.SetPlayerCustomProperties(propeties);

1 using Hashtable = ExitGames.Client.Photon.Hashtable;

1 private Hashtable propeties;

Photonで利用するHashtableは別途usingする必要があります

Hashtableでレベルやランクといった情報を設定し、カスタムプロパティとしてセットします

75

1 RoomOptions roomOptions = new RoomOptions ();

2 roomOptions.isVisible = true;

3 roomOptions.isOpen = true;

4 roomOptions.maxPlayers = 4;

5 roomOptions.customRoomProperties = new Hashtable (){{"Rank", (string)properties["Rank"]} };

6 roomOptions.customRoomPropertiesForLobby = new string[] {"Rank"};

7

8 if (PhotonNetwork.GetRoomList ().Length == 0) {

9 PhotonNetwork.CreateRoom ((string)properties ["RoomName"],roomOptions, null);

10 return;

11 }

12 foreach (RoomInfo roomInfo in PhotonNetwork.GetRoomList()) {

13 if (roomInfo.name != (string)properties ["RoomName"]) {

14 PhotonNetwork.CreateRoom ((string)properties ["RoomName"],roomOptions, null);

15 } else {

16 isRoomEnabled = true;

17 }

18 }

ルームの作成時に、RoomOptions.customRoomPropertiesにHashtabke型でカスタムプロパティにRank情報をセット。同時にロビーからカスタムプロパティが見えるように、customRoomPropertiesForLobbyに、カスタムプロパティのキー情報をセットします。

76

1 RoomOptions roomOptions = new RoomOptions ();

2 roomOptions.isVisible = true;

3 roomOptions.isOpen = true;

4 roomOptions.maxPlayers = 4;

5 roomOptions.customRoomProperties = new Hashtable (){{"Rank", (string)properties["Rank"]} };

6 roomOptions.customRoomPropertiesForLobby = new string[] {"Rank"};

7

8 if (PhotonNetwork.GetRoomList ().Length == 0) {

9 PhotonNetwork.CreateRoom ((string)properties ["RoomName"],roomOptions, null);

10 return;

11 }

12 foreach (RoomInfo roomInfo in PhotonNetwork.GetRoomList()) {

13 if (roomInfo.name != (string)properties ["RoomName"]) {

14 PhotonNetwork.CreateRoom ((string)properties ["RoomName"],roomOptions, null);

15 } else {

16 isRoomEnabled = true;

17 }

18 }

ルームが無い場合、ルームリストを取得し同じ名前のルームが無いことを確認してルームを作成します

77

1 if (PhotonNetwork.GetRoomList ().Length == 0) {

2 isRoomNothing = true;

3 return;

4 }

5

6 foreach (RoomInfo roomInfo in PhotonNetwork.GetRoomList ()) {

7 Hashtable cp = roomInfo.customProperties;

8

9 if (roomInfo.name == (string)properties ["RoomName"]) {

10 if ((string)properties ["Rank"] == (string)cp ["Rank"]) {

11 PhotonNetwork.JoinRoom ((string)properties ["RoomName"]);

12 } else {

13 isLevelUnmatch = true;

14 }

15 return;

16 }

17 }

18

19 isRoomNothing = true;

入室時、ルームリストを取得します。RoomInfoクラスではcustomPropertiesを取得することが可能。

ルームのカスタムプロパティのRankとプレイヤーのカスタムプロパティのRankが一致すると入室を行う。

78

実行結果

デモ

レベルの近い(Rankの一致する)ユーザ同士がマッチングする

マッチングを実装するには

• マッチング情報の近いプレイヤー同士をグルーピングし、そのグループでマッチングを行う

• 例)某イカゲーのマッチングを妄想してみる– 基本的な情報:プレイヤー名、リージョン、など

– マッチング情報:ランク、ウデマエ、勝率、総マッチング数、キルデス比、称号、チーム、など

– プレイヤーのプロパティとしてこれらの情報を保持させる

79

80

プログラムの完成が間に合わなかった

まとめ

• 本日のクライアントサイドの勉強会はいかがでしたか?

• 意外と知らない機能があったのでは無いでしょうか?

• 他にこんな事が知りたいといった話があればご教示ください

• 今回はUnityを使いましたが、他の開発環境と組み合わせた内容をも今後検討して参ります– 何かご要望があればお伝えください!

81

最後に…

最後に…

一緒に盛り上げてくれる

を大募集中!(エンジニア、営業などなど)

83

ありがとうございました!

developer@photoncloud.jp

top related