勉強不足で至らんブログ

勉強不足ですが色々と書いていきます。

UnityのStreamingAssets内でPython環境構築してみた

Pythonで作られたライブラリなどをUnityでも流用したい!

ということで思いついたのがPythonに必要なファイルを全て StreamingAssets に突っ込む方式でした。

その前にクライアント/サーバーでやり取りしろよ!って話ではあります。はい。その通りです!ただスタンドアローンで動かしたかったんです!!

PythonC#のやり取りをする

github.com

C#との連携で他にもツールは出てきたのですが最終的にPythonのライブラリも使いたいと思っており、探すとPythonnetならできるという情報があったので選出しました。

GithubWikiに導入方法などは書いておりスムーズに導入できました。dllをダウンロードしてPluginsディレクトリに入れるだけですね。

中身を弄る必要があればGitHubから簡単にcloneしてdefineを弄ればUnityで動かすカスタムができる印象でした。

突っ込むPythonの環境を取得する

Pythonの環境はAnacondaで取得した環境をStreamingAssetsに入れることにしました。 Pythonのインストールパスを確認してから

Python
|---------DLLs
|---------Lib
|---------Scripts
|-たくさんのdllとpyファイル

をUnityのStreamingsAssetsに入れます。基本的に必要なものは上記でした。他のディレクトリは無くても大丈夫です。

Editorでは動きます。

Editor/Buildの違い

Unity Editorでは Plugins ディレクトリ以外にdllがあっても読み込んでくれます。そのため StreamingAssetsPythonのすべてを入れておくだけでPythonが動きます。しかし、BuildをしてStreamingAssetsにPythonを入れてもdllを検知できません。そのため、Pythonを動かすのに必要なdllをPluginsにいれて他の.pyなどをStreamingAssetsに入れることで解決しました。

そのためdllを全てPluginsに入れて他をStreamingAssetsに入れるようにします。細かいことを気にしなければ全部 PluginsStreamingAssets に入れてしまいます。

ちゃんと分けたい場合は

StreamingAssets

find . -name "*.dll" -type f | xargs rm

などをして必要な拡張子だけを残すようにします。 Plugins だと exepy など dll 以外を消す必要があります。(Windowsだと)

動かした結果

using周りは端折りますが以下のコードを動かしました。(.Net4.0環境)

