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

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

## 参考サイト

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

[How do I let the user load an image from their harddrive into a WebGL app? | Unity Community](https://forum.unity3d.com/threads/how-do-i-let-the-user-load-an-image-from-their-harddrive-into-a-webgl-app.380985/)

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

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

* テンプレートに以下を追加する

```html
<input file type="file" onchange='gameInstance.SendMessage("GameObject", "FileSelected", window.URL.createObjectURL(this.files[0]));' /> 
```

* GameObjectという名前のEmptyを作成し、以下のファイルを割り当てる

```csharp
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テンプレートの設定

* [Unity - マニュアル: WebGL テンプレートの使用]
(https://docs.unity3d.com/ja/current/Manual/webgl-templates.html)
[Custom WebGL Templates in Project Assets (do they actually work?) | Unity Community](https://forum.unity3d.com/threads/custom-webgl-templates-in-project-assets-do-they-actually-work.298756/)
* [Unity - Manual: Using WebGL Templates](https://docs.unity3d.com/Manual/webgl-templates.html)

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

```
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](http://albatrus.com/main/unity/7510)

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

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

* Assets/Plugins/FileImporter.jslibを作成する

```js
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に以下のスクリプトを割り当てる

```csharp
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で機能が無効化されないようにする](http://combitaro.net/article/141)

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