为什么在尝试播放动画剪辑时出现InvalidOperationException异常?

时间:2019-01-07 19:58:27

标签: c# unity3d

using System.Collections;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using UnityEngine;

public static class IListExtensions
{
    public static void Shuffle<T>(this IList<T> ts)
    {
        var count = ts.Count;
        var last = count - 1;
        for (var i = 0; i < last; ++i)
        {
            var r = UnityEngine.Random.Range(i, count);
            var tmp = ts[i];
            ts[i] = ts[r];
            ts[r] = tmp;
        }
    }
}

public class PlayAnimations : MonoBehaviour
{
    public Animator animator;
    private AnimationClip[] clips;
    private List<AnimationClip> clipsList = new List<AnimationClip>();
    private string[] names;

    private void Awake()
    {
        clips = animator.runtimeAnimatorController.animationClips;

        for (int i = 0; i < clips.Length; i++)
        {
            if (clips[i].name.Contains("mixamo"))
            {
                clipsList.Add(clips[i]);
            }
        }
    }

    public void Init()
    {
        if (clipsList.Count > 0)
        {
            StartCoroutine(PlayRandomly());
        }
    }

    private IEnumerator PlayRandomly()
    {
        while (true)
        {
            clipsList.Shuffle();

            foreach (var randClip in clipsList)
            {
                animator.Play(randClip.name);
                yield return new WaitForSeconds(randClip.length);
            }
        }
    }
}

异常消息:

  

InvalidOperationException:集合已修改;枚举操作可能无法执行。

在线:

foreach (var randClip in clipsList)

我正在尝试播放包含maximo的所有动画剪辑。

此脚本中我唯一调用Init方法并开始播放动画的播放器:

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class AnimatorController : MonoBehaviour
{
    public Animator[] animators;
    public Transform target;
    public float speed = 1f;
    public float rotationSpeed;
    public bool slowDown = false;
    public PlayAnimations playanimation;

    private bool endRot = false;
    private Vector3 center;

    // Use this for initialization
    void Start()
    {
        center = target.GetComponent<Renderer>().bounds.center;

        for (int i = 0; i < animators.Length; i++)
        {
            animators[i].SetFloat("Walking Speed", speed);
        }
    }

    // Update is called once per frame
    void Update()
    {
        float distanceFromTarget = Vector3.Distance(animators[2].transform.position, target.position);

        for(int i = 0; i < animators.Length; i++)
        {
            animators[2].transform.position = Vector3.MoveTowards(animators[2].transform.position, center, 0);
        }

        if (slowDown)
        {
            if (distanceFromTarget < 10)
            {
                float speed = (distanceFromTarget / 10) / 1;
                for (int i = 0; i < animators.Length; i++)
                {
                    animators[i].SetFloat("Walking Speed", speed);
                }
            }
        }

        if (distanceFromTarget < 5f)
        {
            for (int i = 0; i < animators.Length; i++)
            {
                //animators[i].SetFloat("Walking Speed", 0);
                animators[i].SetBool("Idle", true);
                playanimation.Init();
            }

            if (!endRot)
            {
                Quaternion goalRotation = Quaternion.Euler(0f, 0f, 0f);
                float angleToGoal = Quaternion.Angle(
                        goalRotation,
                        animators[0].transform.localRotation);
                float angleThisFrame = Mathf.Min(angleToGoal, rotationSpeed * Time.deltaTime);

                // use axis of Vector3.down to keep angles positive for ease of use
                animators[0].transform.Rotate(Vector3.up, angleThisFrame);
                animators[1].transform.Rotate(Vector3.down, angleThisFrame);

                // We end if we rotated the remaining amount.
                endRot = (angleThisFrame == angleToGoal);
            }
            {
                animators[0].SetBool("Rifle Aiming Idle", true);
                animators[1].SetBool("Rifle Aiming Idle", true);
            }
        }
    }
}

在更新内的行上:

playanimation.Init();

1 个答案:

答案 0 :(得分:1)

您要在Update的{​​{1}}中启动协程

AnimatorController

始终位于同一组件if (distanceFromTarget < 5f) { for (int i = 0; i < animators.Length; i++) { //animators[i].SetFloat("Walking Speed", 0); animators[i].SetBool("Idle", true); playanimation.Init(); } //... 上。另外,它被放置在PlayAnimations playanimation中,因此可能会被更频繁地调用。

这使您可以使用多个并发的协程来操纵相同的值Update

当前一个例程可能已经在执行clipsList循环,而第二个例程则更改了

foreach的内容
clipsList

这不是“允许的”->查看您的例外情况


确保只运行一次例程:

clipsList.Shuffle();

或/和通常优化private bool wasInitialized; public void Init() { // Only start the coroutine if not initialized yet if (!wasInitialized && clipsList.Count > 0) { wasInitialized = true; StartCoroutine(PlayRandomly()); } } 来完全避免那些多余的呼叫。