public class PythonLifeCycle : IDisposable
    {

        public void Initialize()
        {
            
            var pythonHome = $"{Application.streamingAssetsPath}/PythonEnv";


            var scripts = $"{pythonHome}/Scripts";
            
            Environment.SetEnvironmentVariable("PATH", $"{pythonHome};{scripts}", EnvironmentVariableTarget.Process);
            Environment.SetEnvironmentVariable("DYLD_LIBRARY_PATH", $"{pythonHome}/Lib", EnvironmentVariableTarget.Process);

            PythonEngine.PythonHome = $"{pythonHome}";
            PythonEngine.PythonPath = $"{pythonHome}/Dlls;{pythonHome}/Lib;{pythonHome}/Lib/site-packages";
            
            PythonEngine.Initialize();
        }

        public void Dispose()
        {
            PythonEngine.Shutdown();
        }
private readonly PythonLifeCycle lifeCycle = new PythonLifeCycle();
        [SerializeField] private TextMeshProUGUI textUI;

        void Start()
        {
            lifeCycle.Initialize();

            RunPython();
        }

        private void RunPython()
        {
            using (Py.GIL())
            {               
                dynamic sysModule = Py.Import("sys");
                
                textUI.text = sysModule.version;
            }
        }

        private void OnDestroy()
        {
            lifeCycle.Dispose();
        }

シンプルにPythonの環境を構築してTextを出力するようにしています。

Editor Windows
f:id:MakeTake:20200906101448p:plain f:id:MakeTake:20200905224738p:plain

最低限Editor/Buildでの差は埋めれました。 他(WebGL/Android)はそのまま動かなかったので放置しています。一旦はWinのみで開発するつもりだったので。 触った感じだとデスクトッププラットフォームだと問題ないと思います。

WebGLで実行とエラーがでるのですがパッとうまい解決策が見つけれず放置 Androidでビルドしてgradleの処理の際にStreamingAssetsのファイルが多すぎてファイルをすべてgroovyのファイルに記述されていて、その記述されている数が多いことでエラーとなり失敗します。これはzipに纏めるなりの回避をすればなんとかなるとは思います。(メモ)

まとめ

今後はpythonのライブラリを導入してC#から使えるようにしていこうと思います。1プラットフォームでもPythonを内蔵して動いて良かったです。

magentaセットアップをwslでしたら躓いてDockerの良さを再認識したメモ

音の自動生成を試したい!

ということでgoogleさんが作っているmagentaというツールを使おうと思いセットアップをしてみました。結構躓いたのでメモ

github.com

↓の手順で触っていきました。

qiita.com

1 Windowsで行うためwslの設定から

www.kkaneko.jp

pipでインストールすると新しいtensorflowのサポートがされておらず以下の公式サイトをみつつ情報をあつめ進めていきました。

www.tensorflow.org

2 ハンズオンを触っていく

ここから出会ったエラーと解決を書いていますが 3 本番はここからだった へ飛べばスキップできるものです。さくさくセットアップしたい場合は飛ばしてください。

省略可能領域

python scripts/data/create_note_sequences.py を行おうとするとそもそもtensorflowのバージョンに合っていないので

    def log_statistics_list(stats_list, logger_fn=tf.logging.info):
AttributeError: module 'tensorflow' has no attribute 'logging'

みたいなエラーとなる

pipには既にtensorflow1系は提供されていないのでpipでインストールした場所 /home/{ユーザー名}/.local/lib/python3.8/site-packages/magenta/ に行って中身の必要箇所を置き換える手段をとりました

すると

    class NoteSequenceRecordWriter(tf.python_io.TFRecordWriter):
AttributeError: module 'tensorflow' has no attribute 'python_io'

という別のエラーが出てきたので、さっきの箇所は通ったようです、同じようにエラーに対応していきます。

基本的には tf.hogetf.compat.v1.hoge にすれば通ります。

この調子で進めると

‘‘‘ AttributeError: module 'tensorflow' has no attribute 'contrib' ‘‘‘

というエラーで行き詰ってしまいました。調べると Migrate your TensorFlow 1 code to TensorFlow 2  |  TensorFlow Core

一切のサポートが切られていて、tensorflow-addonというのも試しましたがうまくいきませんでした。

3 本番はここからだった

そこで、ふとmagentaのReleaseを確認したらtensorflow2のサポートのsourcecodeが貼ってあり

Releases · magenta/magenta · GitHub

ひとまず Magenta v2.0.1 をダウンロードして、pipでインストールしていた場所 /home/{ユーザー名}/.local/lib/python3.8/site-packages/magenta/ で中身を全部置き換えると違うエラーとなり進んだようです。

pipでインストールしたmagenta自体を置き換えたので 2 ハンズオンを触っていく の労力は意味なかったようです。pipのパッケージ更新してくれんかな…

ModuleNotFoundError: No module named 'tensorflow_probability'

pipで tensorflow_probability をインストールすれば解決

ModuleNotFoundError: No module named 'tensor2tensor'

pipで tensor2tensor をinstallしようとすると

Collecting pygame
  Using cached pygame-1.9.6.tar.gz (3.2 MB)
    ERROR: Command errored out with exit status 1:

となりpygameというものでコケてしまう 調べると Python - pythonのライブラリをインストールしたいのですが、エラーが出てしまい進みません|teratail

を発見、Python3.8を使っているから良くないみたいなのでpyenvを使ってPython3.7に切り替えると無事成功

そして最初のコマンドは通ったので python scripts/data/convert_to_melody_dataset.py --config attention_rnn を実行していく

AttributeError: module 'tensorflow' has no attribute 'app'

が再び浮上、今回はmagenta_sessionの方で出ているようだったのでmagenta_sessionのディレクトリを全検索して tf.app から tf.compat.v1.app で対処

magenta_session/convert_to_melody_dataset.py at master · icoxfog417/magenta_session · GitHub

    pipeline_instance = md.get_pipeline(config, md.FLAGS.eval_ratio)

get_pipeline が無いと言われるので調べると

    pipeline_instance = md.melody_rnn_pipeline.get_pipeline(config, md.FLAGS.eval_ratio)

にすることで解決

TypeError: 'float' object is not iterable

magenta_session/convert_to_melody_dataset.py at master · icoxfog417/magenta_session · GitHub

        md.pipeline.tf_record_iterator(tgt.SEQUENCE_FILE, pipeline_instance.input_type),

tf_record_iteratorは使われなくなるそうで今後は

tf.data.TFRecordDataset  |  TensorFlow Core v2.3.0

を使うようにするのが良いそうでした

        tf.data.TFRecordDataset(tgt.SEQUENCE_FILE, pipeline_instance.input_type),

に置き換えて通った

2020-08-10 19:54:24.507256: E tensorflow/stream_executor/cuda/cuda_driver.cc:314] failed call to cuInit: CUDA_ERROR_NO_DEVICE: no CUDA-capable device is detected

