一起來玩U3D之坦克大戰(單機)

The War Of Tanks!!!

項目需求:

  • 主⻆移動、坦克轉身、開炮
  • 敵⼈⼯⼚ ⽣成敵⼈
    必須在空地⽣成
    隨機位置 、每隔3s⽣成⼀輛坦克 、設置敵⼈數量<=50輛
  • 攝像機跟隨
  • 炮彈
  • 敵⼈AI:隨機轉身、隨機移動、開炮智能 距離判斷
  • 道具管理器:隨機⽣成道具
    每隔5秒⽣成⼀個道具
  • 道具:禁錮敵⼈3s 50%、摧毀所有敵⼈ 5%、⽆敵5s 40%、召喚有個友⽅坦克 5%

大概就是這些功能啦!

博主只貼出腳本的實現,模型上的操作省略不表。

主角坦克類

博主簡單的設計了坦克的移動、狀態、以及開炮動作
腳本代碼如下:

using UnityEngine;

public class Tank : Unit {

	[Header("坦克移動速度")]
	public float moveSpeed;

	[Header("坦克轉向速度")]
	public float turnSpeed;
	
	//獲取坦克移動軸
	private float hor;
	private float ver;
	//坦克的武器組件
	private TankWeapon tankWeapon;

	[HideInInspector]
	//用於判斷是否處於無敵狀態
	public bool isInvincible = false;
	private float invincibleTime = 5f;
	private float invincibleTimeCD = 0f;
	void Start () {
		tankWeapon = GetComponent<TankWeapon>();
	}
	
	void Update () {

		//判斷坦克是否無敵
		if (isInvincible)
		{
			invincibleTimeCD += Time.deltaTime;
			if (invincibleTimeCD > invincibleTime)
			{
				isInvincible = false;
				invincibleTimeCD = 0f;
			}

		}

		//以下都是按鍵響應相應操作
		if (Input.GetMouseButtonDown(0))
		{
			tankWeapon.Shoot();
		}

		hor = Input.GetAxis("HorizontalUI");

		ver = Input.GetAxis("VerticalUI");

		//控制前後移動
		transform.Translate(Vector3.forward * ver * moveSpeed * Time.deltaTime);

		//控制左右轉向
		transform.Rotate(Vector3.up * hor * turnSpeed * Time.deltaTime);

	}
	//調用此方法可以使坦克無敵5秒
	public void Invincible()
	{
		isInvincible = true;
		invincibleTimeCD = 0f;
	}

}

坦克武器類

這個腳本中實現了坦克武器的基本操作

代碼如下:

using UnityEngine;

public class TankWeapon : MonoBehaviour {

	[Header("炮彈對象")]
	public GameObject shell;

	[Header("炮彈發射點")]
	public Transform shootPoint;

	[Header("發射力量")]
	public float shootPower;

	//聲音組件
	private AudioSource audioSource;

	void Start()
	{
		audioSource = GetComponent<AudioSource>();
	}

	/// <summary>
	/// 射擊方法
	/// </summary>
	public void Shoot()
	{
		//調用此方法時生成一個炮彈
		GameObject newShell = Instantiate(shell, shootPoint.position, shootPoint.rotation);

		//獲取炮彈的剛體組件
		Rigidbody shellRigidbody = newShell.GetComponent<Rigidbody>();

		//給炮彈一個初速度
		shellRigidbody.velocity = shootPoint.forward * shootPower;
		//播放發射的聲音
		audioSource.Play();

	}

}

攝像機跟隨主角坦克

這裏我用的是最簡陋的方式

代碼如下:

using UnityEngine;

public class TankCamera : MonoBehaviour {
	
	//一個空對象掛載着主角
	public Transform target;

	void LateUpdate() {

		if (target == null)
		{
			return;
		}
		//這個空對象中有一個攝像機的子對象,讓這個空對象跟着坦克走
		transform.position = target.position;
	}
}

炮彈實際效果類

細心的讀者可能已經發現,博主在之前的武器類中和主角類中雖然寫了發射炮彈,但是好像這個炮彈不會爆炸,也不會造成傷害。沒錯,這個腳本就是完成這件工作的。

代碼如下:

using UnityEngine;

public class ShellExplosion : MonoBehaviour {

	[Header("爆炸特效")]
	public GameObject shellExplosionPrefab;

	[Header("爆炸特效最大存留時間")]
	public float explosionTime;

	[Header("爆炸衝擊力")]
	public float explosionForce;

	[Header("爆炸半徑")]
	public float explosionRadius;

	[Header("爆炸傷害")]
	public int damage;


