ゴイサギ日記

東京でエンジニアとして頑張って何とか生きてます。。ゆる~く更新していきます

【Unity】3Dオブジェクトに動画を埋め込む

会社のアーティストさんから3Dオブジェクト内に動画を埋め込む方法を教えて欲しいと言われたので需要があるか分かりませんがその辺をまとめてみました。

リソースの準備

まずは必要リソースを準備します。

動画ファイル

動画ファイルです。まずはこれが無いと何も始まりませんね

RenderTexture

動画のレンダリング先となるテクスチャを用意します。
Projectビュー内で 右クリック > Create > Render Texture で作成します。

RenderTexture の Inspectorで重要な項目を以下にまとめました。

項目 概要
Dimension ここは2DでOKです。
Size テクスチャの解像度を設定します。設定する数値は2のべき乗(128, 256, 512...)にする必要があります。
Anti-aliasing アンチエイリアスを設定します。必要無ければ None にしておきます。
Enable Compatible
Color Format
チェックを入れておくと Color Format で設定したフォーマットが未対応ハードの場合、互換性のあるフォーマットを設定してくれます。
Color Format テクスチャのカラーフォーマットを設定します。
Depth Buffer Depthを用いて何か処理をしない限り必要ないのでサイズ削減のために None を選択します。デフォルトでは「At least 24 bit ...」が設定されているので注意
マテリアル

次にマテリアルを用意します。先ほど同様に Projectビュー内で 右クリック > Create > Material で作成します。Shader は今回はシンプルに Unlit > Texture にしています。あとはこのマテリアルに先ほどのRenderTexture を設定して準備は完了です。

設定
  1. まずはシーン内に適当な3Dオブジェクトを配置します。
    今回はHierarchyビュー上で 右クリック > 3D Object > Cube で立方体を作成しました。

  2. 次に対象の3DオブジェクトのMaterials に作成したマテリアルを設定します。

  3. Inspectorビューから Add Component を選択して VideoPlayer を接続します。

  4. Video Clipに 動画ファイルを設定します。

  5. Render Mode を Render Texture に変更後、Target Textuure に先ほど作成した RenderTexture を設定します。

  6. 以上で準備は完了です。シーンを再生すると3Dオブジェクト内に動画が再生されてます。

まとめ

というわけで今回は VideoPlayer と RenderTexture を使って
3Dオブジェクトに動画を埋め込む方法をまとめてみました。
スクリプトなしでここまで出来るUnityやっぱり凄いですね。

ちなみにスクリプト制御での動画再生やコーデックについてはこちらにまとめてます。
goisagi-517.hatenablog.com

参考

Unity - Manual: Render Texture

【Unity】UI以外のタッチ制御

UI(ボタンやスクロールビュー)領域外のタッチ制御やスマホであればフリック、ピンチの制御をサンプルプログラムを作るたびに作成していたのでクラスにまとめてみました。

github.com

機能

UI領域外のクリック(ダブルクリック含む)、ピンチ、フリックのイベントを受け取ることができます。

参考

入力制御のベースとなる部分はこちらを参考にしました。
kan.kikuchiさん、ありがとうございます。
kan-kikuchi.hatenablog.com

【Unity】サウンド AudioClip と AudioMixer

久しぶりの更新です。。今回は備忘録がてらにUnityサウンドの AudioClip と AudioMixer についてまとめてみました。

AudioClip

WAV,MP3,OGG等の音声ファイルをUnityにインポートして Projectビューからファイルを選択すると Inspectorビュー に AudioClip の設定画面が表示されます。

f:id:aki517:20190724225526p:plain

Force To Mono

モノラルにします。データサイズが半分になります。SEなど音質をあまり気にしないデータは有効にすると良いです。

Load In Background

チェックを入れるとバックグラウンド読込が有効になります。ロードが完了したかどうかは AudioClip.loadStateで確認できます。

Load Type

音声データの読込方法を設定します。この設定はメモリやCPU負荷に影響するため、用途に応じた適切な設定が必要です。

