Unity error: InitWWW can only be called from the main thread

Unity error: InitWWW can only be called from the main thread

我有一个功能可以从文件夹中获取图像并在 RawImage 上显示每个图像 5 秒,然后重新开始。

在 looper() 函数调用 medialogic() 函数时显示第一张图像后,我遇到了这个问题。它给出了标题错误。

我该如何解决这个问题或者为什么会发生这种情况?我是 unity 和 C# 的新手。

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

public class Test2 : MonoBehaviour {

    // Use this for initialization

    //Publics
    private string path = "file:///";
    public string folder = "C:/medias/";
    int interval = 7000;
    int arrLength;
    int i = 0;

    string source;
    string str;
    WWW www;

    string[] extensions = new[] { ".jpg", ".JPG", ".jpeg", ".JPEG", ".png", ".PNG", ".ogg", ".OGG" };

    FileInfo[] info;
    DirectoryInfo dir;

    void Start() {
        dir = new DirectoryInfo(@folder);
        info = dir.GetFiles().Where(f => extensions.Contains(f.Extension.ToLower())).ToArray();
        arrLength = info.Length;

        looper ();
    }

    // Update is called once per frame
    void Update () {

    }

    void looper() {
        medialogic ();
        EasyTimer.SetInterval(() =>
        {
            if (i == arrLength-1) {
                info = dir.GetFiles().Where(f => extensions.Contains(f.Extension.ToLower())).ToArray();
                arrLength = info.Length;
                i = 0;
            } else {
                i++;
            }
            medialogic();
        }, interval);
    }

    void medialogic() {
        source = info [i].ToString();
        str = path + source;
        www = new WWW(str);
        string[] extType = source.Split('.');
        int pos = Array.IndexOf(extensions, "."+extType[1]);
        if (pos > -1) {
            GetComponent<RawImage> ().texture = www.texture;
            Debug.Log (extType[1]);
        } else {
            //videos here
            Debug.Log (extType[1]);
        }
    }

    public static class EasyTimer
    {
        public static IDisposable SetInterval(Action method, int delayInMilliseconds)
        {
            System.Timers.Timer timer = new System.Timers.Timer(delayInMilliseconds);
            timer.Elapsed += (source, e) =>
            {
                method();
            };

            timer.Enabled = true;
            timer.Start();

            // Returns a stop handle which can be used for stopping
            // the timer, if required
            return timer as IDisposable;
        }

        public static IDisposable SetTimeout(Action method, int delayInMilliseconds)
        {
            System.Timers.Timer timer = new System.Timers.Timer(delayInMilliseconds);
            timer.Elapsed += (source, e) =>
            {
                method();
            };

            timer.AutoReset = false;
            timer.Enabled = true;
            timer.Start();

            // Returns a stop handle which can be used for stopping
            // the timer, if required
            return timer as IDisposable;
        }
    }
}

编辑:下面的用户程序员有正确答案。

更新

正如@Programmer 回答的那样,我的 post 不是在 Unity3D 中处理 WWW 交互的正确方法。 InvokeRepeating() 是您为简单起见的第一个学习。请准确阅读他的回答,以了解我的示例中错误的地方。 #CleanerCoding

此外,根据该站点上的用户,InvokeRepeating() 正在使用反射,这会产生更大的开销。

旧答案

看看MonoBehaviour.InvokeRepeating()

它在您的代码中应该像这样工作:

void Start() 
{
    dir = new DirectoryInfo(@folder);
    info = dir.GetFiles().Where(f => extensions.Contains(f.Extension.ToLower())).ToArray();
    arrLength = info.Length;

    // Starting in 0.1 seconds.
    // and will be launched every 5 seconds
    InvokeRepeating("medialogic", 0.1f, 5f);
}

并摆脱这个丑陋的 looper() 函数。

尽管这被标记为已解决,但我确实认为您的代码中还有许多其他问题,您现在可能没有注意到但稍后会出现。

使用InvokeRepeating 调用API(WWW) 是完全错误的。

主要问题是 Timer class 中的 SetInterval 函数正在另一个 Thread 中调用,但您不能使用 Unity 的 API 来自另一个线程。

您可以使用 class:

在主线程中创建 medialogic() 函数 运行
void Awake()
{
    UnityThread.initUnityThread();
}

void looper()
{
    medialogic();
    EasyTimer.SetInterval(() =>
    {
        if (i == arrLength - 1)
        {
            info = dir.GetFiles().Where(f => extensions.Contains(f.Extension.ToLower())).ToArray();
            arrLength = info.Length;
            i = 0;
        }
        else
        {
            i++;
        }

        UnityThread.executeInUpdate(() =>
        {
            medialogic();
        });

    }, interval);
}

这不是您代码中的唯一问题:

您使用 WWW API 不当。它需要在协程函数中使用。在将图像与 GetComponent<RawImage>().texture = www.texture; 一起使用之前,您目前没有等待它完成图像下载。

由于WWW需要协程,协程也可以用于定时器,去掉Timerclass,在协程中使用WaitForSeconds等待5秒功能。

正确的做法是:

public class Test2 : MonoBehaviour
{

    // Use this for initialization

    //Publics
    private string path = "file:///";
    public string folder = "C:/medias/";
    int interval = 7000;
    int arrLength;
    int i = 0;

    string source;
    string str;
    WWW www;

    string[] extensions = new[] { ".jpg", ".JPG", ".jpeg", ".JPEG", ".png", ".PNG", ".ogg", ".OGG" };

    FileInfo[] info;
    DirectoryInfo dir;

    void Start()
    {
        dir = new DirectoryInfo(@folder);
        info = dir.GetFiles().Where(f => extensions.Contains(f.Extension.ToLower())).ToArray();
        arrLength = info.Length;

        StartCoroutine(looper());
    }


    IEnumerator looper()
    {
        yield return StartCoroutine(medialogic());

        WaitForSeconds waitTime = new WaitForSeconds(5);

        //Run forever
        while (true)
        {

            if (i == arrLength - 1)
            {
                info = dir.GetFiles().Where(f => extensions.Contains(f.Extension.ToLower())).ToArray();
                arrLength = info.Length;
                i = 0;
            }
            else
            {
                i++;
            }
            yield return StartCoroutine(medialogic());

            //Wait for 5 seconds 
            yield return waitTime;
        }
    }

    IEnumerator medialogic()
    {
        source = info[i].ToString();
        str = path + source;
        www = new WWW(str);

        //Wait for download to finish
        yield return www;

        string[] extType = source.Split('.');
        int pos = Array.IndexOf(extensions, "." + extType[1]);
        if (pos > -1)
        {
            GetComponent<RawImage>().texture = www.texture;
            Debug.Log(extType[1]);
        }
        else
        {
            //videos here
            Debug.Log(extType[1]);
        }
    }
}