Crystal(水晶塔)是一个继承自 TowerPersonalProperty 的特殊塔脚本。它代表一种激光/光束类持续攻击塔,与普通塔的“发射子弹”方式完全不同。而是生成一个拉伸模型连接塔与目标,通过模型上的 Bullect 组件(如 CrystalBullect)周期性造成伤害。其特点:

  • 每帧更新光束的位置和缩放,保持视觉连接。

  • 无攻击冷却,攻击频率由子弹脚本控制。

  • 预取和回收光束资源,遵循对象池模式。

  • 持续播放攻击音效。

普通塔(如便便塔、风车塔)会生成子弹对象飞向目标,而水晶塔生成一个拉伸的平面模型(类似激光束),直接连接塔与目标,并持续造成伤害(通过 Bullect 脚本处理持续伤害逻辑)。

核心特点

特点 说明
持续攻击 只要有目标在范围内,激光会一直激活并造成持续伤害(而非单次伤害)。
动态激光模型 激光的外观(长度、宽度)会根据塔与目标的距离实时计算和拉伸。
复用子弹预制体 不发射新子弹,而是激活一个代表激光的 GameObject,并设置其位置和缩放。
音效持续播放 攻击期间循环播放攻击音效。
对象池管理 激光对象在塔销毁时回收到对象池。

与父类 TowerPersonalProperty 的主要差异

行为 普通塔(父类) 水晶塔(Crystal)
攻击方式 周期性生成子弹预制体 持续激活并调整激光模型
Attack() 方法 每 CD 调用一次,生成新子弹 每帧调用,更新激光的位置/缩放
攻击 CD 使用 timeVal 计时 不使用 CD(只要目标存在就持续攻击)
Update() 逻辑 计时 + 旋转 + 满足 CD 时调用 Attack() 直接每帧调用 Attack()(无 CD 检查)
子弹管理 每次攻击生成新子弹 只使用一个激光对象,反复激活/停用
音效 每次攻击播放一次音效 持续循环播放,不攻击时停止

字段声明

字段 类型 说明
distance private float 塔与目标之间的距离(用于计算光束的长度和宽度)
bullectWidth private float 光束的宽度(缩放因子,Y轴缩放,范围限制在 0.5~1 之间)
bullectLength private float 光束的长度(缩放因子,Z轴缩放 = 距离的一半)
audioSource private AudioSource 攻击音效播放组件
  • bullectGo 是从父类继承的,这里用作激光模型的 GameObject,而不是普通的子弹预制体。

  • bullectLength 设为 distance / 2,因为激光模型的原点在其中心,要连接塔和目标,需要让模型长度等于距离的一半,然后位置放在中点。

生命周期方法

protected override void Start()  重写方法

protected override void Start () {
        base.Start();
        bullectGo = GameController.Instance.GetGameObjectResource
        ("Tower/ID"+tower.towerID.ToString()+"/Bullect/"+towerLevel.ToString());
        bullectGo.SetActive(false);
        audioSource = GetComponent<AudioSource>();
        audioSource.clip= GameController.Instance.GetAudioClip
        ("NormalMordel/Tower/Attack/" + tower.towerID.ToString());
    }
  • 调用 base.Start() 初始化父类(获取 gameController、计算价格、查找 Animator 等)。

  • 从对象池获取子弹资源(bullectGo),路径:"Tower/ID{塔id}/Bullect/{塔等级}"
    (注意:此处获取的是预制体实例,但立即设为不可见(SetActive(false)),留待攻击时激活并拉伸。)

  • 获取自身 AudioSource 组件,设置 clip 为攻击音效(路径同父类 Attack 音效)。

注意:水晶塔将子弹资源预先取出并缓存,而不是每次攻击时重新获取。这与其他塔(攻击时临时取子弹)不同。

private void OnEnable()

private void OnEnable()
    {
        if (animator==null)
        {
            return;
        }
        bullectGo = GameController.Instance.GetGameObjectResource
        ("Tower/ID" + tower.towerID.ToString() + "/Bullect/" + towerLevel.ToString());
        bullectGo.SetActive(false);
    }

原因:塔可能被回收再取出,旧的 bullectGo 可能已被销毁或状态不对,重新获取确保资源有效。

  • 塔从对象池激活时调用。

  • 若 animator 为空(未初始化),直接返回。

  • 重新获取子弹资源并设为不可见。

