はじめに
おえかきアプリを作っていて保存機能を実装することになったが、WebGLなのでFile.WriteAllBytes()
もApplication.persistentDataPath
使えない…となったので、javascriptプラグインを作ったお話。
※おえかきアプリの実装はメインではないので割愛します。
開発環境
- Unity 2020.3.25f1
- Visual Studio Code
プラグインの作成(.jslib)
Unityには、ブラウザのスクリプトと通信を行う方法が大きく2つあります。
-
Application.ExternalCall()
かSendMessage()
を使う。 - プラグイン(.jslib)を作り、コードを記述。
今回は後者の方法で実装を進めます。
mergeInto(LibraryManager.library, {
ExportTextureJS:function(base64){
// 手順1
const image = Pointer_stringify(base64);
// 手順2
var bin = atob(image.replace(/^.*,/, ''));
var buffer = new Uint8Array(bin.length);
for (var i = 0; i < bin.length; i++) {
buffer[i] = bin.charCodeAt(i);
}
try{
var blob = new Blob([buffer.buffer], {
type: 'image/png'
});
}catch (e){
return;
}
// 手順3
var url = (window.URL || window.webkitURL);
var dataUrl = url.createObjectURL(blob);
var a = document.createElement('a');
a.download = "picture.png";
a.href = dataUrl;
a.click();
},
});
手順1 javascript文字列への変換
const image = Pointer_stringify(base64);
まず引数から入ってきたbase64文字列を、ヘルパー関数Pointer_stringify()
を使用してJavaScript文字列に変換します。
これは文字列がemscripten ヒープ内のポインターとして渡されるためです。
手順2 Blob形式への変換
var bin = atob(image.replace(/^.*,/, ''));
var buffer = new Uint8Array(bin.length);
for (var i = 0; i < bin.length; i++) {
buffer[i] = bin.charCodeAt(i);
}
try{
var blob = new Blob([buffer.buffer], {
type: 'image/png'
});
}catch (e){
return;
}
変換した文字列をBlob形式のFileに変換します。
手順3 アンカー要素を使用して保存
var url = (window.URL || window.webkitURL);
var dataUrl = url.createObjectURL(blob);
var a = document.createElement('a');
a.download = "picture.png";
a.href = dataUrl;
a.click();
HTMLのアンカー要素<a>
を作ってdataURLを渡し、クリックイベントを強制的に呼び出します。
プラグイン呼び出し側の実装(C#)
using System;
using UnityEngine;
using UnityEngine.UI;
using System.Runtime.InteropServices;
public class ExportTexture : MonoBehaviour
{
#if UNITY_WEBGL && !UNITY_EDITOR
[DllImport("__Internal")]
private static extern void ExportTextureJS(string base64);
#endif
[SerializeField]
private RawImage _image;
public void OnPressExport()
{
#if UNITY_WEBGL && !UNITY_EDITOR
var texture = (Texture2D)_image.texture;
var png = texture.EncodeToPNG();
var base64 = Convert.ToBase64String(png);
ExportTextureJS("data:image/png;base64," + base64);
#endif
}
}
ブラウザでのみ動くため#if UNITY_WEBGL && !UNITY_EDITOR
#endif
で括っています。
プラグインの関数のインポート
[DllImport("__Internal")]
private static extern void ExportTextureJS(string base64);
jslibに記述した関数を呼び出せるようにしています。
この時、関数名と引数を一致させる必要があります。
テクスチャのbase64文字列への変換、関数の呼び出し
var texture = (Texture2D)_image.texture;
var png = texture.EncodeToPNG();
var base64 = Convert.ToBase64String(png);
ExportTextureJS("data:image/png;base64," + base64);
今回はRawImage
を使用しているので、RawImage
のtexture
をTexture2D
にキャストして、EncodeToPNG()
を使用してbyte[]
に変換。さらにそれをSystem.Convert.ToBase64String()
に渡してbase64形式に変換しています。
ExportTextureJS()
に渡す際に"data:image/png;base64,"
と連結しているのは、今回HTMLのアンカー要素に埋め込む手順を踏んでいるためです。(ただここでやる理由はない)
動作物
おわりに
物凄く雑に紹介しましたが、面倒くさいというのが伝わったなら幸いです。
追伸:PlayCanvasならこんなに面倒くさくない
リンク・参考記事など
GitLab
Unity(WebGL)のC#とブラウザのJavaScriptの連携
JS: base64文字列をBlob形式のFileに変換する