Unity3D – 将图像从PC内存上传到WebGL应用程序

我需要用户将他(她)的头像图片从PC上传到游戏

如何创建文件对话框并将图像上传到WebGL游戏?

今天是你的幸运日:PI接受了你的挑战。 这是你如何做到的。

首先是关于如何将JavaScript与Unity连接的说明。

读到我做了这个文件,我把它放在Assets/Plugins/WebGL/GetImage.jslib就像docs所说的那样

 var getImage = { getImageFromBrowser: function(objectNamePtr, funcNamePtr) { // Because unity is currently bad at JavaScript we can't use standard // JavaScript idioms like closures so we have to use global variables :( window.becauseUnitysBadWithJavacript_getImageFromBrowser = window.becauseUnitysBadWithJavacript_getImageFromBrowser || { busy: false, initialized: false, rootDisplayStyle: null, // style to make root element visible root_: null, // root element of form ctx_: null, // canvas for getting image data; }; var g = window.becauseUnitysBadWithJavacript_getImageFromBrowser; if (g.busy) { // Don't let multiple requests come in return; } g.busy = true; var objectName = Pointer_stringify(objectNamePtr); var funcName = Pointer_stringify(funcNamePtr); if (!g.initialized) { g.initialized = true; g.ctx = window.document.createElement("canvas").getContext("2d"); // Append a form to the page (more self contained than editing the HTML?) g.root = window.document.createElement("div"); g.root.innerHTML = [ ' ', '
', '
', ' ', '
', ' cancel ', '
', '
', ].join('\n'); var input = g.root.querySelector("input"); input.addEventListener('change', getPic); // prevent clicking in input or label from canceling input.addEventListener('click', preventOtherClicks); var label = g.root.querySelector("label"); label.addEventListener('click', preventOtherClicks); // clicking cancel or outside cancels var cancel = g.root.querySelector("a"); // there's only one cancel.addEventListener('click', handleCancel); var getImage = g.root.querySelector(".getimage"); getImage.addEventListener('click', handleCancel); // remember the original style g.rootDisplayStyle = g.root.style.display; window.document.body.appendChild(g.root); } // make it visible g.root.style.display = g.rootDisplayStyle; function preventOtherClicks(evt) { evt.stopPropagation(); } function getPic(evt) { evt.stopPropagation(); var fileInput = evt.target.files; if (!fileInput || !fileInput.length) { return sendError("no image selected"); } var picURL = window.URL.createObjectURL(fileInput[0]); var img = new window.Image(); img.addEventListener('load', handleImageLoad); img.addEventListener('error', handleImageError); img.src = picURL; } function handleCancel(evt) { evt.stopPropagation(); evt.preventDefault(); sendError("cancelled"); } function handleImageError(evt) { sendError("Could not get image"); } function handleImageLoad(evt) { var img = evt.target; window.URL.revokeObjectURL(img.src); // We probably don't want the fullsize image. It might be 3000x2000 pixels or something too big g.ctx.canvas.width = 256; g.ctx.canvas.height = 256; g.ctx.drawImage(img, 0, 0, g.ctx.canvas.width, g.ctx.canvas.height); var dataUrl = g.ctx.canvas.toDataURL(); // free the canvas memory (could probably be zero) g.ctx.canvas.width = 1; g.ctx.canvas.height = 1; sendResult(dataUrl); g.busy = false; } function sendError(msg) { sendResult("error: " + msg); } function hide() { g.root.style.display = "none"; } function sendResult(result) { hide(); g.busy = false; SendMessage(objectName, funcName, result); } }, }; mergeInto(LibraryManager.library, getImage);

该代码遵循此示例,说明如何从HTML5中的用户获取图像 。

基本上它是一个覆盖整个浏览器窗口的小表单。 它有一个只接受图像的元素。 如果您要求提供其他图像,它会附加文档正文并再次使用它。 (参见g.initializedg.root

同样地,尝试一次只能调用一次。 (见g.busy

一旦用户选择了图像,图像就会被绘制成一个较小的canvas,因为我只是猜测你真的不想要3000×2000像素图像或用户照片的任何巨大尺寸。

您可能希望调整用于调整canvas大小并绘制图像的代码。 当前代码始终将图像大小调整为256×256

  g.ctx.canvas.width = 256; g.ctx.canvas.height = 256; g.ctx.drawImage(img, 0, 0, g.ctx.canvas.width, g.ctx.canvas.height); 

例如,您可能希望将canvas大小设置为与原始图像相同的宽高比,但仍然需要更小的尺寸。 或者,如果您想要原始大小,则将大小设置为img.widthimg.height

在任何情况下,在将图像绘制到canvas之后,我们调用canvas.toDataURL ,它返回编码为字符串dataURL的PNG。 然后,它使用Unity的SendMessage函数调用命名的GameObject上的命名方法,并传递dataURL。

为了将该代码与Unity接口,我创建了这个文件Assets/GetImage.cs

 using UnityEngine; using System.Collections; using System.Runtime.InteropServices; public class GetImage { #if UNITY_WEBGL [DllImport("__Internal")] private static extern void getImageFromBrowser(string objectName, string callbackFuncName); #endif static public void GetImageFromUserAsync(string objectName, string callbackFuncName) { #if UNITY_WEBGL getImageFromBrowser(objectName, callbackFuncName); #else Debug.LogError("Not implemented in this platform"); #endif } } 

此代码的工作方式使用您调用GetImage.GetImageFromBrowserAsync 。 您传递GameObject的名称和要调用的方法的名称。 GameObject的名称必须是唯一的 (好吧,如果它不是唯一的Unity将尝试在每个具有相同名称的对象上调用该方法)

该方法将使用字符串调用。 如果该字符串以data:image/png;base64,开头data:image/png;base64,则用户选择图像。 我们将其转换回二进制PNG数据,然后调用Texture2D.LoadImage

如果字符串不是以data:image/png;base64,开头data:image/png;base64,则表示错误。 也许用户选择取消?

注意:代码当前不处理所有错误。

为了使用它,我制作了一个Cube GameObject,添加了一个材料,然后我添加了一个新脚本Assets/ClickAndGetImage.cs

 using UnityEngine; using System; using System.Collections; public class ClickAndGetImage : MonoBehaviour { void OnMouseOver() { if(Input.GetMouseButtonDown(0)) { // NOTE: gameObject.name MUST BE UNIQUE!!!! GetImage.GetImageFromUserAsync(gameObject.name, "ReceiveImage"); } } static string s_dataUrlPrefix = "data:image/png;base64,"; public void ReceiveImage(string dataUrl) { if (dataUrl.StartsWith(s_dataUrlPrefix)) { byte[] pngData = System.Convert.FromBase64String(dataUrl.Substring(s_dataUrlPrefix.Length)); // Create a new Texture (or use some old one?) Texture2D tex = new Texture2D(1, 1); // does the size matter? if (tex.LoadImage(pngData)) { Renderer renderer = GetComponent(); renderer.material.mainTexture = tex; } else { Debug.LogError("could not decode image"); } } else { Debug.LogError("Error getting image:" + dataUrl); } } } 

例

你可以在这里看到它 。

代码在github上 。

这是一个.unitypackage 。