小白贪狼做塔防(4)

现在呢我们来搞一下防御塔对怪物的检测逻辑。
首先呢,我想到了触发器检测,并配以一条列表储存在该防御塔射程内的所有敌人。
那么我们来建一个CheckEnemy脚本,用来监测敌人,跟随注视并攻击。

组件及脚本设置

话不多说,贴上代码先:

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class CheckEnemy : MonoBehaviour {
    /// <summary>
    /// 炮塔价格
    /// </summary>
    public float value = 100;
    /// <summary>
    /// 开火频率
    /// </summary>
    public int firewaittime = 2;
    /// <summary>
    /// 临时ID,测试
    /// </summary>
    public int idi = 0;
    /// <summary>
    /// 炮塔转动速度
    /// </summary>
    public float turnSpeed = 2;
    /// <summary>
    /// 最远攻击距离
    /// </summary>
    public float disFire = 12;
    /// <summary>
    /// 是否有正在攻击的目标
    /// </summary>  
    private bool isAlarm;
    /// <summary>
    /// 子弹
    /// </summary>
    public GameObject ziDan;
    /// <summary>
    /// 子弹生成位置
    /// </summary>
    public Transform firePos;
    /// <summary>
    /// 攻击目标
    /// </summary>
    private Transform targetEnemy;
    /// <summary>
    /// 存储所有检测到的敌人
    /// </summary>
    private List<Transform> enemyList;
    /// <summary>
    /// 开火的协程是否开启
    /// </summary>
    private bool isFire = false;

    private Input_Key ik;
    void Start () 
        {
        enemyList = new List<Transform>();
    }
    
    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<EnemyContrl>().Blood <= 0)
                {
                    enemyList.Remove(enemyList[0]);
                }
            }
        }
        else
        {
            if (isFire)
            {
                StopCoroutine("Fire");
                isFire = false;
            }
        }
    }
    /// <summary>
    /// 检测触发物是否为敌人,是的话放入攻击列表
    /// </summary>
    void OnTriggerEnter(Collider other) 
    {
        //可以改成检测空中和地上
        if (other.transform.tag == "Enemy"||other.transform.tag == "FlyEnemy")
        {
            if (!enemyList.Contains(other.transform)) {
                enemyList.Add(other.transform);
            }        
        }
    }
    /// <summary>
    /// 炮塔射击敌人
    /// </summary>
    IEnumerator Fire() {       
        while (true) {
            if (enemyList.Count != 0 && enemyList[0] != null)
            {
                //生成子弹并指定发射的目标
                GameObject go = Instantiate<GameObject>(ziDan, firePos.transform.position, firePos.transform.rotation);
                go.GetComponent<ZhaDan>().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;
    /// <summary>
    /// 攻击目标
    /// </summary>
    public Transform target;
    /// <summary>
    /// 子弹发射速度
    /// </summary>
    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<EnemyContrl>().EnemyHurt(damageValue);
            Destroy(this.gameObject);
        }
    }
}

然后是范围伤害类:

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class ZhaDan : MonoBehaviour {

    public float DamageOffeset;
    public float damageValue;
    /// <summary>
    /// 攻击目标
    /// </summary>
    public Transform target;
    /// <summary>
    /// 子弹发射速度
    /// </summary>
    public float speed = 10f;
    // Use this for initialization
    /// <summary>
    /// AOE半径
    /// </summary>
    public float r = 1f;
    void Start()
    {

    }

    void Update()
    {
        if (target.GetComponent<CapsuleCollider>() !=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<EnemyContrl>().EnemyHurt(dam);
            }
             
            }
        StartCoroutine(Boom());
    }

    IEnumerator Boom() {
        transform.GetComponent<MeshRenderer>().enabled = false;
        GameObject go;
        if (transform.tag == "Bomb")
        {
            go = Instantiate<GameObject>((GameObject)Resources.Load("fireball/Boom/Prefabs/Bomb"), transform.position, transform.rotation);
        } else if (transform.tag == "Ice") {
            go = Instantiate<GameObject>((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<NavMeshAgent>())
         {
            other.transform.GetComponent<EnemyContrl>().DealSpeed();
         }       
    }
}

这时,我们需要在EnemyContrl脚本及加些“零件”:

private bool isLowSpeed = false;
public void DealSpeed() {
        if (isLowSpeed)
        {
            return;
        }
        else {
            isLowSpeed = true;
            StartCoroutine(LowSpeed());          
        }
    }

    IEnumerator LowSpeed() {
        transform.GetComponent<NavMeshAgent>().speed /= 2;
        yield return new WaitForSeconds(2.5f);
        if(transform.GetComponent<NavMeshAgent>())
            transform.GetComponent<NavMeshAgent>().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判断)。

第二种思路是我们偷懒想出的办法,特效照开不误,但是用“隐形的”子弹和炮弹(激光单体,喷火群体)以很快的速度攻击怪物(哈哈这样就可以用旧代码实现新功能了,我好懒)。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 199,271评论 5 466
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 83,725评论 2 376
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 146,252评论 0 328
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 53,634评论 1 270
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 62,549评论 5 359
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 47,985评论 1 275
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,471评论 3 390
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,128评论 0 254
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,257评论 1 294
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,233评论 2 317
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,235评论 1 328
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 32,940评论 3 316
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,528评论 3 302
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,623评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 30,858评论 1 255
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,245评论 2 344
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 41,790评论 2 339

推荐阅读更多精彩内容

  • 无尽之塔角色图鉴1 无尽之塔角色图鉴2 无尽之塔链接1 无尽之塔链接2 游戏更新频繁,且本文由代码自动生成,...
    z_fire阅读 1,400评论 0 1
  • 法术;乱魂钉、幽冥鬼火、天雷怒火、龙卷雨击、三味真火、落日熔金、龙腾水溅、 烈火骄阳、冥烟销骨、九龙啸天、九阴纯火...
    空白_7阅读 4,751评论 0 6
  • 现在我们来探讨怪物的相关事宜:首先我们给怪物加一个EnemyContrl脚本,用来储存怪物的各种属性以及寻路,各种...
    貪狼大人阅读 228评论 0 1
  • 下面,我们为游戏加一点变数——天气最初的设想为晴天时,火焰·炮弹防御塔正常运行,弩箭·激光武器威力加1/2,冰塔减...
    貪狼大人阅读 362评论 2 2
  • 在和群友聊天时,突发灵感,原来我最近是在玩码字游戏,说是写作太牵强,码字最贴切。因为每天要写500...
    一哈哈儿阅读 656评论 2 1