Web コンテンツを構築していくとき Web ページ上の要素と通信を行う必要が出てくる場合があります。または Unity API として実装されていない Web API を使用して機能を実装したい場合もあるかもしれません。これらは、直接ブラウザの JavaScript エンジンと直接インターフェースで繋ぐ必要があります。Unity の WebGL はこれらを行うためのさまざまな方法を提供しています。
今から説明することは Unity Web Player とブラウザーの通信 と同じです。Application.ExternalCall() と Application.ExternalEval() 関数を使用することで JavaScript コードを埋め込み、実行することができます。ブラウザの JavaScript から Unity のゲームオブジェクトにアタッチされているスクリプトのメソッドを呼び出すには以下のコードを使用します:
SendMessage ('MyGameObject', 'MyFunction', 'foobar');
ブラウザの JavaScript を使用する他の方法として、プロジェクト内に JavaScript のソースを組み込み、スクリプトコードから直接 JavaScript のコードを呼び出す方法があります。この方法を行うには、“Assets/Plugins/WebGL” フォルダーに .jslib 拡張子で JavaScript のファイルを配置することです( .js だと UnityScript コンパイラーでコンパイルされてしまうため .jslib にしています)。プラグインとしての JavaScript ファイルは以下の様なシンタックスを持ちます:
Assets/Plugins/WebGL/MyPlugin.jslib
var MyPlugin = {
Hello: function()
{
window.alert("Hello, world!");
},
HelloString: function(str)
{
window.alert(Pointer_stringify(str));
},
PrintFloatArray: function(array, size)
{
for(var i=0;i<size;i++)
console.log(HEAPF32[(array>>2)+size]);
},
AddNumbers: function(x,y)
{
return x + y;
},
StringReturnValueFunction: function()
{
var returnStr = "bla";
var buffer = _malloc(lengthBytesUTF8(returnStr) + 1);
writeStringToMemory(returnStr, buffer);
return buffer;
},
BindWebGLTexture: function(texture)
{
GLctx.bindTexture(GLctx.TEXTURE_2D, GL.textures[texture]);
}
};
mergeInto(LibraryManager.library, MyPlugin);
次に、上記の関数を C# スクリプトから呼び出す例です:
using UnityEngine;
using System.Runtime.InteropServices;
public class NewBehaviourScript : MonoBehaviour {
[DllImport("__Internal")]
private static extern void Hello();
[DllImport("__Internal")]
private static extern void HelloString(string str);
[DllImport("__Internal")]
private static extern void PrintFloatArray(float[] array, int size);
[DllImport("__Internal")]
private static extern int AddNumbers(int x, int y);
[DllImport("__Internal")]
private static extern string StringReturnValueFunction();
[DllImport("__Internal")]
private static extern void BindWebGLTexture(int texture);
void Start() {
Hello();
HelloString("This is a string.");
float[] myArray = new float[10];
PrintFloatArray(myArray, myArray.Length);
int result = AddNumbers(5, 7);
Debug.Log(result);
Debug.Log(StringReturnValueFunction());
var texture = new Texture2D(0, 0, TextureFormat.ARGB32, false);
BindWebGLTexture(texture.GetNativeTextureID());
}
}
シンプルな数値であれば特に変換の必要せずに関数の引数パラメータとして JavaScript に渡すことができます。他のデータ型である場合は、(JavaScript の大きな配列として)emscripten ヒープ内のポインタとして渡されます。文字列の場合は、Pointer_stringify ヘルパー関数を使用して JavaScript の文字列へと変換することができます。JavaScript から 文字列を返すには malloc_ を呼び出してメモリを割り当て、writeStringToMemory__ ヘルパー関数で JavaScript の文字列を書き込む必要があります。Unity 側で戻り値として文字列を取得した場合は、IL2CPP のランタイムがメモリ周りの開放を世話してくれます。プリミティブ型の配列の場合、integer、unsigned integer、floating のメモリ表現が異なるサイズでヒープされるため、さまざまな ArrayBufferView を提供します: HEAP8、HEAPU8、HEAP16、HEAPU16、HEAP32、HEAPU32、HEAPF32、HEAPF64 。 WebGL のテクスチャにアクセスするために、emscripten は Unity から WebGL テクスチャオブジェクトの ネイティブテクスチャ ID のマップである GL.textures を提供します。
Unity は ソースコードを emscripten を使用して C++ のコードから JavaScript へとコンパイルしているので、同じように C または C++ のコードをプラグインとして書くことができます。そして C# のコードからそれらの関数を呼び出すこともできます。よって上記の例のような jslib の代わりに、下記のような c ファイルを扱うことが可能です。- このプラグインは自動でコンパイルされ、上記の JavaScript コードのサンプルのようにスクリプト側から呼び出すことが可能です。
C++ (.cpp) を使用してプラグインを実装する場合は、マングリング(name mangling)問題を回避するために C リンケージを持つようにしなければいけません。
Assets/Plugins/WebGL/MyPlugin.c
#include <stdio.h>
void Hello ()
{
printf("Hello, world!\n");
}
int AddNumbers (int x, int y)
{
return x + y;
}
Unity はデフォルトでは、プラットホームの互換性チェックとエラー制御のために、ビルド時にいくらかの JavaScript を作成します。このコードは、モバイルデバイスで Unity の WebGL コンテンツを実行しようとした場合など、サポートしていないプラットホームで実行された時に、警告メッセージを表示します。既知のエラーをチェックし、より判りやすいエラーメッセージとともにエラーダイアログを表示するために、ブラウザからエラーと例外値の追跡もできます。
例えば、モバイル上で WebGL コンテンツを表示したときに発生する警告メッセージを非表示にしたい場合に、カスタマイズすることによって実現が可能です。カスタマイズする方法は、生成された index.html ファイルにある errorhandler と compatibilitycheck が null のところに Javascript 関数を実装することです。
var Module = {
TOTAL_MEMORY: 268435456,
errorhandler: null,
compatibilitycheck: null,
};
compatibilitycheck 関数は、起動時に呼び出され、サポートされていないデバイスだと検出したときに表示されるメッセージをコードによって実装することができます。
errorhandler 関数は、window.onerror イベントハンドラーが実行されたときに呼び出されるものです。引数も window.onerror と同じものが渡されます。 true を返すと場合はデフォルトのエラーハンドラーが実行されず、false を返すとUnityのデフォルトハンドラーが実行されます。
例:
var Module = {
TOTAL_MEMORY: 268435456,
errorhandler: function(err, url, line) {
alert("error " + err + " occurred at line " + line);
// return true so Unity's error handler is not executed.
return true;
},
compatibilitycheck: function() {
// check compatibility ...
}
};