LoginSignup
17
10

More than 5 years have passed since last update.

UnityのWebGL出力でHTMLのファイル選択ダイアログを表示して外部ファイルを読み込む

Posted at

UnityのWebGL出力でHTMLのファイル選択ダイアログを表示して外部ファイルを読み込むのをやってみたので方法をメモしておく。

参考サイト

ここに載せた方法はほぼ以下のサイトそのまま。

How do I let the user load an image from their harddrive into a WebGL app? | Unity Community

HTML側からファイルを読み込む方法

type="file"のinput要素からファイルを読み込む。

  • テンプレートに以下を追加する
<input file type="file" onchange='gameInstance.SendMessage("GameObject", "FileSelected", window.URL.createObjectURL(this.files[0]));' /> 
  • GameObjectという名前のEmptyを作成し、以下のファイルを割り当てる
using UnityEngine;
using System.Collections;

public class ReadScript : MonoBehaviour {
    IEnumerator LoadTexture (string url) {
        WWW www = new WWW (url);
        yield return www;
        Debug.Log(www.bytes);
    }

    public void FileSelected (string url) {
        StartCoroutine(LoadTexture (url));
    }
}

WebGLテンプレートの設定

デフォルトテンプレートは上記のサイトにあるように以下の場所にある(英語版のマニュアルの方にしかパスがなかったのでしばらくさがした)。

Both minimal and default templates can be found in the Unity installation folder under Editor\Data\PlaybackEngines\WebGLSupport\BuildTools\WebGLTemplates on Windows or /PlaybackEngines/WebGLSupport/BuildTools/WebGLTemplates on Mac.

コルーチンについて

最初WWWクラスでコルーチンを利用していなかったらファイルを読み込めずにハマった。

以下にあるようにコルーチンを利用するとファイルを読み込んだ後に処理ができる。
参考サイト:【Unity】WWWクラスを利用して、JSONを読み込む | albatrus.com

uGUI上からファイルを読み込む方法

uGUIのボタンから読み込んでみる。

  • Assets/Plugins/FileImporter.jslibを作成する
var FileImporterPlugin = {
  FileImporterCaptureClick: function() {
    if (!document.getElementById('FileImporter')) {
      var fileInput = document.createElement('input');
      fileInput.setAttribute('type', 'file');
      fileInput.setAttribute('id', 'FileImporter');
      fileInput.setAttribute('accept', '.json')
      fileInput.style.visibility = 'hidden';
      fileInput.onclick = function (event) {
        this.value = null;
      };
      fileInput.onchange = function (event) {
        SendMessage('ImportButton', 'FileSelected', URL.createObjectURL(event.target.files[0]));
      }
      document.body.appendChild(fileInput);
    }

    var OpenFileDialog = function() {
      document.getElementById('FileImporter').click();
      document.getElementById('#canvas').removeEventListener('click', OpenFileDialog);
    };

    document.getElementById('#canvas').addEventListener('click', OpenFileDialog, false);
  }
};
mergeInto(LibraryManager.library, FileImporterPlugin);
  • ImportButtonというボタンを作成する。
  • ImportButtonに以下のスクリプトを割り当てる
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using System.Runtime.InteropServices;

public class ImportButton : MonoBehaviour {
    [DllImport("__Internal")]
    private static extern void FileImporterCaptureClick();

    // Use this for initialization
    void Start () {

    }

    // Update is called once per frame
    void Update () {

    }

    public void OnButtonPointerDown()
    {
        FileImporterCaptureClick();
    }

    public void FileSelected(string url)
    {
        StartCoroutine(LoadJson(url));
    }

    private IEnumerator LoadJson(string url)
    {
        WWW www = new WWW(url);
        yield return www;
        Debug.Log(www.bytes);
    }
}
  • ImportButtonにEventTrrigerコンポーネントを追加して、Pointer Downイベントに対してImportButton.OnButtonPointerDown()を割り当てる

制限

参考サイトにも書いてあるがマウスをボタン上で押したままcanvasから離れると反応しない。
その後再度canvas上をクリックするとダイアログが表示されてしまう。

Pluginでやっていること

最初はPluginを使わなくてもボタンを押したら外部javascriptと連携してinput要素をclick()すればいいんじゃないと考えていたが出来なかった。

display:noneで機能が無効化されているのかと思ったがそうではなかった。
参考サイト:[CSS] display:noneで機能が無効化されないようにする

原因は以下のようにユーザが起こしたクリックイベント中でのみinput要素のclick()を実行できるからだった。
参考サイト:<input type="file">のクリックイベントをコードから発行する作法
このため、PluginではUnityのUI上でボタンがダウンされた時にcanvasにマウスアップ時に反応するクリックイベントを設定しそこを起点にclick()を実行している。

17
10
4

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
17
10