WallpaperEngineの壁紙をUnityのWebGLで作るためのメモ

WallpaperEngineの壁紙をUnityで作成する場合、Windowsの実行形式(.exe)で出力すれば手軽に作成することができます。
しかし、Web形式(HTML)以外ではWallpaperEngineで壁紙を設定する際のプロパティを作成者で任意に作成できないため、ユーザにパラメータ変更を委ねたいときに面倒です。
UnityにはWebGLで出力する機能もあり、そちらをWallpaperEngineに使用する方法を調べたのですが、まとまった情報がなかったのでメモしておきます。

以下はUnity Ver 2017.1 で確認した内容です。

WebGLテンプレート

WallpaperEngineで表示した時に表示を最大化し、必要なJavascript外部ファイルを読み込むためHTMLのテンプレートを設定します。

参考: Unity – Manual: Using WebGL Templates

配置 : Assets > WebGLTemplates > (任意の名前) > index.html

<!DOCTYPE html>
<html lang="en-us">

  <head>
    <meta charset="utf-8">
    <meta http-equiv="Content-Type" content="text/html; charset=utf-8">
    <title>Unity WebGL Player | %UNITY_WEB_NAME%</title>
    <script src="PropertyScript.js"></script>
    <script src="%UNITY_WEBGL_LOADER_URL%"></script>
    <script>
      var gameInstance = UnityLoader.instantiate("gameContainer", "%UNITY_WEBGL_BUILD_URL%");
    </script>
    <style>
      body {
        overflow: hidden;
        margin: 0;
        padding: 0;
        background-color: black;
      }
    </style>
  </head>
  <body>
    <div id="gameContainer" style="width: 100%; height: 100%; position: absolute;"></div>
  </body>
</html>

Edit > Project Settings > Playerから追加したテンプレートを選択しておくこと。

WallpaperEngineで読み込むプロジェクトファイル

プロパティの値が定義されています。
プロパティ部分は、WallpaperEngineの作成ガイドにあるテンプレと同様です。

参考: Advanced: Web user customization

配置 : ビルド先 > project.json

{
    "file" : "index.html",
    "preview" : "prev.png",
    "general" : 
    {
        "properties" : 
        {
            "customcolor" : 
            {
                "text" : "User accent",
                "type" : "color",
                "value" : "0.2 0.8 1"
            },
            "schemecolor" : 
            {
                "text" : "Scheme color",
                "type" : "color",
                "value" : "0.7 0.2 0.1"
            },
            "custombool" : 
            {
                "text" : "User bool",
                "type" : "bool",
                "value" : false
            },
            "customint" :
            {
                "text" : "User slider",
                "type" : "slider",
                "value" : 50,
                "min" : 0,
                "max" : 100
            },
            "customcombo" :
            {
                "text" : "User combo",
                "type" : "combo",
                "value" : 1,
                "options" :
                [
                    {
                        "value" : 1,
                        "label" : "First"
                    },
                    {
                        "value" : 2,
                        "label" : "Second"
                    },
                    {
                        "value" : 3,
                        "label" : "Third"
                    }
                ]
            }
        }
    },
    "title" : "WebGL Template",
    "type" : "web"
}

プロパティを受け取るスクリプト

WallpaperEngineのプロパティはブラウザ?のJavascriptに用意した特定の関数が呼び出されることで受け渡しをするため、Unityのエディタとは別にスクリプトを用意します。
後述する「Javascript->C#」間の値の受け渡しの都合から、格納する値をINT型にしています。

参考: Advanced: Web user customization

配置 : ビルド先 > PropertyScript.js

var WALLPAPER_ENGIN_PROPERTY = WALLPAPER_ENGIN_PROPERTY || {};
window.wallpaperPropertyListener = {
    applyUserProperties: function(properties) {
        // Read scheme color
        if (properties.schemecolor) {
            WALLPAPER_ENGIN_PROPERTY.schemecolor = PropertyColorToInt(properties.schemecolor.value);
        }

        // Read custom color
        if (properties.customcolor) {
            WALLPAPER_ENGIN_PROPERTY.customcolor = PropertyColorToInt(properties.customcolor.value);
        }

        // Read custom boolean
        if (properties.custombool) {
            WALLPAPER_ENGIN_PROPERTY.custombool = PropertyBoolToInt(properties.custombool.value);
        }

        // Read custom slider
        if (properties.customint) {
            WALLPAPER_ENGIN_PROPERTY.customint = properties.customint.value;
        }

        // Read custom combo box
        if (properties.customcombo) {
            WALLPAPER_ENGIN_PROPERTY.customcombo = properties.customcombo.value;
        }

        function PropertyColorToInt(colorValue){
            var Colors = colorValue.split(' ');
            colorInt = Math.ceil(Colors[0] * 255) * 1000 * 1000;
            colorInt += Math.ceil(Colors[1] * 255) * 1000;
            colorInt += Math.ceil(Colors[2] * 255);
            return colorInt;
        }

        function PropertyBoolToInt(boolValue){
            return Number(boolValue);
        }
    }
};

WebGLからブラウザのJavascript関数を呼び出すプラグイン

