ゴイサギ日記

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

【Unity】自作Package を Unity Package Manager と連携させる

今回は自作Package を Unity Package Manager (upm) に対応させる方法をまとめました。

Packageを作成
  • まずはPackageフォルダを作成します。今回はPackage名を「test-upm」とします。
    f:id:aki517:20200126213842p:plain

  • Packageフォルダ直下に package.json を作成します。
    f:id:aki517:20200126213925p:plain

  • 必須項目は name, version のみです。他項目の詳細はこちら

{
    "name": "jp.goisagi.test-upm",
    "displayName": "test-upm",
    "version": "0.0.1",
    "unity": "2019.3",
    "description": "Description for this package."
}
  • 次に検証用Unityプロジェクトを作ります。
    今回は名前を「test-proj」として配置場所は以下のようにPackageと同じ階層にします。
    f:id:aki517:20200127083119p:plain

  • test-proj > Packages > manifest.json に自作PackageのPackage名とパスを追加します。
    パスは Packagesフォルダからの相対パスになります。

  "dependencies": {
    "jp.goisagi.test-upm": "file:../../test-upm"
  }
  • 検証用Unityプロジェクトを起動すると自作Packageの読込が行われます。
    以下のようにPackageが追加されていればOKです。
    f:id:aki517:20200127212002p:plain

  • 念のため、Package Manager に追加されているかも確認します。
    f:id:aki517:20200127214416p:plain

  • 次にスクリプト配置用のフォルダを作成します。Unity標準のフォルダ構成に合わせて Scripts > Runtime で作成してます。
    f:id:aki517:20200127212626p:plain

  • Assembly Definition File を追加します。ProjectビューからRuntimeフォルダを右クリック > Create > Assembly Definition で asmfファイルが作成されます。
    f:id:aki517:20200127212821p:plain

  • 次にPackage用のcsファイルを作成します。
    f:id:aki517:20200127212932p:plain

  • 最後にcsファイルをInspectorから確認して Assembly Information に先程のasmfファイルが設定されていることを確認します。
    f:id:aki517:20200127213932p:plain

  • Package作成の大まかな流れは以上となります。他のUnityプロジェクトにインポートしたい場合は同様に対象UnityプロジェクトのPackages/manifest.json に自作Packageのパスを設定するだけです。

GitHub連携

自作PackageをGitHubと連携する方法です。

GitHubに登録

これは対象Package を任意のリポジトリにpushするだけです。今回の場合は「test-upm」以下が対象となります。

また、バージョンに応じてインポートをしたい場合は以下のようにタグを追加しておきます。

git tag 1.0.3
git push --tags
GitHubからインポート

インポートは対象Unityプロジェクトの Packages/manifest.jsonGitHubのURLを記述するだけです。

  "dependencies": {
    "jp.goisagi.test-upm": "https://github.com/aki517/test-upm.git"
  }

バージョンを指定する場合は以下になります。

  "dependencies": {
    "jp.goisagi.test-upm": "https://github.com/aki517/test-upm.git#0.0.1"
  }
サンプルの追加

Unity2019.1以降からサンプルの追加が可能になりました。

  • サンプルを配置するルートフォルダを作成します。
    必ず末尾に「~」を付けてください。 f:id:aki517:20200127221720p:plain

  • 次に各サンプルのフォルダを作成して、サンプル用のスクリプト、シーンを置きます。
    f:id:aki517:20200127221738p:plain

  • package.json に以下のようにサンプル設定を追加します。(サンプルは複数設定可能です)

{
    "samples": [
        {
            "displayName": "Sample1",
            "description": "Description for sample 1",
            "path": "Samples~/Sample1"
        },
        {
            "displayName": "Sample2",
            "description": "Description for sample 2",
            "path": "Samples~/Sample2"
        }
    ]
}
  • Package Manager に「Import into Project」ボタンが追加されていればOKです。 f:id:aki517:20200127222457p:plain

  • サンプルをインポートすると Assets 以下に配置されます。バージョン分けしてくれるのは便利ですね ^_^
    f:id:aki517:20200127222515p:plain

依存関係について

自作Packageをインポート時に依存するPackageも一緒にインポートすることできます。

設定は package.jsondependencies を追加して、関係するPackageとバージョンを記述します。

{
    "name": "jp.goisagi.test-upm",
    "displayName": "test-upm",
    "version": "0.0.1",
    "unity": "2019.3",
    "description": "Description for this package."
    "dependencies": {
        "com.unity.textmeshpro": "2.0.0"
    }
}

