【Unity9】UNETのSyncVarのhookの使用例と、前時代の同期方法【UNET5】
Unity5.1.1p3 Personal(2015年7月)
前回の続きです〜
前回までで遅延時間を画面に表示させ、遅延時間を短くしようと試みてきました〜
今回は更に遅延時間を短縮して効率化していくようです〜
UNET Part 5 - Almost Time Travel! - YouTube
【目標】最新の同期方法と前時代の同期方法を実装し、位置情報を同期する
①前時代の同期方法を使ってみる(HistoricalLerping)
UNETの利点を認識するため、UNETが誕生する前の前時代的な同期方法を試してみるみたいです〜
前時代の同期は、キャラクターが移動した時に位置情報をListに保存し、それを元に位置情報を修正していく手法をとっています〜
それでは、前回までに作成したPlayer_SyncPositionスクリプトに加筆していきます〜
using UnityEngine; using System.Collections; using UnityEngine.Networking; using UnityEngine.UI; //********** 開始 **********// using System.Collections.Generic; //Listを使う時のライブラリ //********** 終了 **********// [NetworkSettings(channel=0, sendInterval=0.033f)] public class Player_SyncPosition : NetworkBehaviour { //********** 開始 **********// //hook: SyncVar変数が変更された時、指定メソッドを実行するようサーバーから全クライアントへ命令を出す [SyncVar (hook = "SyncPositionValues")] //********** 終了 **********// private Vector3 syncPos; [SerializeField] Transform myTransform; //********** 開始 **********// float lerpRate; float normalLerpRate = 15; float fasterLerpRate = 25; //********** 終了 **********// private Vector3 lastPos; private float threshold = 0.5f; private NetworkClient nClient; private int latency; private Text latencyText; //********** 開始 **********// //Position同期用のList private List<Vector3> syncPosList = new List<Vector3>(); //HistoricalLerpingメソッドを使う時はtrueにする //SerializeFieldなのでInspectorビューから変更可能 [SerializeField] private bool useHistoricalLerping = false; //2点間の距離を判定する時に使う private float closeEnough = 0.1f; //********** 終了 **********// void Start () { nClient = GameObject.Find("NetworkManager").GetComponent<NetworkManager>().client; latencyText = GameObject.Find("Latency Text").GetComponent<Text>(); //********** 開始 **********// lerpRate = normalLerpRate; //********** 終了 **********// } void Update () { LerpPosition(); ShowLatency(); } void FixedUpdate () { TransmitPosition(); } void LerpPosition () { if (!isLocalPlayer) { //********** 開始 **********// if (useHistoricalLerping) { //前時代の補間メソッド HistoricalLerping(); } else { //通常の補間メソッド OrdinaryLerping(); } //********** 終了 **********// // Debug.Log(Time.deltaTime.ToString()); } } [Command] void CmdProvidePositionToServer (Vector3 pos) { syncPos = pos; // Debug.Log("Command"); } [ClientCallback] void TransmitPosition () { if (isLocalPlayer && Vector3.Distance(myTransform.position, lastPos) > threshold) { CmdProvidePositionToServer(myTransform.position); lastPos = myTransform.position; } } //********** 開始 **********// //クライアントのみ有効 [Client] //hookで指定されたメソッド 全クライアントが実行 void SyncPositionValues (Vector3 latestPos) { syncPos = latestPos; //ListにPosition追加 syncPosList.Add(syncPos); } //********** 終了 **********// void ShowLatency () { if (isLocalPlayer) { latency = nClient.GetRTT(); latencyText.text = latency.ToString(); } } //********** 開始 **********// //通常使われる補間メソッド void OrdinaryLerping () { myTransform.position = Vector3.Lerp(myTransform.position, syncPos, Time.deltaTime * lerpRate); } //過去使用されていた補間メソッド void HistoricalLerping () { //Listが1以上あったら if (syncPosList.Count > 0) { //現在位置とListの0番目の位置との中間値を補間 myTransform.position = Vector3.Lerp (myTransform.position, syncPosList [0], Time.deltaTime * lerpRate); //2点間がcloseEnoughより小さくなった時 if (Vector3.Distance (myTransform.position, syncPosList [0]) < closeEnough) { //Listの0番目を削除 syncPosList.RemoveAt (0); } //syncPosList.Countが0に戻った時、同期が追いついたことを意味する Debug.Log(syncPosList.Count.ToString()); } } //********** 終了 **********// }
Player_SyncPosition.cs
PlayerプレハブのuseHistoricalLerpingというBool型の変数をInspectorビューから切り替えることによって、かつて使われていた補間メソッドを試すことができます〜
useHistoricalLerping
UpdateからLerpPositionメソッドへ飛び、useHistoricalLerpingがtrueならHistoricalLerpingメソッドへ、falseならOrdinaryLerpingメソッドへ飛びます〜
OrdinaryLerpingメソッドの内容は、前回までと全く同じなので割愛します〜
HistoricalLerpingメソッドでは、syncPosList配列の配列数が1つ以上である場合、現在位置と配列0番目の位置とを補間し、その2点間の距離がcloseEnough(0.1)よりも小さくなった場合、配列の0番目を消去します〜
配列の0番目が消去されると、配列の1番目が繰り上がって0番目となり、次の補間が実行されます〜
全ての配列が補間されるまでこれを繰り返していきます〜
それではsyncPosList配列はどうやって追加されていくのかというと、SyncPositionValuesメソッド内で追加されています〜
それではSyncPositionValuesメソッドはどうやって呼び出されるのでしょうか〜
CmdProvidePositionToServerメソッド内でSyncVar変数であるsyncPosが変更された時、今回新たに追加されたhookが機能して、SyncPositionValuesメソッドが呼び出されます〜
hookは、SyncVarが機能してサーバーから全クライアントへ変数を同期する命令が出される代わりに、指定したメソッドを実行するように命令します〜
指定したメソッドの引数には、SyncVar変数の値が送られます〜
SyncVarにhookを付けなければ全クライアントのSyncPos変数は自動的に同期されますが、hookを使った場合は自分で同期しなければなりません〜(今回でいう「syncPos = latestPos;」の部分)
変数を全クライアントと同期するだけならhookは必要ないのですが、今回はListにSyncPosを加えるという作業が必要であるため、hookを使っています〜
すみません解説が長くなってしまいました〜
それでは前時代の同期方法で、実際に動かしてみましょう〜
コンソールに表示された配列数
動けば動くほど配列の数が増えていって、遅延がどんどんひどくなっていきます〜
①位置情報の配列増加への対応
位置情報の配列が増えてくると、遅延がきつくなってきます〜
遅延がきつくなってきたらlerpRate変数の値を変更することで対応していきます〜
HistoricalLerpingメソッドに加筆していきます〜
void HistoricalLerping () { if (syncPosList.Count > 0) { myTransform.position = Vector3.Lerp (myTransform.position, syncPosList [0], Time.deltaTime * lerpRate); if (Vector3.Distance (myTransform.position, syncPosList [0]) < closeEnough) { syncPosList.RemoveAt (0); } //********** 開始 **********// if (syncPosList.Count > 10) { lerpRate = fasterLerpRate; } else { lerpRate = normalLerpRate; } //********** 終了 **********// Debug.Log(syncPosList.Count.ToString()); } }
Player_SyncPosition.cs
配列が10以上になった時、lerpRateをfasterLerpRateにして、位置情報を補間するスピードを早めています〜
それでは実行してみましょう〜すると・・・確かに位置情報の同期はかなり改善されたのですが、normalLerpRateの時よりも動きがカクカクになってしまっています〜(動画でもテレポーテーションハプニング〜と言ってます〜笑)
②前時代の同期方法をもう少し改善する
このあと動画では何点か改善して、前時代の同期方法を少しでも遅延のないように改善していきます〜
例えばnormalLerpRateやfasterLerpRate、closeEnough等の値を最適化したり、Simulated Average Latencyの値を増やして条件を悪くしたりして、テストを繰り返します〜
英語がほとんど分からないので自信はないですが、前時代の手法でも最適化すれば、悪条件でも割とスムーズに同期させることができる・・・
でも、UNETならそんな調整の手間をかけなくても、かなりの悪条件下でも、自動的にいい感じに同期してくれますよ〜ということを伝えたいんじゃないかと思います〜
気になる方は動画をチェックしてみて下さい〜
というわけで、今回はここまでです〜
ありがとうございました〜
【Unity9】UNETでマルチプレイヤーなオンラインゲーム開発【UNET1】
【Unity9】UNETでプレイヤーの動きを補間し、なめらかな動きを実現する【UNET1-2】
【Unity9】UNETでネットワーク越しに傾き(Rotation)を同期させる【UNET2】
【Unity9】UNETでオンライン開始時のプレイヤー生成位置を変更する【UNET2-2】
【Unity9】UNETのネットワークトラフィックを軽減し、効率化する【UNET3】
【Unity9】UNETのlatency(遅延時間)を改善する【UNET4】
【Unity9】UNETのlatency(遅延時間)を表示して、ちょっとだけ改善する【UNET4-2】
【Unity9】UNETのSyncVarのhookの使用例と、前時代の同期方法【UNET5】
【Unity9】UNETを使ってRotationを同期させる【UNET6】
【Unity9】UNETで各プレイヤーにPlayerIDを設定する【UNET7】
【Unity9】UNETで敵プレイヤーにダメージを与える!【UNET8】
【Unity9】UNETでHPを画面に表示し、Playerへダメージを与える【UNET9】
【Unity9】UNETでHPが0以下になった時、Playerを破壊する!【UNET10】
【Unity9】UNETで死んだPlayerを生き返らせる!【UNET11】
【Unity9】UNETでゾンビAIを出現させる!【UNET12】
【Unity9】UNETでゾンビ生成時にユニークなIDを付ける!【UNET13】
【Unity9】UNETでゾンビを撃つ!【UNET14】
【Unity9】UNETでゾンビに攻撃させる!【UNET15】
【Unity9】UNETでゾンビの動きをスムーズにシンクロさせる!【UNET16】
【Unity9】UNETでゾンビ発生地点を増やす【UNET17】
【Unity9】UNETで発生したバグを取り除く【UNET18】
【Unity9】Unity MultiPlayerを使ってネットワーク越しにマッチメイキング!【UNET19】
【Unity9】GUIを改善して、Network Managerを見やすくする!【UNET20】
【Unity9】表示したGUI(Network Manager)を機能させる!【UNET20-2】
【Unity9】UNETでAnimationを同期させる!【UNET21】