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

非MonoBehaviour脚本中调用Coroutine失效问题排查与解决

问题分析
  1. 空引用错误:你声明的mono字段没有被赋值就调用了StartCoroutine,运行时会直接抛出NullReferenceException,导致协程根本无法启动。
  2. 重复启动协程:UpdateLogic方法每帧都会执行,你在这里直接调用StartCoroutine会导致每一帧都启动一个新的DelayFunction协程,2秒后会批量打印大量日志,完全不符合预期逻辑。
解决方案

下面提供两种靠谱的实现方式,根据你的项目结构选择:

方式一:利用状态机本身的MonoBehaviour(推荐)

如果你的MovementSM是继承自MonoBehaviour的状态机类(这在Unity状态机实现中很常见),直接通过状态机对象启动协程即可,不需要额外声明mono字段:

using System.Collections;
using UnityEngine;

public class Attacking : BaseState
{
    private MovementSM _sm;
    public float movementSpeed;
    private bool _isCoroutineRunning; // 标记协程是否正在运行

    public Attacking(MovementSM stateMachine) : base("Attacking", stateMachine)
    {
        _sm = stateMachine;
    }

    public override void Enter()
    {
        base.Enter();
        _sm.animator.SetTrigger("attack");
        _isCoroutineRunning = false; // 进入状态时重置标记
    }

    public override void UpdateLogic()
    {
        base.UpdateLogic();
        // 只有协程未运行时才启动
        if (!_isCoroutineRunning)
        {
            _isCoroutineRunning = true;
            _sm.StartCoroutine(DelayFunction());
        }
    }

    IEnumerator DelayFunction()
    {
        yield return new WaitForSeconds(2);
        Debug.Log("Test!");
        _isCoroutineRunning = false; // 协程结束后重置标记,允许后续再次触发
    } 
}

方式二:全局协程管理器(适合状态机非MonoBehaviour的场景)

如果你的状态机本身不是MonoBehaviour,可以创建一个全局单例的MonoBehaviour专门管理协程:

1. 创建协程管理器脚本

using UnityEngine;
using System.Collections;

public class CoroutineManager : MonoBehaviour
{
    public static CoroutineManager Instance { get; private set; }

    private void Awake()
    {
        // 单例初始化,保证全局唯一
        if (Instance == null)
        {
            Instance = this;
            DontDestroyOnLoad(gameObject); // 场景切换不销毁
        }
        else
        {
            Destroy(gameObject);
        }
    }

    // 对外暴露的协程启动方法
    public Coroutine StartManagedCoroutine(IEnumerator coroutine)
    {
        return StartCoroutine(coroutine);
    }
}

把这个脚本挂载到任意一个全局存在的GameObject上(比如空的Manager对象)。

2. 修改Attacking脚本调用协程

using System.Collections;
using UnityEngine;

public class Attacking : BaseState
{
    private MovementSM _sm;
    public float movementSpeed;
    private bool _isCoroutineRunning;

    public Attacking(MovementSM stateMachine) : base("Attacking", stateMachine)
    {
        _sm = stateMachine;
    }

    public override void Enter()
    {
        base.Enter();
        _sm.animator.SetTrigger("attack");
        _isCoroutineRunning = false;
    }

    public override void UpdateLogic()
    {
        base.UpdateLogic();
        if (!_isCoroutineRunning)
        {
            _isCoroutineRunning = true;
            CoroutineManager.Instance.StartManagedCoroutine(DelayFunction());
        }
    }

    IEnumerator DelayFunction()
    {
        yield return new WaitForSeconds(2);
        Debug.Log("Test!");
        _isCoroutineRunning = false;
    } 
}

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.01 01:32:02