	void OnCollisionEnter()
	{
		//實例化爆炸效果且在最大留存時間到時銷燬
		Destroy(Instantiate(shellExplosionPrefab, transform.position, transform.rotation), explosionTime);

		//爆炸後銷燬炮彈對象
		Destroy(gameObject);

		//獲取所有受到爆炸影響的碰撞體
		Collider[] cols = Physics.OverlapSphere(transform.position, explosionRadius);

		//判斷抓取的碰撞體集合是否爲空
		if (cols.Length>0)
		{
			//遍歷所有碰撞體
			for (int i = 0; i < cols.Length; i++)
			{
				//獲取碰撞體所屬對象的剛體
				Rigidbody rig = cols[i].GetComponent<Rigidbody>();

				if (rig != null)
				{
					//給對象剛體添加爆炸力
					rig.AddExplosionForce(explosionForce, transform.position, explosionRadius);
				}

				//獲取碰撞體所屬對象的血條
				Unit tankHP = cols[i].GetComponent<Unit>();
				
				//如果這個對象不爲空且是處於無敵狀態則不受傷害
				if (tankHP != null && tankHP.tag.Equals("Player") && tankHP.GetComponent<Tank>().isInvincible)
				{
					tankHP.UnderAttack(0);
					return;
				}

				if (tankHP != null)
				{
					//調用遭受攻擊方法
					tankHP.UnderAttack(damage);
					
				}

			}
		}
		
	}

}

血條等(以後可能會加別的功能)類

代碼如下:

using UnityEngine;

public class Unit : MonoBehaviour {

	[Header("血量")]
	public int health = 100;
	
	//死亡後特效
	public GameObject deadEffect;

	public void UnderAttack(int damage)
	{
		if (health > damage)
		{
			health -= damage;
		}
		else
		{
			//五秒後銷燬坦克殘骸
			Destroy(Instantiate(deadEffect, transform.position, transform.rotation), 5f);
			Destruct();
		}

	}

	void Destruct()
	{
		Destroy(gameObject);
	}

	

}

AI坦克(敵方)

這個就是重中之重了,不能寫完了之後沒有人跟我們對抗,那就沒意思了,也不能這個敵方NPC太弱智,那也沒意思,所以我們要寫一個:人工智能(障)!

代碼如下:

using UnityEngine;
using UnityEngine.AI;

public class AITank : Unit {

	[Header("開火的距離")]
	public float distance;

	[Header("移動速度")]
	public float moveSpeed;

	[Header("轉向速度")]
	public float turnSpeed;

	[Header("射擊冷卻")]
	public float CDTime;

	//武器組件
	private TankWeapon tankWeapon;
	//自動尋路組件
	private NavMeshAgent navMeshAgent;
	private float time = 0f;
	private Vector3 randomPosition;
	private float randomTurnTime = 5f;

	//用以檢測玩家
	private GameObject player;

	//用於判斷是否處於凍結狀態
	bool isFrozen = false;
	private float frezonTime = 3f;
	private float frezonTimeUp = 0f;

	void Start()
	{
		tankWeapon = GetComponent<TankWeapon>();
		navMeshAgent = GetComponent<NavMeshAgent>();
		randomPosition = new Vector3(Random.Range(-50f, 50f), 0.05f, Random.Range(-50f, 50f));
		player = GameObject.FindWithTag("Player");
	}

	//void FixedUpdate () {

	//	time += Time.fixedDeltaTime;

	//	if (player == null)
	//	{
	//		return;
	//	}

	//	float dis = Vector3.Distance(player.transform.position, transform.position);

	//	if (dis > distance)
	//	{
	//		transform.Translate(Vector3.forward * Time.deltaTime * moveSpeed);
	//	}
	//	else
	//	{
	//		transform.LookAt(player.transform.position);

	//		if (time > CDTime)
	//		{
	//			tankWeapon.Shoot();
	//			time = 0f;
	//		}
	//	}

	//}

