フロントエンド開発Blog

オレには鈍器がある

JavaScript , unity , webview

gree様のwebviewパッケージを使ってassets内のHTMLコンテンツを表示し、更にJSからUnityコードを実行してファイルの読み書きをしてみました。

  • webview上でassets内のindex.htmlを表示する(assets内のimg,js,cssも使う)
  • webview上のJSからUnity.call関数を使ってUnityコードを実行
  • Unityコードでテキストファイルの書き込み、読み込みをする

用意するHTMLについて

imgやjs,cssはすべて相対パスで記述します。そうすることでインターネットアクセスをせずにローカルだけで完結できます。

感覚的にはindex.htmlをブラウザにドラッグアンドドロップした状態でWEBコンテンツを作っていく感じです。ローカルサーバもいらないので楽チン。

HTMLコンテンツのインポート場所

Unityへインポートする際、パスが決まっています。「./assets/StreamingAssets/」配下です。それ以外の場所にインポートしてしまうとwebviewから呼び出せなかったり、HTMLコンテンツ用に用意した.jsファイルをUnityがコンパイルしようとしてbuildエラーになります。

Unityの決まり上、./assets/StreamingAssets/配下に置いたファイルはコンパイルの対象外となり、標準的なファイルシステム上に配置されます。

ちなみにandroidでは

"jar:file://" + Application.dataPath + "!/assets/"

のようなパスを指定するとアクセスできるようですが、webviewの場合「file:///android_asset/index.html」としないと読み込めませんでした。NOT FOUNDとか、UNKNOWN PROTOCOLエラーになってしまいます。

Unity側の設定

Build Settings > Player Settings > Other Settingsを変更します。Install LocationをAutomatic、Write AccessをExternalにしないとファイルの書き込みがどうしてもできませんでした。準備は以上です。

Csharpスクリプトを以下のように記述し、mainシーンの空オブジェクトに割り当てます。

using UnityEngine;
using System.Collections;
using System.IO;
using System.Text;

public class attachWevView : MonoBehaviour {
    WebViewObject webViewObject;
    
    // Use this for initialization
    void Start () {
        webViewObject = (new GameObject("WebViewObject")).AddComponent();
        webViewObject.Init((msg) =>
        {
            // JS側でUnity.call("save")が実行されたとき
            if (msg == "save")
                Debug.Log("persistentDataPath:" + Application.persistentDataPath);
                StreamWriter sw = new StreamWriter(Application.persistentDataPath + "/oredon_save.txt", false); //true=追記 false=上書き
                sw.WriteLine(msg);
                sw.Flush();
                sw.Close();
            }
            // JS側でUnity.call("load")が実行されたとき
            else
            {
                FileInfo fi = new FileInfo(Application.persistentDataPath + "/oredon_save.txt");
                using (StreamReader sr = new StreamReader(fi.OpenRead(), Encoding.UTF8))
                {
                    Debug.Log("read: " + sr.ReadToEnd());
                }
            }
        });
        
        // index.htmlを実行
        webViewObject.LoadURL("file:///android_asset/index.html");

        webViewObject.SetMargins(0, 0, 0, 0);
        webViewObject.SetVisibility(true);
    }
    
    // Update is called once per frame
    void Update () {
    
    }
}

シンプルな処理ですね。ローカルのindex.htmlをwebviewで表示し、Unity.callで渡された文字列に応じてファイルの書き込みと読み込み処理を分岐しています。

using System.IOを指定することでStreamReaderとStreamWriterを使えるようにします。Csharpになじみのある方にとっては良く使うクラスらしいです。using System.Textを指定するとEncodingの指定が可能になります。

iosなど、他のプラットフォームでは書き方が若干異なるようですのでandroid以外のプラットフォームへの展開も考えている方はLoadURL部分に特に気をつけて実装してください。

ファイルの読み書き部分で目にするApplication.persistentDataPathは、Unityアプリケーションで永続的に保管したいファイルの設置場所としてUnityから定数で渡されるパス文字列だそうです。基本的にセーブデータの保存や読み出し時はこの値を使用するとよさそうです。デバイス上では/Android/data/○○○○/files/oredon_save.txtに保存されました。(○○○○の部分はunityのビルド設定のBundle Identifierの文字列が入ります)

任意のタイミングでUnity.call("save")およびUnity.call("load")を書くだけです。簡単!

webviewでローカルHTMLを表示する方法ですが、最初はgithubのissueに書かれている方法を試しました。

一番下にWWWを使った方法が書かれています。確かにこの方法でも「HTMLは」出力できますがapkに同梱したcss,js,imgはリンク切れになります。処理の内容を読み解くと、assets内のHTMLをwwwで取得し、セーブデータとかを配置するApplication.persistentDataPathにHTMLを再配置してwebviewに渡している感じですね。

するとHTML以外の静的ファイルは置いてけぼりをくらうので当然リンク切れになります。

単一ファイルのみで完結するなら良い方法ですが今回の趣旨には合わなかったため、./assets/StreamingAssets/を直接読み込ませる方法を採用しました。

UnityはWEB上にもたくさん情報が公開されていますが、古い情報だったり自分がやりたいこととドンピシャじゃない情報だったりしてかえって調査に時間がかかりすぎてしまう点が弱点ですね。とはいえ、慣れてくればそうした情報の取捨選択も巧くこなせるようになると思うので、今はただただ精進ですね。

2016/05/05 追記

実際にブラウザゲームのセーブデータの保存/読み込みをやってみました。

ページトップへ

関連ページ

ページトップへ