ゴイサギ日記

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

【Unity】IParticleSystemJob を使ってみた

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

はじめに

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

実装

最もシンプルな実装はこちらにまとまっています。今回は折角なので 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スレッド側で処理されてます。 f:id:aki517:20190427221207p:plain

比較用に上記のワーカースレッドによる処理とメインスレッドで同じ処理をしたものが入っているプロジェクトをgithubに上げておきました。

github.com

まとめ

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

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

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

参考

https://forum.unity.com/threads/particle-system-c-job-system-support.529284/

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

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

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

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

f:id:aki517:20190211213755p:plain

実装

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時に黄色 に変えています。
f:id:aki517:20190211232743g:plain

気になる点

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

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

コライダーの登録が面倒
インスペクタ上から 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

f:id:aki517:20181212095658p:plain
メインモジュールに追加された機能です。画面に表示されるパーティクル数が Max Particles で設定した上限値に達した時の処理を設定できます。

パラメータ
説明
Disabled Ring Buffer Mode を無効にします。上限値に達した場合、そのパーティクルの寿命(Start Lifetime)がきて上限値未満になるまで次のパーティクルは発生しなくなります。 f:id:aki517:20181213082637g:plain
Pause Until Replaced 上限値に達した場合、古いパーティクルを即時削除して次のパーティクルを発生させます。
f:id:aki517:20181213082629g:plain
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 間はαフェードを設定しています。
f:id:aki517:20181213082436g:plain
パーティクルが出ている間は緑色に発光し続けて、上限値に達して削除対象になった古いパーティクルはαフェードで徐々に消えているのが確認できます。

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

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

f:id:aki517:20181214090808g:plain

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

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

f:id:aki517:20181214091141g:plain

Shape : Rectangle

f:id:aki517:20181214091741p:plain

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

f:id:aki517:20181214090019g:plain

パラメータ 説明
Texture テクスチャを設定します。対象テクスチャはRead/Writeを有効にする必要があります
Clip Channel クリップするRGBAチャンネルを設定します。選択したチャンネルで発生開始位置をマスクする感じになります。
Clip Threshold Clip Channel で設定したチャンネルの閾値を設定します。Clip ChannelでAlphaを設定した場合は適切な閾値を設定しないと下図のように見えないパーティクルが生成され余計な描画負荷が発生するので注意が必要です。f:id:aki517:20181214085418p:plain
Color affects Particles チェックを入れるとパーティクルがテクスチャのカラーの影響を受けます。
Alpha affects Particles チェックを入れるとパーティクルがテクスチャのα値の影響を受けます。
Shape : MeshRenderer

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

パラメータ 説明
Type パーティクルの発生位置を設定します。f:id:aki517:20181214235847g:plain
Mode どのようにパーティクルを発生させるか設定します。Type が Vertex, Edge で設定可能
Spread パーティクルの発生間隔を設定できます。、Type が Edge で設定可能f:id:aki517:20181215001541p:plain
Speed 頂点間を移動する際の速度を設定できます。Type が Edge で設定可能
Mesh Meshを設定します。対象メッシュは Read/Write を有効にする必要があります。
Texture Sheet Animation : FPS

f:id:aki517:20181215002717p:plain
Texture Sheet Animation モジュールの Time Mode に追加されたパラメータです。テクスチャアニメーションの FPS が設定できます。
f:id:aki517:20181215003319g:plain

所感

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

参考

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

【Unity】パーティクルのボリューム表現

煙や雲を表現する場合、パーティクルのビルボードを使って実装するのが一般的です。(真面目に煙や雲のポリゴンモデルを用意して描画すると負荷が凄い事になってしまうので・・・)

ただ、この方法は処理が軽くなるけど四角形ポリゴンにテクスチャを貼り付けてるだけなのでボリューム感が出ない事があります。
f:id:aki517:20181027192548p:plain

ライトの影響を受ければ立体感が出るはずなので単純に光源からの距離に応じて頂点単位で明るさを調整するシェーダーを適用してみました。
f:id:aki517:20181026010111p:plain
だいぶマシになりましたがもう少しボリューム感が欲しいです ^^;

最終的にフラグメントシェーダーでテクスチャのα値を雲の厚みとして扱うようにして明るさの調整をしてみました。
f:id:aki517:20181026010150g:plain
だいぶ良い感じになりました。

テクスチャのα値とは別で設定したい場合は専用のテクスチャを用意してそちらの値を参照するのが良さそうです。

プロジェクト一式はこちらにアップしました。
github.com

今回はなるべく低コストでモバイルでも動かせるものにしたかったので擬似表現になりましたが、多光源対応やより精度の高いボリューム表現をしたい場合はボリュームレンダリングを使うとかになると思います。

参考

3Dゲームファンのための「ワンダと巨像」グラフィックス講座

【Unity】Unity2018.3からのPrefabワークフロー

Unity2018.3のβ版でPrefabのワークフローが色々と変更されました。今回はその中でもメインとなる Prefab Mode, Nested Prefab, Prefab Variant についてまとめてみました。

Prefab Mode

2018.3より前のバージョンでPrefabを編集する時は以下のような流れが一般的でした。

  1. ProjectビューのPrefabをHierarchyビューにドラッグ&ドロップ
  2. Inspector/Sceneビュー上でPrefabを編集
  3. InspectorビューからApplyボタンを押して編集内容を保存
  4. Hierarchyビュー上からPrefabを削除

