Unity3D - 将图像从 PC 内存上传到 WebGL 应用程序
Unity3D - Upload a image from PC memory to WebGL app
我需要用户从PC上传他(她)的头像到游戏
如何创建文件对话框并将图像上传到 WebGL 游戏?
今天是你的幸运日:P 我接受了你的挑战。这是您的操作方法。
首先关闭 how to interface JavaScript with Unity are here 上的说明。
读到我制作了这个文件,我把它放在 Assets/Plugins/WebGL/GetImage.jslib
中,就像文档说的那样
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 = [
'<style> ',
'.getimage { ',
' position: absolute; ',
' left: 0; ',
' top: 0; ',
' width: 100%; ',
' height: 100%; ',
' display: -webkit-flex; ',
' display: flex; ',
' -webkit-flex-flow: column; ',
' flex-flow: column; ',
' -webkit-justify-content: center; ',
' -webkit-align-content: center; ',
' -webkit-align-items: center; ',
' ',
' justify-content: center; ',
' align-content: center; ',
' align-items: center; ',
' ',
' z-index: 2; ',
' color: white; ',
' background-color: rgba(0,0,0,0.8); ',
' font: sans-serif; ',
' font-size: x-large; ',
'} ',
'.getimage a, ',
'.getimage label { ',
' font-size: x-large; ',
' background-color: #666; ',
' border-radius: 0.5em; ',
' border: 1px solid black; ',
' padding: 0.5em; ',
' margin: 0.25em; ',
' outline: none; ',
' display: inline-block; ',
'} ',
'.getimage input { ',
' display: none; ',
'} ',
'</style> ',
'<div class="getimage"> ',
' <div> ',
' <label for="photo">click to choose an image</label> ',
' <input id="photo" type="file" accept="image/*"/><br/>',
' <a>cancel</a> ',
' </div> ',
'</div> ',
].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);
代码如下 this example of how to get an image from a user in HTML5.
基本上它是一个覆盖整个浏览器的小表单window。它有一个只接受图像的 <input>
元素。它附加在文档正文中,如果您要求另一个图像,它将再次使用它。 (参见 g.initialized
和 g.root
)
同样有一种尝试,一次只能调用一次。 (参见 g.busy
)
一旦用户选择了一张图片,该图片就会被绘制成一个较小的 canvas,因为我只是猜测您并不真的想要 3000x2000 像素的图片或用户照片的任何大尺寸。
您可能需要调整调整 canvas 大小和绘制图像的代码。当前代码总是将图像调整为 256x256
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.width
和 img.height
.
无论如何,在图像被绘制成canvas之后我们调用canvas.toDataURL
其中returns一个PNG编码成一个字符串dataURL。然后它使用 Unity 的 SendMessage
函数调用命名游戏对象的命名方法并传递 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,
开头,则用户选择了一张图片。我们将其转换回二进制 PNG 数据,然后调用 Texture2D.LoadImage
如果字符串不是以 data:image/png;base64,
开头,那么这是一个错误。也许用户选择了取消?
注意:代码目前不处理所有错误。
为了使用它,我制作了一个 Cube GameObject,添加了一个 material,然后我添加了一个新脚本 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>();
renderer.material.mainTexture = tex;
}
else
{
Debug.LogError("could not decode image");
}
}
else
{
Debug.LogError("Error getting image:" + dataUrl);
}
}
}
我需要用户从PC上传他(她)的头像到游戏
如何创建文件对话框并将图像上传到 WebGL 游戏?
今天是你的幸运日:P 我接受了你的挑战。这是您的操作方法。
首先关闭 how to interface JavaScript with Unity are here 上的说明。
读到我制作了这个文件,我把它放在 Assets/Plugins/WebGL/GetImage.jslib
中,就像文档说的那样
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 = [
'<style> ',
'.getimage { ',
' position: absolute; ',
' left: 0; ',
' top: 0; ',
' width: 100%; ',
' height: 100%; ',
' display: -webkit-flex; ',
' display: flex; ',
' -webkit-flex-flow: column; ',
' flex-flow: column; ',
' -webkit-justify-content: center; ',
' -webkit-align-content: center; ',
' -webkit-align-items: center; ',
' ',
' justify-content: center; ',
' align-content: center; ',
' align-items: center; ',
' ',
' z-index: 2; ',
' color: white; ',
' background-color: rgba(0,0,0,0.8); ',
' font: sans-serif; ',
' font-size: x-large; ',
'} ',
'.getimage a, ',
'.getimage label { ',
' font-size: x-large; ',
' background-color: #666; ',
' border-radius: 0.5em; ',
' border: 1px solid black; ',
' padding: 0.5em; ',
' margin: 0.25em; ',
' outline: none; ',
' display: inline-block; ',
'} ',
'.getimage input { ',
' display: none; ',
'} ',
'</style> ',
'<div class="getimage"> ',
' <div> ',
' <label for="photo">click to choose an image</label> ',
' <input id="photo" type="file" accept="image/*"/><br/>',
' <a>cancel</a> ',
' </div> ',
'</div> ',
].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);
代码如下 this example of how to get an image from a user in HTML5.
基本上它是一个覆盖整个浏览器的小表单window。它有一个只接受图像的 <input>
元素。它附加在文档正文中,如果您要求另一个图像,它将再次使用它。 (参见 g.initialized
和 g.root
)
同样有一种尝试,一次只能调用一次。 (参见 g.busy
)
一旦用户选择了一张图片,该图片就会被绘制成一个较小的 canvas,因为我只是猜测您并不真的想要 3000x2000 像素的图片或用户照片的任何大尺寸。
您可能需要调整调整 canvas 大小和绘制图像的代码。当前代码总是将图像调整为 256x256
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.width
和 img.height
.
无论如何,在图像被绘制成canvas之后我们调用canvas.toDataURL
其中returns一个PNG编码成一个字符串dataURL。然后它使用 Unity 的 SendMessage
函数调用命名游戏对象的命名方法并传递 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,
开头,则用户选择了一张图片。我们将其转换回二进制 PNG 数据,然后调用 Texture2D.LoadImage
如果字符串不是以 data:image/png;base64,
开头,那么这是一个错误。也许用户选择了取消?
注意:代码目前不处理所有错误。
为了使用它,我制作了一个 Cube GameObject,添加了一个 material,然后我添加了一个新脚本 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>();
renderer.material.mainTexture = tex;
}
else
{
Debug.LogError("could not decode image");
}
}
else
{
Debug.LogError("Error getting image:" + dataUrl);
}
}
}