ただし、こちらはUnity標準で公開されているPackageに対してのみで、現時点では自作Packageを依存関係に設定はできないようです。。フォーラムにも上がってます。

解決策としては自前のUnity Package Serverを立て、そこに自作Packageを追加して公開する方法があります。
medium.com

もしくはこちらのPackageをインストールすれば自作Packageを依存関係を解決する方法もありますが、依存関係を解決するアルゴリズムがUnity標準の機能とは異なるため目的のPackageがインストールされない場合があるのと、都度このPackageをUnityプロジェクトにインポートする必要があります ^_^;
github.com

参考

Unity - Manual: Creating custom packages
UPM: How to make a custom package · GitHub
https://forum.unity.com/threads/samples-in-packages-manual-setup.623080/

【Unity】テクスチャの設定について

今回はテクスチャの設定における注意点などをまとめてみました。ゲーム内リソースでテクスチャが占める割合は多く、開発初期にテクスチャの仕様を決めておかないと後半でメモリ不足に苦しむこともありえます。

適切なサイズにする

例えば画面がHD(1280 x 720)なのにHD(1920 x 1080)を基準にテクスチャサイズを決めるのは余計にメモリを消費するだけです。最終的に出力される画面に合わせてサイズを決めましょう。

また、サイズは2のべき乗(128x128, 256x256, 128 x 256)になるようにしましょう。この辺について説明をすると長くなるのでハードウェア都合による仕様と思っていただけるとです ^_^;

ただし、Atlasの対象となるテクスチャは別に問題ないです。最終的にAtlasになったテクスチャのサイズが2のべき乗になれば良いです。

Atlasについてはこちらのサイトが作り方を含めて分かりやすく説明してくれてます。
kan-kikuchi.hatenablog.com

圧縮フォーマットを設定する

圧縮フォーマットを設定する事で更にメモリ消費を抑える事もできます。ただし、こちらは見た目が汚くなったり、端末やOSによってサポートされる圧縮フォーマットが異なるので注意が必要です。

以下、Unityでテクスチャの圧縮フォーマットを設定する方法です。

  • Projectビューから対象テクスチャを選択
  • Inspectorビュー から各OSのアイコンをクリック
  • Override for iOS/Android にチェックを入れる
  • Format のプルダウンから圧縮フォーマットを選択
  • Apply」ボタンをクリック

f:id:aki517:20191116104103g:plain

iOS

iPhone6以降(CPUがA8以降)かで以下のように分かれます。

iPhone6以降 iPhone6以前
・RGB(A) Compressed ASTC ・RGB Compressed PVRTC 4bits
・RGBA Compressed PVRTC 4bits
テクスチャサイズを正方形にする必要あり
Android

Android端末がOpenGLES3.0以降をサポートかで以下のように分かれます。

OpenGLES3.0以降 それ以外
・RGBA Compressed ETC2 8bits
・RGB Compressed ETC2 4bits
・RGB Compressed ETC 4bit
・RGBA 16bit

Override ETC2 fallback
こちらは 対象のAndroid端末が上記のETC2圧縮が未サポートの場合、どのフォーマットを指定するかというものになります。

タイプ 説明
Use build settings BuildSettingsの設定を参照します。
f:id:aki517:20191129082653p:plain
32bit 32bitカラーです。フルカラーなので品質は良くなりますがメモリを多く消費します。
16bit 16bitカラーです。32bitよりメモリ消費を抑えれますが色数が減るので下図のグラデーション破綻が起こりやすいです。
f:id:aki517:20191130100631p:plain
32bit half resolustion 32bitカラーで解像度が半分になります。メモリ消費を抑えれますが解像度が落ちているのでボヤけた見た目になります。
MipMap

段階的に小さくしたテクスチャを複数保持し、カメラからの距離に応じて使用するテクスチャを切り替える事によって描画のパフォーマンスを最適化します。(ちなみに正確にはカメラからの距離でテクスチャを切り替えているのではなく、描画するポリゴンが画面に対して占める面積で判定してます。)

f:id:aki517:20191127042416p:plain

Unityによる設定は対象テクスチャを選択し Inspectorビュー > Advance > Generate Mipmap にチェックを入れます。
f:id:aki517:20191126091907g:plain

デメリットとしては段階ごとの複数テクスチャを持つため、データサイズが 1.34倍になります。また、画像がボヤけた状態になりやすいです。これは距離に対して最適な段階のテクスチャが参照されていないために発生する問題です。 f:id:aki517:20191126091259p:plain

また、UI用のテクスチャはこの設定を無効にした方が良いです。UIは距離によってサイズが変化することがほぼ無いので、MipMapを有効にしても高速化が見込めず、メモリをただ消費するだけです。

