Muh*_*zan 5 unity-game-engine unity3d-editor unity5 unity-webgl
我希望用户在Unity WebGL游戏中从计算机中选择一个图像,但我无法获得任何库或代码.我想要UnityWebGL(Runtime)的EditorUtility.OpenFilePanel的确切功能.
string path = EditorUtility.OpenFilePanel("用png覆盖","","png");
有没有办法在Unity WebGL构建中获得这个打开的对话框?或者我有什么方法可以在java脚本中做到这一点?就像我从用户的java脚本中获取图像并将其传递给我的C#代码.
Yur*_*man 10
这可能听起来很简单,但实际上它很复杂,原因是WebGL构建在浏览器中运行,并受到许多安全限制,其中包括限制其对本地文件系统的访问.但是,有可能以一种黑客的方式做到这一点.
想法是使用HTML文件输入来打开文件浏览对话框.我们可以使用ExternalEval从Unity代码中调用它,详情请见:http : //docs.unity3d.com/Manual/UnityWebPlayerandbrowsercommunication.html http://docs.unity3d.com/ScriptReference/Application.ExternalEval.html
然而,事情并非那么容易.问题是所有现代浏览器都允许仅作为用户点击事件的结果显示文件对话框,作为安全限制,您无法对其进行任何操作.
好的,所以我们可以创建一个按钮,并在点击时打开文件对话框,这样可行,对吗?错误.如果我们只是创建统一按钮并处理单击 - 这将无法工作,因为Unity有自己的事件管理,它与帧速率同步,因此事件将仅在实际的javascript事件结束时发生.这几乎和这里描述的问题一样,http://docs.unity3d.com/Manual/webgl-cursorfullscreen.html除了Unity没有很好的内置解决方案.
所以这是黑客攻击:点击鼠标按下+鼠标向上,对吧?我们将点击监听器添加到HTML文档中,然后在统一中我们按下鼠标按下我们的按钮.当它关闭时,我们知道下一个UP将被点击,所以我们在HTML文档中标记一些标记以记住它.然后,当我们点击文档时,我们可以查看此标志并结束我们的按钮被点击.然后我们调用打开文件对话框的javascript函数,然后使用SendMessage http://docs.unity3d.com/Manual/webgl-interactingwithbrowserscripting.html将结果发送回Unity .最后.
但等等,还有更多.问题是我们不能简单地在浏览器中运行时获取文件路径.我们的应用程序不允许再获取有关用户计算机的任何信息,安全限制.我们能做的最好的事情是使用URL.CreateObjectURL获取一个blob url,它可以在大多数浏览器上运行,http: //caniuse.com/#search=createobjecturl
我们可以使用WWW类从中检索数据,只需记住此URL只能在您的应用程序范围内访问.
所有这些,解决方案非常hacky,但可能.这是一个示例代码,允许用户选择图像,并将其设置为材质.
using UnityEngine;
using UnityEngine.UI;
using UnityEngine.EventSystems;
using System.Collections;
public class OpenFileDialog : MonoBehaviour, IPointerDownHandler {
public Renderer preview;
public Text text;
void Start() {
Application.ExternalEval(
@"
document.addEventListener('click', function() {
var fileuploader = document.getElementById('fileuploader');
if (!fileuploader) {
fileuploader = document.createElement('input');
fileuploader.setAttribute('style','display:none;');
fileuploader.setAttribute('type', 'file');
fileuploader.setAttribute('id', 'fileuploader');
fileuploader.setAttribute('class', 'focused');
document.getElementsByTagName('body')[0].appendChild(fileuploader);
fileuploader.onchange = function(e) {
var files = e.target.files;
for (var i = 0, f; f = files[i]; i++) {
window.alert(URL.createObjectURL(f));
SendMessage('" + gameObject.name +@"', 'FileDialogResult', URL.createObjectURL(f));
}
};
}
if (fileuploader.getAttribute('class') == 'focused') {
fileuploader.setAttribute('class', '');
fileuploader.click();
}
});
");
}
public void OnPointerDown (PointerEventData eventData) {
Application.ExternalEval(
@"
var fileuploader = document.getElementById('fileuploader');
if (fileuploader) {
fileuploader.setAttribute('class', 'focused');
}
");
}
public void FileDialogResult(string fileUrl) {
Debug.Log(fileUrl);
text.text = fileUrl;
StartCoroutine(PreviewCoroutine(fileUrl));
}
IEnumerator PreviewCoroutine(string url) {
var www = new WWW(url);
yield return www;
preview.material.mainTexture = www.texture;
}
}
Run Code Online (Sandbox Code Playgroud)
如果有人设法找到一种更简单的方法请分享,但我真的怀疑它是否有可能.希望这可以帮助.
小智 6
哇。Yuri Nudelman 的解决方案令人印象深刻,它仍然是目前能找到的最佳解决方案。但是:WWW-class 和ExternalEval 现已弃用。
它确实运行(带有警告),但在不久的将来将停止工作。
因此,为了帮助任何想要实现这一点的人:
两个 Javascript 函数都必须放在“Plugins”文件夹内的 .jslib 中。第一个是这样的:
mergeInto(
LibraryManager.library,
{
AddClickListenerForFileDialog: function () {
document.addEventListener('click', function () {
var fileuploader = document.getElementById('fileuploader');
if (!fileuploader) {
fileuploader = document.createElement('input');
fileuploader.setAttribute('style', 'display:none;');
fileuploader.setAttribute('type', 'file');
fileuploader.setAttribute('id', 'fileuploader');
fileuploader.setAttribute('class', '');
document.getElementsByTagName('body')[0].appendChild(fileuploader);
fileuploader.onchange = function (e) {
var files = e.target.files;
for (var i = 0, f; f = files[i]; i++) {
window.alert(URL.createObjectURL(f));
SendMessage('BrowserFileLoading', 'FileDialogResult', URL.createObjectURL(f));
}
};
}
if (fileuploader.getAttribute('class') == 'focused') {
fileuploader.setAttribute('class', '');
fileuploader.click();
}
});
}
}
);
Run Code Online (Sandbox Code Playgroud)
请注意,我添加了两项更改:a) 我删除了“重点”。这可以防止脚本在程序启动时触发:
fileuploader.setAttribute('class', '');
Run Code Online (Sandbox Code Playgroud)
b) 我手动添加了 Unity 游戏对象的名称。这必须与您放置(unity-)脚本的游戏对象相同:
SendMessage('BrowserFileLoading', 'FileDialogResult', URL.createObjectURL(f));
Run Code Online (Sandbox Code Playgroud)
您可以使用以下方式调用此外部函数:
using UnityEngine;
using UnityEngine.UI;
using UnityEngine.EventSystems;
using System.Collections;
using UnityEngine.Networking;
using System.Runtime.InteropServices;
public class BrowserFileLoadingDialog : MonoBehaviour
{
[DllImport("__Internal")] private static extern void AddClickListenerForFileDialog();
void Start()
{
AddClickListenerForFileDialog();
}
public void FileDialogResult(string fileUrl)
{
Debug.Log(fileUrl);
UrlTextField.text = fileUrl;
StartCoroutine(LoadBlob(fileUrl));
}
IEnumerator LoadBlob(string url)
{
UnityWebRequest webRequest = UnityWebRequest.Get(url);
yield return webRequest.SendWebRequest();
if (!webRequest.isNetworkError && !webRequest.isHttpError)
{
// Get text content like this:
Debug.Log(webRequest.downloadHandler.text);
}
}
Run Code Online (Sandbox Code Playgroud)
第二个脚本(可以放入同一个 .jslib 文件中)如下所示:
mergeInto(
LibraryManager.library,
{
FocusFileUploader: function () {
var fileuploader = document.getElementById('fileuploader');
if (fileuploader) {
fileuploader.setAttribute('class', 'focused');
}
}
}
);
Run Code Online (Sandbox Code Playgroud)
这里没有大的变化,像上面的那样使用,并且应该(就像 Yuri Nudelman 建议的那样)在 CursorDown 上调用。
| 归档时间: |
|
| 查看次数: |
4444 次 |
| 最近记录: |