Web コンテンツを構築していくとき Web ページ上の要素と通信を行う必要が出てくる場合があります。または Unity API として実装されていない Web API を使用して機能を実装したい場合もあるかもしれません。これらは、直接ブラウザの JavaScript エンジンと直接インターフェースで繋ぐ必要があります。Unity の WebGL はこれらを行うためのさまざまな方法を提供しています。
Application.ExternalCall() と Application.ExternalEval() 関数を使って、埋め込まれた web ページで JavaScript コードを起動できます。ブラウザから JavaScript を使ってコンテンツのゲームオブジェクト上にメソッドを呼び出すには、以下のコードを使用できます。
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 ...
}
};