その他

その他でUnityのテクスチャ設定で重要なものを幾つか以下にまとめました。

sRGB

Project Settings > Other Settings > Color Space が Gamma の場合はチェックを入れる必要があります。

ちなみに sRGB とはカラースペースを定義する国際規格です。 Windowsの基準になっている色空間(Color Space)でデジタルカメラ、プリンタ、PCモニター等での標準色として使われています。

omoide-photo.jp

Read/Write Enable

基本はチェックを外します。スクリプト内でデータを書き換える場合は有効にする必要がありますがメモリ消費が2倍になるのと、対象テクスチャが非圧縮テクスチャとDXTテクスチャに対してだけ有効なためほぼ使いどころは無いです。
f:id:aki517:20191129234230p:plain

Wrap Mode

タイリング時にテクスチャをどう処理するか設定します。

モード 説明
Repeat f:id:aki517:20191130105901p:plain:w200 テクスチャを繰り返し表示します。
Clamp f:id:aki517:20191130105911p:plain:w200 テクスチャ端のピクセルを引き伸ばします。
Mirror f:id:aki517:20191130105946p:plain:w200 Repeatと同じで繰り返しですが鏡のように反転させて表示します。
Mirror Once f:id:aki517:20191130105953p:plain:w200 UV座標の0,0を中心にMirrorを一度だけ行い、移行はClampと同じ引き伸ばし処理をします。

ちなみに Mirror Once は利用できない端末があるので、 SystemInfo.supportsTextureWrapMirrorOnce を使って端末がサポートしているか確認する必要があります。

Filter Mode

3Dモデルのスケールなどでテクスチャを引き伸ばした際の処理(フィルタリング)を設定します。 Trilinear > Biliner > Point の順に高品質かつ処理が重くなります。

モード 説明
Point(no filter) フィルタなしです。ブロック状のジャギジャギした見た目になります。
Bilinear 隣接ピクセル間の平均を取ってぼかした見た目になります。
Trilinear Bilinear処理 + Mipmap間でのぼかしも入ります。

f:id:aki517:20191130093537p:plain

Aniso Level

急角度でテクスチャを見た際の品質を設定します。地面や壁などが該当します。 ちなみに下図のように0(OFF)と1では見た目に分かりやすい差が出ていますが、それより大きい値を設定してもそんなに見た目は変化しないので基本は 0(OFF) か 1(ON) で良いです。
f:id:aki517:20191130095406p:plain
f:id:aki517:20191130095403p:plain

まとめ

用途やゲーム仕様に応じて設定するのが良いです。例えば、エフェクトなど一瞬で消える演出に使われるテクスチャはサイズを小さくして圧縮をかけて品質を落とす等です。

あとは規模にもよりますが、ここでまとめた設定を AssetPostProcessor を使ったスクリプトを書いてインポート時に自動設定できるようにしておくと良いでしょう。どんどん自動化して空いた時間をゲームの品質向上に割り当てれるようにするのはとても大事です。

参考

テクスチャ - Unity マニュアル
Unity - Manual: Using sampler states

【Unity】サウンド AudioManager

ゲームサウンドで最低限必要な機能をまとめたAudioManagerを作ってみました。他にも足りてないものがあるけど一旦はここまで・・^_^;

github.com

参考

https://www.asoundeffect.com/game-audio-scripting-part-1/
How To Optimize #Game #Sound In #Unity And Boost Your Game
【Unite Tokyo 2018】実践的なパフォーマンス分析と最適化
How to fade audio in Unity: I tested every method, this one's the best - John Leonard French
Gamasutra: Zander Hulme's Blog - Unity Audio Import Optimisation - getting more BAM for your RAM

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

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

リソースの準備

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

動画ファイル

動画ファイルです。まずはこれが無いと何も始まりませんね
f:id:aki517:20191006184016p:plain

RenderTexture

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

f:id:aki517:20191007221628p:plain

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 を設定して準備は完了です。

f:id:aki517:20191007225155p:plain

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

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

  3. Inspectorビューから Add Component を選択して VideoPlayer を接続します。
    f:id:aki517:20181229123808p:plain

  4. Video Clipに 動画ファイルを設定します。
    f:id:aki517:20191006184223p:plain

  5. Render Mode を Render Texture に変更後、Target Textuure に先ほど作成した RenderTexture を設定します。
    f:id:aki517:20191006184213p:plain

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

まとめ

というわけで今回は 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版までは今後も変更が入ってくる可能性がありますが・・

実装

最もシンプルな実装はこちらにまとまっています。今回は折角なので 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 ドキュメント