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

