6-25 Synchronizing Player Position To Server
前幾章稍微離題了,都在講效能優化的內容。本章開始要回到網路相關的內容啦,如果有人看到這篇文章卻不懂範例中使用的架構的話,請先將第6章的文章再複習一遍唷!
6-5 Sending Request And Response
上述是Unity的Client實作過程,接著我們進行PhotonServer端的開發。
新增一個SyncOtherPlayerHandler,繼承我們自定義的BaseHandler物件,並覆寫OnOperationRequest方法。
OnOperationRequest的實作如下,透過Parameters取得座標x, y, z的資料,然後使用logger顯示出來。

最後,記得要在NoliahFantasyServer.cs中添加Handler到handlerDict中。
從PhotonServer的Log檔中可見確實收到新的x, y, z資料。
6-5 Sending Request And Response
6-7 Sending Request And Response And Event With Parameters
6-14 Using Dictionary To Manage All Request
6-15 Using Handler To Respond Requisition In Photon Server
6-16 Finishing Sign Up Mechanism
接著繼續實作位置同步的功能,首先,我們先說明本次功能需要添加的OperationCode與ParameterCode。如下圖,OperationCode新增SyncPosition。
ParameterCode新增x, y, z。
接著,新增一個Script,名為SyncPositionRequest,繼承我們自定義的Request。覆寫抽象方法OnDefaultRequest後,建立Dictionary物件來儲存position資料,最後使用PhotonEngine的OpCustom方法傳送Request給Server。
Request物件建立好以後,我們還需要一個物件來管理所有『同步位置』的相關功能。所以,請大家再新增一個Script,名為SyncPosition。於SyncPosition中使用GetComponent取得SyncPositionRequest,然後紀錄Player的初始位置。接著用StartCoroutine呼叫同步功能。
此處使用StartCoroutine是為了實行每秒定時更新位置資訊,如下圖,每隔0.2秒同步一次,使用Vector3.Distance判斷玩家是否有移動,有移動再呼叫SyncPositionRequest的OnDefaultRequest方法。
回到場景中,建立SyncPotision物件。
在GameObject內加入SyncPosition與SyncPositionRequest兩個腳本,接著需要從Inspector中選擇Operation Code,此處選擇Sync Position。
上述是Unity的Client實作過程,接著我們進行PhotonServer端的開發。
新增一個SyncOtherPlayerHandler,繼承我們自定義的BaseHandler物件,並覆寫OnOperationRequest方法。
OnOperationRequest的實作如下,透過Parameters取得座標x, y, z的資料,然後使用logger顯示出來。

最後,記得要在NoliahFantasyServer.cs中添加Handler到handlerDict中。

