【问题标题】:Animation doesn't play while coroutine is running协同程序运行时动画不播放
【发布时间】:2020-06-10 13:13:51
【问题描述】:

当我的场景开始时,我会启动一个协程来生成一个向用户显示的谜题。有时,这一代可能需要几秒钟,但通常很快。因为用户也可以从场景中刷新拼图,所以我不想来回跳转到单独的加载场景,而是在协程运行时显示一个简单的动画。我在同一个精灵上测试了两个动画:精灵的动画师和更新时的简单旋转。然而,就像标题所说的,当协程运行时,这两个都没有运行。我可以看到该动画有效,因为在协程完成后,两个动画都开始了(请参阅下面注释掉的 SetActive(false))。我怎样才能让这个动画工作?

结构是这样的:

-游戏场景

--画布

---PuzzleControl 游戏对象(带有 PuzzleControl 脚本)

----动画游戏对象(带有Sprite Renderer和Animator组件)

---LoadingScreen 游戏对象(带有 LoadingAnimation 脚本)

public class PuzzleControl : MonoBehaviour
{
    private PuzzleBuilder builder;
    void Start()
    {
        builder = this.gameObject.AddComponent<PuzzleBuilder>();
        LoadingScreen.SetActive(true);
        StartCoroutine(builder.Refresh());
    }

    public void RebuildPuzzle()
    {
        LoadingScreen.SetActive(true);
        StartCoroutine(builder.Refresh());
    }

    public void BuildComplete()
    {
        // This will be called by our builder -- currently turned off
        //LoadingScreen.SetActive(false);
    }
}


public class LoadingAnimation : MonoBehaviour
{
    public GameObject animation;
    private float rotateSpeed = 200f;

    // Update is called once per frame
    void Update()
    {
        animation.GetComponent<Transform>().Rotate(new Vector3(0f,0f, rotateSpeed * Time.deltaTime));
    }
}

【问题讨论】:

    标签: unity3d animation loading coroutine


    【解决方案1】:

    这是一个常见的误解,协程实际上并不在单独的线程上运行,所以如果你有很多计算要做,你需要将它移动到一个任务中并在线程池上运行它,否则 Unity 仍然会冻结而它正在做这项工作。协程仅在它们产生时返回对 cpu 的控制,每个帧统一将重新进入它停止的协程,这给人一种代码在同一时间运行的错觉,而实际上它只是使用 yield 关键字被切成块。

    将事物移动到线程中的一个限制是,作为一般规则,您实际上无法触及 Unity 引擎中的任何内容,因此您需要收集所需的数据,然后将其发送到线程中计算,然后在完成后将更改应用到场景。

    这是一个快速示例,说明如何将长时间运行的计算发送到一个单独的线程(或至少将其并行化,因为 C# 会将其发送到 ThreadPool 以处理分发):

    using System.Threading.Tasks;
    
    void Start()
    {   
        // send it 
        var taskHandle = Task<MapBuildData>.Run(DoLongBuildCalculation).ContinueWith(OnBuildComplete);
    }
    
    MapBuildData DoLongBuildCalculation()
    {
        // do long calculation.
        return result;
    }
    
    void OnBuildComplete(Task<MapBuildData> buildTask)
    {
        if(buildTask.IsFaulted || buildTask.IsCanceled)
        {
            // handle it.
        }
    
        // do something in with buildTask.Result
    } 
    

    编辑:为澄清起见,您不应与 DoLongBuildCalculation() 中的 Unity 对象进行交互。

    【讨论】:

    • 感谢您的解释。不幸的是,我的构建器正在创建游戏对象,所以这不适用于我的情况。也许动画可以以某种方式放入单独的线程中?
    • 不幸的是,这是不可能的。我的建议是在完成后保存一个您想要生成对象的列表并将其发送回主线程。如果您的地图构建过程依赖于响应引擎中的重叠之类的事情,那么恐怕您就不走运了。
    • 您当然可以将构建步骤分解为更小更易于管理的块并将控制权交还给引擎。因此,如果您迭代 1000 多个对象,每 50 个左右就会产生一次,这会给 CPU 时间来更新动画。
    • 谢谢!那是我需要的小费。我有一个特别重的部分导致延迟,在内部循环中添加收益返回实际上导致整个过程在我的动画运行愉快的同时受到重大影响。通过将收益放在外部循环中,我能够做出妥协。现在我得到了一个生涩的动画和不错的加载时间。不完美,但我可以从这里开始。现在我对结果很满意,多亏了你,我对协程有了更好的理解。
    • 很高兴为您提供帮助!这绝对是其中一件事,一旦你开始思考,就会开启一个全新的理解:)
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2018-08-19
    • 2015-07-01
    • 2017-06-04
    • 2015-01-15
    • 1970-01-01
    相关资源
    最近更新 更多