項目 処理 データサイズ 用途
Decompressed On Load ・読込と同時にメモリへ展開される
・再生時の負荷が少ない
200KB未満 SE
Compressed In Memory ・圧縮されてメモリに載り、再生時に展開される
・消費メモリが抑えられる
・再生時のCPU負荷が大きい
200KB〜1MB Voice, Jingle
Streaming ・データを段階的にメモリへ読込で展開する
・消費メモリは最小になる
・即時再生ができない
・読込と展開が都度行われるのでCPU負荷が大きい
1MB以上 BGM, Voice(長尺)
Preload Audio Data

チェックを入れるとアプリ起動時にメモリへ展開されます。再生時の負荷を抑えれますがデータ数に比例してアプリ起動の時間がかかるようになります。システムSE(決定音、キャンセル音)などゲーム内で常駐する必要があるものはチェックを入れておくと良いかもです。

Compression Format

圧縮フォーマットを設定します。ADPCMは圧縮率がVorbisほど高くはないですがCPU負荷が小さいメリットがあります。Vorbisは圧縮率が良いですが展開コストが高いです。

Quality

Compression Format で Vorbis を選択した場合に設定可能です。圧縮率の設定を行い、0に近いほどサイズが小さくなるがノイズが発生しやすくなります。70〜100の範囲は普通の人には差が分からないレベルなので、50〜70の間で設定すると良いでしょう。

AudioMixer

BGM, SE, Voiceなどのカテゴリーに応じたボリュームやエフェクトの設定が行えます。

作り方

Projectビュー内で右クリック > Create > Audio Mixer で作成出来ます。

f:id:aki517:20190725085100p:plain

Groups

カテゴリーの設定を行います。Masterをルートに階層で設定ができます。

f:id:aki517:20190727100504p:plain

Snapshots

AudioMixerの設定を保存することができます。また、このsnapshotを使って場面に応じたボリュームやエフェクトの設定に切り替えることができます。例えばバトル中にメニューを開いた時にメニュー系システムSEのみ聞こえる かつ BGMボリュームを下げる設定にする場合は下図のようなsnapshotを作成しておき、ゲーム内で切り替えることによって実現できます。

f:id:aki517:20190727102355p:plain

スクリプトでsnapshotを変更する方法は以下の2通りになります。また、方法2の weights は 0.5 に設定すると2つのsnapshotがミックスされた設定になります。

[SerializeField] AudioMixer audioMixer;

// 方法2. 1秒かけて OpenMenu の snapshot へ移行する.
void Example1()
{
    var snapshotOpenMenu = audioMixer.FindSnapshot( "OpenMenu" );
    snapshotOpenMenu.TransitionTo( 1f ); 
}

// 方法2. 1秒かけて, Default > OpenMenu の Snapshot へ移行する.
void Example2()
{
    var snapShotDefault = audioMixer.FindSnapshot( "Default" );
    var snapShotOpenMenu = audioMixer.FindSnapshot( "OpenMenu" );
    var snapshots = new AudioMixerSnapshot[]{ snapShotDefault, snapShotOpenMenu };
    var weights = new float[]{ 0.0f, 1.0f };
    audioMixer.TransitionToSnapshots( snapshots, weights, 1f );
}

切り替え時の補間方法はデフォルトではLinear(線形)になっています。snapshot単位で調整したい場合は対象のsnapshotのMasterを選択 > Inspector の各項目で右クリックをすると下図のように補間方法を選択することができます。

f:id:aki517:20190727103733p:plain

スクリプトからボリュームを制御したい

AudioMixerのsnapshotを使わないでスクリプトからボリュームを制御する方法は以下になります。

  1. AudioMixerから対象Groupを選択
  2. Inspector の Volume を右クリック >「Expose 〜」 をクリック
    f:id:aki517:20190803180450p:plain

  3. AudioMixer 右上にある Exposed Parameters をクリックして対象パラメータをリネーム
    f:id:aki517:20190803181210g:plain

  4. 制御スクリプトは以下になります。

