MRが楽しい

MRやVRについて学習したことを書き残す

Blender3.0で利用可能なpythonスクリプトを作る その136(正規表現の指定でオブジェクトの参照リストを取得する)

本日は Blender の技術調査枠です。
Blender3.0で利用可能なpythonスクリプトを作ります。

正規表現の指定でオブジェクトの参照リストを取得する

python で文字列が正規表現に完全一致するか確認するには re.fullmatch を利用します。
docs.python.org

オブジェクト名は bpy.types.Object の name から取得可能です。
docs.blender.org

サンプルスクリプト

以下のサンプルスクリプトを作成しました。
arg_pattern 引数の正規表現に完全一致するオブジェクト名を持つオブジェクトの参照リストを返します。
・Script_get_objectlist_patternmatch.py

# bpyインポート
import bpy
# 正規表現を利用するためインポート
import re

# 指定の正規表現とオブジェクト名が一致したオブジェクトの参照リストを返す
def select_object_patternmatch(arg_pattern:str) -> list:
    """指定の正規表現とオブジェクト名が一致したオブジェクトの参照リストを返す

    Keyword Arguments:
        arg_pattern {str} -- 指定文字列

    Returns:
        list -- オブジェクトの参照リスト
    """
    
    # 処理対象のオブジェクトリストを作成する
    targetobject_list = []

    # シーン内の全オブジェクトを走査する
    for check_obj in bpy.context.scene.objects:
        # オブジェクト名が指定の正規表現と一致するか
        if re.fullmatch(arg_pattern, check_obj.name):
            # 一致すれば対象とする
            targetobject_list.append(check_obj)

    # リストを返却する
    return targetobject_list

# 関数の実行例
# 指定の正規表現とオブジェクト名が一致したオブジェクトのリストを取得する
# ("Cube.00~ のオブジェクト名を持つオブジェクトを指定")
targetobj_list = select_object_patternmatch(r"Cube\.00.+")

# シーン内の全オブジェクトを走査する
for check_obj in bpy.context.scene.objects:
    # 一旦すべてのオブジェクトを非選択状態にする
    check_obj.select_set(False)

# 取得した対象オブジェクトを走査する
for check_obj in targetobj_list:
    # 全ての対象オブジェクトを選択状態にする
    check_obj.select_set(True)

・実行前

・実行後

SDキャラクターの3Dモデルを作成する その39(シェイプキーを持つメッシュにミラーモディファイアを適用する)

本日はSDキャラクターの作成枠です。
SDホロラボちゃんの3Dモデル作成を進めていきます。
今回はシェイプキーを持つメッシュにミラーモディファイアを適用します。

シェイプキーを持つメッシュにミラーモディファイアを適用する

左右非対称なシェイプキーを作成するため、シェイプキーを持つ頭部オブジェクトにミラーモディファイアを適用します。
ただしデフォルトではシェイプキーを持つメッシュオブジェクトにはジオメトリ変更系のモディファイアは適用できません。

今回、ミラーモディファイアに関しては下記の Lazy Shapekeys アドオンを使うことでモディファイアの適用ができました。
bluebirdofoz.hatenablog.com

後々の再編集を容易にするため、ミラーモディファイアを適用する前のオブジェクトについても[オブジェクト -> オブジェクトを複製]で複製を残しておきました。

左右非対称なシェイプキーを作成する

モディファイアを適用したオブジェクトで左右非対称なシェイプキーを追加で作成しました。
以下は左右片目のみの瞬きモディファイアです。

片目のみの瞬きシェイプキーは、両目の瞬きシェイプキーを元に片方の目の変形を戻す方法で作成しました。

変形を戻したい頂点を選択した状態でメニューから[頂点 -> シェイプキーからブレンド]を実行します。

詳細パネルで[シェイプ]で[Basis(基本形)]を指定し、[ブレンド]を[1.0]、[追加]のチェックを外します。
これで選択した部分の頂点の変形のみを元に戻すことができます。

SDキャラクターの3Dモデルを作成する その38(喜怒哀楽とリップシンクのシェイプキー作成)