が出てきて初CUDA実行の気配

ここで色々躓いて、そもそも環境のセットアップがきちんと行えておらずwsl側でgpuを認識していないようでした。driverのインストール/アンインストールなどもしたのですが改善せず。

待ってました CUDA on WSL2 - Qiita

を頼りに色々やってみたのですがWindowsがプレビューじゃないからぽいのでWindowsのダウンロードを待ちながらmagentaにはdockerもあるので、そちらを試したところ秒で処理が動いたのっでもうDockerでいいんじゃないかと思いました。wslでgpuを使いたい気持ちがあったものの結構格闘してすり減っていたのでDocker万歳ということで動かしていきます。(Windowsプレビューでも動かなかったのでもうわからないからやっぱDocker最高)

追記 (2020/08/12) wslのバージョンが足りてなかった、MicrosoftStoreからインストールが何故かできなかったのでマニュアルインストールの手段をとる docs.microsoft.com

dockerのセットアップがうまくいかず

Windows Subsystem for Linux(WSL)で Docker を利用する - simplestarの技術ブログ

Repository configuration | nvidia-docker

この2つの記事を参考にセットアップして動くことを確認

docker runするとエラーが出ていて

cgroups: cannot find cgroup mount destination

Docker command でドッカー練習する時のメモ | Hapicode

を参考に解決

TensorFlowからGPUが認識できているかを2行コードで確認する - 動かざることバグの如し

などをみてgpuの認識を確認

sudo mkdir /sys/fs/cgroup/systemd
sudo mount -t cgroup -o none,name=systemd cgroup /sys/fs/cgroup/systemd

すると動いた

以下のサイトでDockerfileのベースを探すとcuda周りのセットアップをしなくていいので最高

docs.nvidia.com

おわり

セットアップまでにかなり引っかかるポイントがあり折れました。自分の環境、セットアップの順番で起こったことなので他の人の環境だとまた違うことが起こる可能性は高いですが、何か参考になればと思います。(最初からDocker使う方が時短にはなる)

また個人的にjetbrains系のエディターをかなりつかうのでwsl環境と同期する方法があるのか調べると Configure an interpreter using WSL - Help | PyCharm あったのでjetbrainsを使ってwslで環境構築が終わった人

SAColliderBuilderでNull Reference Exceptionが出てしまった時の応急処置

SA Mesh Collider Builderを使おうとしたらNullExceptionで処理が止まってしまった

超絶便利なSAColliderBuilderを使おうとしたらエラーで怒られました

assetstore.unity.com

上記のAssetはMeshに対して当たり判定を良い感じにつけてくれるものなのですが、AssetStoreにある別の建物などの環境Assetに対して使う際にエラーがでました。

その時のエラー箇所が SAMeshColliderEditorCommon.cs

 public static string GetSAMeshColliderName_Material( Material[] materials, int index )
    {
                // ↓ ここ
        if( materials != null && index < materials.Length && !string.IsNullOrEmpty(materials[index].name)) {
            return materials[index].name;
        }

        return "Mesh." + index.ToString("D8");
    }

