エディターで WebView を使う話【エディター拡張】

  • 29
    Like
  • 3
    Comment
More than 1 year has passed since last update.

この投稿は Unity Advent Calendar 2015 の 22日目の記事です。

※ この話は、ゲーム内の WebView の話ではなく、EditorWindow 内で WebView を使用する話です。
※ おもいっきり非推奨です。

現在までの WebView に関する情報

今まで、WebView を使った情報はチラホラと出してきました。

UnityEditor上で艦これはできる




最近はこちらの記事が話題になりました。

【Unity】僕の考えた最強のエディタ拡張【AdventCalendar】

そして更に試行錯誤して双方向通信が実現しました。


Unity上の C# コード -> WebView の HTML


WebView の HTML -> Unity上の C# コード

今回は WebView を表示して双方向通信を行うまでを書いていきたいと思います。

まずは DLL をデコンパイル。話はそれから

まずは、WebViewを使っていそうな AssetStore と Unity Services 系、そして「WebView」と言うキーワードを元に UnityEditor.dll をデコンパイルしたソースコードの中から該当しそうなクラスを探します。

関係ありそうなクラスが見つかりました。

  • UnityEditor
    • WebView
  • UnityEditor.Web
    • WebViewEditorWindow
    • WebScriptObject
    • JSProxyMgr
クラス 説明
WebView これが WebView 本体。
WebViewEditorWindow WebView を載せる EditorWindow。基本的な操作はこのウィンドウを介して行う。
WebScriptObject Unity と WebView が通信を行うときに使用するクラス。このクラスをいじることはないので知らなくていい。
JSProxyMgr Unity と WebView の双方向通信を担うクラス。ちょっと難解だった。

WebViewEditorWindow を作成する

まずは単純に WebViewEditorWindow を作成して任意のサイトを表示してみましょう。

WebViewEditorWindow.Creat<T>(string title, string url, int mixWidth, int mixHeight, int maxWidth, int maxHeight) を呼び出すだけで作成することができます。

using UnityEngine;
using UnityEditor;
using System.Reflection;

public class Kancolle
{
    static BindingFlags Flags = BindingFlags.Public | BindingFlags.Static;

    [MenuItem("Window/Kancolle")]
    static void Open ()
    {
        var type = Types.GetType ("UnityEditor.Web.WebViewEditorWindow", "UnityEditor.dll");
        var methodInfo = type.GetMethod ("Create", Flags);
        methodInfo = methodInfo.MakeGenericMethod (typeof(Kancolle));
        methodInfo.Invoke (null, new object[]{
            "Kancolle",
            "http://www.dmm.com/netgame/social/-/gadgets/=/app_id=854854/",
            200, 530, 800, 600
        });
    }
}

Unity (C#) から WebView の Javascript を呼び出す

これを実現させるには WebView.ExecuteJavascript(string scriptCode) を実行します。これのラッパーとして WebViewEditorWindow.InvokeJSMethod(string objectName, string name, params object[] args) もあります。 InvokeJSMethod を採用したものが最初のほうで見せた Tweet の内容になります。

これは、EditorApplication.update を使用して毎回 Time.realtimeSinceStartup の値を WebView 側に渡しているものになります。

WebView から Unity (C#) を呼び出す

これが少し手こずりましたが、なんとかなったので紹介します。

WebView から Unity (C#) を呼び出す、細かく言うと任意のオブジェクトのメソッドを呼び出すことができます。モバイルや WebPlayer の SendMessage と同じイメージです。

WebView で表示されるページには unityAsync と言うオブジェクトが自動で登録されます。これが Unity (C#) と通信を行うためのものです。

unityAsync({
    className: "window.webScriptObject",
    funcName: "ProcessMessage",
    funcArgs: [
        JSON.stringify({
            type: "INVOKE",
            messageID: 1,
            version: "0",
            reference: "ExampleWindow",
            destination: "ExampleWindow",
            method: 'Play',
            params: []
        })],
        onSuccess: function(res) {
            console.log("returnValue: " + res)
        }
})
プロパティー 説明
className 値は固定(たぶん)
funcName 値は固定(たぶん)
funcArgs ここに呼び出すメソッドの情報を書いていく
type "INVOKE" がメソッド実行機能。他にも type があるが使わないかも?
messageID 値が固定でも問題はないが呼び出すごとにインクリメントしておくといいかも?
version 0 固定
reference GlobalObject に登録されている名前を設定する(Unity側で作成したクラスのこと)
destination GlobalObject に登録されている名前を設定する(Unity側で作成したクラスのこと)
method 呼び出すメソッド名
params メソッドの引数に渡す値
onSuccess (エラーであっても)何らかのレスポンスが帰ってくると呼び出される。Unity側のメソッドの戻り値も取得可能。

実際に使ってみましょう。

まずは、HTML側の実装です。

<html>
  <head>
    <meta charset="UTF-8">
  </head>
  <body style="background-color:gray">
    <button onclick="sendMessage('Play')">再生ボタン</button>
    <button onclick="sendMessage('Pause')">一時停止ボタン</button>
    <button onclick="sendMessage('Step')">ステップボタン</button>
    <script type="text/javascript">

      function sendMessage(method) {

          unityAsync({
            className: "window.webScriptObject",
            funcName: "ProcessMessage",
            funcArgs: [JSON.stringify(
                {
                    type:"INVOKE",
                    messageID:1,
                    version:"0",
                    reference:"ExampleWindow",
                    destination:"ExampleWindow",
                    method: method,
                    params:[]
                })],
            onSuccess: function(a) {
                console.log("returnValue: " + a)
          }})
      }
    </script>
  </body>
</html>

次に Unity 側。ラッパークラスを作成しているので混乱してしまうかもしれませんが、ここで見て欲しいのは ExampleWindow クラスに作成した Play Pause Step のメソッドです。

using UnityEngine;
using UnityEditor;

// ラッパー書いてます
// https://gist.github.com/anchan828/2405b9b792366327e502
public class ExampleWindow : CustomWebViewEditorWindow
{

    [MenuItem ("Window/Example")]
    static void Open ()
    {
        CreateWebViewEditorWindow<ExampleWindow> (
            "Example",
            Application.dataPath + "/index.html", 200, 530, 800, 600);
    }

    public void Play ()
    {
        EditorApplication.isPlaying = !EditorApplication.isPlaying;
    }

    public void Pause ()
    {
        EditorApplication.isPaused = !EditorApplication.isPaused;
    }

    public void Step ()
    {
        EditorApplication.Step ();
    }
}

実行するとこうなります。

まとめ

いかがだったでしょうか。私はなんでもできそうな気がしてきました。

今回は技術面の解説をあまりいれていないので、「エディター拡張入門」にて書こうかなと思っております。(宣伝)