今回はUnityNetworkで自身のキャラクターのみ動かし、他のプレイヤーキャラクターを動かさないようにする処理と、キャラクターの位置やアニメーションの同期を行ってみたいと思います。
前回はオンライン上にキャラクターを登場させるところまでをやりました。
自身のキャラクターのみ動かし、他のキャラクターを動かさない
前回、オンラインに接続した時に自身のキャラクターを登場させる事が出来ましたが、自身のアプリケーションに存在する他のプレイヤーのキャラクターも一緒に動いてしまいました。
そこで、今回は自身のキャラクターのみ動かす事が出来るようにします。
キャラクターが自分のものかどうかを判別する為に、MonoBehaviourクラスを継承して作成したUCharaスクリプトをNetworkBehaviourクラスに変更し、
NetworkBehaviourのisLocalPlayerプロパティを参照し、自身のキャラクターかどうかを判別します。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | using UnityEngine; using System.Collections; using UnityEngine.Networking; public class UChara : NetworkBehaviour { // Update is called once per frame void Update () { if (!isLocalPlayer) { return; } } } |
↑のスクリプトは変更部分を記載しています。
NetworkBehaviourを使用する為、using UnityEngine.Networkingディレクティブを追記します。
Updateメソッド内では移動処理や攻撃処理が記述してありますので、自身のキャラクター以外だった時に実行しないようにすれば移動や攻撃処理を行いません。
そこでUpdateメソッドの初めにisLocalPlayerで自身のキャラクターではない時にreturnを使って処理を実行しないようにします。
試しにUnityEditor上で実行し、ホストで接続して作成したキャラクターと、スタンドアロンで実行しクライアントで接続し生成したキャラの違いを見てみます。
左がUnityEditorで実行し生成したキャラで、右がスタンドアロンで実行しクライアントで接続した時に作成したキャラで、UnityEditor上に登場したプレハブになります。
左側は自身のキャラなのでHas AuthorityとIs Local Playerがyesになっているのに対して、右側は他のプレイヤーキャラなのでnoとなっています。
キャラクター位置とアニメーションの同期
自身のキャラクターのみを操作する事が出来るようになりましたが、他のプレイヤーが操作しているアプリケーション上の自分のキャラクターは動いたり攻撃したりをしません。
これは自分の動かしたキャラクターの位置やアニメーションを他のプレイヤーのアプリケーションと同期していない為です。
そこで、位置やアニメーションをオンライン上で同期し、他のプレイヤーのアプリでも自身のキャラクターが動くようにしましょう。
キャラクター位置の同期
キャラクターの位置を同期するにはキャラクターにNetworkTransformコンポーネントを取り付けます。
Add Component→Network→NetworkTransformを選択し、取り付けます。
Transform Sync ModeをCharacter Controllerに変更します。
これで位置の同期が可能となりました。
↑のようにキャラクターの位置が他のアプリケーションでも同期されるようになりました。
キャラクターアニメーションの同期
キャラクターの位置を同期する事は出来ましたが、スーッとお化けのように動いていますね。
これは位置はNetworkTransformで同期しているけれど、アニメーションの同期を行っていないからです。
そこでキャラクターのアニメーションも同期するようにしていきましょう。
アニメーションの同期にはNetworkAnimatorを使用します。
Add Component→Network→NetworkAnimatorを選択し取り付けます。
NetworkAnimatorを取り付けたら、同期するAnimatorをNetworkAnimatorにドラッグ&ドロップします。
すると↑のように同期するアニメーションパラメータが表示されるのでSpeedとAttackの両方のチェックを入れておきます。
このチェックボックスは同期を行うかどうかではなく、自動でアニメーションパラメータのデータを送るかどうかみたいです。
↑のSetParameterAutoSendを使った処理でtrueを指定したのと同じですね。
1 2 3 4 5 6 | // Speedパラメータを自動で送信設定 GetComponent<NetworkAnimator>().SetParameterAutoSend(0, true); // Attackパラメータを自動で送信設定 GetComponent<NetworkAnimator>().SetParameterAutoSend(0, true); |
NetworkAnimatorのチェックをする事で同期するかしないかだと勝手に思ってましたが、違うみたいですね・・・(^_^;)
ここで、試しにビルドし二つのアプリケーションを起動し実行してみてください。
すると歩行アニメーションは再生されるけど、攻撃アニメーションが再生されないと思います。
NetworkAnimatorではTrigger型のアニメーションパラメータを反映するにはAnimatorではなくNetworkAnimatorのSetTriggerを使ってトリガーする必要があるみたいです。
そこでUCharaスクリプトのAnimator部分を変更します。
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 | using UnityEngine; using System.Collections; using UnityEngine.Networking; using UnityStandardAssets.Utility; public class UChara : NetworkBehaviour { private NetworkAnimator netAnim; private CharacterController characterController; private float x; private float y; private Vector3 velocity; // Use this for initialization void Start () { netAnim = GetComponent<NetworkAnimator>(); characterController = GetComponent<CharacterController>(); velocity = Vector3.zero; } // Update is called once per frame void Update () { if (!isLocalPlayer) { return; } // 地面に接地してる時は初期化 if(characterController.isGrounded) { velocity = Vector3.zero; x = Input.GetAxis("Horizontal"); y = Input.GetAxis("Vertical"); Vector3 input = new Vector3(x, 0, y); // 方向キーが多少押されている if(input.magnitude > 0.1f && !netAnim.animator.GetCurrentAnimatorStateInfo(0).IsName("Attack")) { netAnim.animator.SetFloat("Speed", input.magnitude); transform.LookAt(transform.position + input); velocity += transform.forward * 2; // キーの押しが小さすぎる場合は移動しない } else { netAnim.animator.SetFloat("Speed", 0); } if(Input.GetButtonDown("Fire1") && !netAnim.animator.GetCurrentAnimatorStateInfo(0).IsName("Attack") && !netAnim.animator.IsInTransition(0) ) { // NetworkAnimatorのAttackをトリガー netAnim.SetTrigger ("Attack"); } } velocity.y += Physics.gravity.y * Time.deltaTime; characterController.Move(velocity * Time.deltaTime); } } |
Animator型のanimatorとしていた箇所をNetworkAnimatorのnetAnimと宣言を変更します。
アニメーションパラメータを操作している箇所で、Trigger以外を操作している箇所をNetworkAnimatorを介したAnimatorの操作に変更し、Triggerを操作している部分はNetworkAnimatorのSetTriggerを使った処理に変更します。
これでアニメーションの同期が行われますが、実は一つだけ問題が残ります。
ホストになったらアニメーションが2回再生される
Triggerを使う場合はNetworkAnimatorのSetTriggerを使用すればアニメーションの同期はされますが、実はホストであるプレイヤーキャラクターは攻撃アニメーションが2回再生されてしまいます。
クライアントで接続した場合はちゃんと1回だけの再生なんですが・・・(^_^;)
ここら辺の問題はUnityのフォーラムにも記載がありました。
そこでサーバーとして稼働していた場合はResetTriggerメソッドを使用してトリガーをキャンセルするようにします。
1 2 3 4 5 6 7 8 9 10 11 12 13 | if(Input.GetButtonDown("Fire1") && !netAnim.animator.GetCurrentAnimatorStateInfo(0).IsName("Attack") && !netAnim.animator.IsInTransition(0) ) { // NetworkAnimatorのAttackをトリガー netAnim.SetTrigger ("Attack"); // サーバーとして稼働している場合はNetworkAnimatorのAnimatorのAttackトリガーをリセットし2重の再生を防ぐ if (isServer) { netAnim.animator.ResetTrigger ("Attack"); } } |
最初にNetworkAnimatorのSetTriggerでAttackをトリガーして、NetworkBehaviourのisServerプロパティでサーバーであればNetworkAnimatorを介してAnimatorのAttackトリガーをリセットします。
こうすることで、ホスト(サーバー兼クライアント)でも攻撃アニメーションの再生が1回になりました。
どういう挙動で2回アニメーションが再生されるのかはわかりませんが・・・・、とりあえず問題がなければこのままいきます。
自分用カメラの設定
現状ではゲームの世界を写すカメラは固定カメラのみがそれぞれのアプリケーションに存在しています。
ですが、キャラクターが移動したら自分専用のカメラが自分のキャラクターを追いかけてくるようにしたいところです。
そこで自分のキャラクターがオンラインに接続した時にカメラが自分を追いかけるようにします。
まずは元々存在するMainCameraのインスペクタのAdd ComponentからScripts→UnityStandardAssets.Utility→Follow Targetを選択し取り付けます。
Follow Targetには追いかける対象を設定しないといけませんが、自身のキャラクターを設定しないといけない為、スクリプトからターゲットを指定する事にします。
設定は↑のような感じにしました。
ターゲットをスクリプトから設定
UCharaスクリプトで自身のキャラクターが登場した時にFollow TargetのTargetに自身のTransformを指定するようにします。
自身のキャラクターがオンラインに登場した時はOnStartLocalPlayerメソッドが呼ばれるので、その中に処理を記述します。
1 2 3 4 5 6 | // ローカルプレイヤーがスタートした時 public override void OnStartLocalPlayer() { Camera.main.GetComponent <FollowTarget> ().target = transform; } |
↑のような処理をUCharaスクリプトに追記します。
こうすることで、自身のキャラクターを追いかけるカメラの設定が出来ます。
動作確認
これでキャラクターの位置とアニメーションの同期が出来るようになりましたので、確認してみましょう。
↑のようになりました。
キャラクターの位置とアニメーションの同期、カメラが自身のキャラクターを追いかけるようになりました。
次回はキャラクターのHP表示機能とプレイヤー同士で戦えるようにしていきます。