C#非MonoBehaviour类中添加1秒动画延迟的实现难题
非MonoBehaviour类中实现延迟动画的可行方案
针对你的场景:AnimationManager不是继承自MonoBehaviour无法用协程,DoSomething因含ref参数无法转为async Task,以下是三种可行解决方案:
方案1:使用Unity协程包装器(推荐)
通过一个独立的MonoBehaviour类托管协程,让非MonoBehaviour类也能调用协程实现延迟,完全贴合Unity工作流:
步骤1:创建协程托管类
public class CoroutineRunner : MonoBehaviour { public static CoroutineRunner Instance { get; private set; } private void Awake() { if (Instance != null && Instance != this) { Destroy(gameObject); } else { Instance = this; DontDestroyOnLoad(gameObject); } } public void RunCoroutine(IEnumerator coroutine) { StartCoroutine(coroutine); } }
将这个脚本挂载到场景中的空GameObject上(比如命名为CoroutineManager),确保场景加载时它存在。
步骤2:修改AnimationManager的方法
public class AnimationManager { public static string CreateAnimation(GameData data) { Sword newSword = new Sword(data.SwordData); string returnString = newSword.GetSwordString(); AnimateSwords(data.SwordData, data.SwordObjects, returnString); return returnString; // 补充原代码遗漏的return语句 } public static void AnimateSwords(SwordData data, List<Swords> swordObjects, string returnString) { // 委托给协程托管类执行延迟动画 CoroutineRunner.Instance.RunCoroutine(DelayedAnimationLogic(data, swordObjects, returnString)); } private static IEnumerator DelayedAnimationLogic(SwordData data, List<Swords> swordObjects, string returnString) { // 延迟1秒 yield return new WaitForSeconds(1f); // 这里写你的动画逻辑 foreach (var sword in swordObjects) { // 示例:播放剑的动画 sword.PlayAnimation(data.AnimationClip); } } }
方案2:使用async/await结合主线程调度
利用Task.Delay实现延迟,同时通过主线程调度器确保Unity API在主线程执行:
步骤1:创建主线程调度器
public class UnityMainThreadDispatcher : MonoBehaviour { public static UnityMainThreadDispatcher Instance { get; private set; } private readonly Queue<Action> _pendingActions = new Queue<Action>(); private void Awake() { if (Instance != null && Instance != this) { Destroy(gameObject); } else { Instance = this; DontDestroyOnLoad(gameObject); } } private void Update() { lock (_pendingActions) { while (_pendingActions.Count > 0) { _pendingActions.Dequeue().Invoke(); } } } public void Enqueue(Action action) { lock (_pendingActions) { _pendingActions.Enqueue(action); } } }
同样挂载到场景中的空GameObject上。
步骤2:修改AnimateSwords为异步方法
public static async void AnimateSwords(SwordData data, List<Swords> swordObjects, string returnString) { // 延迟1秒 await Task.Delay(1000); // 调度到主线程执行动画(Unity API必须在主线程调用) UnityMainThreadDispatcher.Instance.Enqueue(() => { // 动画逻辑 foreach (var sword in swordObjects) { sword.PlayAnimation(data.AnimationClip); } }); }
方案3:使用System.Timers.Timer
通过定时器实现延迟,同样需要主线程调度确保Unity API正常执行:
public static void AnimateSwords(SwordData data, List<Swords> swordObjects, string returnString) { var delayTimer = new System.Timers.Timer(1000); delayTimer.Elapsed += (sender, e) => { delayTimer.Stop(); delayTimer.Dispose(); // 调度到主线程执行动画 UnityMainThreadDispatcher.Instance.Enqueue(() => { // 动画逻辑 foreach (var sword in swordObjects) { sword.PlayAnimation(data.AnimationClip); } }); }; delayTimer.Start(); }
(此方案依赖方案2中的UnityMainThreadDispatcher)
方案对比
- 方案1:最符合Unity开发习惯,无需处理线程问题,协程天然在主线程执行,推荐使用。
- 方案2:代码简洁,适合熟悉异步编程的开发者,但需注意
async void的异常处理。 - 方案3:适合习惯传统定时器的场景,但线程处理逻辑较繁琐。
内容的提问来源于stack exchange,提问作者Rg Santos
相关产品推荐
相关产品推荐

