You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Unity协程WaitForSeconds等待失效 Boss攻击颜色切换逻辑异常

问题原因
  • 协程重复启动:Update()是每帧执行的生命周期方法,你在其中每帧调用StartCoroutine(AttackChoices()),会导致每秒生成几十上百个独立运行的协程实例,多个协程同时修改颜色、触发等待,直接打乱计时逻辑,出现等待被跳过、颜色快速闪烁的现象。
  • 随机值范围错误:Unity中Random.Range(0,4)返回的整数范围是包含0、不包含4,也就是只会返回0/1/2/3四个值,你写的AttackChoice ==4分支永远不会触发,值为0时也没有任何处理逻辑。
  • 缺少循环逻辑:现有协程执行完单次攻击流程后就直接退出,无法自动重复执行你预期的循环攻击流程。
  • 冗余性能开销:代码中多次调用GetComponentInChildren<Renderer>()获取组件,没有提前缓存,带来不必要的性能消耗。
修复步骤
  1. 将协程启动逻辑从Update()移除,放到Start()生命周期中仅执行一次,避免重复生成协程实例。
  2. 在协程内部添加无限循环逻辑,让单次攻击流程执行完成后,自动回到流程开头等待下一轮攻击。
  3. 修正随机数取值范围,匹配你预设的4个攻击分支;也可以根据需求补全值为0时的攻击逻辑。
  4. 提前缓存Renderer组件和初始材质颜色,避免重复查找组件。
  5. 可以将硬编码的等待时长抽为可配置的public字段,方便在Inspector面板直接调整参数,无需修改代码。
修复后完整代码
using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class BossMovement : MonoBehaviour
{
    [Header("颜色配置")]
    public Color originalColor;
    private Material mymaterial;

    [Header("攻击配置")]
    public float attackWaitDuration = 15f;
    public float colorHoldDuration = 15f;
    int attackChoice;

    [Header("移动配置")]
    public Transform spawnPoint;
    public float distanceStop;
    public float distanceR;
    public float distance;
    public float distance3;
    public float speed = 4f;
    public float speedR;

    [Header("组件引用")]
    public Animator anim;
    public Transform target;
    Rigidbody rig;

    private Coroutine attackRoutine;

    private void Start()
    {
        rig = GetComponent<Rigidbody>();
        anim = GetComponentInChildren<Animator>();
        // 缓存材质组件
        mymaterial = GetComponentInChildren<Renderer>().material;
        originalColor = mymaterial.color;
        // 仅启动一次攻击协程
        attackRoutine = StartCoroutine(AttackChoices());
    }
    
    private void Update()
    {
        whento();
        StopFollowing();
    }

    void followPlayer()
    {
        var step = speed * Time.deltaTime;
        transform.position = Vector3.MoveTowards(rig.transform.position, target.position, step);
    }
            
    IEnumerator AttackChoices()
    {
        // 无限循环实现攻击流程重复执行
        while (true)
        {
            // 等待指定间隔后随机选攻击
            yield return new WaitForSeconds(attackWaitDuration);
            attackChoice = Random.Range(1, 5); // 取值1/2/3/4,匹配四个分支

            switch (attackChoice)
            {
                case 1:
                    mymaterial.color = Color.white;
                    yield return new WaitForSeconds(colorHoldDuration);
                    mymaterial.color = originalColor;
                    // 此处添加白色对应攻击的逻辑
                    break;
                case 2:
                    mymaterial.color = Color.blue;
                    yield return new WaitForSeconds(colorHoldDuration);
                    mymaterial.color = originalColor;
                    // 此处添加蓝色对应攻击的逻辑
                    break;
                case 3:
                    mymaterial.color = Color.red;
                    yield return new WaitForSeconds(colorHoldDuration);
                    mymaterial.color = originalColor;
                    // 此处添加红色对应攻击的逻辑
                    break;
                case 4:
                    // 无颜色提示的攻击分支
                    yield return new WaitForSeconds(colorHoldDuration);
                    // 此处添加对应攻击的逻辑
                    break;
            }
        }
    }

    void whento()
    {
        transform.LookAt(target);
        RaycastHit hit;
        if (Physics.Raycast(transform.position, transform.forward, out hit, distance))
        {
            if (hit.transform.CompareTag("Player"))
            {
                followPlayer();
                anim.SetFloat("Speed", 1, 0f, Time.deltaTime);
            }
        }
        else
        {
            anim.SetFloat("Speed", 0, 0.1f, Time.deltaTime);
        }
    }

    void StopFollowing()
    {
        RaycastHit hit;
        if (Physics.Raycast(transform.position, transform.forward, out hit, distance3))
        {
            if (hit.transform.CompareTag("Player"))
            {
                distance = distanceStop;
            }
        }
        else
        {
            distance = distanceR;
        }
    }

    private void ChargeUpAndRealse()
    {
        anim.SetLayerWeight(anim.GetLayerIndex("ChargeLayer"), 1);
        anim.SetTrigger("Charge");
        anim.SetLayerWeight(anim.GetLayerIndex("ChargeLayer"), 0);
    }
}

补充说明:如果后续需要在Boss死亡/进入其他状态时终止攻击循环,直接调用StopCoroutine(attackRoutine)即可。

内容的提问来源于stack exchange,提问作者user19548636

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.26 15:42:28