放浪軍師のアプリ開発局

Xamarin.Formsを使ってAndroid,iOS,UWP,WPFで動くアプリを開発したりしています。Unityも始めます。尚、このブログはわからないところを頑張って解決するブログであるため、正しい保証がありませんのでご注意ください。

【夏狂乱ちゃん誕生祭】Unity で 乱ちゃん( VRM モデル) を躍らせたい

さて、始まりました。放浪軍師のアプリ開発局。本日は記念すべき夏狂乱ちゃん生誕11周年でございます!パチパチパチ!いやぁ単なるランダム抽選アプリとして誕生した乱ちゃんがよくぞここまできたもんだね(しみじみ)…

さて、誕生日を記念して、そして区切りとして、本日より Unity での開発をスタートしたいと思います。また初心者からスタートだよ!今後ともよろしくお願いします。

Unity で VRM を躍らせる

さて、最初にやってみたのは乱ちゃんを躍らせるという奴です。記念としてちょうど良さそうなのでいってみよー!!!

環境

Microsoft Visual Studio Community 2019 Version 16.6.4
Unity 2019.4.5f1

参考資料

VTuber をされている 月夜莉央🌙🌌リアルドールVtuber (@VirtualDollRio) | Twitter さんの書かれたこちらの記事を参考にしました。実は彼は以前から知っておりまして、デビューした当日ぐらいからの相互フォロワーだったりします。
qiita.com
これ、初心者向けに Unity のインストールからすごく丁寧に書かれた記事なので、Unity に触れたことない方も是非やってみてください。是非!

ちなみに躍らせる VRM モデルは VRoidHub というサイトで無料でダウンロードできるものもあるので、モデルを所有していない方はそちらをどうぞ。乱ちゃんも一部モデルはダウンロード可能だよ! hub.vroid.com

やってみた

やってみた内容はもうまるっきり前述の記事どおりなので基本的に割愛します。ただ、ひとつだけ気になる事があったので追加で調べてみました。

VRMBlendShapeProxy.ImmediatelySetValue() が旧型式

記事内では表情や口の動きを VRMBlendShapeProxy クラスを使って操作していますが、ImmediatelySetValue メソッドを記述すると以下のように古いコードだと警告が出ます。例えば LipSyncController クラスであれば、こんな感じ。

public class LipSyncController : MonoBehaviour
{
    ~略~
    void LateUpdate()
    {
        var total = 1.0f;

        var w = total * GetWeight(nodeA);
        target.ImmediatelySetValue(BlendShapePreset.A, w); //←旧式
        total -= w;

        w = total * GetWeight(nodeI);
        target.ImmediatelySetValue(BlendShapePreset.I, w); //←旧式
        total -= w;

        w = total * GetWeight(nodeU);
        target.ImmediatelySetValue(BlendShapePreset.U, w); //←旧式
        total -= w;

        w = total * GetWeight(nodeE);
        target.ImmediatelySetValue(BlendShapePreset.E, w); //←旧式
        total -= w;

        w = total * GetWeight(nodeO);
        target.ImmediatelySetValue(BlendShapePreset.O, w); //←旧式
        target.Apply();
    }

f:id:roamschemer:20200806183701p:plain

これは一応そのままでも動きはするのですが、いつか消されるかもしれませんのでできれば使わない方がいいでしょう。きっと代替のメソッドが存在するはず…という事で、調べてみると以下のような公式ページを発見!ここを見れば一目瞭然だ! vrm.dev

…って、ImmediatelySetValueの使い方同じやんけー!!!ドキュメントが追い付いていないのか?まぁ仕方ないのでコードの中を確認します。

[Obsolete("Use BlendShapeKey.CreateFromPreset")]
public static void ImmediatelySetValue(this VRMBlendShapeProxy proxy, BlendShapePreset key, float value)
{
    proxy.ImmediatelySetValue(BlendShapeKey.CreateFromPreset(key), value);
}

Use BlendShapeKey.CreateFromPreset との事なので、CreateFromPreset を確認。

/// <summary>
/// PresetからBlendShapeKeyを生成
/// </summary>
/// <param name="preset"></param>
/// <returns></returns>
public static BlendShapeKey CreateFromPreset(BlendShapePreset preset)
{
    return new BlendShapeKey(preset.ToString(), preset);
}

なるほど。BlendShapePreset クラスを BlendShapeKey クラスに変換してくれるメソッドのようです。そして ImmediatelySetValue はオーバーロードで別のメソッドが存在しています。

/// <summary>
/// Immediately SetValue
/// </summary>
/// <param name="key"></param>
/// <param name="value"></param>
public void ImmediatelySetValue(BlendShapeKey key, float value)
{
    if (m_merger != null)
    {
        m_merger.ImmediatelySetValue(key, value);
    }
}

こちらは BlendShapeKey クラスを引数にとる形。ということは…こういう事ですね。

