UNET連線遊戲(八){鬼抓人}

本篇使用的是5.3版後有UNET的UNITY
目標:基本連線遊戲機制+線上怪物
1.延續前篇(UNET連線遊戲(七){原來這就是掛掉的感覺})
除了連線玩家,以及場景的布置外,還可以在場中加入一些由伺服器控制的NPC怪物增添遊戲的樂趣。首先處理場景的布置~
unet-hit-116為了節省效能,我們開啟Window→Lighting關閉Auto Bake並烘焙貼圖後關閉即時的GI渲染,讓遊戲運行的快些節省網路效能。
unet-hit-117

2.選取全部的地板Window→Navigation按下Bake創建一個導航平面給怪物NPC使用。
unet-hit-118
3.創建一個空物件當作怪物的重生點,並掛上MonsterSpawner.cs

using UnityEngine;
using System.Collections;
using UnityEngine.Networking;
public class MonsterSpawner : NetworkBehaviour {
	public GameObject monster;
	public int monsterNum;
	public float R=3;
	void Start () {
		InvokeRepeating ("CmdScanMonster",3f ,10f);
	}
	[Command]
	void CmdSpawnMonster(){
		for(int i=0 ; i < monsterNum ; i++){
			Vector3 pos = new Vector3 (this.transform.position.x + Random.Range(-R,R) , this.transform.position.y , this.transform.position.z + Random.Range(-R,R) );
			GameObject mon = (GameObject)Instantiate (monster , pos ,this.transform.rotation);
			mon.name = "Monster";
			NetworkServer.Spawn (mon);
		}
	}
	[Command]
	void CmdScanMonster(){
		if (!GameObject.FindWithTag ("MONSTER")) {
			CmdSpawnMonster ();
		}
	}
}

內容如下
InvokeRepeating (“CmdScanMonster”,3f ,10f); 開始3秒後掃描場上是否存在怪物,之後每10秒檢查一次。若無就執行創造怪物的方法。
unet-hit-119
4.怪物的造型就要發威各位的創意了,該卦的程式還是要掛上。
unet-hit-120 unet-hit-121由於這些程式要改成也能怪物使用,稍做改寫。