WebGLからブラウザのJavascriptの関数を呼び出せるよう、プラグインとしてスクリプトファイルを作成します。

参考: Unity – マニュアル: WebGL: ブラウザのスクリプトと通信を行う

配置 : Assets > Plugins > WebGL > PropertyPlugin.jslib

var PropertyPlugin = {
    GetSchemeColor: function()
    {
        return WALLPAPER_ENGIN_PROPERTY.schemecolor;
    },
    GetCustomColor: function()
    {
        return WALLPAPER_ENGIN_PROPERTY.customcolor;
    },
    GetCustomBool: function()
    {
        return WALLPAPER_ENGIN_PROPERTY.custombool;
    },
    GetCustomInt: function()
    {
        return WALLPAPER_ENGIN_PROPERTY.customint;
    },
    GetCustomCombo: function()
    {
        return WALLPAPER_ENGIN_PROPERTY.customcombo;
    }
};

mergeInto(LibraryManager.library, PropertyPlugin);

WebGLからブラウザのJavascript関数を呼び出すスクリプト

Asset内の任意の場所に保存し、適当なオブジェクトでコンポーネントとして呼び出し。
マニュアルにINT型とString型を呼び出す方法しかなかったため、全てINTでやり取りしています。
以下の例ではUI>Textオブジェクトにプロパティで取得した値を反映しています。

参考: Unity – マニュアル: WebGL: ブラウザのスクリプトと通信を行う

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;
using System.Runtime.InteropServices;
using System;

public class PropertyTestScript : MonoBehaviour {
    public Text[] TextObj;

    [DllImport("__Internal")]
    private static extern int GetSchemeColor();

    [DllImport("__Internal")]
    private static extern int GetCustomColor();

    [DllImport("__Internal")]
    private static extern int GetCustomBool();

    [DllImport("__Internal")]
    private static extern int GetCustomInt();

    [DllImport("__Internal")]
    private static extern int GetCustomCombo();

    private static Color SchemecolorIntToColor(int schemecolorInt)
    {
        int red = schemecolorInt / 1000 / 1000;
        int green = schemecolorInt / 1000 % 1000;
        int blue = schemecolorInt % 1000;
        return rgb(red, green, blue);
    }

    private static Color rgb(int red, int green, int blue)
    {
        return new Color(red / 255f, green / 255f, blue / 255f);
    }

    private static bool SchemeboolIntToBool(int schemeboolInt)
    {
        return Convert.ToBoolean(schemeboolInt);
    }

    void Start () {
    }

    void Update () {
        TextObj[0].color = SchemecolorIntToColor(GetSchemeColor());
        TextObj[1].color = SchemecolorIntToColor(GetCustomColor());
        TextObj[2].text = "GetCustomBool : " + SchemeboolIntToBool(GetCustomBool()).ToString();
        TextObj[3].text = "GetCustomInt : " + GetCustomInt().ToString();
        TextObj[4].text = "GetCustomCombo : " + GetCustomCombo().ToString();
    }
}


UnityのSteamVRでGripMove的に位置移動するスクリプト

Unityに読み込んだキャラモデルをSteamVRでいやらしく眺めるためにGripMoveっぽい事ができるスクリプトを結構前に作った。
しばらくしてからもっといいのがないか探したけど上手く見つからないので、前のをなくさないようメモしておく。
Viveしか持ってないのでそれのみ動作確認。
SteamVR Pluginのバージョンは1.2.2。

使い方

コントローラにスクリプトをアタッチし、InspectorからObjCameraRig変数にCameraRigかそれの入っているオブジェクトを指定する。
このサンプルではViveコントローラのグリップボタン(にぎりの所のボタン)で移動する。