[SerializeField] AudioMixer audioMixer;
void Example()
{
    float masterVolume;
    audioMixer.GetFloat( "MaserVolume", out masterVolume );
    masterVolume *= 0.5f  // 適当にボリュームを半分にする.
    audioMixer.SetFloat( "MasterVolume", masterVolume );
}

ちなみにAwake()内でAudioMixer.SetFloat()を実行しても適用されません。Start()であれば問題ないです。こちらのフォーラムにも上がっていますが私が動作を確認した Unity2019.1でも修正されていませんでした・・(^_^;

まとめ

AudioClip は 用途に応じて Load Type を設定して Compression Format, Quality の設定を適切に行うのが良いでしょう。最終的にサウンドデータは数が多くなるので Presets機能 を使って自動設定ができるようにするのが良いかと思います。

goisagi-517.hatenablog.com

AudioMixerはsnapshot機能はボリューム調整を個別に対応しなくて良いので便利ですが、ボリュームを調整したい場面が増えてくるとそれに比例してsnapshot数が増えていくので注意が必要です。

参考

【Unite Tokyo 2018】実践的なパフォーマンス分析と最適化
コンセプトと AudioMixer の概要 - Unity マニュアル

【Unity】IParticleSystemJob を使ってみた

今回は IParticleSystemJob を使ってみました。
確認バージョンは Unity2019.1.0f2 です。

はじめに

IParticleSystemJobとは、昨年くらいから何かと話題の「JobSystem」を使ってパーティクルデータの操作が出来るものです。試験的な機能なのでFix版までは今後も変更が入ってくる可能性がありますが・・

2021/05/02 追記
Unity2019.3から正式版として UnityEngine.ParticleSystemJobs に移行

実装

最もシンプルな実装はこちらにまとまっています。今回は折角なので CustomData モジュールで定義したカーブアニメーションを IParticleSystemJob から取得してカラー変更する処理を実装してみます。

以下コードです。

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

using UnityEngine;
using UnityEngine.Experimental.ParticleSystemJobs;

public class SampleParticle : MonoBehaviour
{
    void Awake()
    {
        ParticleSystem ps = GetComponent<ParticleSystem>();
        ps.SetJob( new ParticleJob());
    }

    struct ParticleJob : IParticleSystemJob
    {
        public void ProcessParticleSystem( ParticleSystemJobData jobData )
        {
            Color customColor = Color.white;

            var startColors = jobData.startColors;
            var customDatas = jobData.customData1;
            for( int idx=0 ; idx < startColors.Length ; idx++ )
            {
                customColor.r = customDatas.x[ idx ];
                customColor.g = customDatas.y[ idx ];
                customColor.b = customDatas.z[ idx ];
                customColor.a = customDatas.w[ idx ];
                startColors[ idx ] = customColor;
            }
        }
    }
} 

殆ど説明が必要ない短さですね(^^;)
1. IParticleSystemJob を継承した構造体を定義
2. ProcessParticleSystem() を定義
3. 引数の ParticleSystemJobData に Active状態のパーティクルデータが入ってくるので必要な更新を記述
4. 対象ParticleSystem の SetJob() にジョブを設定

実行画面はこんな感じです。
f:id:aki517:20190427221558g:plain

Profiler で確認すると Worker Threadで処理されてます。 f:id:aki517:20190427221207p:plain

Worker Thread と Main Threadで比較が出来るプロジェクトをgithubに上げてあります。

github.com

まとめ

パーティクルデータのコピーなしでデータ操作出来るのは便利です。ParticleSystemのGetParticles/SetParticles は呼び出しコストが高いのでそこが無くなるだけでも恩恵はあるかなと・・

あとフォーラムにも書いてましたが将来的に Trigger/Collisionモジュールの衝突検知用コールバックが IParticleSystem に追加されるとゲームロジックに合わせた処理を入れたりと実装の幅が広がりそうですね。

ただ、ゲームロジックが絡んでこない処理なら Custom Vertex Stream を使ってシェーダ側で処理をした方が速度が出ると思います。

参考

Unity - Particle System C# Job System support - Unity Forum

Particle System Improvements (2019.1) - Google ドキュメント

【Unity】Triggers モジュールについて

明けましておめでとうございます。もう2月ですが・・ ^_^;

今回は Particle System の Triggers モジュールについて調べてみました。
確認バージョンは 2019.1.0b2 です。

実装

Trigger モジュールは Colliders に設定したコライダーに対してパーティクルが接触した際の処理を設定することができます。

設定できる処理は以下になります。

項目 処理内容
Ignore 何もしない
Kill ParticleSystemが設定されているGameObjectを削除
Callback スクリプトに通知

今回はこの中の Callback についてまとめてみました。

コールバックを設定するスクリプトを用意します。今回は ParticleTriggerTest.cs という名前で作成して Particle System と同じ GameObject にアタッチします。

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

public class ParticleTriggerTest : MonoBehaviour
{
    ParticleSystem m_particleSystem;
    List<ParticleSystem.Particle> m_enterList = new List<ParticleSystem.Particle>();
    List<ParticleSystem.Particle> m_exitList = new List<ParticleSystem.Particle>();

    void Start()
    {
        m_particleSystem = this.GetComponent<ParticleSystem>();
    }

    /// <summary>
    /// .
    /// </summary>
    void OnParticleTrigger()
    {
        // 条件に一致するパーティクルを ParticleSystem から取得する.
        int numEnter = m_particleSystem.GetTriggerParticles( ParticleSystemTriggerEventType.Enter, m_enterList );
        int numExit = m_particleSystem.GetTriggerParticles( ParticleSystemTriggerEventType.Exit, m_exitList );

        // 取得したパーティクルの色を変更する.
        for( int idx=0 ; idx < numEnter ; idx++ ){
            ParticleSystem.Particle p = m_enterList[ idx ];
            p.startColor = Color.red;   // 赤色.
            m_enterList[ idx ] = p;
        }
        for( int idx=0 ; idx < numExit ; idx++ ){
            ParticleSystem.Particle p = m_exitList[ idx ];
            p.startColor = Color.yellow; // 黄色.
            m_exitList[ idx ] = p;
        }

        // 変更したパーティクルを ParticleSystem に再設定.
        m_particleSystem.SetTriggerParticles( ParticleSystemTriggerEventType.Enter, m_enterList );
        m_particleSystem.SetTriggerParticles( ParticleSystemTriggerEventType.Exit, m_exitList );
    }
}
  1. Triggersモジュールを使用すると OnParticleTrigger が呼び出されます。
  2. インスペクタで Callback に設定した条件(Outside, Inside, Exit, Enter)ごとのパーティクルを取得するには GetTriggerParticles を使います。
  3. 取得したパーティクルのパラメータに変更を加えたら SetTriggerParticles で変更内容を適用してあげます。

上記のサンプルスクリプトはパーティクルのカラーを Enter時に赤色、Exit時に黄色 に変えています。

気になる点

OnParticleTrigger が毎フレーム呼ばれる
Outside のパーティクルを取得するために毎フレーム呼び出しになっているようです。しかも Triggers モジュールのチェックが有効になるだけで呼ばれてしまうところにも注意が必要です。

GetTriggerParticles / SetTriggersParticles の負荷が高い
こちらは ParticleSystem.GetParticles/SetParticles と同様にパーティクルの情報に直接アクセスする事が出来ず、一度配列にコピーした後に変更を加えて、再度データを流し込む必要があるため負荷が高くなります。

Colliderの登録が面倒
インスペクタ上から Colliders にコライダーを登録する必要があるのですが、数が多いと大変です。ParticleSystem.trigger.SetCollider() を使えばコライダーを登録する事ができるので スクリプトから制御するのも手ですね。

まとめ

パフォーマンスの面で気になるところが多い印象でした。こちらのフォーラムにあるように今後の更新で JobSystem によるパフォーマンス改善が出来ないかUnityさんの方で色々と試しているようですね。表現の幅は広がりそうなのでとても楽しみです。^_^

参考

Triggers モジュール - Unity マニュアル

【Unity】VideoPlayerを使ってみた

最近、Unityでの動画再生について調べる必要があったので自身の備忘録としてまとめました。

VideoPlayer

Unityの動画再生はUnity5.6から追加された VideoPlayer コンポーネントを使う必要があります。Unity5.6より前は MovieTexture を使っていましたが Unity2018.3 から廃止されてしまったので VideoPlayer 一択となりました。

設定方法

VideoPlayerで動画の再生を確認するだけであれば割と簡単です。

  1. 動画ファイルをProjectビューに配置します。 f:id:aki517:20181229124056p:plain

  2. Main Camera が設定されている GameObject に VideoPlayerコンポーネントを追加
    f:id:aki517:20181229123808p:plain

  3. VideoPlayerコンポーネンとの Video Clip に動画ファイルを設定
    f:id:aki517:20181229125744p:plain

  4. Unityエディタを再生
    f:id:aki517:20181229130311g:plain

事前読込

VideoPlayer.Prepare() を使えばバッファへ動画データを事前に読込んでおくことができます。VideoPlayer.prepareComplete にコールバックを設定すれば読込完了を検出することができます。

void Start()
{
    // VideoPlayerコンポーネント取得.
    videoPlayer = obj.GetComponent<VideoPlayer>();
    // 即再生されるのを防ぐ.
    videoPlayer.playOnAwake = false;
    // パス or VideoClip を設定.
    videoPlayer.url = "Assets/Resources/testfile.mp4";
    // videoPlayer.clip = videoClip;
    // 読込完了時のコールバックを設定.
    videoPlayer.prepareCompleted = OnCompletePrepare;
    
    // 読込開始.
    videoPlayer.Prepare();
}

// 読込完了時のコールバック.
void OnCompletePrepare()
{
    // 読込が完了したら再生.
    videoPlayer.Play();
}
その他

あと動画に関する知識があまりに無かったので、その辺もメモしておきます ^_^;

コーデック

データ(映像、音声)の圧縮・変換と復元の方法です。COmpression/DEcompressionを縮めたもので映像と音声でコーデックがそれぞれ異なります。圧縮・変換を「エンコード」復元を「デコード」と言います。

映像コーデック
コーデック 概要
MPEG-4 第三世代携帯電話や携帯ゲーム機などモバイルで主流になりつつある
H.264 ビットレートで高画質を維持することができるため、動画配信サービスやiPhone,Androidなどのスマホに採用されている、現時点で最も主流な映像コーデック
H.265 H.264の後継、H.264よりも圧縮効率に優れているがエンコード時の負荷が高い、また、Unity2018.3時点ではVideoPlayerで再生サポートされていない(Unity2019.1aから公式サポートとのこと)
WMV9 マイクロソフトが開発、Windowsでの普及率は高い
Divx Divx, Inc がMPEG-4をベースに独自開発、DVDプレイヤー、カーナビなどのデジタル家電でよく利用される
Xvid フリーの映像コーデック、エンコードDivxより速いと言われている、商業使用時はそのソフトのソースコードを公開する義務が発生するので殆ど使用されていない
音声コーデック
コーデック 概要
MP3 最も使用されている音声コーデック
AAC MP3の後継、同程度のビットレートであればMP3より高い音声品質が実現、
AC3 5.1chの出力に対応、圧縮率が高い場合は他音声コーデックより音声品質が良い、DVD・Blu-ray等に使用される
コンテナ(動画形式)

映像コーデック + 音声コーデックのセット、正式名称は「コンテナフォーマット」と言うそうです。コンテナごとに扱える映像コーデックと音声コーデックが決まっているので目的に応じたコンテナを選択する必要があります。以下に代表的なものをまとめてみました。

コンテナ 映像コーデック 音声コーデック 拡張子
MP4 MPEG-4, H.264, H.265 AAC, AC3, MP3 .mp4, .m4a
MOV MPEG-4, H.264, MJEG AAC, MP3, LPCM .mov, .qt
WMV WMV9 WMA,AAC,MP3 .wmv
AVI MPEG-4, Divx, Xvid, H.264, H.265 AAC, MP3 .avi

MP4 が割と一般的なのかなと思いました。iPhone,Android, 携帯ゲーム機などで広く使われていますし ^_^

また、iPhone, Androidの端末やOSバージョンで対応しているコーデックは以下ページで確認できます。

iPhone
「Video Playback」「Audio Playback」参照
iPhone - Compare Models - Apple

Android
Supported media formats  |  Android Developers

参考

Unity - Scripting API: VideoPlayer

【Unity】Shuriken Particle Unity2018

Unity2018以降で Shuriken Particle に追加された機能で個人的に気になったものを書いていきます。

確認バージョンは 2018.3.0f1 です。

Ring Buffer Mode


メインモジュールに追加された機能です。
パーティクル数が Max Particles 到達時の処理を設定します。

パラメータ
説明
Disabled Ring Buffer Mode を無効にします。上限値に達した場合、そのパーティクルの寿命(Start Lifetime)がきて上限値未満になるまで次のパーティクルは発生しなくなります。
Pause Until Replaced 上限値に達した場合、古いパーティクルを即時削除して次のパーティクルを発生させます。
Loop Until Replaced Pause Until Replaced 同様に古いパーティクルを削除しますが、上限値に達するまで Loop Range で設定した値内でアニメーションをループ再生できます。

下の動画では Loop Range を 0.0 - 0.5 で設定した後に Color over Lifetime で 0.0 - 0.5 間は白→緑→白、0.5 - 1.0 間はαフェードを設定しています。

パーティクル発生中は緑色に発光し続けて、上限値に達して削除対象になった古いパーティクルはαフェードで徐々に消えています。

設定はこんな感じです。
注意点として、古いパーティクルが完全に消える前に次のパーティクルが発生するため一時的に Max Particles を超えたパーティクル数が発生します。
Orbital Velocity


Velocity over Lifetime モジュールに追加されたパラメータです。任意軸を中心に渦を巻くパーティクルの動きを設定できます。Orbital で軸を設定します。軸の中心はエミッターの位置になります。

(動画はパーティクルの動きを確認しやすくするためにTrailモジュールを使っています)

Offset パラメータで軸の中心を変更できます。エミッターの位置から離れるほど下の動画のように大きな渦になります。

Shape : Rectangle

Shape モジュールに追加されたパラメータです。テクスチャの色情報を元にパーティクルの発生開始位置を設定できます。

パラメータ 説明
Texture テクスチャを設定します。対象テクスチャはRead/Writeを有効にする必要があります
Clip Channel クリップするRGBAチャンネルを設定します。選択したチャンネルでマスクして発生開始位置を調整します。
Clip Threshold Clip Channel で設定したチャンネルの閾値を設定します。注意点として、Clip ChannelでAlphaを設定した場合は適切な閾値を設定しないと透明なパーティクル(下図)を生成して余計な描画負荷を引き起こします。
Color affects Particles チェックを入れるとパーティクルがテクスチャのカラーの影響を受けます。
Alpha affects Particles チェックを入れるとパーティクルがテクスチャのα値の影響を受けます。
Shape : MeshRenderer


こちらも Shape モジュールに追加された機能です。指定Mesh上からパーティクルを発生させます。

パラメータ 説明
Type パーティクルの発生位置を設定します。
Mode どのようにパーティクルを発生させるか設定します。Type を Vertex, Edge で設定可能
Spread パーティクルの発生間隔を設定できます。Type を Edge で設定可能
Speed 頂点間を移動する際の速度を設定できます。Type を Edge で設定可能
Mesh Meshを設定します。対象メッシュは Read/Write を有効にしてください。
Texture Sheet Animation : FPS


Texture Sheet Animation モジュールの Time Mode に追加されたパラメータです。テクスチャアニメーションの FPS を設定できます。

所感

あと今回の機能はβ版で確認したので正式リリースでは仕様変更される可能性もありますが、色々とゲームの演出に使えそうな機能を追加しており表現の幅は広がりそうです。

参考

【Unite Tokyo 2018】パーティクル・マニアクス - YouTube
【CEDEC2018】 パーティクル新機能の紹介 - YouTube