using UnityEngine;
using System.Collections;
using UnityEngine.Networking;
using UnityEngine.UI;
public class EnemyHP : NetworkBehaviour {
	private GameObject HudBar;
	// Use this for initialization
	void Start () {
		SpawnHpBar ();
	}
	// Update is called once per frame
	void Update () {
	}
	void SpawnHpBar ()
	{
		if (!isLocalPlayer) {
			HudBar = Instantiate (Resources.Load ("HP_ENEMY")) as GameObject;
			if (this.tag == "MONSTER") {
				HudBar.name = "MONSTER_" + GetComponent<NetworkIdentity> ().netId.ToString ();
			}else
			HudBar.name = "PLAYER_" + GetComponent<NetworkIdentity> ().netId.ToString ();
			HudBar.transform.parent = GameObject.Find ("HUD").transform;
			HudBar.GetComponent<UIReset> ().playerName = this.gameObject;
		} else {
			HudBar = Instantiate (Resources.Load ("HP")) as GameObject;
			if (this.tag == "MONSTER") {
				HudBar.name = "MONSTER_" + GetComponent<NetworkIdentity> ().netId.ToString ();
			}else
			HudBar.name = "PLAYER_" + GetComponent<NetworkIdentity> ().netId.ToString ();
			HudBar.transform.parent = GameObject.Find ("HUD").transform;
			HudBar.GetComponent<UIReset> ().playerName = this.gameObject;
			HudBar.GetComponent<UIReset> ().offsetXY = 10f;
		}
		this.gameObject.GetComponent<DamageScript> ().HP_BAR = HudBar.GetComponent<Image>();
	}
}
using UnityEngine;
using System.Collections;
using UnityEngine.Networking;
using UnityEngine.UI;
using UnityStandardAssets.Characters.FirstPerson;
public class DamageScript : NetworkBehaviour {
	AudioSource damageAudio;
	GunController gunCtrl;
	FirstPersonController fpsCtrl;
	CharacterController charCtrl;
	public Image HP_BAR;
	public Image DEAD_BG;
	public const int HP_MAX = 3;
	public const int HPMON_MAX = 300;
	float rotaSpeed;
	[SyncVar(hook = "HpBar")]public int HP = HP_MAX;
	void Awake()
	{
	}
	void Start (){
		if (this.gameObject.tag == "MONSTER") {
			HP = HPMON_MAX;
		}
		damageAudio = GetComponent<AudioSource>();
		gunCtrl = GetComponent<GunController> ();
		fpsCtrl = GetComponent<FirstPersonController> ();
		charCtrl = GetComponent<CharacterController> ();
		DEAD_BG = GameObject.Find ("BG").GetComponent<Image> ();
	}
	public void TakeDamage(int D){
//		if (!isServer) {
//			return;
//		}
		print ("GET");
		if (HP > 0) {
			HP -= D;
			if (HP <= 0) {
				if (this.gameObject.tag == "MONSTER") {
					NetworkServer.Destroy (this.gameObject);
					Destroy (this.gameObject);
				}else RpcRebirth ();
			}
		}
	}
	void HpBar(int h){
		print (h);
		if (this.gameObject.tag == "MONSTER") {
			if(HP_BAR)HP_BAR.fillAmount = (float)h / (float)HPMON_MAX;
		}else if(HP_BAR)HP_BAR.fillAmount = (float)h / (float)HP_MAX;
	}
	void Alive(bool b){
		gunCtrl.alive = b;
		fpsCtrl.enabled = b;
		charCtrl.enabled = b;
		rotaSpeed = 270f;
		InvokeRepeating ("Dead",0.1f,0.02f);
	}
	void Dead (){
		transform.Rotate(Vector3.right * Time.deltaTime * -30);
		transform.Rotate(Vector3.up * Time.deltaTime * rotaSpeed);
		rotaSpeed-=1.5f;
		DEAD_BG.color += new Color (0, 0, 0, 0.003f);
	}
	[ClientRpc]
	void RpcRebirth(){
		Invoke ("Go",5.0f);
		print ("DIE");
		if (isLocalPlayer)Alive (false);
	}
	void Go(){
		HP = HP_MAX;
		if (isLocalPlayer) {
			DEAD_BG.color = new Color (1, 0, 0, 0);
			Transform spawn = NetworkManager.singleton.GetStartPosition();
			transform.position = spawn.position;
			Alive (true);
			CancelInvoke ("Dead");
		}
	}
}
using UnityEngine;
using System.Collections;
using UnityEngine.Networking;
using UnityEngine.UI;
public class PlayerID : NetworkBehaviour {
	void Start () {
		GetNetIdentity ();
	}
	// Update is called once per frame
	void Update () {
	}
	void GetNetIdentity(){
		if (this.tag == "MONSTER") {
			this.gameObject.name = "MONSTER_" + GetComponent<NetworkIdentity> ().netId.ToString();
		}else
		this.gameObject.name = "PLAYER_" + GetComponent<NetworkIdentity> ().netId.ToString();
	}
	public string ID(){
		return this.gameObject.name;
	}
}

以上都是為了是否為怪物增加的條件判斷。
5.怪物身上重點功能導航目標,首先新增Component零件NavMeshAgent,以及球形Trigger把半徑加大為5。unet-hit-122
unet-hit-123
6.接下來撰寫SearchPlayer.cs搜索玩家的功能