using System.Collections; using System.Collections.Generic; using UnityEngine; using UnityEngine.VR; public class GripMove : MonoBehaviour { // CameraRigもしくはそれを入れたオブジェクトを指定する public GameObject ObjCameraRig; //1フレーム前と差を比較するために前回のコントローラ位置・回転を格納する変数 private Vector3 beforeControllerPosition; private Quaternion beforeControllerRotation; void Update () { SteamVR_TrackedObject trackedObject = GetComponent<SteamVR_TrackedObject>(); var device = SteamVR_Controller.Input((int)trackedObject.index); //現在(移動後)のコントローラの位置・回転を格納 Vector3 afterControllerPosition = this.gameObject.transform.position; Quaternion afterControllerRotation = this.gameObject.transform.rotation; //"Grip"Moveなのでグリップボタンを入力したときに移動処理を実行 if (device.GetPress(SteamVR_Controller.ButtonMask.Grip)) { //1フレーム前と比較したコントローラの移動距離を算出 Vector3 distanceDifference = beforeControllerPosition - afterControllerPosition; //1フレーム前と比較したコントローラのY軸回転を算出 float rotationDifferenceY = Mathf.DeltaAngle(beforeControllerRotation.eulerAngles.y, afterControllerRotation.eulerAngles.y); //コントローラが移動した距離分、CameraRigを移動 ObjCameraRig.transform.position += distanceDifference; //コントローラのY軸が回転した分、コントローラ位置を軸としてCameraRigを回転移動 ObjCameraRig.transform.RotateAround(afterControllerPosition, Vector3.down, rotationDifferenceY); } //現在のコントローラの位置・回転を次回に移動前として使うために格納 beforeControllerPosition = afterControllerPosition; beforeControllerRotation = afterControllerRotation; } }

ワールド座標でルームスケールする位置を移動するので、部屋ごと移動してるような不思議感覚。


VultrのStartupScriptでUbuntuの設定を簡略化

ちょっとした用途で一時的なVPSサーバを借りるときに、初期費用のいらないVultrがとても便利。
https://www.vultr.com/

検証のため何度もサーバデプロイするのだが、同じ設定を繰り返すのは面倒なのでStartupScriptを毎回使っており、その内容をメモする。
スナップショットからデプロイする手もあるけど、なんとなく手垢がついてそうでいやだった。

スクリプトはVultrのVC2で次のOSにて動作確認済み。

  • Ubuntu16.04
  • Ubuntu17.04

使う前に後述の準備で取得した値を定数に入れる。

スクリプト

#!/bin/bash

#ログインユーザ名
username='Foo'

#ログインパスワード(ハッシュ)
password_hash='$6$Q8~'

#SSH用公開認証鍵
public_key='ssh-rsa AAA~'

#ユーザを追加し、ついでにsudoできるようにする
useradd -p ${password_hash} -s /bin/bash -m $username
gpasswd -a ${username} sudo

#SSH用公開認証鍵を追加する
mkdir -p /home/${username}/.ssh
chmod 700 /home/${username}/.ssh
echo "${public_key}" > /home/${username}/.ssh/authorized_keys
chmod 600 /home/${username}/.ssh/authorized_keys
chown -R ${username}:${username} /home/${username}/.ssh

#sshdのパスワード認証を無効化し、公開鍵認証のみできるようにする
sed -i".org" -e '/^#PasswordAuthentication yes$/a PasswordAuthentication no' /etc/ssh/sshd_config

#ついでにSSHでrootログインできないようにする
sed -i".org" -e 's/^PermitRootLogin yes$/PermitRootLogin no/' /etc/ssh/sshd_config

#sshdへ設定反映
systemctl reload sshd

#スワップ領域の追加(以下は1GBの場合)
dd if=/dev/zero of=/swapfile count=1024 bs=1M
chmod 600 /swapfile
mkswap /swapfile
swapon /swapfile
sed -e '$ a /swapfile        none        swap    sw    0        0' /etc/fstab

#ufwでsshの通信を許可し、その他をブロックする
ufw allow OpenSSH
ufw enable

#ufwのロギングをオフにする
ufw logging off

#日本語を設定する
apt install language-pack-ja-base language-pack-ja
localectl set-locale LANG=ja_JP.UTF-8 LANGUAGE="ja_JP:ja"

#日本時間にする
timedatectl set-timezone Asia/Tokyo

#パッケージを最新にする
apt update && apt -y upgrade

(準備)ログインパスワード用ハッシュの作成

ハッシュにしておけばスクリプトが見られてもあんまり問題にならないはず。
以下の[Password]と[Salt]の2箇所を変更して実行した結果を使用。

perl -e 'print crypt("Password", "\$6\$Salt");'

(準備)認証鍵の作成

公開鍵(id_rsa.pub)の内容をスクリプトで使用し、秘密鍵(id_rsa)はクライアントに保存しておく。

ssh-keygen -t rsa

bgm.xflから切り出したwavをそれっぽいoggに整える

必要になるたびメモを探すのでこっちにも残しておく。
oggSplitを使用するため、OSにUbuntuの入っている環境が必要になり、大抵の場合は準備に手間がかかると思われる。

うろ覚えだが、対象は中身のエンコードがoggでヘッダや曲情報?がwaveと同様なファイルになっていたはずなので、正常なoggにする加工が必要となるが変換ではないため「整える」と表記する。
昔はWindowsのエンコード変換ツールでヘッダを無視できるものを使ってたはずなのだが、何だったか思い出せなかった。

手順


切り出し

X-Ripperやスペシャルねこまんま57号などでWAVを指定して切り出し。

ヘッダをoggとして整える

以下の手順はUbuntu 16.04で確認済み

頭の66バイトを削除する

tail -c +67 sample.wav > sample_tmp

必要となるoggSplitがoggvideotoolsパッケージに含まれるためインストール

sudo apt-get install  oggvideotools

oggSplitでヘッダを綺麗にする

oggSplit sample_tmp

oggSplit実行後に出力される「vorbis_0.oga」というファイルが目的物なので、oggに拡張子を変更

mv vorbis_0.oga sample.ogg

以上で1ファイルの処理が完了だが、不要な中間ファイルなどは適当に削除すること。

複数ファイルを纏めて処理する場合


ヘッダを整える部分は以下を使うと、カレントディレクトリ内をまとめて処理できる。

for file in `\find . -maxdepth 1 -name "*.wav" -type f`; do
  tail -c +67 $file > "${file:0:-4}_tmp"
  oggSplit "${file:0:-4}_tmp"
  mv vorbis_0.oga "${file:0:-3}ogg"
done