以下提供本次修改的完整程式碼:
以下提供Client端原始碼:
ParameterCode.cs:
1 2 3 4 5 6 7 8 | public enum ParameterCode : byte { Acccount, Password, x, y, z } |
OperationCode.cs:
1 2 3 4 5 6 | public enum OperationCode : byte { Login, Signup, SyncPosition } |
SyncPosition.cs:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 | using System.Collections; using System.Collections.Generic; using UnityEngine; using RPG.Character; using RPG.Core; public class SyncPosition : MonoBehaviour { GameObject player; SyncPositionRequest syncPosiRequest; Vector3 lastPosition; void Start () { syncPosiRequest = GetComponent<syncpositionrequest>(); player = Game.Instance.playerMovement.gameObject; // 初始化Player位置 lastPosition = player.transform.position; // 每段時間同步玩家位置 StartCoroutine (UploadPosition ()); } IEnumerator UploadPosition(){ while ( true ) { // 每秒同步五次 yield return new WaitForSeconds (0.2f); // 判斷是否有移動,若Player沒有在移動便不需要同步 Vector3 nowPosition = player.transform.position; if (Vector3.Distance(nowPosition, lastPosition) > 0.1f){ lastPosition = nowPosition; syncPosiRequest.position = nowPosition; // 發送位置更新訊息 syncPosiRequest.OnDefaultRequest (); } } } } </syncpositionrequest> |
SyncPositionRequest.cs:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 | using System.Collections; using System.Collections.Generic; using UnityEngine; using ExitGames.Client.Photon; public class SyncPositionRequest : Request { [HideInInspector] public Vector3 position; public override void OnDefaultRequest () { Dictionary< byte , object = "" > data = new Dictionary< byte , object = "" > (); data.Add(( byte )ParameterCode.x, position.x); data.Add(( byte )ParameterCode.y, position.y); data.Add(( byte )ParameterCode.z, position.z); // 向Server發出Request,true代表建立可靠連接 PhotonEngine.Instance.GetPeer().OpCustom(( byte )operationCode, data, true ); } public override void OnOperationResponse (OperationResponse operationResponse) { } } </ byte ,></ byte ,> |
以下提供Server端原始碼:
NoliahFantasyServer.cs:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 | using System.IO; using System.Collections.Generic; using Photon.SocketServer; using ExitGames.Logging; using log4net.Config; using NoliahFantasyServer.Constant; using NoliahFantasyServer.Handler; namespace NoliahFantasyServer { public class NoliahFantasyServer : ApplicationBase { public static readonly ILogger logger = LogManager.GetCurrentClassLogger(); public static Dictionary<operationcode, basehandler= "" > handlerDict = new Dictionary<operationcode, basehandler= "" >(); // 透過PeerList,可向任何一個客戶端發送數據 public static List<myclientpeer> peerList = new List<myclientpeer>(); // 當Client端發出Request的時候 protected override PeerBase CreatePeer(InitRequest initRequest) { MyClientPeer peer = new MyClientPeer(initRequest); peerList.Add(peer); return peer; } // Server端啟動的時候初始化 protected override void Setup() { InitLogger(); InitHandler(); } // Server端關閉的時候 protected override void TearDown() { } void InitLogger() { // 日誌初始化 log4net.GlobalContext.Properties[ "Photon:ApplicationLogPath" ] = Path.Combine( this .ApplicationRootPath, "bin_Win64" , "log" ); FileInfo loggerConfig = new FileInfo(Path.Combine( this .BinaryPath, "log4net.config" )); if (loggerConfig.Exists) { // 設置使用log4net的Log功能 LogManager.SetLoggerFactory(ExitGames.Logging.Log4Net.Log4NetLoggerFactory.Instance); // 讓log4net讀取config XmlConfigurator.ConfigureAndWatch(loggerConfig); } logger.Info( "Setup Log4Net Compeleted!" ); } void InitHandler(){ LoginHandler loginHandler = new LoginHandler(); handlerDict.Add(loginHandler.operationCode, loginHandler); SignupHandler signupHandler = new SignupHandler(); handlerDict.Add(signupHandler.operationCode, signupHandler); SyncPositionHandler syncPositionHandler = new SyncPositionHandler(); handlerDict.Add(syncPositionHandler.operationCode, syncPositionHandler); SyncOtherPlayerHandler syncOtherPlayerHandler = new SyncOtherPlayerHandler(); handlerDict.Add(syncOtherPlayerHandler.operationCode, syncOtherPlayerHandler); } } } </myclientpeer></myclientpeer></operationcode,></operationcode,> |
SyncPositionHandler.cs:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 | using System; using System.Collections.Generic; using Photon.SocketServer; using NoliahFantasyServer.Constant; namespace NoliahFantasyServer.Handler { public class SyncPositionHandler : BaseHandler { public SyncPositionHandler() { operationCode = OperationCode.SyncPosition; } public override void OnOperationRequest(OperationRequest operationRequest, SendParameters sendParameters, MyClientPeer myClientPeer) { object xPosition; operationRequest.Parameters.TryGetValue(( byte )ParameterCode.x, out xPosition); object yPosition; operationRequest.Parameters.TryGetValue(( byte )ParameterCode.y, out yPosition); object zPosition; operationRequest.Parameters.TryGetValue(( byte )ParameterCode.z, out zPosition); NoliahFantasyServer.logger.Info( "Client端Position資料X:" + xPosition); NoliahFantasyServer.logger.Info( "Client端Position資料Y:" + yPosition); NoliahFantasyServer.logger.Info( "Client端Position資料Z:" + zPosition); myClientPeer.xPosition = ( float )xPosition; myClientPeer.yPosition = ( float )yPosition; myClientPeer.zPosition = ( float )zPosition; } } } |
執行遊戲看看吧,移動一下玩家的位置。
接著下一章繼續實作同步其他登入玩家的Position資料。
留言
張貼留言