942行目あたりのif文だったので、ここだけ軽くチェックしたら materials[index] がnullになるパターンがあるようでした。Assetの階層構造によるものなのかは深くチェックしていないのでわかりません。そのためif文に materials[index] != null を追加して以下のようにしました。

 public static string GetSAMeshColliderName_Material( Material[] materials, int index )
    {
        if( materials != null && index < materials.Length && materials[index] != null && !string.IsNullOrEmpty(materials[index].name)) {
            return materials[index].name;
        }

        return "Mesh." + index.ToString("D8");
    }

という風にしました。これで処理が終わり無事Colliderが付けられたので応急処置としては大丈夫そうです。

f:id:MakeTake:20200808195743p:plain

VRMをPartyParrot風に表示できるPartyParrotVRMを作ってみた

PartyParrotVRM

ネット上で時たまみるPartyParrotというgifがあります

f:id:MakeTake:20200618233333g:plain

これをVRM…人型に適応したら面白いのでは?と思って適応させたサイトを作ってみました

mizotake.github.io

機能

ここのREADMEというのに説明を大体書いていますが

PartyParrotVRM/README.md at master · MizoTake/PartyParrotVRM · GitHub

好きなVRMドラッグ&ドロップすることでPartyParrot風に表示します

change

また簡易的なフェイストラッキングをしています

tracking

  • Download Gif

VRMを移動させてPartyParrot風のgifをダウンロードします downloadgif

↓ ダウンロードしたgifがこちら

demo

顔のトラッキングを確認することができます

  • Camera List

ブラウザが認識しているカメラから使用するものを決めれます

ちょいちょい触ったりしないとわかりづらいですが実装しているのはこんな感じです

まとめ

Unityとjsの連携を結構書いたり顔のトラッキングからモデルへの反映を書いてみたりと意外と面白く実装していきました 全体的に雑な実装になっていますが大まかに実装して満足度高く…一旦の区切りとしています…

細かい挙動まできちんとこだわないと自分の思う面白さ表現できないと思ったので時間があればまた触ろうかと思います

Cinemachine Sampleを触ったメモ

Cinemachine v2.6 Sampleが公開された

blogs.unity3d.com

の記事をみてCinemachineって結構いろんなことができることを知ったのでSampleを触ることにしました。元々Cinemachineに興味はあったが触ってなかったのでSampleを触って面白かったものをメモりました。

v2.6のサンプルですが新機能の紹介ではありませんしComponentのパラメーターを細かく紹介することもないのでご了承ください。

Cinemachineのベースの考え方

CinemachineBrainというコンポーネントが実際にUnityのCameraコンポーネントを動かすものです。

映し出す位置の指定を CinemachineVirtualCamera などの仮想カメラとなるコンポーネントが指定して、それをCinemachineBrainが優先度などのパラメーターを加味してCameraに映し出すような作りになっています。 仮想カメラはCameraコンポーネントはありませんが仮想カメラのTransform等をみてBrainが映し出します。

CinemachineVirtualCamera 以外にも

  • CinemachineStateDrivenCamera
  • CinemachineFreeLook

などの複数のコンポーネントを使い分けつつ仮想カメラを配置して挙動を制御していきます。

StateDrivenCamera

sample

概要

Animatorの状態遷移によってCameraを切り替えるもの

  • 歩いているとマウスで操作したカメラワークになる
  • 走っているとキャラクターを下から見上げる角度に変わる

をComponentの設定で指定できるもの

各オブジェクトの設定

Hierarchyは

├── MainCamera
├── 3rdPersonController (子オブジェクト省略)
├── Directional Light
├── Environment (子オブジェクト省略)
└── CM StateDrivenFreeLook
    ├── CM FreeLook
    └── CM FreeLook sprint

という風になっています。

MainCameraにはCinemachineを使う際の中枢となる CinemachineBrain が追加されています。

CM StateDrivenFreeLook はAnimatorのどのStateを見てVirtualCameraの切り替えを行うのか設定できます。

UnityEditorのMenuにある Cinemachine > Create State-Driven Camera がベースとなっています。

menu

シンプルな構成を確認したいときは新しいSceneを作成してCreateすると良いと思います。

CM StateDrivenFreeLook には対象となるAnimatorとStateによってどのCameraを使用するのか設定しています。