using UnityEngine;
using System.Collections;
using UnityEngine.Networking;
public class SearchPlayer : NetworkBehaviour {
	NavMeshAgent Nav;
	public GameObject target;
	public bool serverMonster;
	// Use this for initialization
	void Start () {
		if (isServer) {
			serverMonster = true;
			Nav = GetComponent<NavMeshAgent> ();
		} else
			serverMonster = false;
	}
	// Update is called once per frame
	void FixedUpdate () {
		if (target && target != null && serverMonster) {
			Nav.SetDestination(target.transform.position);
			if (CloseYou ()) {
				target = null;
				Nav.Stop(true);
			}
		}
	}
	void OnTriggerStay(Collider other) {
		if(other.tag == "PLAYER" && serverMonster){
			Nav.Resume ();
			target = other.gameObject;
		}
	}
	bool CloseYou(){
		float dis = Vector3.Distance(transform.position, target.transform.position);
		if (dis < 5f) {
			Nav.Stop ();
			target.GetComponent<DamageScript> ().TakeDamage (3);
			return true;
		} else {
			Nav.Resume ();
			return false;
		}
	}
}

首先判定是否為SERVER端取得導航控件,然後將導航目標設為踩進球型偵測區的PLAYER,還有是否進入到攻擊範圍的CloseYou方法,執行傷害玩家以及導航關閉的功能。將怪製成預制物件後從場景刪除,指定給生怪程式還有NetworkManager的線上成員註冊。
7.另個可以擴增的功能則是攻擊打他的玩家,因此思考下這個程式邏輯便會得到,子彈需要帶有玩家資訊的結論;玩家帶著槍槍發射子彈,所以我們要改寫GunController.cs以及AmmoController.cs的內容,首先子彈的部分:需要一個可以帶玩家資訊的欄位

using UnityEngine;
using System.Collections;
using UnityEngine.Networking;
public class AmmoController : NetworkBehaviour
{
	private float lifeTime;
	public float maxTime = 3.0f;
	public GameObject fromWho;
	void Start ()
	{
		lifeTime = 0.0f;
	}
	[ServerCallback]
	void Update ()
	{
		lifeTime += Time.deltaTime;
		if( lifeTime > maxTime )
		{
			DestroyAmmo();
		}
	}
	void OnCollisionEnter(Collision other)
	{
		if (other.gameObject.tag == "PLAYER") {
			other.gameObject.GetComponent<DamageScript>().TakeDamage (1);
			DestroyAmmo ();
		}
		if (other.gameObject.tag == "MONSTER") {
			other.gameObject.GetComponent<DamageScript>().TakeDamage (1);
			other.gameObject.GetComponent<SearchPlayer> ().target = fromWho;
			DestroyAmmo ();
		}
	}
	public void DestroyAmmo(){
		NetworkServer.Destroy(gameObject);
	}
}

這裡我們用fromWho來裝載這個資訊,並寫在打到怪物標籤物體時遞給怪物的目標物。
接著槍的部分:則是發射時將發射玩家的資訊貼到子彈的fromWho欄位中。

using UnityEngine;
using System.Collections;
using UnityEngine.Networking;
public class GunController : NetworkBehaviour
{
	private float power;
	private Transform playerCamera;
	public bool alive = true;
	void Start ()
	{
		power = 800.0f;
		playerCamera = transform.FindChild("FirstPersonCharacter");
	}
	void Update ()
	{
		if(isLocalPlayer)
		{
			if(Input.GetButtonDown("Fire1") && alive)
			{
				CmdSpawnAmmo();
			}
		}
	}
	[Command]
	void CmdSpawnAmmo ()
	{
		GameObject instance = Instantiate (Resources.Load ("Ammo")) as GameObject;
		instance.name = "Ammo";
		instance.transform.position = playerCamera.position + playerCamera.forward * 1.5f + playerCamera.up * -.5f;
		instance.GetComponent<Rigidbody> ().AddForce (playerCamera.forward * power);
		instance.GetComponent<AmmoController> ().fromWho = this.gameObject;
		NetworkServer.Spawn (instance);
	}
}

驗收!!
unet-hit-124

發佈留言

發佈留言必須填寫的電子郵件地址不會公開。 必填欄位標示為 *