本日はSDキャラクターの作成枠です。
SDホロラボちゃんの3Dモデル作成を進めていきます。
今回は前回記事に引き続き、喜怒哀楽とリップシンクのシェイプキー作成を行いました。

シェイプキーの選定

今回のモデルでは以下の VRM 形式のプリセットに対応した表情のシェイプキーを作成しました。
vrm.dev

A, I, U, E, O
リップシンクのあ・い・う・え・おの音声に対応します。

Blink
まばたきです。

Blink_L, Blink_R
片目だけつぶる動作です。

Joy, Angry, Sorrow, Fun
喜怒哀楽です。

LookUp, LookDown, LookLeft, LookRight
目線がモーフで制御されているタイプのモデルで使います。

喜怒哀楽

以下の喜怒哀楽に当たる4種類のシェイプキーを作成しました。

Fun(喜び)


Angry(怒り)


Sorrow(哀しみ)


Joy(楽しみ)


リップシンク

以下のリップシンク用の「あいうえお」の5種類のシェイプキーを作成しました。

「あ」


「い」


「う」


「え」


「お」

瞬き

以下の両目、片目の瞬きのシェイプキーを作成しました。

瞬き(両目)


瞬き(左目)


瞬き(右目)

目線の動き

以下の4種類の目線の動きのシェイプキーを作成しました。

上向き目線


下向き目線


右向き目線


左向き目線


各表情を作成する際の要点は以下の記事にまとめているのでこちらも参考にしてください。
bluebirdofoz.hatenablog.com

シェイプキーを設定したオブジェクトにモディファイアを適用する(Lazy Shapekeys アドオン)

本日は Blender の技術調査枠です。
Blender の Lazy Shapekeys アドオンを使い、シェイプキーを設定したオブジェクトにモディファイアを適用してみます。

Lazy Shapekeys のインストール

Lazy Shapekeys は以下のいずれかページから取得できます。
bookyakuno.gumroad.com
blendermarket.com

上記のページから lazy_shapekeys_bxxx_verx.zip ファイルをダウンロードしました。

Blender を起動してメニューから[編集 -> プリファレンス]を開きます。

プリファレンスダイアログの[アドオン]タブを開き、[インストール]を選択します。

先ほどダウンロードした zip ファイルを選択して[アドオンをインストール]を実行します。

Lazy Shapekeys がアドオンの一覧に表示されるのでチェックを入れて有効化します。

Lazy Shapekeys を有効化していると[オブジェクトデータ]タブ内の[シェイプキー]パネルが以下のような見た目になります。

シェイプキーを設定したオブジェクトにモディファイアを適用する

Lazy Shapekeys を使ってシェイプキーを設定したオブジェクトにモディファイアを適用してみます。

[データプロパティ]タブの[シェイプキー]パネルにあるプルダウンを開きます。
[モディファイアを適用(シェイプキー保持)]を選択します。

適用するモディファイアを選択し、[OK]で実行します。
今回はミラーモディファイアを選択します。

これでシェイプキーを維持したままミラーモディファイアを適用できました。

VContainerのHelloWorldを試す その2(ユーザの入力を受けて実行する)

本日は VContainer の小ネタ枠です。
VContainer の HelloWorld を試したので記事に残します。
今回はユーザの入力を受けて、ロジックが実行されるケースについてです。

前回記事

以下の前回記事の続きです。
bluebirdofoz.hatenablog.com

6. 制御の反転(IoC=Inversion Of Control)

次に Unity の Button コンポーネントを例にユーザの入力を受けて、ロジックが実行されるケースを考えます。
以下のような View コンポーネントを作成します。

using UnityEngine;
using UnityEngine.UI;

public class HelloScreen : MonoBehaviour
{
    public Button HelloButton;
}

View が入力を受けた際の処理については、以下のように仕事を委譲するクラスに記述します。
IStartable は Unity の Start のライフサイクルで処理を行うためのインタフェースです。

using VContainer.Unity;

namespace MyGame
{
    // UpdateのEntryPointを作るため、IStartableのインタフェースを実装する
    public class GamePresenter : IStartable
    {
        readonly HelloWorldService helloWorldService;
        readonly HelloScreen helloScreen;

