凹みTips

C++、JavaScript、Unity、ガジェット等の Tips について雑多に書いています。

Unity で Web をテクスチャとして表示して操作する方法について調べてみた

追記(2014/05/05)

現在、本エントリで利用している Awesomium v1.6.2 はダウンロード出来ないため、v1.7.1 を利用したエントリを書きました:

ただし Mac では利用できません。

はじめに

Unity 上で Web を表示できるプラグインについて調べてみました。

デモ

出来るようになることのデモ動画です:

環境

Plugin の種類

Unity 上で Web を表示する方法は沢山あるようです。

この他にも PhantomJS などのヘッドレスブラウザと通信して…、みたいなことをやっている人も居ました。
Unity だと少々面倒くさい UI の構築の代替 & モバイル向け広告などの用途であれば unity-webview が良いように思われます(何もせずとも正常に動作してお手軽です)。私はテクスチャとして利用したかったので、Mac なら HtmlTexturePlugin、Win なら Berkelium が素性が良さそうです。また、Berkelium は Mac でも行けるとの情報もありました。Win/Mac 両方いける Awesomium は商用は有償ですが、個人開発用途では無料のようなので最も良さそうに思えます。
そこで、HtmlTexturePlugin、Berkelium、Awesomium について調べてみました。結果としては Awesomium が Win / Mac 共に動作したのでこれをメインで解説します。HtmlTexturePlugin と Berkelium は正常な動作まで持っていけませんでしたが、失敗談を後述します。

