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

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

【Unity9】UNETでゾンビに攻撃させる!【UNET15】

Unity5.1.1p3 Personal(2015年7月)


前回の続きです〜


今回は、ゾンビにPlayerを攻撃させてダメージを与えていきたいと思います〜



UNET Part 15 - Zombies Bite Back! - YouTube

【目標】ゾンビにPlayerを攻撃させる!

①Zombie_Attackスクリプトを新規作成

まずはMaterialを新規作成します〜「Zombie Red」という名前で、赤色にして下さい〜


f:id:hiyotama:20150716205844p:plain
Zombie Redを新規作成


次にZombie_Targetスクリプトを1箇所変更します〜

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

public class Zombie_Target : NetworkBehaviour {

	private NavMeshAgent agent;
	private Transform myTransform;
//********** 開始 **********//
//	private Transform targetTransform;
	public Transform targetTransform;
//********** 終了 **********//
	private LayerMask raycastLayer;
	private float radius = 100f;
	
	void Start ()
	{

          〜省略〜

Zombie_Target.cs


targetTransform変数をpublic変数に変更しました〜


それではZombie_Attackスクリプトを新規作成していきます〜
完成したスクリプトは、Zombieプレハブにつけましょう〜

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

public class Zombie_Attack : NetworkBehaviour {
	
	//次の攻撃までの間隔(秒)
	private float attackRate = 3;
	//次に攻撃する時間
	private float nextAttack;
	private int damage = 10; //ダメージ量
	private float minDistance = 2; //攻撃の射程距離
	//現在のゾンビとPlayerとの距離
	private float currentDistance;
	private Transform myTransform; //現在位置
	private Zombie_Target targetScript; //ターゲットの位置
	
	[SerializeField] private Material zombieGreen; //通常時の色
	[SerializeField] private Material zombieRed; //攻撃時の色

	void Start ()
	{
		//現在位置とZombie_Targetスクリプトをキャッシュ
		myTransform = transform;
		targetScript = GetComponent<Zombie_Target>();
		//自分がサーバーだったらコルーチン実行
		if (isServer) {
			StartCoroutine (Attack ());
		}
	}
	
	void CheckIfTargetInRange ()
	{
		//ターゲットが決まっていたら
		if (targetScript.targetTransform != null) {
			//ターゲットと自分の距離を計算
			currentDistance = Vector3.Distance (targetScript.targetTransform.position, myTransform.position);
			//ターゲットが射程距離内&ゲーム開始からの経過時間がnextAttackより大きかったら
			if (currentDistance < minDistance && Time.time > nextAttack) {
				//nextAttackに3秒足す
				nextAttack = Time.time + attackRate;
				
				//Playerにダメージを与える
				targetScript.targetTransform.GetComponent<Player_Health>().DeductHealth(damage);
				StartCoroutine(ChangeZombieMat()); //ホストサーバー向け
				RpcChangeZombieAppearance(); //クライアントサーバー向け
			}
		}
	}
	
	IEnumerator ChangeZombieMat()
	{
		//攻撃を実行したら1.5秒間赤くなり、緑に戻る
		GetComponent<Renderer>().material = zombieRed;
		yield return new WaitForSeconds(attackRate / 2);
		GetComponent<Renderer>().material = zombieGreen;
	}
	
	[ClientRpc]
	void RpcChangeZombieAppearance ()
	{
		//クライアントサーバー向け
		StartCoroutine(ChangeZombieMat());
	}

	IEnumerator Attack ()
	{
		//無限ループ 0.2秒に1回メソッド実行
		for (;;) {
			yield return new WaitForSeconds(0.2f);
			CheckIfTargetInRange();
		}
	}
}

Zombie_Attack.cs


まずはSerialize Fieldに各Materialを指定していきます〜


f:id:hiyotama:20150716210025p:plain


StartメソッドにてAttackコルーチンを呼び出し、0.2秒に1度CheckIfTargetInRangeメソッドを呼び出します〜
1秒に5回攻撃できるかどうか確認している感じです〜


CheckIfTargetInRangeメソッド内では、まずTargetが決まっているか確認し、決まっていたら射程距離内にいるか&攻撃するタイミングかを確認しています〜

Time.timeがゲームの経過時間を表すので、nextAttackを過ぎていたら攻撃します〜
nextAttackは攻撃の度にattackRate分(デフォルトは3秒)加算されていきます〜


次にPlayer_HealthスクリプトのDeductHealthメソッドを呼び出し、ダメージを与えます〜


次にChangeZombieMatメソッドを呼び出し、攻撃後1.5秒間ゾンビを赤くしています〜

ChangeZombieMatメソッドはホストサーバーからとクライアントサーバーからで別々のルートから呼び出されています〜理由はよく分かりません〜

②なぜかうまくいかなかった点を修正

動画では特別問題なく進んでいたのですが、僕がやったら1つ問題が出てしまいました〜
ゾンビのターゲットに、Player以外にCubeが指定されるパターンがあり、その場合NullReferenceExceptionエラーとなってしまいます〜


f:id:hiyotama:20150716211423p:plain
Cubeがターゲットに指定される


f:id:hiyotama:20150716211500p:plain
エラー発生


ゾンビがPlayer_HealthスクリプトのDeductHealthメソッドを呼び出そうとした時、メソッドが付いているのはCubeオブジェクトではなくPlayerオブジェクトなので、NullReferenceExceptionエラーとなったと思われます〜


とりあえずPlayerプレハブをHierarchyビューに戻してCubeを選択し、


f:id:hiyotama:20150716211817p:plain


CubeのレイヤーをDefaultに戻し、Applyボタンを押し、HierarchyからPlayerを削除します〜


f:id:hiyotama:20150716211947p:plain
レイヤーを戻してApply


これでエラーは発生しなくなりました〜

③結果


それでは結果を見ていきましょう〜


f:id:hiyotama:20150716211023p: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】