	void Update()
	{	
		//判斷是否處於凍結狀態
		if (isFrozen)
		{
			navMeshAgent.ResetPath();
			frezonTimeUp += Time.deltaTime;
			if (frezonTimeUp > frezonTime)
			{
				isFrozen = false;
				frezonTimeUp = 0f;
			}
			return;
		}
		//計算開火CD
		time += Time.deltaTime;

		if (player == null)
		{
			return;
		}
		
		//計算與玩家的距離
		float dis = Vector3.Distance(player.transform.position, transform.position);
		
		//與玩家距離過遠則執行以下代碼
		if (dis > distance)
		{	
			//計算隨機轉向的時間
			randomTurnTime -= Time.deltaTime;
			
			//如果隨機轉向時間滿足,就轉向
			if (randomTurnTime <= 0)
			{
				randomTurnTime = 5f;
				randomPosition = new Vector3(Random.Range(-50f, 50f), 0.05f, Random.Range(-50f, 50f));
			}

			if (Vector3.Distance(randomPosition,transform.position) < 11f)
			{
				randomPosition = new Vector3(Random.Range(-50f, 50f), 0.05f, Random.Range(-50f, 50f));
			}
			
			//自動尋路
			navMeshAgent.SetDestination(randomPosition);
		}
		else
		{
			//navMeshAgent.ResetPath();
			
			//與玩家距離近了之後,自動尋路到玩家
			navMeshAgent.SetDestination(player.transform.position);

			Vector3 dir = player.transform.position - transform.position;
			
			Quaternion target = Quaternion.LookRotation(dir);
			
			//插值轉向,使AI面對玩家
			transform.rotation = Quaternion.Lerp(transform.rotation, target, turnSpeed * Time.deltaTime);

			//射線檢測碰撞
			Ray ray = new Ray(transform.position + Vector3.up, transform.forward);

			RaycastHit hitInfo;
			
			//以下檢測若射線碰撞到的不是玩家,就不開炮,否則開炮
			if (Physics.Raycast(ray, out hitInfo))
			{
				GameObject obj = hitInfo.collider.gameObject;

				if (obj != null && obj.tag.StartsWith("Player"))
				{
					Debug.DrawLine(ray.origin, hitInfo.point);
					if (time > CDTime)
					{
						tankWeapon.Shoot();
						time = 0f;
					}
				}
			}
		}
	}
	//調用此方法將是AI凍結3秒
	public void Frezon()
	{
		isFrozen = true;
		frezonTimeUp = 0f;
	}


}

坦克工廠

有了AI坦克,我們需要隨機生成AI
代碼如下:

using UnityEngine;

public class TanksFactory : MonoBehaviour {

	[Header("想要在地圖上隨機生成的對象")]
	public GameObject randomProductObj;

	[Header("生成時的障礙檢測半徑")]
	public float testRadius;

	[Header("生成對象間隔")]
	public float prodcutCD;

	//計時器
	private float productTime;

	//設置隨機生成地點
	private Vector3 randomProductPosision;

	void Start()
	{
		productTime = prodcutCD;
	}

	void Update()
	{
		//計算生成冷卻時間
		productTime -= Time.deltaTime;

		if (productTime > 0)
		{
			return;
		}
		
		//獲取一個隨機位置
		randomProductPosision = new Vector3(Random.Range(-45f, 45f), testRadius + 0.1f, Random.Range(-45f, 45f));
		//檢測獲取的位置是否是空地
		Collider[] cols = Physics.OverlapSphere(randomProductPosision, testRadius);
		//若是空地,生成一輛AI坦克
		if (cols.Length < 1)
		{
			Instantiate(randomProductObj, randomProductPosision, Quaternion.identity);
			productTime = prodcutCD;
		}
	}

}

AI隊友

那麼道具中有一個是生成一個隊友,我們就做個人工智能隊友,因爲跟AITANK差不多,我就簡寫了一點

代碼如下:

using UnityEngine;
using UnityEngine.AI;
public class AIFriend : Unit {

	[Header("射擊冷卻時間")]
	public float shootCD;

	[Header("轉身速度")]
	public float turnSpeed;
	//用以獲取敵人位置
	private GameObject enemy;
	private TankWeapon tankWeapon;
	//自動尋路
	NavMeshAgent agent;
	float shootTimeCD = 0f;

	void Start()
	{
		//每隔五秒重新獲取一個敵人位置
		InvokeRepeating("GetTarget", 0.5f, 5f);

		tankWeapon = GetComponent<TankWeapon>();

		agent = GetComponent<NavMeshAgent>();
	}

	void Update()
	{
		shootTimeCD += Time.deltaTime;

		if (enemy == null)
		{
			return;
		}
		
		//自動尋路到敵人
		agent.SetDestination(enemy.transform.position);

		Vector3 dir = enemy.transform.position - transform.position;

		Quaternion rotate = Quaternion.LookRotation(dir);

		transform.rotation = Quaternion.Lerp(transform.rotation, rotate,turnSpeed * Time.deltaTime);

		//射線碰撞檢測
		Ray ray = new Ray(transform.position + transform.up, transform.forward);

		RaycastHit hitInfo;

		if (Physics.Raycast(ray,out hitInfo))
		{
			GameObject obj = hitInfo.collider.gameObject;
			if (obj != null && obj.tag.Equals("enemy"))
			{
				if (shootTimeCD > shootCD)
				{
					tankWeapon.Shoot();
					shootTimeCD = 0f;
				}
			}
		}
	}