        public GamePresenter(HelloWorldService helloWorldService, HelloScreen helloScreen)
        {
            this.helloWorldService = helloWorldService;
            this.helloScreen = helloScreen;
        }

        // Startのタイミングで呼ばれる
        public void Start()
        {
            // ボタンのクリックリスナーにボタン押下時の処理を設定する
            helloScreen.HelloButton.onClick.AddListener(() =>
            {
                helloWorldService.Hello();
            });
        }
    }
}

シーン上にボタンを配置します。
Hierarchy に UI -> Button でボタンオブジェクトを作ります。

作成したボタンオブジェクトに先ほどの View コンポーネントをアタッチして Button への参照を設定します。

参照関係を解決するため、View コンポーネントもDIコンテナに登録する必要があります。
LifetimeScope を継承したクラスを以下の通り更新してDIコンテナへコンポーネントの登録を追加します。

using UnityEngine;
using VContainer;
using VContainer.Unity;

namespace MyGame
{
    public class GameLifetimeScope : LifetimeScope
    {
        [SerializeField]
        private HelloScreen helloScreen;

        protected override void Configure(IContainerBuilder builder)
        {
            // HelloWorldServiceをDIコンテナに登録する
            builder.Register<HelloWorldService>(Lifetime.Singleton);
            // GamePresenterをVContainerのEntryPointに登録する
            builder.RegisterEntryPoint<GamePresenter>();
            // HelloScreenコンポーネントをDIコンテナに登録する
            builder.RegisterComponent<HelloScreen>(helloScreen);
        }
    }
}

コンポーネントへの参照を Inspector に設定して準備は完了です。

実際にシーンを再生して動作を確認します。
ボタンを押下すると処理が呼び出されることが確認できます。

VContainerのHelloWorldを試す その1(DIコンテナへの登録)

本日は VContainer の小ネタ枠です。
VContainer の HelloWorld を試したので記事に残します。

VContainerのHelloWorld

以下のドキュメントページで手順がまとめられています。
本記事ではこちらの手順をキャプチャを交えて記事に残します。
vcontainer.hadashikick.jp

VContainer のインストール手順で作成した以下のプロジェクトを利用します。
bluebirdofoz.hatenablog.com

HelloWorld

本記事では以下の VContainer の基本的な使い方を試します。
・1つのコンテナとなる LifetimeScope を継承したコンポーネントを作り、シーンに置きます
・LifetimeScope のメソッドをオーバライドして、依存関係を管理したいオブジェクトを登録します
・LifetimeScope を使って Player Loop System のスケジュールの動作を試します

1. 他オブジェクトへ機能を委譲するクラスをつくる

一例として「Hello World」のログを出力するだけのクラスを作成します。
これがロジックを保持する純粋な C# のクラスになります。

namespace MyGame
{
    public class HelloWorldService
    {
        public void Hello()
        {
            UnityEngine.Debug.Log("Hello world");
        }
    }
}


2. DIの設定を施す(Composition Root)

先ほどのクラスをDIコンテナに登録して参照関係を作ります。
コンテナをつくるため、LifetimeScope を継承した以下のコンポーネントを作成します。

using VContainer;
using VContainer.Unity;

namespace MyGame
{
    public class GameLifetimeScope : LifetimeScope
    {
        protected override void Configure(IContainerBuilder builder)
        {
            // HelloWorldServiceをDIコンテナに登録する
            builder.Register<HelloWorldService>(Lifetime.Singleton);
        }
    }
}


3. 作成したLifetimeScopeをGameObjectにアタッチ

LifetimeScope を継承したコンポーネントをシーンに配置します。
Hierarchy に Create Empty でGameObjectを作ります。

適当な名前を設定しコンポーネントをアタッチします。

4. HelloWorldService を呼び出す

次に以下のように、HelloWorldService へ仕事を委譲するクラスを作成します。

using VContainer;
using VContainer.Unity;

namespace MyGame
{
    public class GamePresenter
    {
        readonly HelloWorldService helloWorldService;