StateDrivenInspector

Custom BlendsというところにScriptableObjectが設定されていますが、これはカメラを切り変える際のパラメーターが設定できます。

CustomBlends

仮想カメラが切り替わる際のEasingや秒数が指定できます。また特定のカメラから特定のカメラへの遷移も個別に設定できるようになっています。

CinemachineStateDrivenCamera がメインの処理となりますが同じGameObjectに CinemachineCollider というコンポーネントがあります。Cameraに収める対象のオブジェクトが壁などでCameraに映らないようなことが起こると自動的にCameraに収めてくれるコンポーネントになります。

【Unity】【Cinemachine】対象が障害物に隠れた時に対象が映る位置にカメラをいい感じに移動するCinemachine Collider

というブログなどを見るとパラメーターがどう反映するのか確認できます。

CM FreeLookCM FreeLook sprintCinemachineFreeLook Componentを付けてパラメーターが違うオブジェクトになります。それにより見え方が変わっています。

Impulse

Impulse

概要

衝撃を通知するオブジェクトに近づくとカメラが距離や威力によって揺れるものです。

各オブジェクトの設定

Hierarchyは

├── MainCamera
├── Directional Light
├── Ground
├── Player (子オブジェト省略)
├── BouncingBall
├── Magic Cube (子オブジェト省略)
└── CM PlayerCam

MainCameaには相も変わらず CinemachineBrain です。そしてCM PlayerCam には CinemachineVirtualCamera という標準な構成です。

今回のキモである衝撃の扱いについては、BouncingBallとCM PlayerCamにあるコンポーネントで表現できます。

BouncingBallには Cinemachine Collision Impulse Source

Source

CM PlayerCamには Cinemachine Impulse Listener が設定されています。

Listener

Impulseの設定はSourceの方で設定します。

Raw Signalでどういう風に揺らすのか設定できます。SignalSourceAsset.csというclassを継承したScriptableObjectを設定すれば、XYZ毎にアニメーションカーブを設定して揺れ方を変えることが可能です。

Amplitude GainFrequency Gain でカメラの揺れる威力などを調整できます。

CameraMagnets

Impulse

概要

設定したObjectが範囲内にいると、元々対象としているObjectも含めてカメラに収めるようにする。 また、壁(指定した範囲)外を表示しないように収めている。

各オブジェクトの設定

Hierarchyは

├── CamCollider
├── MainCamera
├── Environment (子オブジェト省略)
├── CM vcam2D
├── Directional Light (1)
├── 3rdPersonController (子オブジェト省略)
├── TargetGroup1
└── CameraMagnets
    ├── CameraMagnet
    ├── CameraMagnet(1)
    └── CameraMagnet(2)

となっています。 まず指定した範囲を収めている処理から説明します。 CamColliderにて Polygon Collider 2D で壁をなぞるように値を入れています。

collider

上記のように設定したColliderをCM vcam2Dに追加されている Cinemachine Confiner のInspectorに設定します。

confiner

これによってColliderで設定した範囲内をカメラに収めることができます。

次に設定したObjectに近づいた時にカメラをフォーカスさせる方法です。 Cinemachineで用意されている CinemachineTargetGroup というコンポーネントと自前で少しだけcsを書いているようです。

targetGroup

CinemachineTargetGroup に近づいたらカメラに収めたい対象のTransformを入れておきます。

CameraMagnetTargetController.cs

using System.Collections;
using System.Collections.Generic;
using Cinemachine;
using UnityEngine;

public class CameraMagnetTargetController : MonoBehaviour
{
    public CinemachineTargetGroup targetGroup;

    private int playerIndex;
    private CameraMagnetProperty[] cameraMagnets;
    // Start is called before the first frame update
    void Start()
    {
        cameraMagnets = GetComponentsInChildren<CameraMagnetProperty>();
        playerIndex = 0;
    }