この流れだとApplyボタンを押し忘れて保存されてなかったり、編集完了のPrefabを削除し忘れてシーンに不要なデータが残ってしまう問題が起こりやすかったです。

Prefab Mode はそれらの問題を解決するためのもの、、とUnityの公式ブログに書いてました ^^;

Prefab Mode への移行方法は以下の3パターンがあります。
・Projectビューで対象Prefabをダブルクリック
・ProjectビューでPrefabを選択してInspectorビューの「Open Prefab」をクリック
・Hierarchyビュー上の対象Prefabの右側にある「>」をクリック

上記のいずれかを実行すると以下のようなPrefab編集用の専用画面に移行します。

f:id:aki517:20181008085606p:plain

保存方法

Sceneビュー上部右端の「Auto Save」のチェックが有効なら編集内容は自動で保存されます。以前のように編集後に「Apply」ボタンを押す手間が無くなりました。

f:id:aki517:20181008083723p:plain

ただし、サイズの大きいPrefabを自動保存が有効な状態で編集すると動作が遅くなる場合があるので、その時は「Auto Save」のチェックを外します。手動で保存する場合は「Auto Save」の隣にある「Save」ボタンを押すか、または 「Ctrl + S」(Mac : Cmd + S) です。

f:id:aki517:20181008090406p:plain

環境設定

Prefab Modeでは 専用Sceneファイルを設定できます。 Edit > Project Settings > Editor > Prefab Editing Environments で設定できます。

f:id:aki517:20181008093715p:plain

例えば UI Environment にヘッダーとフッターのUIとCanvasを配置した Sceneファイルを設定しておけば、UI関連のPrefabを編集する際にヘッダー/フッターUIが既に配置された状態で編集できます。

f:id:aki517:20181008121807g:plain

(わざわざヘッダー/フッターUIのPrefabも Hierarchyビューにドラッグ&ドロップしてという手間が省けますね)

スクリプトからはEditorSettingsで設定できます。

EditorSettings.prefabRegularEnvironment = customScene;
EditorSettings.prefabUIEnvironment = customSceneUI;
終了方法

Prefab Mode を終了するには Hierarchyビューの「<」をクリックするか、Sceneビュー上部の「Scenes」をクリックします。
f:id:aki517:20181008092025p:plain

Nested Prefab

Prefabの中にPrefabを設定できる仕組みです。これの便利なところは例えば下図のようにある Prefab に Particle System が設定された別Prefab が3個配置されている状態から、Prefab Mode で Particle System のカラーを1つ編集するだけで他の2つにもその設定が適用されます。

f:id:aki517:20181008202446g:plain

今までのように個別で設定を行いたい場合は対象Prefabを右クリックして「Unpack Prefab」か「Unpack Prefab Completely」を選択します。2つの違いは選択したPrefabに対してのみ適用されるか、その中にある別Prefabにも適用されるかになります。

ちなみにNestは「入れ子」という意味です ^_^

Prefab Variant

あるPrefabをベースにして異なるパラメータや、追加のコンポーネントを設定したPrefabを作成したい場合に使います。

作成方法はベースとなるPrefabを右クリック > Create > Prefab Variant で作成できます。 f:id:aki517:20181008203353p:plain

下図のようにPrefabアイコンに矢印が描かれたPrefabが作成されます。今回はパーティクルサイズが異なる Prefab を作ってみるので「PrefabParticle_Large」としておきます。

f:id:aki517:20181008204111p:plain

PrefabParticle_Large の Prefab Mode に移行して Start Size を変更します。ベースと異なる部分があるとInspectorの左端に青いラインが表示されます。

f:id:aki517:20181008204514p:plain

保存方法

Variant Prefabの保存方法は通常Prefabと同様ですが、ベースのPrefabに設定値を適用したい場合はInspectorビューからOverrides > Apply All to Base をクリックするか
f:id:aki517:20181008210025p:plain

Overrides > 変更したコンポーネントを選択して 下図のようにベースとの変更差分を確認しながら 対象パラメータ上で右クリック > Apply to Prefab XXXXX で適用する方法があります。
f:id:aki517:20181008210022p:plain

所感

Prefab Modeでの編集はとても便利で今まで起こっていた操作ミスが減りそうです。Nested Prefab と Prefab Variant はきちんと仕様を把握しておかないと編集した内容が他のSceneやPrefabに影響を及ぼす可能性があるので注意が必要かなぁと思いました。

参考

Prefabs Manual - Google ドキュメント

Prefab Workflow - Unity

【Unity】Projectビューのお気に入り情報を抜き出してみる

前回に引き続き今回もProjectビューです。お気に入り情報を抜き出してファイルにエクスポート/インポートするエディタ拡張を作ってみました。

別PCのUnityエディタに自分のお気に入り情報をインポートしたい時くらいにしか使えない気がしますが内部処理を把握するのに良い勉強になりました ^^;

github.com

Unityのソースコードが公開されているのでそちらを参考にしました。調べたところSavedSearchFilter がお気に入り情報を管理してたのですが internal class だったのでリフレクションを使って参照しています・・ ^^; tsubakit1.hateblo.jp