protected override void Update()  重写方法

 protected override void Update () {
       
        if (GameController.Instance.isPause||targetTrans==null||GameController.Instance.gameOver)
        {
            if (targetTrans==null)
            {
                bullectGo.SetActive(false);
            }
            return;
        }
        Attack();       
    }
  • 判断:若游戏暂停、无目标、游戏结束 →
    其中如果目标为空,则隐藏子弹(bullectGo.SetActive(false)),然后返回。

  • 否则直接调用 Attack()
    关键点:没有父类中的 CD 检查(timeVal >= attackCD/gameSpeed),也没有 timeVal 累加。这意味着水晶塔每帧都执行攻击,攻击频率等于帧率(约每秒 30-60 次)。伤害由子弹脚本中的周期性逻辑(如 CrystalBullect 的定时器)来控制实际扣血频率。

核心方法

protected override void Attack() 重写方法

激光模型的动态计算与显示

  • 若目标为空,直接返回。

  • 确保音效播放(audioSource.Play() 如果未在播放)。

  • 播放攻击动画(animator.Play("Attack"))。


计算塔到目标的距离(物品目标有z轴偏离)

 if (targetTrans.gameObject.tag == "Item") {
        distance = Vector3.Distance(transform.position, 
                   targetTrans.position + new Vector3(0,0,3));
    } else {
        distance = Vector3.Distance(transform.position, targetTrans.position);
    }
  • 计算距离:

    • 若目标是物品,则距离 = 塔位置到目标位置+Z轴3的偏移。

    • 否则直接到目标位置。


计算激光的宽度(距离越近越宽,范围限制在0.5~1之间)

    bullectWidth = 3 / distance;
    if (bullectWidth <= 0.5f) bullectWidth = 0.5f;
    else if (bullectWidth >= 1) bullectWidth = 1;
    
    bullectLength = distance / 2;   // 激光长度 = 塔到目标距离的一半
  • 计算光束的宽与长:

    • bullectLength = distance / 2(光束长度设为距离的一半)

    • bullectWidth = 3 / distance(宽度随距离增大而减小)

    • 限制宽度范围 [0.5, 1](避免过细或过粗)


设置激光位置为塔与目标的中点

 bullectGo.transform.position = new Vector3(
        (targetTrans.position.x + transform.position.x) / 2,
        (targetTrans.position.y + transform.position.y) / 2,
        0
    );
  • 设置光束位置:塔与目标的中点((塔位置 + 目标位置)/2,Z 轴为 0)。

其他激光设置

        bullectGo.transform.localScale = new Vector3(1,bullectWidth,bullectLength);
        bullectGo.SetActive(true);
        bullectGo.GetComponent<Bullect>().targetTrans = targetTrans;
  • 设置光束缩放:new Vector3(1, bullectWidth, bullectLength)
    假设光束模型的原始长度为 2 单位,通过 Z 轴缩放达到长度 = 2 * bullectLength = distance,正好连接塔与目标。原始宽度为 1,通过 Y 轴缩放实现宽度变化。

  • 激活光束(SetActive(true))。

  • 将目标引用传递给激光上的 Bullect 脚本(负责持续伤害逻辑)
    获取光束上的 Bullect 组件,将其 targetTrans 设置为当前目标。
    这意味着光束上的子弹脚本负责造成伤害(例如 CrystalBullect 每 0.5 秒调用 DecreaseHP)。

protected override void DestoryTower() 重写方法

销毁时的清理

  • 将激光对象回收到对象池,而不是直接销毁。

  • 然后调用 base.DestoryTower() 回收塔对象本身。

protected override void DestoryTower() {
    bullectGo.SetActive(false);   // 先隐藏激光
    GameController.Instance.PushGameObjectToFactory
   ("Tower/ID" + tower.towerID.ToString() + "/Bullect/" + towerLevel.ToString(), bullectGo);
    bullectGo = null;
    base.DestoryTower();   // 调用父类销毁塔自身
}

与其他脚本的关联

脚本 关联方式 说明
TowerPersonalProperty 继承 复用其 targetTranstoweranimatortowerLeveltowerID 等字段及生命周期方法
GameController 通过
 GameController.Instance
获取对象池资源、音效、检查游戏状态
Bullect(及其子类 CrystalBullect 使用 光束物体上挂载了 Bullect 脚本(很可能是 CrystalBullect),塔设置其 targetTrans,由该脚本负责周期性伤害
Tower 间接关联 水晶塔的 tower 字段由 Tower 脚本在 Init() 中赋值 towerPersonalProperty.tower = this
对象池工厂(BaseFactory 间接 通过 
GameController.GetGameObjectResource 和 PushGameObjectToFactory 管理光束资源
Logo

AtomGit 是由开放原子开源基金会联合 CSDN 等生态伙伴共同推出的新一代开源与人工智能协作平台。平台坚持“开放、中立、公益”的理念,把代码托管、模型共享、数据集托管、智能体开发体验和算力服务整合在一起,为开发者提供从开发、训练到部署的一站式体验。

更多推荐