    // Update is called once per frame
    void Update()
    {
        for (int i = 1; i < targetGroup.m_Targets.Length; ++i)
        {
            float distance = (targetGroup.m_Targets[playerIndex].target.position - 
                              targetGroup.m_Targets[i].target.position).magnitude;
            if (distance < cameraMagnets[i-1].Proximity)
            {
                targetGroup.m_Targets[i].weight = cameraMagnets[i-1].MagnetStrength * 
                                                  (1 - (distance / cameraMagnets[i-1].Proximity));
            }
            else
            {
                targetGroup.m_Targets[i].weight = 0;
            }
        }
    }
}

CameraMagnetProperty.cs

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

[ExecuteInEditMode]
public class CameraMagnetProperty : MonoBehaviour
{
    [Range(0.1f, 50.0f)]
    public float MagnetStrength = 5.0f;
    
    [Range(0.1f, 50.0f)]
    public float Proximity = 5.0f;

    public Transform ProximityVisualization;

    [HideInInspector] public Transform myTransform;

    void Start()
    {
        myTransform = transform;
    }
    void Update()
    {
        if (ProximityVisualization != null)
            ProximityVisualization.localScale = new Vector3(Proximity * 2.0f, Proximity * 2.0f, 1);
    }
}

Cinemachineのコンポーネントではない追加されていたcsは上記です。要約すると操作Objectからの距離をみてTargetGroupに設定してあるTargetのWeightを変更するものになります。

Weightの数値が上がることによってCameraに収める対象物として認識されます。それによってgifのような動作となります。

まとめ

かなりお手軽にカメラワークを作ることが可能なことがわかりました。今後個人で何かするなら絶対にいれるか…とは思っています。 PostProcessingについても対応するComponentがあるのでUnity機能を組み合わせるのにも問題ない気がしています。

OBS StudioでChromeのウィンドウキャプチャ方法

ブラウザの描画をGPUを使ったままにしたい

Chromeのブラウザをウィンドウキャプチャしようとしたときにもしかしたら動かないかもしれません。

以前以下の記事にて

maketake.hatenablog.com

これはChromeの設定にある ハードウェア アクセラレーションが使用可能な場合は使用する という項目をオフにすることで解決ができる

と書きましたがオフにしなくても動く方法がありました。

chrome://flags/ をurlを入れるところにコピペして開いてください。

Choose ANGLE graphics backend という項目の選択肢を OpenGL にします。 f:id:MakeTake:20200526123859p:plain

再起動を求められるのでChromeを再起動します。

するとOBS Studioでウインドウキャプチャが可能になります。

描画にGPUを使った方が負荷としては軽いと思うのでできるだけこちらで使っていこうかなと思いました。 Chromeの設定で完結するので楽ですね。

リアルタイムに声をテキストとして表示する「Zimack」というサイトを作った

OBSで配信するときに字幕に特化したサイトがあればいいのに

という気持ちでちょっとだけ作った

mizotake.github.io

https://mizotake.github.io/zimack/resources/demo.gif

1日くらいで作ってるのでまだまだ機能が足りないし作っててUIミスったなと思っているのですが、、、いずれ…いずれな…

とりあえず、マイクの許可与えればテキストに起こしてくれるし、画面クリックすればオプションで少しだけカスタマイズできるやつ

https://mizotake.github.io/zimack/resources/img1.jpg

思いついたのはTwitterのおかげ

別の記事で

Youtubeでライブ配信する際にボイチェンと自動字幕を入れてみた - 勉強不足で至らんブログ

紹介した上記のTweetを観て字幕特化のサイトあればいいのにと思いました。

まだまだ理想はOBSのブラウザで動くことなのでできてないのですが、ウィンドウキャプチャでは表示できるのはひとまずいいかなと

あとは背景に画像貼れたりとかテキストのフォント変えれたり、UIが字幕の表示場所と被らないようにするとかしたいですがいずれ…

まとめ

github.com

GitHubPages使っているのでバリバリソースコード公開してるのですがすげー無駄な書き方をしている気しかしてない リファクタも兼ねていずれな…って感じですね

あとは今まであまりサービス名を考えたりしてなかったので「Zimack」とつけてみました付けてみたかった。

追記

実際に使って配信してみました

www.youtube.com

f:id:MakeTake:20200525003107j:plain

色の変更もちゃんとできて配信できたので満足 自分で足りない機能は随時追加してきたい所存