Awesomium(Mac

手順は Awesomium v1.6.2 のようで、現行の v1.7.1 に対するチュートリアルは見つかりませんでした。そこでフォーラムで検索してみました。

殺伐としている…、もう少し待ってね、という内容から進捗がないようです。v1.7.1 で v1.6.2 のチュートリアルに従ってやってみましたが、「EntryPointNotFoundException」で落ちてしまいました。自前でバインディングするのはコストが高いので、なるべく楽にできそうな方法を探します。

古いバージョンを探す

ここに書いてある v1.6.5 のキャッシュを試してみましたが、Unity が落ちて駄目でした。

v1.6.2 を探す

やはり、v1.6.2 でなければ駄目なようです。そこでものは試しと、上記リンクの v1.6.5 の部分を v1.6.2 に変えてみました。

あった!やってみるものですね。

そして上手く行ったのでこの手順を記しておきます。

まず、「build/bin/release/Awesomium.framework」を 「/Applications/Unity.app/Contents/Frameworks」に移動します。これで Unity 上から Framework を参照できるようになります。
次に以下の github より「UnityAwe」を落としてきます。

適当な Unity の新しいプロジェクトを作成し、「bin/mac/AwesomiumMono.dll」「WebCoreHelper.cs」「WebTexture.cs」の 3 点を Unity の「Project」へ放り込みます。そして、「WebTexture.cs」を Plane に追加して、テクスチャサイズと URL を指定して実行すると、以下のように表示されます。

ゲーム画面ではスクロールやリンククリックともに可能です!素晴らしい。
しかしながら、ゲームの実行を終了すると Unity が落ちます。これを回避するためには以下の2箇所を修正して下さい。

WebTexture.cs: 127 行目
    private void OnDisable()
    {
        // Free the pinned array handle.
        PixelsHandle.Free();

        webViewList.Remove(webView);
        webView = null;

        if (WebCore.IsRunning)
            webView.Close();
    }

webView が null になった後に Close() され得るので、以下のように順番を入れ替えます。

    private void OnDisable()
    {
        // Free the pinned array handle.
        PixelsHandle.Free();

        if (WebCore.IsRunning)
            webView.Close();

        webViewList.Remove(webView);
        webView = null;
    }
WebTexture.cs: 238 行目
    private void OnApplicationQuit()
    {
        // We shutdown the WebCore only once
        if (WebCore.IsRunning)
        {
            Destroy(GameObject.Find("WebCoreHelperInstance"));

            WebCore.Shutdown();
        }
    }

この「Shutdown」で落ちるので、内部の処理を肩代わりしようかと思ったのですが private メンバでアクセス出来ないので、メモリリークに目を瞑りつつ泣く泣くコメントアウトします。

    private void OnApplicationQuit()
    {
        // We shutdown the WebCore only once
        if (WebCore.IsRunning)
        {
            Destroy(GameObject.Find("WebCoreHelperInstance"));

            // WebCore.Shutdown();
        }
    }

これで落ちずに動くようになります。

Awesomium(Windows

Windows も同様の手順で行えば OK です。

  1. Windows 用の SDK の v1.6.2 をダウンロードしてインストール
  2. デスクトップにできた「awesomium_v1.6.2_sdk_win\build\bin\release」の中身を「C:\Program Files (x86)\Unity\Editor」へコピー
  3. github より「UnityAwe」をダウンロード
  4. 「bin\win\AwesomiumMono.dll」「WebCoreHelper.cs」「WebTexture.cs」を Project ビューへドラッグ&ドロップ
  5. 適当なプレーンに「WebTexture.cs」をアタッチして Width / Height / Initial URL を指定
  6. 実行

WindowsMac と違ってエラーが出て落ちるということはありませんでした。ただ終了時に「Non matching Profiler.EndSample」というプロファイラ関連のエラーが吐かれてしまいます。調べた所、Unity のバグではないか、とのことでした。

特に困ることは無いので放っておきます。

複数テクスチャのレンダリング

WebCore は共有しているので 2 枚、3 枚の WebView のレンダリングも良い感じの速度で動きます。もちろんプレーン以外の形状にもテクスチャとして貼り付けられます。ただその場合は操作は破綻しますのでご注意を。

ビルド

公開する際はそのまま Build & Run しただけでは動きません。

Mac
  1. ビルドしたアプリケーションを右クリックして「パッケージの内容を表示」をクリック
  2. 「Contents/Framework」内に先ほど同様に「Awesomium.framework」を入れる
Windows
  1. ビルドしたアプリケーション(exe)のあるフォルダに「awesomium_v1.6.2_sdk_win\build\bin\release」の中身をコピー

これでビルドしたアプリケーションでも使えるようになります。

Awesomium の機能を調べてみる

せっかく動くようになったので機能を調べてみます。基本的な機能(ナビゲーション等)は WebTexture が既にメンバとして持っています。より踏み込んだ機能を使いたい場合は、以下の API リファレンスを参考に直接 WebView の public メンバを利用します。

欲しいものはひと通り揃っている感じしますね。試しに ExecuteJavascriptWithResult を以下のように Update 内に仕込んでみます。

if ( Input.GetKeyDown(KeyCode.A) ) {
	JSValue result = webView.ExecuteJavascriptWithResult("document.title");
	Debug.Log( result.ToString() );
}

これでページタイトルがデバッグビューに表示されます(※ タイトルは Title プロパティからも調べられます)。面白いですね。

(失敗編 1)HtmlTexturePlugin

結論から述べると、一瞬しか表示されない問題に悩まされているので、現状使えていないです…。が、そこまでの手順をメモしておきます。

  1. 以下からダウンロード
  2. Assets/Plugins 下に htmlTexture.bundle を移動
  3. 後述のスクリプトを Plane にアタッチ
  4. 反転してしまうので x スケール値を負の値に変更
  5. File > Build & Run から実行
実行結果

一瞬表示されたタイミングを見計らってキャプりました。すぐ真っ黒になります。

スクリプト
using UnityEngine;
using System.Collections;
using System.Text;
using System.Runtime.InteropServices;

public class HtmlTexture : MonoBehaviour {

	[DllImport ("htmlTexture")]
	private static extern void htmlTexture_start(int textID, int width, int height, string url);
 
	[DllImport ("htmlTexture")]
	private static extern void htmlTexture_stop();
 
	[DllImport ("htmlTexture")]
	private static extern void htmlTexture_update(int texID);
 
	[DllImport ("htmlTexture")]
	private static extern void htmlTexture_setURL(int texID, string url);
 
	[DllImport ("htmlTexture")]
	private static extern void htmlTexture_getURL(int texID, StringBuilder url, int stringCapacity);
 
	[DllImport ("htmlTexture")]
	private static extern void htmlTexture_goBack(int texID);
 
	[DllImport ("htmlTexture")]
	private static extern void htmlTexture_goForward(int texID);
 
	[DllImport ("htmlTexture")]
	private static extern void htmlTexture_sendJavascript(int texID, string js);
 
	[DllImport ("htmlTexture")]
	private static extern void htmlTexture_sendJavascript(int texID, string js, StringBuilder result, int stringCapacity);
 
	[DllImport ("htmlTexture")]
	private static extern void htmlTexture_sendKeypress(int texID, string s);
	
	[DllImport ("htmlTexture")]
	private static extern void htmlTexture_mousemoved(int texID, int _x, int _y);
 
	[DllImport ("htmlTexture")]
	private static extern void htmlTexture_mousedown(int texID,  int _x, int _y);
 
	[DllImport ("htmlTexture")]
	private static extern void htmlTexture_mouseup(int texID, int _x, int _y);
 
	[DllImport ("htmlTexture")]
	private static extern void htmlTexture_leftclick(int texID, int _x, int _y);
	
	public int Width  = 1024;
	public int Height = 512;
	private Texture2D texture_;
	
	void Start() {
		texture_ = new Texture2D(Width, Height, TextureFormat.ARGB32, false);
		texture_.Apply();
		htmlTexture_start(texture_.GetInstanceID(), Width, Height, "http://google.com");
		transform.renderer.sharedMaterial.mainTexture = texture_;
	}
	 
	void Update() {
		htmlTexture_update(texture_.GetInstanceID());
		texture_.Apply();
		
		if (Input.GetKeyDown (KeyCode.R)) {
			htmlTexture_setURL(texture_.GetInstanceID(), "http://google.com");
		}
	}
	 
	void OnApplicationQuit() {
		htmlTexture_stop();
	}
}

何故一瞬しか表示されないのかは不明です…。操作については説明にあるようにここから RayCast を使ってやれば良いようです(正常に表示されればでうが…)。JS のインジェクションも出来て素性がよさそうなだけに残念です。動いた方がいらっしゃいましたら方法を教えて下さい。

(失敗編 2) Berkelium

スレ主の reissgrant さんが作成した Windows 版が大本で、stubborngorilla さんが Mac 版を作成されたようです。が、結論からいうと、どちらも動きませんでした。更新が 3 年前で止まっていることもあり、現環境ではどこか上手く動いていないようです。Windows で試してみた感じは Berkelium.exe 自体がうまく動作していないので(Chromium のバージョンも相当古いです)、新しい Berkelium をビルドしなおせばうまくいくかもしれません。

おわりに

3D と Web の世界の融合は色々な可能性があってとても面白そうです。私は Oculus Rift の世界に Web を持ち込みたいと思っていますので、これを利用して何か作ってみようと思います。