Unity3D - 将图像从 PC 内存上传到 WebGL 应用程序 [英] Unity3D - Upload a image from PC memory to WebGL app

查看:39
本文介绍了Unity3D - 将图像从 PC 内存上传到 WebGL 应用程序的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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

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

解决方案

今天是你的幸运日 :P 我接受了你的挑战.这是您的操作方法.

首先,这里有关于如何将 JavaScript 与 Unity 交互的说明.

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

var getImage = {getImageFromBrowser: 函数(objectNamePtr, funcNamePtr) {//因为 unity 目前在 JavaScript 方面很糟糕,所以我们不能使用标准//JavaScript 习惯用法类似于闭包,因此我们必须使用全局变量 :(window.becauseUnitysBadWithJavacript_getImageFromBrowser =window.becauseUnitysBadWithJavacript_getImageFromBrowser ||{忙:假,初始化:假,rootDisplayStyle: null,//使根元素可见的样式root_: null,//表单的根元素ctx_: null,//用于获取图像数据的画布;};var g = window.becauseUnitysBadWithJavacript_getImageFromBrowser;如果(g.busy){//不要让多个请求进来返回;}g.busy = true;var objectName = Pointer_stringify(objectNamePtr);var funcName = Pointer_stringify(funcNamePtr);如果(!g.初始化){g.初始化=真;g.ctx = window.document.createElement("canvas").getContext("2d");//向页面附加一个表单(比编辑 HTML 更自包含?)g.root = window.document.createElement("div");g.root.innerHTML = ['<风格>','.getimage { ',' 位置:绝对;','左:0;','顶部:0;',' 宽度:100%;',' 高度:100%;',' 显示:-webkit-flex;',' 显示:弹性;',' -webkit-flex-flow: 列;',' 弹性流:列;',' -webkit-justify-content:中心;',' -webkit-align-content:居中;',' -webkit-align-items:居中;',' ',' 对齐内容:居中;','对齐内容:居中;','对齐项目:居中;',' ',' z-index: 2;','    白颜色;',' 背景颜色:rgba(0,0,0,0.8);',' 字体:无衬线;',' 字体大小:x-大;','} ','.getimage a, ','.getimage 标签 { ',' 字体大小:x-大;',' 背景色:#666;',' 边界半径:0.5em;',' 边框:1px 纯黑色;',' 填充:0.5em;',' 边距:0.25em;',' 大纲:无;',' 显示:内联块;','} ','.getimage 输入 { ',' 显示:无;','} ','</风格>','<div class="getimage">',' 

',' <label for="photo">点击选择图片</label>',' <input id="photo" type="file" accept="image/*"/><br/>',' <a>取消</a>',' </div>','</div>',].join(' ');var input = g.root.querySelector("input");input.addEventListener('change', getPic);//防止点击输入或标签取消input.addEventListener('click', preventOtherClicks);var label = g.root.querySelector("label");label.addEventListener('click', preventOtherClicks);//点击取消或外部取消var cancel = g.root.querySelector("a");//只有一个cancel.addEventListener('click', handleCancel);var getImage = g.root.querySelector(".getimage");getImage.addEventListener('click', handleCancel);//记住原来的样式g.rootDisplayStyle = g.root.style.display;window.document.body.appendChild(g.root);}//让它可见g.root.style.display = g.rootDisplayStyle;函数 preventOtherClicks(evt) {evt.stopPropagation();}函数 getPic(evt) {evt.stopPropagation();var fileInput = evt.target.files;if (!fileInput || !fileInput.length) {return sendError("没有选择图像");}var picURL = window.URL.createObjectURL(fileInput[0]);var img = new window.Image();img.addEventListener('load', handleImageLoad);img.addEventListener('error', handleImageError);img.src = picURL;}函数句柄取消(evt){evt.stopPropagation();evt.preventDefault();发送错误(取消");}函数 handleImageError(evt) {sendError("无法获取图片");}函数 handleImageLoad(evt) {var img = evt.target;window.URL.revokeObjectURL(img.src);//我们可能不想要全尺寸图像.它可能是 3000x2000 像素或太大的东西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();//释放画布内存(可能为零)g.ctx.canvas.width = 1;g.ctx.canvas.height = 1;发送结果(数据网址);g.busy = false;}函数发送错误(味精){sendResult("错误:" + msg);}函数隐藏(){g.root.style.display = "无";}函数发送结果(结果){隐藏();g.busy = false;SendMessage(objectName, funcName, result);}},};mergeInto(LibraryManager.library, getImage);

代码遵循.

代码在 github 上.

还有 这里有一个 .unitypackage.

I need user to upload his(her) avatar picture from PC to game

How can I create a file dialog and upload a image to WebGL game?

解决方案

Today is your lucky day :P I took up your challenge. Here's how you do it.

First off the instructions on how to interface JavaScript with Unity are here.

Reading that I made this file which I put in Assets/Plugins/WebGL/GetImage.jslib like the docs said

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('
');
          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);

The code follows this example of how to get an image from a user in HTML5.

Basically it makes a small form that covers the entire browser window. It has an <input> element that only accepts an image. It appends that do the body of the document and will use it again if you ask for another image. (see g.initialized and g.root)

Similarly there's an attempt that you can only call it once at a time. (see g.busy)

Once the user chooses an image the image is then drawn into a smaller canvas because I'm just guessing that you don't really want a 3000x2000 pixel image or whatever giant size the user's photo is.

You may want to adjust the code that sizes the canvas and draws the image. The current code always resizes the image to 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);

For example you might want to set the canvas size to be the same aspect ratio as the original image but still some smaller size. Or, if you want the original size then set the size to img.width and img.height.

In any case, after the image is drawn into the canvas we call canvas.toDataURL which returns a PNG encoded into a string dataURL. It then calls a named method on a named GameObject using Unity's SendMessage function and passes the dataURL.

To interface that code with Unity I made this file 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
    }
}

The way this code works use you call GetImage.GetImageFromBrowserAsync. You pass it the name of a GameObject and the name of a method to call. The name of the GameObject MUST BE UNIQUE (well, if it's not unique Unity will attempt to call the method on every object with the same name)

The method will be called with a string. If that string starts with data:image/png;base64, then the user chose an image. We convert that back to binary PNG data and then call Texture2D.LoadImage

If the string does not start with data:image/png;base64, then it's an error. Maybe the user picked cancel?

Note: The code doesn't handle all errors currently.

To use it I made a Cube GameObject, added a material, then I added a new script 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);
        }
    }
}

You can see it live here.

The code is on github.

And here's a .unitypackage.

这篇关于Unity3D - 将图像从 PC 内存上传到 WebGL 应用程序的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