	void GetTarget()
	{
		enemy = GameObject.FindWithTag("enemy");
	}

}

道具之凍結敵人3秒

代碼如下:

using UnityEngine;

public class FrozenEnemy : MonoBehaviour {

	void OnTriggerEnter(Collider player)
	{	//如果玩家觸發此觸發器,凍結所有敵人3秒並銷燬此觸發器
		if (player.tag.Equals("Player"))
		{

			GameObject[] enemys = GameObject.FindGameObjectsWithTag("enemy");

			if (enemys.Length > 0)
			{
				foreach (var item in enemys)
				{
					item.GetComponent<AITank>().Frezon();
				}
			}

			Destroy(gameObject);
		}
		else
		{
			return;
		}
	}

}

道具之無敵

代碼如下:

using UnityEngine;

public class Invincible : MonoBehaviour {

	void OnTriggerEnter(Collider player)
	{	//如果玩家觸發此觸發器,玩家將獲得無敵狀態5秒
		if (player.tag.Equals("Player"))
		{
			player.GetComponent<Tank>().Invincible();

			Destroy(gameObject);
		}
		else
		{
			return;
		}

	}

}

道具之召喚友軍

代碼如下:

using UnityEngine;

public class CallSupport : MonoBehaviour {

	[Header("友方AI坦克對象")]
	public GameObject friendTankPrefab;

	void OnTriggerEnter(Collider player)
	{
		//當玩家觸發此觸發器,將召喚一個友軍坦克加入戰鬥
		if (player.tag.Equals("Player"))
		{
			Instantiate(friendTankPrefab, transform.forward, transform.rotation);
			Destroy(gameObject);
		}
		else
		{
			return;
		}
	}

}

道具之摧毀全部敵方坦克

代碼如下:

using UnityEngine;

public class KillAllEnemy : MonoBehaviour {

	void OnTriggerEnter(Collider player)
	{
		//若玩家觸發此觸發器,將清除場景中所有的敵方坦克
		if (player.tag.Equals("Player"))
		{
			GameObject[] enemys = GameObject.FindGameObjectsWithTag("enemy");

			if (enemys.Length > 0)
			{
				foreach (var item in enemys)
				{
					Destroy(item);
				}
			}
			Destroy(gameObject);
		}
		else
		{
			return;
		}
	}

}

道具生成器

代碼如下:

using UnityEngine;

public class PropFactory : MonoBehaviour {

	[Header("凍結敵人的道具")]
	public GameObject frezon;

	[Header("清除所有敵人的道具")]
	public GameObject killAllEnemy;

	[Header("無敵5秒的道具")]
	public GameObject invincible;

	[Header("召喚支援的道具")]
	public GameObject callSupport;

	[Header("生成時的障礙檢測半徑")]
	public float testRadius;

	[Header("生成對象間隔")]
	public float prodcutCD;

	//計時器
	private float productTime;

	//設置隨機生成地點
	private Vector3 randomProductPosision;

	void Start()
	{
		productTime = prodcutCD;
	}

	void Update()
	{
		//計算CD
		productTime -= Time.deltaTime;

		if (productTime > 0)
		{
			return;
		}
		
		//獲取隨機位置
		randomProductPosision = new Vector3(Random.Range(-45f, 45f), testRadius + 0.1f, Random.Range(-45f, 45f));
		
		//判斷該位置是不是空地
		Collider[] cols = Physics.OverlapSphere(randomProductPosision, testRadius);

		//若是空地則按照概率隨機生成上面四個道具中的一個
		if (cols.Length < 1)
		{	//獲取0-100的一個隨機數模擬一個概率
			float probability = Random.Range(0f, 100f);

			if (probability < 5f)
			{
				Instantiate(killAllEnemy, randomProductPosision, Quaternion.identity);
				productTime = prodcutCD;
			}
			else if(probability < 10f)
			{
				Instantiate(callSupport, randomProductPosision, Quaternion.identity);
				productTime = prodcutCD;
			}
			else if (probability < 50f)
			{
				Instantiate(invincible, randomProductPosision, Quaternion.identity);
				productTime = prodcutCD;
			}
			else
			{
				Instantiate(frezon, randomProductPosision, Quaternion.identity);
				productTime = prodcutCD;
			}


		}
	}
}

好啦,那我們的The War Of Tanks!!!的1.0版本就做好了,有時間我會更新2.0的!

點個關注,給個讚唄!

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章