        public GamePresenter(HelloWorldService helloWorldService)
        {
            this.helloWorldService = helloWorldService;
        }
    }
}

LifetimeScope で登録したクラスたちは自動的に参照関係が構築されます。
上記のクラスはコンストラクタで HelloWorldService を引数にとっています。
DIコンテナはこのような定義をみつけて、自動的に必要なオブジェクトをコンストラクタに渡してくれます。

5. 独自のC#クラスをUnityのPlayerLoopSystem上で走らせる

このままでは仕事を委譲するクラスで処理を実行するタイミングがないため、処理の実行タイミングを実装してみます。

Unity では Unity のライフサイクルイベント上でコードを実行するのが一般的です。
通常は MonoBehaviour の Start / Update などを使用します。
このような処理の起点のことを、VContainerでは 「EntryPoint」と呼びます。

VContainer では特別な Interface を実装したクラスを登録することで MonoBehaviour を使用せずに EntryPoint を作ることができます。
以下の通り、仕事を委譲するクラスを更新します。

using VContainer.Unity;

namespace MyGame
{
    // UpdateのEntryPointを作るため、ITickableのインタフェースを実装する
    public class GamePresenter : ITickable
    {
        readonly HelloWorldService helloWorldService;

        public GamePresenter(HelloWorldService helloWorldService)
        {
            this.helloWorldService = helloWorldService;
        }

        // Updateのタイミングで呼ばれる
        public void Tick()
        {
            helloWorldService.Hello();
        }
    }
}

実際に Unity のライフサイクルイベントで動作させるには、RegisterEntryPoint メソッドで登録しておく必要があります。
LifetimeScope を継承したクラスを以下の通り更新してDIコンテナへの登録を追加します。

using VContainer;
using VContainer.Unity;

namespace MyGame
{
    public class GameLifetimeScope : LifetimeScope
    {
        protected override void Configure(IContainerBuilder builder)
        {
            // HelloWorldServiceをDIコンテナに登録する
            builder.Register<HelloWorldService>(Lifetime.Singleton);
            // GamePresenterをVContainerのEntryPointに登録する
            builder.RegisterEntryPoint<GamePresenter>();
        }
    }
}

実際にシーンを再生して動作を確認します。
Update のタイミングで処理が呼び出されていることが確認できます。

この VContainer の EntryPoint はドメインロジックとプレゼンテーションロジックを分離するために役に立ちます。
記事が長くなったので分けます。次は View からユーザの入力を受けて処理を実行するケースについてです。
bluebirdofoz.hatenablog.com

OpenUPMを使ってUnityプロジェクトにUPM経由でVContainerをインポートする

本日は VContainer の小ネタ枠です。
OpenUPM を使って Unity プロジェクトに UnityPackageManager 経由で VContainer をインポートする手順を記事にします。

VContainer

VContainer は Unity 上で動作するDIフレームワークです。
VContainer を用いることでオブジェクト同士の参照関係/所有関係を自由に構築したり、純粋な C# クラスのエントリポイントをつくることができます。
github.com

VContainer の公式のドキュメントページは以下を参照ください。
vcontainer.hadashikick.jp

OpenUPM に登録されている VContainer のページは以下になります。
openupm.com

OpenUPMを使ってVContainerをインポートする

VContainer は Unity 2018.4 以上の環境で動作します。
今回、サンプルプロジェクトとして Unity 2021.3 で新規プロジェクトを作成しました。

最初に OpenUPM をスコープレジストリに登録します。
メニューから[Edit -> ProjectSettings..]を開き、[Package Manager]タブを開きます。

[New Scope Registry]に OpenUPM の参照先の URL と VContainer の Scope を設定して[Save]します。

Name:任意の名前
URL:https://package.openupm.com
Scope:jp.hadashikick.vcontainer

次にメニューから[Window -> PackageManager]を選択して PackageManager ウィンドウを開きます。

[Packages]から[My Registries]を選択します。

VContainer のパッケージが表示されるので選択して[install]を実行します。

これで UPM 経由で VContainer を Unity プロジェクトにインポートできました。