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
需要协程,协程也可以用于定时器,去掉Timer
class,在协程中使用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]);
}
}
}
我有一个功能可以从文件夹中获取图像并在 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
需要协程,协程也可以用于定时器,去掉Timer
class,在协程中使用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]);
}
}
}