    void LateUpdate()
    {
        var total = 1.0f;

        var w = total * GetWeight(nodeA);
        //target.ImmediatelySetValue(BlendShapePreset.A, w); //←旧式
        target.ImmediatelySetValue(BlendShapeKey.CreateFromPreset(BlendShapePreset.A), w); //←新式
        total -= w;

        w = total * GetWeight(nodeI);
        //target.ImmediatelySetValue(BlendShapePreset.I, w);
        target.ImmediatelySetValue(BlendShapeKey.CreateFromPreset(BlendShapePreset.I), w);
        total -= w;

        w = total * GetWeight(nodeU);
        //target.ImmediatelySetValue(BlendShapePreset.U, w);
        target.ImmediatelySetValue(BlendShapeKey.CreateFromPreset(BlendShapePreset.U), w);
        total -= w;

        w = total * GetWeight(nodeE);
        //target.ImmediatelySetValue(BlendShapePreset.E, w);
        target.ImmediatelySetValue(BlendShapeKey.CreateFromPreset(BlendShapePreset.E), w);
        total -= w;

        w = total * GetWeight(nodeO);
        //target.ImmediatelySetValue(BlendShapePreset.O, w);
        target.ImmediatelySetValue(BlendShapeKey.CreateFromPreset(BlendShapePreset.O), w);
        target.Apply();
    }

これで旧型式警告は出なくなります。 同じく FaceChanger クラスも以下のようにしておきます。文字列で指定している箇所も enum 型を使用しておいたほうが無難でしょうね。

public class FaceChanger : MonoBehaviour {
    VRMBlendShapeProxy proxy;

    void Start() {
        proxy = GetComponent<VRMBlendShapeProxy>();
    }
    //表情を変えるイベントをここで受け取る.
    public void OnCallChangeFace(string str) {
        switch (str) {                                    //受け取ったメッセージが
            case "eye_close@unitychan":                 //「目を閉じる」であれば
                //proxy.ImmediatelySetValue("BLINK", 1);   //目を閉じる
                proxy.ImmediatelySetValue(BlendShapeKey.CreateFromPreset(BlendShapePreset.Blink), 1);
                break;
            case "smile3@unitychan":                    //「笑顔」であれば
                //proxy.ImmediatelySetValue("JOY", 1);     //笑顔
                proxy.ImmediatelySetValue(BlendShapeKey.CreateFromPreset(BlendShapePreset.Joy), 1);
                break;
            case "conf@unitychan":                      //「怒る」であれば
                //proxy.ImmediatelySetValue("ANGRY", 1);   //怒る
                proxy.ImmediatelySetValue(BlendShapeKey.CreateFromPreset(BlendShapePreset.Angry), 1);
                break;
            case "default@unitychan":                   //それ以外なら
                //proxy.ImmediatelySetValue("BLINK", 0);   //初期化
                //proxy.ImmediatelySetValue("JOY", 0);
                //proxy.ImmediatelySetValue("ANGRY", 0);
                proxy.ImmediatelySetValue(BlendShapeKey.CreateFromPreset(BlendShapePreset.Blink), 1);
                proxy.ImmediatelySetValue(BlendShapeKey.CreateFromPreset(BlendShapePreset.Joy), 1);
                proxy.ImmediatelySetValue(BlendShapeKey.CreateFromPreset(BlendShapePreset.Angry), 1);
                break;
        }
    }
}

これでOKです。この警告が出たおかげで表情などをどうやって操作するのかをより理解できたのでラッキーでした!

録画する

さて、躍らせたはいいが録画はどうするんだ?と思ったら、やっぱり便利なのがありました。その名も Unity Recorder。簡単に録画することができます。詳しくは以下記事などをご覧ください。 qiita.com

パッケージインストールで謎エラー

さて、簡単にできる事ほどエラーに巻き込まれるのはいつもの事。今回も以下謎エラーが発生しました。上記 Unity Recorder だけでなくどんなパッケージをインストールしようとしてもこのエラーでインストールできず。ググっても有力な情報が出てこなくてお手上げかと思ったんですが、なんかいつのまにかエラーが出なくなりました。やったことと言えば Unity 再起動とプレイボタンを押したぐらいで、以降再現も出来なかったのでよくわかりませんが、いつか誰かの役に立つ情報かもしれないのでここに残しておきます。

f:id:roamschemer:20200806223333p:plain

10452:error:1408F119:SSL routines:ssl3_get_record:decryption failed or bad record mac:c:\users\vssadministrator.nexe\12.2.0\deps\openssl\openssl\ssl\record\ssl3_record.c:667:

踊る夏狂乱

f:id:roamschemer:20200806233956p:plain …ということで、躍らせて録画してみました!実況プレイのお供として作成したはずのランダムツールが、姿かたちを変えてこうやって踊っているのを見るとなんか感慨深いものがありますね。ほんとどうしてこうなった…

www.youtube.com

アプリで見ると断然高画質だよ!New Unity Project2.exeを起動してみよう(適当なexe名にしちゃったごめん) github.com

…ということで、放浪軍師のアプリ開発局~ Unity 編 ~スタートです!!!これからもよろしくね!!!がんばるぞー!!!