一起来玩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的!

点个关注,给个赞呗!

发布了34 篇原创文章 · 获赞 25 · 访问量 9856

猜你喜欢

转载自blog.csdn.net/maybe_ice/article/details/104738968