现在呢我们来搞一下防御塔对怪物的检测逻辑。
首先呢,我想到了触发器检测,并配以一条列表储存在该防御塔射程内的所有敌人。
那么我们来建一个CheckEnemy脚本,用来监测敌人,跟随注视并攻击。
话不多说,贴上代码先:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class CheckEnemy : MonoBehaviour {
///
/// 炮塔价格
///
public float value = 100;
///
/// 开火频率
///
public int firewaittime = 2;
///
/// 临时ID,测试
///
public int idi = 0;
///
/// 炮塔转动速度
///
public float turnSpeed = 2;
///
/// 最远攻击距离
///
public float disFire = 12;
///
/// 是否有正在攻击的目标
///
private bool isAlarm;
///
/// 子弹
///
public GameObject ziDan;
///
/// 子弹生成位置
///
public Transform firePos;
///
/// 攻击目标
///
private Transform targetEnemy;
///
/// 存储所有检测到的敌人
///
private List enemyList;
///
/// 开火的协程是否开启
///
private bool isFire = false;
private Input_Key ik;
void Start ()
{
enemyList = new List();
}
void Update () {
//攻击队列里有敌人
if (enemyList.Count != 0)
{
if (enemyList[0] == null)
{
enemyList[0] = enemyList[1];
enemyList.Remove(enemyList[1]);
}
else
{
if (!isFire)
{
isFire = true;
StartCoroutine("Fire");
}
//瞄准敌人
//transform.LookAt(enemyList[0]);
Vector3 direction = enemyList[0].position - transform.position;
Quaternion qua = Quaternion.LookRotation(direction);
transform.rotation = Quaternion.Lerp(transform.rotation, qua, Time.deltaTime * turnSpeed);
////如果敌人脱离攻击范围,或者死亡
if (Vector3.Distance(transform.position, enemyList[0].position) > disFire || enemyList[0].GetComponent().Blood <= 0)
{
enemyList.Remove(enemyList[0]);
}
}
}
else
{
if (isFire)
{
StopCoroutine("Fire");
isFire = false;
}
}
}
///
/// 检测触发物是否为敌人,是的话放入攻击列表
///
void OnTriggerEnter(Collider other)
{
//可以改成检测空中和地上
if (other.transform.tag == "Enemy"||other.transform.tag == "FlyEnemy")
{
if (!enemyList.Contains(other.transform)) {
enemyList.Add(other.transform);
}
}
}
///
/// 炮塔射击敌人
///
IEnumerator Fire() {
while (true) {
if (enemyList.Count != 0 && enemyList[0] != null)
{
//生成子弹并指定发射的目标
GameObject go = Instantiate(ziDan, firePos.transform.position, firePos.transform.rotation);
go.GetComponent().target = enemyList[0];
}
else if (enemyList[0] == null) {
enemyList.Remove(enemyList[0]);
}
yield return new WaitForSeconds(firewaittime);
}
}
}
这里有一小点:如果不写下面的这个就会出现n座塔同时攻击有一个目标,怪物被其中一座塔收走人头,其他以该怪物为目标的塔就会丢失目标而哑火。
if (enemyList[0] == null)
{
enemyList[0] = enemyList[1];
enemyList.Remove(enemyList[1]);
}
为了改正这个bug,我一开始是想直接在enemyList[0] = null时enemyList.Remove(enemyList[0]),但是不知道为什么还是会出错,所以我就改成把enemyList[1]提前一个单位,并enemyList.Remove(enemyList[1]),这样就好使了,坑爹啊,各位大神如果知道原因请在评论区赐教贪狼感激不尽。
然后我们迎来了本游戏的重头戏“防御塔”攻击方式——弹药。我分成了三大类:1.单体攻击类,2.范围攻击类。而这两类又可以分别派生出对地类,对空类甚至减速buff这种buff类
首先,我们来看看单体攻击类:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class Bullet : MonoBehaviour {
public float damageValue = 25f;
///
/// 攻击目标
///
public Transform target;
///
/// 子弹发射速度
///
public float speed = 10f;
void Update ()
{
if (target&&target!=null)
{
transform.position = Vector3.MoveTowards(transform.position, target.transform.position+new Vector3(0,1.5f,0), speed * Time.deltaTime);
}
//丢失目标
else {
Destroy(transform.gameObject);
}
}
//此处为对地对空双功能,你可以改成单一功能
void OnTriggerEnter(Collider other) {
if (other.transform.tag == "Enemy"|| other.transform.tag == "FlyEnemy") {
other.GetComponent().EnemyHurt(damageValue);
Destroy(this.gameObject);
}
}
}
然后是范围伤害类:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class ZhaDan : MonoBehaviour {
public float DamageOffeset;
public float damageValue;
///
/// 攻击目标
///
public Transform target;
///
/// 子弹发射速度
///
public float speed = 10f;
// Use this for initialization
///
/// AOE半径
///
public float r = 1f;
void Start()
{
}
void Update()
{
if (target.GetComponent() !=null)
{
if (transform.tag != "Ice")
{
transform.position = Vector3.MoveTowards(transform.position, target.transform.position + new Vector3(0, 2.5f, 0), speed * Time.deltaTime);
}
else {
transform.position = Vector3.MoveTowards(transform.position, target.transform.position + new Vector3(0, 0.1f, 0), speed * Time.deltaTime);
}
}
else
{
Destroy(transform.gameObject);
}
}
void OnTriggerEnter(Collider other)
{
if (other.transform.tag == "Enemy")
{
Grenade_AOE_Damage(transform, r, damageValue);
}
}
void Grenade_AOE_Damage(Transform _grenade, float _AOE_radius, float _damage)
{
//获取手雷_AOE_radius范围内所有的碰撞体(敌人)
Collider[] colliders = Physics.OverlapSphere(_grenade.position, _AOE_radius);
//遍历范围内所有敌人并给予伤害
for (int i = 0; i < colliders.Length; i++)
{
bool isCap = colliders[i].GetType() == (typeof(CapsuleCollider));
if (isCap && (colliders[i].gameObject.tag=="Enemy"))
{
//判断距离中心爆炸点距离,并实现伤害衰减
float dis = Vector3.Distance(colliders[i].transform.position, _grenade.position);
float dam = _damage - dis * DamageOffeset;
//加上特效
//获取生命脚本组件,调用伤害函数
colliders[i].gameObject.GetComponent().EnemyHurt(dam);
}
}
StartCoroutine(Boom());
}
IEnumerator Boom() {
transform.GetComponent().enabled = false;
GameObject go;
if (transform.tag == "Bomb")
{
go = Instantiate((GameObject)Resources.Load("fireball/Boom/Prefabs/Bomb"), transform.position, transform.rotation);
} else if (transform.tag == "Ice") {
go = Instantiate((GameObject)Resources.Load("fireball/IceEffect/Prefabs/Fx/IceGo"), transform.position, transform.rotation);
}
else {
go = null;
}
yield return new WaitForSeconds(0.8f);
Destroy(this.gameObject);
if (go != null) {
Destroy(go);
}
}
}
然后我们上网下载一个冰霜特效和爆炸特效,可以增加真实感,并在冰霜特效上挂载脚本控制减速协程:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.AI;
public class IceBullet : MonoBehaviour {
void Start () {
}
// Update is called once per frame
void Update () {
}
void OnTriggerEnter(Collider other) {
if (other.transform.tag == "Enemy" && other.transform.GetComponent())
{
other.transform.GetComponent().DealSpeed();
}
}
}
这时,我们需要在EnemyContrl脚本及加些“零件”:
private bool isLowSpeed = false;
public void DealSpeed() {
if (isLowSpeed)
{
return;
}
else {
isLowSpeed = true;
StartCoroutine(LowSpeed());
}
}
IEnumerator LowSpeed() {
transform.GetComponent().speed /= 2;
yield return new WaitForSeconds(2.5f);
if(transform.GetComponent())
transform.GetComponent().speed *= 2;
![Uploading IS}]3IW`}1{XXX_(8YB{98F_078727.png . . .]
isLowSpeed = false;
}
其中isLowSpeed是为了防止同一怪物被反复减速,然后可以将减速幅度设为public类型,以便想要防御塔升级时加大减速幅度的朋友控制。
另外还有激光枪,喷火筒两种,这里我将粒子特效挂在枪口,并为粒子特效增加碰撞器,使其只检测Enemy层敌人。
然后有两种思路,第一种是在CheckEnemy时,判断自己是否是激光枪,喷火筒(tag=“strong”)
然后监测到敌人就向目标放“特效”(用ParticleSystem my_PS;my_PS.Stop();my_PS.Play();)控制。然后通过粒子碰撞器,将碰到的敌人扣除血量(可以用OnParticleCollisionStay判断)。
第二种思路是我们偷懒想出的办法,特效照开不误,但是用“隐形的”子弹和炮弹(激光单体,喷火群体)以很快的速度攻击怪物(哈哈这样就可以用旧代码实现新功能了,我好懒)。