WindmillBullect.cs

这个脚本定义了一种特殊的子弹------风车子弹WindmillBullect),它继承自一个通用的子弹基类 Bullect。实现直线飞行存在时间限制命中后不立即销毁的行为;带追踪 + 生命周期管理的子弹。

继承关系

  • 父类Bullect

新增声明

  • hasTarget:是否已经锁定目标并开始追踪(防止每帧重复初始化方向,只在第一次获得目标时执行一次 InitTarget。)
  • timeVal:计时器(子弹存活)

生命周期方法

OnEnable()

复制代码
private void OnEnable()
{
    hasTarget = false;  
    timeVal = 0;
}
  • OnEnable 在子弹 被激活(生成)时 调用(对象池复用场景尤为重要)。

  • 重置状态:未锁定目标、计时归零。

这样做避免了上次使用留下的脏数据。

Update()

  1. 检查游戏是否结束或超时,或者子弹的timeVal>= 2.5f → 调用timeVDestoryBullect()立即销毁子弹(调用父类 DestoryBullect 回池)。。

  2. 处理游戏暂停,不执行移动、不更新计时,子弹定在原地,直接返回

  3. 更新时间计时器,timeVal每帧累加,但不超过 2.5 秒

  4. 移动逻辑(核心)
    hasTargettrue → 沿自身 forward 方向移动(世界坐标系)。
    hasTargetfalse → 检查目标是否存在且激活→ 锁定目标(hasTargettrue) → 调用 InitTarget() 计算朝向(延迟启动:首帧不移动,只转向)。

)。

复制代码
 if (hasTarget)
    {
        transform.Translate(transform.forward * moveSpeed * Time.deltaTime, Space.World);
    }
    else
    {
        // 等待目标有效,转向并开始移动
        if (targetTrans != null && targetTrans.gameObject.activeSelf)
        {
            hasTarget = true;
            InitTarget();
        }
    }

辅助方法

private void InitTarget()

InitTarget() 的主要作用是 让子弹的正面(forward 方向)指向目标,并修正子弹的最终朝向 ,使其能够在后续飞行中沿 transform.forward 方向直线飞向目标并符合美术期望的飞行姿态。
具体步骤

  1. 根据目标的标签("Item" )计算瞄准点。

  2. 调用 transform.LookAt 让子弹的 forward 轴指向该点。

  3. 如果子弹当前的 Y 轴欧拉角为 0,则强制旋转到 90 度。


复制代码
if (targetTrans.gameObject.tag == "Item")
{
    transform.LookAt(targetTrans.position + new Vector3(0, 0, 3));
}
else
{
    transform.LookAt(targetTrans.position);
}

为什么要 + new Vector3(0, 0, 3)?

原因:

  • 物品("Item")可能是地面上的可破坏对象(如桶、箱子),它的 pivot 点(锚点)可能位于地面(Z=0 附近) 。若直接 LookAt 物品位置,子弹的朝向会指向地面,导致:

    • 子弹飞向地面,视觉上像是"扎地"而不是水平飞向物品。

    • 在 2D 游戏中,物品的碰撞体通常是一个水平放置的方形,子弹从侧面水平击中才合理。

解决办法:

对物品的瞄准点增加 (0, 0, 3) 偏移,即让子弹朝物品上方 3 个单位(沿 Z 轴)的方向瞄准。这样子弹会从物品的"正面"或"上方"掠过,配合飞行逻辑,实际碰撞可能依然有效,且视觉上更自然。


子弹为什么要强制旋转到 90 度?

  • 子弹模型(如风车叶片)可能设计为 XZ 平面内旋转,期望的飞行方向是 y 轴朝向侧面

  • LookAt 指向目标后,子弹的 Y 轴欧拉角有时会变成 0(例如子弹初始朝向和瞄准方向刚好使模型侧面朝上)。将 Y 角强制设为 90 度,能确保子弹的"正面"朝向正确的飞行方向,符合美术设计的视觉预期。

重写方法

protected override void OnTriggerEnter2D()

此脚本没有调用 base.OnTriggerEnter2D,意味着父类的通用碰撞逻辑(如销毁子弹)被覆盖。

执行流程

  1. 只关心标签为 "Monster""Item" 的对象。

  2. 确保碰撞对象处于激活状态。

  3. 防御性检查:如果目标是无效的,或当前物品目标已被清除,则提前返回。

  4. 伤害触发条件
    如果是怪物 → 直接造成伤害。
    如果是物品 → 需要判断 该物品是否为子弹当前锁定的那个目标(通过 GameController.Instance.targetTrans 对比)。这暗示游戏可能只允许破坏当前指定的某个物品(例如任务目标)
    → 调用目标的 TakeDamage 方法:向碰撞体发送 "TakeDamage" 消息,参数为 attackValue碰撞,同时播放命中特效CreateEff()

复制代码
 if (collision.gameObject.activeSelf)
        {
            if (targetTrans.position == null || (collision.tag == "Item" && GameController.Instance.targetTrans == null))
            {
                return;
            }
            if (collision.tag == "Monster" || (collision.tag == "Item" && GameController.Instance.targetTrans == collision.transform))
            {
                collision.SendMessage("TakeDamage", attackValue);
                CreateEffect();
            }
        }
相关推荐
小白学大数据1 小时前
Python 爬虫爬取应用商店数据:请求构造与数据解析
前端·爬虫·python·数据分析
pkowner1 小时前
若依分页问题及解决方法
java·前端·算法
golang学习记1 小时前
Cursor官方团队的AI指南:Cursor Team Kit
前端·cursor
Lee川1 小时前
RAG 知识库问答:从概念到代码的完整实现
前端·人工智能·后端
计算机安禾2 小时前
【c++面向对象编程】第22篇:输入输出运算符重载:<< 与 >> 的友元实现
java·前端·c++
redreamSo2 小时前
14 小时烧光 200 美金:Codex 和 Claude 的 /goal 命令打开了"放手跑"模式
前端
TingTing2 小时前
Webpack5 前端工程化建设
前端
A不落雨滴AI2 小时前
DKERP客户端重构纪实:4天自研控件库的“短命”教训,以及为什么我坚定选择原生Qt
前端
我叫黑大帅2 小时前
通过白名单解决 pnpm i 报错 Ignored build scripts
前端·javascript·面试