Unity3D - Загрузить изображение из памяти компьютера в приложение WebGL

Мне нужен пользователь, чтобы загрузить свою аватарку с ПК в игру

Как я могу создать диалоговое окно файла и загрузить изображение в игру WebGL?

1 ответ

Решение

Сегодня твой счастливый день: пи принял твой вызов. Вот как ты это делаешь.

Прежде всего, инструкции по интерфейсу JavaScript с Unity находятся здесь.

Читая, что я сделал этот файл, который я положил в 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);

Код следует за этим примером того, как получить изображение от пользователя в HTML5.

В основном это небольшая форма, которая покрывает все окно браузера. Имеет <input> элемент, который принимает только изображение. Он добавляет, что делает тело документа и будет использовать его снова, если вы попросите другое изображение. (увидеть g.initialized а также g.root)

Точно так же есть попытка, которую вы можете вызвать только один раз за раз. (увидеть g.busy)

Как только пользователь выбирает изображение, оно затем рисуется на меньшем холсте, потому что я просто предполагаю, что вам не нужно изображение размером 3000x2000 пикселей или любого другого гигантского размера, как у пользователя.

Вы можете настроить код, который определяет размер холста и рисует изображение. Текущий код всегда изменяет размер изображения до 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);

Например, вы можете установить размер холста таким же, как у исходного изображения, но с небольшим размером. Или, если вы хотите оригинальный размер, установите размер img.width а также img.height,

В любом случае, после того, как изображение нарисовано на холсте, мы называем canvas.toDataURL который возвращает PNG, закодированный в строку dataURL. Затем он вызывает именованный метод для именованного GameObject, используя 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, добавил материал и добавил новый скрипт. 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);
        }
    }
}

пример

Вы можете увидеть это в прямом эфире здесь.

Код есть на github.

А вот и пакет.unity.

Другие вопросы по тегам