Unity(C#)初心者・入門者向けチュートリアル ひよこのたまご

AndroidやiOS向けアプリを簡単に作れるゲーム開発環境Unity(ユニティ)の使い方を、チュートリアル方式で一緒に学びましょう!

【Unity9】UNETで発生したバグを取り除く【UNET18】

Unity5.1.1p3 Personal(2015年7月)

前回の続きです〜

今回はpart17までで出てきたバグを取り除いていきます〜



UNET Part 18 - Fixing my bugs 1 - YouTube

【目標】バグを取り除く

①GameManagerの修正(SpawnManagerの作成)

今のだとラグが大きくなる可能性が高いようなので、改善していきます〜

まずはSpawnManagerという空ゲームオブジェクトを作成して下さい〜

次にGameManagerに付いているGameManager_ZombieSpawnerスクリプトをSpawnManagerに付け替えます〜スクリプトの名前はSpawnManager_ZombieSpawnerに変更して下さい〜
また、付け替えた時にNetwork Identityコンポーネントが自動的に付くと思うので、Server Onlyにチェックを入れて下さい〜(Client側では表示されなくなる)


f:id:hiyotama:20150722093927p:plain
スクリプト名を変更 Server Onlyにチェック

②SpawnManager_ZombieSpawnerスクリプト修正

続いて先ほど名前を修正したSpawnManager_ZombieSpawnerスクリプトの内容を修正していきます〜以下ソースです〜

using UnityEngine;
using System.Collections;
using UnityEngine.Networking;

//********** 開始 **********//
//public class GameManager_ZombieSpawner : NetworkBehaviour {
public class SpawnManager_ZombieSpawner : NetworkBehaviour {
//********** 開始 **********//

	[SerializeField] GameObject zombiePrefab;
	private GameObject[] zombieSpawns;
	
	private int counter;
	private int numberOfZombies = 20;
	private int maxNumberOfZombies = 80;
	private float waveRate =10;
	private bool isSpawnActivated = true;
	
	public override void OnStartServer ()
	{
		zombieSpawns = GameObject.FindGameObjectsWithTag("ZombieSpawn");
		StartCoroutine(ZombieSpawner());
	}
	
	IEnumerator ZombieSpawner ()
	{
		for (;;) {
			yield return new WaitForSeconds (waveRate);
			GameObject[] zimbies = GameObject.FindGameObjectsWithTag ("Zombie");
			if (zimbies.Length < maxNumberOfZombies) {
				CommenceSpawn();
			}
		}
	}
	
	void CommenceSpawn ()
	{
		if (isSpawnActivated) {
			for (int i = 0; i < numberOfZombies; i++) {
				int randomIndex = Random.Range(0, zombieSpawns.Length);
				SpawnZombies(zombieSpawns[randomIndex].transform.position);
			}
		}
	}
	
	void SpawnZombies (Vector3 spawnPos)
	{
		counter++;
		GameObject go = GameObject.Instantiate(zombiePrefab, spawnPos, Quaternion.identity) as GameObject;
//********** 開始 **********//
//		NetworkServer.Spawn(go);
//********** 終了 **********//
		go.GetComponent<Zombie_ID>().zombieID = "Zombie " + counter;
//********** 開始 **********//
		NetworkServer.Spawn(go);
//********** 終了 **********//
	}
	
	// Use this for initialization
	void Start () {
	
	}
	
	// Update is called once per frame
	void Update () {
	
	}
}

SpawnManager_ZombieSpawner.cs


まずはスクリプト名を修正します〜


続いて公式マニュアルに従い、NetworkServer.Spawn(go)メソッドの位置をメソッドの一番下に移動させます〜

③Startメソッドの修正

今までStartメソッド内にてisLocalPlayerを使って自プレイヤーであることを判断するやり方は、どうやら潜在的なバグの原因となってしまうようなので、全てOnStartLocalPlayerメソッドへ修正していきます〜

また、クライアントがGameObjectを参照する処理はPreStartClientというメソッドに変更するそうです〜具体的には、クライアントオブジェクトのGetComponentやEvent登録などに対して使うそうです〜


それではまずはPlayer_NetworkSetupスクリプトから修正していきます〜

using UnityEngine;
using System.Collections;
using UnityEngine.Networking;

public class Player_NetworkSetup : NetworkBehaviour {
	[SerializeField]Camera FPSCharacterCam;
	[SerializeField]AudioListener audioListener;
	
//********** 開始 **********//
	//Startメソッドから修正
	public override void OnStartLocalPlayer ()

	{
		//削除
		//if (isLocalPlayer) {
//********** 終了 **********//
			GameObject.Find("Scene Camera").SetActive(false);
			GetComponent<UnityStandardAssets.Characters.FirstPerson.FirstPersonController>().enabled = true;
			FPSCharacterCam.enabled = true;
			audioListener.enabled = true;
//		}
	}
}

Player_NetworkSetup.cs


isLocalPlayerのif文判定を削除し、StartからOnStartLocalPlayerへメソッド名を変更しています〜


続いてPlayer_Deathスクリプトです〜

using UnityEngine;
using System.Collections;
using UnityEngine.Networking;
using UnityEngine.UI;

public class Player_Death : NetworkBehaviour {
	
	private Player_Health healthScript;
	private Image crossHairImage;
	
	
//********** 開始 **********//
	//Startメソッドから修正
	public override void PreStartClient ()
	{
		healthScript = GetComponent<Player_Health>();

		healthScript.EventDie += DisablePlayer;
	}
	
	//Startメソッドから修正
	public override void OnStartLocalPlayer ()
	{
		crossHairImage = GameObject.Find("Crosshair Image").GetComponent<Image>();
	}
//********** 終了 **********//
	
//********** 開始 **********//
	public override void OnNetworkDestroy()
//********** 終了 **********//
	{
		healthScript.EventDie -= DisablePlayer;
	}
	
	void DisablePlayer ()
	{
		GetComponent<CharacterController> ().enabled = false;
		GetComponent<Player_Shooting> ().enabled = false;
		GetComponent<BoxCollider> ().enabled = false;
		
		Renderer[] renderers = GetComponentsInChildren<Renderer> ();
		
		foreach (Renderer ren in renderers) {
			ren.enabled = false;
		}
		
		healthScript.isDead = true;
		
		if(isLocalPlayer){
			GetComponent<UnityStandardAssets.Characters.FirstPerson.FirstPersonController> ().enabled = false;
			crossHairImage.enabled = false;
			GameObject.Find("GameManager").GetComponent<GameManager_References>().respawnButton.SetActive(true);
		}
	}
}

Player_Death.cs


StartメソッドをPreStartClientメソッドとOnStartLocalPlayerメソッドに分けています〜

また、OnDisableはOnNetworkDestroyへメソッド名を変更しています〜


続いてPlayer_Healthスクリプトです〜

using UnityEngine;
using System.Collections;
using UnityEngine.Networking;
using UnityEngine.UI;

public class Player_Health : NetworkBehaviour {
	
	[SyncVar (hook = "OnHealthChanged")] private int health = 100;
	private Text healthText;
	
	private bool shouldDie = false;
	public bool isDead = false;
	
	public delegate void DieDelegate();
	public event DieDelegate EventDie;
	
	public delegate void RespawnDelegate();
	public event RespawnDelegate EventRespawn;
	
//********** 開始 **********//
	//Startメソッドからメソッド名修正
	public override void OnStartLocalPlayer ()
	{
		healthText = GameObject.Find("Health Text").GetComponent<Text>();
		SetHealthText();
	}
//********** 終了 **********//

	中略
}

Player_Health.cs


こちらもStartからOnStartLocalPlayerへメソッド名を変更です〜


続いてPlayer_Respawnスクリプトです〜

using UnityEngine;
using System.Collections;
using UnityEngine.Networking;
using UnityEngine.UI;

public class Player_Respawn : NetworkBehaviour {

	private Player_Health healthScript;
	private Image crossHairImage;
	private GameObject respawnButton;
	
//********** 開始 **********//
	//メソッド名修正(Start → PreStartClient)
	public override void PreStartClient()
	{
		healthScript = GetComponent<Player_Health>();
		healthScript.EventRespawn += EnablePlayer;
	}
	
	//メソッド名修正(Start → OnStartLocalPlayer)
	public override void OnStartLocalPlayer () {
		
		crossHairImage = GameObject.Find("Crosshair Image").GetComponent<Image>();
		SetRespawnButton();
	}
//********** 終了 **********//
	
	void SetRespawnButton ()
	{
		if (isLocalPlayer) {
			respawnButton = GameObject.Find("GameManager").GetComponent<GameManager_References>().respawnButton;
			respawnButton.GetComponent<Button>().onClick.AddListener(CommenceRespawn);
			respawnButton.SetActive(false);
		}
	}
	
	void Update () {
	
	}
	
//********** 開始 **********//
	//メソッド名修正(OnDisable → OnNetworkDestroy)
	public override void OnNetworkDestroy()
//********** 終了 **********//
	{
		healthScript.EventRespawn -= EnablePlayer;
	}
	
	void EnablePlayer()
	{
		GetComponent<CharacterController> ().enabled = true;
		GetComponent<Player_Shooting> ().enabled = true;
		GetComponent<BoxCollider> ().enabled = true;
		
		Renderer[] renderers = GetComponentsInChildren<Renderer> ();
		foreach (Renderer ren in renderers) {
			ren.enabled = true;
		}
		
		if(isLocalPlayer){
			GetComponent<UnityStandardAssets.Characters.FirstPerson.FirstPersonController> ().enabled = true;
			crossHairImage.enabled = true;
			respawnButton.SetActive(false);
		}
	}
	
	void CommenceRespawn()
	{
		CmdRespawnOnServer();
	}
	
	[Command]
	void CmdRespawnOnServer()
	{
		healthScript.ResetHealth();
	}
	
}

Player_Respawn.cs


こちらもStartメソッドとOnDisableメソッドの名前を修正しています〜

④位置情報に関する修正

続いて位置情報に関する修正です〜


以前取り外したNetwork Transformというコンポーネントを、再度Playerプレハブに取り付けるようです〜

Rotation AxisはNoneに変更して下さい〜

また、Transform Sync ModeをSync Character Controllerに、Network Send Rate (seconds)を11に変更します〜
Transform Sync Modeは、同期をとる基準になるコンポーネントを指定し、Network Send Rateは、1秒間に何回同期をとるかを指定します〜


f:id:hiyotama:20150722102449p:plain
Network Component設定


これでPlayer_SyncPositionスクリプトは必要なくなったので非アクティブ化します〜

また、Player_SyncPositionスクリプト内のゲーム画面左下に遅延度を表示している部分のみを別スクリプトに移動させます〜Player_Latencyという名前でスクリプトを新規作成します〜


f:id:hiyotama:20150722103931p:plain
Player_SyncPositionを非アクティブ化しPlayer_Latency作成

using UnityEngine;
using System.Collections;
using UnityEngine.Networking;
using UnityEngine.UI;

public class Player_Latency : NetworkBehaviour {

	//Player_SyncPositionスクリプトのメンバ変数をコピー
	private NetworkClient nClient;
	private int latency;
	private Text latencyText;
	
	
	public override void OnStartLocalPlayer()
	{
		//Player_SyncPositionスクリプトのStartメソッドからコピー
		nClient = GameObject.Find("NetworkManager").GetComponent<NetworkManager>().client;
		latencyText = GameObject.Find("Latency Text").GetComponent<Text>();
	}
	
	// Update is called once per frame
	void Update () {
		//Player_SyncPositionスクリプトのUpdateメソッドからコピー
		ShowLatency();
	}
	
	//Player_SyncPositionスクリプトからコピー
	void ShowLatency ()
	{
		if (isLocalPlayer) {
			latency = nClient.GetRTT();
			latencyText.text = latency.ToString();
		}
	}
}

Player_Latency.cs

Player_SyncPositionスクリプトからLatency表示に関する部分をコピーしているだけです〜

⑤結果


f:id:hiyotama:20150722104627p:plain
遅延なし


特に遅延なくちゃんと動いています〜


今回はここまでです〜

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


【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】