Related Posts Plugin for WordPress, Blogger...

6-25 Synchronizing Player Position To Server

前幾章稍微離題了,都在講效能優化的內容。本章開始要回到網路相關的內容啦,如果有人看到這篇文章卻不懂範例中使用的架構的話,請先將第6章的文章再複習一遍唷!

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:

public enum ParameterCode : byte
{
 Acccount,
 Password,
 x,
 y,
 z
}

OperationCode.cs:

public enum OperationCode : byte
{
 Login,
 Signup,
 SyncPosition
}

SyncPosition.cs:

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();
  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.cs:

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 data = new Dictionary ();
  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)
 {
  
 }
}


以下提供Server端原始碼:
NoliahFantasyServer.cs:

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 handlerDict =
            new Dictionary();
        // 透過PeerList,可向任何一個客戶端發送數據
        public static List peerList = new List();

        // 當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);
        }

    }
}


SyncPositionHandler.cs:

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;
        }
    }
}


執行遊戲看看吧,移動一下玩家的位置。

 從PhotonServer的Log檔中可見確實收到新的x, y, z資料。

接著下一章繼續實作同步其他登入玩家的Position資料。

留言