Unity错误:只能从主线程调用InitWWW

时间:2017-03-07 11:09:08

标签: c# unity3d setinterval unity5

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

当looper()函数调用medialogic()函数时,显示第一个图像后出现此问题。它给出了标题中的错误。

我怎么能解决这个问题或为什么会这样?我是团结和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;
        }
    }
}

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

2 个答案:

答案 0 :(得分:3)

更新

正如@Programmer回答的那样,我的帖子并不是处理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()功能。

答案 1 :(得分:3)

即使已经标记为已解决,我也认为您的代码中存在许多其他问题,您现在可能不会注意到这些问题,但稍后会出现。

使用需要屈服的InvokeRepeating调用API(WWW)是完全错误的。

主要问题是来自SetInterval类的Timer函数正在另一个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需要协同程序,协同程序也可用于计时器,删除Timer类并使用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]);
        }
    }
}