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的!
點個關注,給個讚唄!