Crystal.cs
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 |
继承 | 复用其 targetTrans, tower, animator, towerLevel, towerID 等字段及生命周期方法 |
GameController |
通过GameController.Instance |
获取对象池资源、音效、检查游戏状态 |
Bullect(及其子类 CrystalBullect) |
使用 | 光束物体上挂载了 Bullect 脚本(很可能是 CrystalBullect),塔设置其 targetTrans,由该脚本负责周期性伤害 |
Tower |
间接关联 | 水晶塔的 tower 字段由 Tower 脚本在 Init() 中赋值 towerPersonalProperty.tower = this |
对象池工厂(BaseFactory) |
间接 | 通过 GameController.GetGameObjectResource 和 PushGameObjectToFactory 管理光束资源 |
AtomGit 是由开放原子开源基金会联合 CSDN 等生态伙伴共同推出的新一代开源与人工智能协作平台。平台坚持“开放、中立、公益”的理念,把代码托管、模型共享、数据集托管、智能体开发体验和算力服务整合在一起,为开发者提供从开发、训练到部署的一站式体验。
更多推荐



所有评论(0)