unityでいってみよう!

unityがチョットワカル位の人のブログ

Assembly Definitionを利用してUnityのバージョンを判定してみよう!

はじめに

Unityでプログラムを記述していると、Unityのバージョンで処理を分岐したいということが良くあります。 そんな時、 #define ディレクティブを使用して処理を分岐さっせてきましたが、実はUnity2019からはAssembly DefinitionにUnity Editor本体や利用しているパッケージのバージョンに応じて自身で定数を定義出来る機能が追加されていますので今回はその紹介とやり方になります。

Assembly Definitionの生成方法

Assets > Create > Assembly DefinitionでAssembly Definationを作成します。

f:id:kimukats:20210915092249p:plain

Assembly DefinitionへのVersion Defineの設定方法

Assembly DefinitionをInspectorで表示し、右下にある+ボタンを押します。

f:id:kimukats:20210915092626p:plain

このような表示に切り替わります、リソースのプルダウンメニューを選択すると指定項目が表示されます。 f:id:kimukats:20210915092833p:plain

各項目の概要は下記の通りです。

項目 説明
Resource Defineを設定したいUnityやパッケージを指定します
Define 定義したいDefineを指定します。
Expression Defineが有効となるバージョンの範囲をNuGetと同じ表記方法で指定します
Expression outcome Expressionで設定した結果を表示します

NuGetのバージョン指定方法

docs.microsoft.com

数値,(,),[,],,の組み合わせで範囲を指定します。 以下に組み合わせのサンプルを示します。

Expression Expression outocome 説明
1.0 x >= 1.0 指定バージョン以上であれば
(1.0,) x > 1.0 指定バージョンより大であれば
[1.0] x == 1.0 指定バージョンと等しければ
(,1.0] x <= 1.0 指定バージョン以下であれば
(,1.0) x < 1.0 指定バージョン未満であれば
[1.0,2.0] 1.0 <= x <= 2.0 左辺以上且つ右辺以下であれば
(1.0,2.0) 1.0 < x < 2.0 左辺より大且つ右辺未満であれば
[1.0,2.0) 1.0 <= x < 2.0 左辺以上且つ右辺未満であれば
(1.0,2.0] 1.0 < x <= 2.0 左辺より大且つ右辺未満であれば

(1.0)のような定義は使用出来ません。

バージョン番号は下記のように指定します。 メージャー番号.マイナー番号.パッチ番号 [サフィックス]

2019.4.28f1であれば

メジャー番号:2019 マイナー番号:4 パッチ番号:28 サフィックス:f1

となります。

サフィックスは省略可能ですが、マイナーとパッチに関しては省略すると0として処理されることに注意してください。

組み合わせ例

本題のUnityバージョンの指定方法です。 概ね下記のパターンで乗り切れると思いますが、これ以外の範囲の指定が必要な場合は上記を参考にして下さい。

Unity2019系でのみ有効にしたい定数がある場合

f:id:kimukats:20210915102025p:plain

Unity2019.4LTSでのみ有効にしたい定数がある場合

f:id:kimukats:20210915102236p:plain

Unity2019.4.28からUnity2019.4.30迄有効にしたい定数がある場合

f:id:kimukats:20210915102908p:plain

特定のバージョン間で不具合があって、その間ワークアラウンドで乗り切る時とかに使えそうですね。

Unity2019.4.28f1でのみ有効にしたい定数がある場合

f:id:kimukats:20210915101707p:plain

最後に

#define ディレクティブによる指定ではかなり大雑把のバージョン指定しか出来ませんでしたが、Assembly Definitionを使用すること本来やりたかったことがようやく出来るようになったというのが自分の心象です。 今更Unity2019の機能紹介も如何なものですが、知らずにUNITY_2017_1_OR_NEWER等を未だに使い続けているプロジェクトもあると思いますので参考になれば幸いです。

Runtimeで不足しているShaderバリアントをEditorから取得してみよう!

はじめに

shader_featureで用いてバリアントを分けたものの、Runtimeで実行すると思った通りに表示されないのだが・・・」といった経験はないでしょうか? 開発中に表示されないことに気がつくことが出来ればまだセーフですが、リリースされた後に判明することも稀ではありません。

そんな経験のある人に朗報です。

Unity2020.2からビルドオプションBuildOptions.ShaderLivelinkSupportが追加されていることにお気付きでしょうか?

このオプション、試しにググってみましたが、まったくヒットしませんが、試して見たら機能でした。

f:id:kimukats:20210914133429p:plain
ShaderLiveLinkをググってみた結果

実はShaderLiveLinkの機能を使用することで、Runtime上で存在しないバリアントがリクエストされた場合、UnityEditor上のコンソールにキーワードを表示し、Editorから不足しているバリアントを読み込みRuntime上で表示することが可能です・・・。

検証

検証にはこちらプロジェクトを利用します。 このプロジェクトはAssetBundleからShaderVariantを読み込んで使用するサンプルですが、確認用として意図的に存在しないバリアントへのアクセスを行っています。 このRuntimeに存在しないバリアントが描画出来ればShaderLiveLinkが動作しているという証明になります。

初めにこのプロジェクトに含まれるSampleSceneをAndroidバイス向けにBuild & Runを実行します。

f:id:kimukats:20210914122439p:plain

画面右側のシリンダーオブジェクトが真っ黒になっていることが見えるでしょうか? 本来このシリンダーは青で表示されるのですが、Runtime上には赤と緑のバリアントのみ存在する為、シリンダーが黒くなっているという訳です。

それでは、ShaderLiveSupportを有効にしてみます。 MenuからBuild > Android(ShaderLiveLinkSupport)を選択します。これでShaderLiveSupportが有効になった状態でビルド&ランが実行されます。

f:id:kimukats:20210914123304p:plain

端末上でバイナリが起動した結果です。

f:id:kimukats:20210914125232p:plain

Runtimeには存在しない青色のShaderでシリンダーが描画されていることが確認出来ます。 また、UnityEditorのコンソール上には

Autoconnected Player Requesting non existen keyword at index xxxx.

と存在しないkeywordのインデックスが表示されていることが確認出来ます。

f:id:kimukats:20210914125616p:plain

それでは Android(ShaderLiveLinkSupport)で実行されるプロセスを確認してみます。

public class Build : MonoBehaviour
{
#if ENABLE_SHADER_LIVE_LINK
    [MenuItem("Build/Android(ShaderLivelinkSupport)")]
    public static void BuildAndroid()
    {
        BuildPlayerOptions buildPlayerOptions = new BuildPlayerOptions();
        buildPlayerOptions.scenes = new[] { "Assets/Scenes/SampleScene.unity"};
        buildPlayerOptions.locationPathName = "binary.apk";
        buildPlayerOptions.target = BuildTarget.Android;

        // ShaderLivelinkSupportはDevelopmentとConnectToHostの両方との組み合わせが必須
        buildPlayerOptions.options = BuildOptions.AutoRunPlayer|BuildOptions.Development|BuildOptions.ShaderLivelinkSupport| BuildOptions.ConnectToHost;
        
        BuildReport report = BuildPipeline.BuildPlayer(buildPlayerOptions);
        BuildSummary summary = report.summary;
        
        if (summary.result == BuildResult.Succeeded)
        {
            Debug.Log("Build succeeded: " + summary.totalSize + " bytes");
            Debug.Log("outputpath " + summary.outputPath);
        }

        if (summary.result == BuildResult.Failed)
        {
            Debug.Log("Build failed");
        }
    }
#endif
}

ポイントはこの部分です。

        // ShaderLivelinkSupportはDevelopmentとConnectToHostの両方との組み合わせが必須
        buildPlayerOptions.options = BuildOptions.AutoRunPlayer|BuildOptions.Development|BuildOptions.ShaderLivelinkSupport| BuildOptions.ConnectToHost;

ビルド時のオプションにBuildOptions.ShaderLivelinkSupportが指定されていることが確認出来ます。 BuildOptions.ShaderLivelinkSupportスクリプトリファレンスには、BuildOptions.Developmentを合わせて有効にする必要があると記載されていますが、記載されていないBuildOptions.ConnectToHostも指定されています。

試しにBuildOptions.ConnectToHostを無効にした状態でビルドを確認した所、skyboxも壊れた状態で表示されました。 この結果からShaderLiveLinkはPlayerConnectの機能を使用していることが分かります。

f:id:kimukats:20210914132718p:plain

結論

ShaderLiveLinkの使用方法

ビルドオプションにBuildOptions.ShaderLivelinkSupport | BuildOptions.Development | BuildOptions.ConnectToHost を指定した状態でバイナリのBuild&Runを実行する。

ShaderLiveLinkの効能

  • UnityEditorのコンソール上に不足しているShaderKeywordのインデックスを表示する
  • Runtime上で不足しているバリアントをEditor上からロードする

まとめ

Unity2020.2でひっそりと追加されたShaderLiveLinkが実は高機能であることが伝われば幸いです。 現状スクリプトからのみ指定可能ですので可能であれば、GUIのオプションに追加してくれるとなお使い勝手が良くなると思います。 正直Runtime上で不足しているキーワードが分かるのは大変便利だが、UnityEditorからバリアントを読み込むという機能はどういった局面で使用するか当初見えていなかったのですが、アプリケーションの規模が大きくなるとアプリケーションのビルドには多くの時間がかかります。そういった場合、アプリケーションのビルドは行わずShaderのみ差し替えたいという局面で使用出来そうです。また、なかなかゲームアプリケーションではなかなかないかもしれませんが、アプリケーションとEditorのセットで運用するような業界があればかなり意味がありそうです。

Unity2020でのLegacy Sprite Packerについて

はじめに

Legacy Sprite Packerに関して、Unity2020.3のマニュアルには下記のような記載があります。

Please note that Sprite Packer is deprecated for Unity 2020.1 and newer, and will no longer be available as an option from Sprite Packer Modes. Existing Projects already using Sprite Packer will still be able to continue using it, however any new Project created in 2020.1 onwards will default to the Sprite Atlas system when packing Textures.

これを日本語で箇条書きにすると下記のようになります。

  • Sprite PackerはUnity2020.1以降で非推奨となった
  • Sprite Packer Modeはオプションとして利用出来なくなる
  • Sprite Packerを利用している既存のプロジェクトは引き続き使用可能
  • Unity2020.1以降で作成された新規プロジェクトではテクスチャパックをする際にデフォルトでSprite Atlas Systemが使用される

これを見た時、「Unity2020ではLegacy Sprite Packerは非推奨となるが、それ以前のUnityバージョンで作成したLegacy Sprite Packerを利用しているプロジェクトでは、引き続きLegacy Sprite Packerが使用出来る」と読み取りましたが、現実は違いました。

結論

結論から述べます。

Unity2020.1以降でも、それ以前のバージョンのUnityEditor上で設定したLegacy Sprite Packerのデータを使用可能ですが、Packing Tagが編集不可能である為、Legacy Sprite Packerで新たにAtlasを生成することや構成を変更することは出来ません。 また、AssetBundleにLegacy Sprite Packerで生成されたSpriteAtlasTextureが含まれている場合、AssetBundleのリビルドが発生した段階でそのSpriteAtlasTextureはAssetBundleから削除されます。

文章の

Existing Projects already using Sprite Packer will still be able to continue using it,

この部分がミスリードを誘っているのですが、ここでいうSprite PackerはLegacySpritePackerという機能を指している訳ではなく、LegacySpritePackerで作成したSpriteAtlasTextureを指しているようです。 確かに、UnityがJavaScriptやBooをdeprecationとした時も、UnityEditor上で新たにJavaScriptやBooが生成することが出来なくなったが、既存のファイルは使用出来たので、今回の挙動も筋は通っているよにも見えます。

しかしながら、Unity2020.3のSpritePackerModeのマニュアルでは未だにLegacySpritePackerが使えるかのように記載されていますがこれは完全に間違いでしょう。

f:id:kimukats:20210908123851p:plain

検証

それでは、検証を進めていきます。

初めにUnity2019.4で作成したLegacy Sprite Packerを使用したProjectをUnity2020.2で開いて、Project Settingsを確認するとSprite Packing ModeからLegacy Sprite Packerが無くなっていることを確認出来ます。

f:id:kimukats:20210908124706p:plain
Sprite Packing Modeの選択肢からLegacy Sprite Packerが消えている

次に、Sprite Assetを確認するとPacking Tagの項目は存在しますが、編集出来なくなっていることが分かります。

f:id:kimukats:20210908125402p:plain
Packing Tagがグレーアウト

また、LibraryフォルダにあるAtlasCacheフォルダを削除するとそれ以降AtlasCacheフォルダが生成されないため、Atlasを生成することはできません。

最後にAssetBundleからSpriteを読み込んで表示を行ってみます。 2019で作成したAssetBundleの中身を確認してみると、SpriteとSpriteAtlasTextureの両方が含まれていることが確認できます。

f:id:kimukats:20210908130358p:plain

AssetBundleからSpriteを読み込むコードはこんな感じです。

public class LoadAssetBundle : MonoBehaviour
{
    [SerializeField] SpriteRenderer spriteRenderer;
    [SerializeField] string assetName;


    // Start is called before the first frame update
    void Start()
    {
        var ab = AB.Load("onepiece");
        if (ab != null)
        {
            spriteRenderer.sprite = ab.LoadAsset<Sprite>(assetName);
        }
    }    
}

このコードを実行してみます。

f:id:kimukats:20210908133006p:plain

表示できました。 UnityEditorのPlayMode及びAndroid実機の双方で表示できることを確認しています。 これは、現時点ではLegacy Sprite Packerで生成したSprite Atlas Textureが使用してSpriteを表示することが出来ることを意味します。

最後にUnity2020でAssetBundleのビルドを実行し再びAssetBundleの中身を確認します。

f:id:kimukats:20210908131215p:plain

AssetBundleからSpriteAtlasTextureが消え去り、Spriteと対になった個別のTextureが追加されていることが分かります。 この構成でも実行するとSpriteは表示されますがSpriteAtlasTextureではなく、個別にTextureをロードすることになる訳です。

最後に

ちなみにUpgrade Guide 2020LTSによると

All Asset bundle hashes are different so all bundles are built

とあります。つまり、AssetBundleのHashが変わったので、Unity2020へ移行すると全てのAssetBundleがビルドの対象になるようです。 AssetBundleの温存作戦も無理のようです。

Legacy Sprite Packerを使用していたProjectをUnity2020.1以降にマイグレーションする場合、Legacy Sprite Packerはスパッと諦めてSprite Atlasへ移行しましょう。 その場合、Unity2020へマイグレーションする前に、Legacy Sprite PackerからSprite Atlasへの移行作業を先に行った方が問題の切り分けが楽だと思います。

「新公開の Terrain Tools パッケージで作業を高速化しよう」日本語訳

概要

blog.unity.com

機械翻訳したものに少し手を加えたものです。

新公開の Terrain Tools パッケージで作業を高速化しよう

Unity 2019.1の「Terrain Tools」パッケージ(プレビュー)をご紹介します。このパッケージには、15以上の新しいスカルプトツールと、地形のワークフローを効率化するユーティリティツールボックスが含まれています。

Terrain Toolsパッケージには、全く新しいスカルプトツールと、面倒な作業を自動化するための一般的なユーティリティのコレクションが含まれています。

既存のTerrainスカルプトツールは、回転、スペーシング、スキャッターのコントロールが追加されました。強度、サイズ、回転パラメータにはジッターコントロールが追加され、ブラシストロークに自然なバリエーションを加えることができます。また、Terrainブラシのワークフローを効率化するショートカットキーが追加され、インスペクタウィンドウを操作しなくても、共通のブラシコントロールにすばやくアクセスできるようになりました。

このパッケージでは、ブリッジ、クローン、ノイズ、テラス、ツイストなど、いくつかの新しいスカルプトツールを使用できます。また、水侵、風侵、熱侵に基づいてハイトマップを修正する侵食ツールもあります。パッケージのドキュメントには、すべての新しいスカルプトツールの詳細とその使用方法が記載されています。

youtu.be

このパッケージに含まれる最後の大きな追加要素は、「Terrain Toolbox」です。Unity 2018.3では、複数のTerrainタイルにまたがってペイントできるようになりました。この新機能は素晴らしいものでしたが、多くのタイルを必要とする大規模な地形の作成を効率化し、複数の地形タイルを持つ既存のシーンを管理するために、いくつかの重要なツールが必要でした。

Terrainツールボックスは、 Menu > Window > Terrain > Terrain Toolbox.から起動できます。これは、terrainに関連するすべての設定を一元管理するハブです。プリセットの設定やインポートされたハイトマップを利用して新しいterrainを作成したり、複数のterrainタイルのterrain設定を一括して変更したり、 splat-mapsやハイトマップをインポート/エクスポートして、Unityと他のデジタルコンテンツ制作ソフトウェアとの相互運用性を高めることができます。

https://blog-api.unity.com/sites/default/files/styles/focal_crop_ratio_16_9/public/2019/05/unity-terrain-toolbox-create-.png?imwidth=2048&h=85c37772&itok=CL5sRapv

Installing the Terrain Tools Package

新しいパッケージにアクセスするには、パッケージマネージャから新しい「Terrain Tools」パッケージをインストールする必要があります。 Window > Package Managerに移動します。左側のリストで「Terrain Tools」を見つけ、ウィンドウの右下にある「Install」ボタンをクリックしてください。

https://blog-api.unity.com/sites/default/files/styles/focal_crop_ratio_16_9/public/2019/05/terrain-tools-in-package-manager.png?imwidth=2048&h=a64d11e7&itok=4M5d-O4H

本パッケージは、Unity 2019.1での動作を想定しています。そのリリースに対してのみテストされています。本パッケージはプレビュー版でもあり、そのため頻繁に変更される可能性があり(API、UX、スコープなど)、従来のUnityサポートの対象外となります。問題を発見した場合は、代わりにフォーラムページで報告してください。

Terrain Toolsパッケージを使ってUnityで美しい地形を造形する方法を紹介するために、例を見てみましょう。

Sculpting a Volcano

始める前に、ブラシのホットキーを設定して、よく使うブラシをすばやく切り替えられるようにしましょう。私はスカルプトブラシの切り替えにファンクションキーを使っていますが、ショートカットマネージャー(「Edit」→「Shortcuts menu」)を使えば、好きなように設定することができます。

https://blog-api.unity.com/sites/default/files/styles/focal_crop_ratio_16_9/public/2019/05/terrain-shortcut-manager.png?imwidth=2048&h=f4e427bf&itok=9AerhmP7

さて、ブラシのショートカットを設定したところで、まずは火山のマクロシェイプをラフに描いてみましょう。私は太平洋岸北西部に住んでいるので、1980年に噴火して大規模な地滑りが発生したセントヘレンズ山にインスピレーションを受けました。

新しいプロシージャルなノイズツールを使って、まず大まかな山の形をブロックしていきます。ブラシの設定はあまり気にしていません。有機的な変化が欲しかっただけなのですが、ノイズのオフセットとスケールを少し変えて、さまざまなスケールの特徴を作り出しました。

https://blog-api.unity.com/sites/default/files/styles/focal_crop_ratio_16_9/public/2019/05/terrain-example-rough.png?imwidth=2048&h=2c588e99&itok=Jj1gxyi6

全体的な構図には満足していますが、まだあまり自然な感じではないので、まずThermal Erosionツールを使って凸凹を少しずつ滑らかにし、実際にあるように堆積物が急な斜面に「雪崩」のように落ちていくようにしてみましょう。ここでは、「Granite Scree」プリセットを山頂に均一に使用して、角度をより自然にし、物事を少し滑らかにしました。

https://blog-api.unity.com/sites/default/files/styles/focal_crop_ratio_16_9/public/2019/05/terrain-example-erosion.png?imwidth=2048&h=112e8be9&itok=a9ycpAry

火山の山というよりは、砂の山のように見えますね。 Hydraulic Erosionツールを使って、長い年月をかけて降った雨が地形を侵食していく様子をシミュレートしてみましょう。

このツールは地形の解像度が高いほど効果的なので、地形タイルの解像度を2049に変更して、できるだけ細部まで表現できるようにしましょう。(後で解像度を下げても、侵食のディテールはほとんど維持できます)。)

Hydraulic Erosionツールを使いながら、ディテールにバリエーションを持たせるためにシミュレーションスケールのパラメータを少し調整します。また、川岸のパラメータをいじって、ツールが作成する特徴の形状を変化させます。このツールは非常に複雑なスカルプトツールなので、使いこなすには時間がかかるでしょう。しかし、すぐに結果を出したい場合は、デフォルトの設定で十分だと思います。

https://blog-api.unity.com/sites/default/files/styles/focal_crop_ratio_16_9/public/2019/05/export-highmaps-terrain.png?imwidth=2048&h=7aa0fd84&itok=Xq1vxLYy

World Machineの詳細な設定については、別のブログ記事に譲りますが、浸食、傾斜、凹みの選択ノードを組み合わせて、スプラットマップを生成し、スカルプトされたハイトマップを入力として使用しました。

https://blog-api.unity.com/sites/default/files/styles/focal_crop_ratio_16_9/public/2019/05/world-machine-set-up.png?imwidth=2048&h=e75d6518&itok=4j7XHMPG

スプラットマップが生成されたら、TerrainToolbox を使用して Unity にインポートするだけで、素敵な雪の頂上ができあがります。

https://blog-api.unity.com/sites/default/files/styles/focal_crop_ratio_16_9/public/2019/05/replace-splat-map.png?imwidth=2048&h=546b5d78&itok=tjLVghk7

https://blog-api.unity.com/sites/default/files/styles/focal_crop_ratio_16_9/public/2019/05/terrain-example-final.png?imwidth=2048&h=b98817c8&itok=tNBynlco

最後に、隣接する地形タイルを作成し、侵食ブラシを使ってさらにいたずらをして(new wind erosionブラシを使って、雪山の頂上に自然な風の影響を与えました)、主な頂上を囲む起伏のある丘を追加しました(上記と同じテクニックを使用しています)。

https://blog-api.unity.com/sites/default/files/styles/focal_crop_ratio_16_9/public/2019/05/terrain-example-final-with-snow.png?imwidth=2048&h=bbbe5782&itok=fIC4-baF

Terrain Toolsパッケージに含まれるツールが、UnityのTerrain を使用する際の経験を向上させてくれることを願っていますし、ユーザーが造形する素晴らしい地形を見るのが待ち遠しいです。 Terrain ツールチームの今後のアップデートにご期待ください!この2019年のサイクルでは、皆さんと共有するのが待ちきれないほどのツールを計画しています。

また、フォーラムページでは、新しいパッケージについてのご意見をお待ちしています。

Unity2020.3.13f1 ReleaseNote 解説

はじめに

リリースノートを見ても簡潔過ぎて、実際にどういった不具合が起きていたのかさっぱり分からないということが多々あります。

ここでは、リリースノートからそんなissueを取り出して解説していきます。

 

  • 既知の不具合

  • OpenGL: SRP Batcher not working with OpenGL APIs when the project is built (1331098)

  • Graphic APIOpenGL系を選択していた場合、SRP Batcherは動作していないようです・・・。
  • Unity2020.x系だけではなく、Unity2019以降のRender PipelineにURPを使用している全てのプロジェクトに対する既知の不具合となります。
  • Unity2021.x系ではUnity2021.1.14f1、
  • Unity2020.x系ではUnity2020.3.14f1で修正済みとなっています。
  • Unity2019.x系は現時点では未修正となります。
  • WebGL: [iOS] Video is not playing (1288692)

  • プラットフォームをWebGLでビルドしたPlayerをiOSのSufariで実行した場合にVideoの再生が出来ない不具合のようです。
  • 現時点ではUnity2021.2.0b4でのみ修正済みとなっています。

 

Fixes

  • Asset Pipeline: Fixed an issue that could cause assets with dependencies to be imported out-of-order. (1331373)

  • Issue Trackerに該当するものが無い為詳細は不明ですが、文面通りに捉えると、Asset Import時に依存関係の順番通りにインポートが行われていなかったと思われます。
  • Graphics: Fixed an issue where setting a camera's target texture to null would sometimes increased camera stack size and reduce performance. (1299403)

  • CameraのtargetTextureにnullを設定すると、Camera Stackサイズが増加し、パフォーマンスが劣化することがあった模様です。スクリプトリファレンスにもレンダリング先をRenderTextureをから描画面に切り替える場合、targetTextureにnullを設定するよう記載されているので、おそらく影響を受けているプロジェクトは多々あると思われます。
  • IssueTrackerによると影響を受けているUnityは2018.4,2019.4,2020.2,2021.1,2021.2となっており、Unity2018以降の全てのバージョンが該当しており、影響範囲は大きいと思われます。
  • Camera-targetTexture - Unity スクリプトリファレンス

 

  • Scripting: Fixed an issue when using GetComponents with list caused a memory leak. (1318407)

public void GetComponents (List<T> results)に渡す引数が、staticで空では無い場合、Listから参照されたいたオブジェクトがガーベージコレクトの対象とならないという不具合の模様です。

こちらもUnity2018.4以降のすべてのバージョンが対象となっており、影響範囲は甚大です。

Unity2018はサポート期間終了の為、修正は行われません。また、Unity2019もUnity2019.4.29で修正済みとなっていますが、現時点はリリースされていません。

これらのバージョンのUnityを使用している場合、ワークアラウンドとしては、List.Clear()を実行してからGetComponentsに渡すことで回避可能となっていますので、一旦その方法を試してみるのも良いかもしれません。

 

UI: Fixed an issue where calculation of size was incorrect for cameras that have a view rect set. (1325324)

CanvasのRenderModeがScreen Space-Cameraの場合、CameraのViewport Rectの値が正しく計算されず、Canvas内のUIオブジェクトの表示位置が影響を受けていたようです。

この修正により、UIの表示位置がこれまでと変わってしまう可能性がある為、Unity2020.3.13以降へバージョンアップを行う場合は注意すべきでしょう。

※IssuTrackerでは下記のように記載されているので、Unity2021.x系でのみ発生する不具合のようですが、Unity2020.3.13で修正が行われているので注意が必要です。

 

 

Reproducible with: 2021.1.0a7, 2021.1.1f1, 2021.2.0a12
Not reproducible with: 2018.4.33f1. 2019.4.23f1, 2020.3.2f1, 2021.1.0a6

 

 

 

以上

 

Android SDK,NDK,JDKとGradleへのパスをScriptから取得してみよう!

概要

UnityEditorのPreferences->External Tools->Androidの部分で設定する、JDK,Android SDK,Android NDK,GradleのパスをScriptから取得・設定してみます!。

f:id:kimukats:20210719170819p:plain

EditorPrefs

これらのパスはEditorPrefsの機能を使用して保存されています。 EditorPrefsの保存先はOSによって異なりますが、Windowsの場合、Registryの機能を使用しています。 よって、Registry EditorでUnity関連のパラメータを検索すると下記のKeyを発見する事が出来ます。

名称 Key
Android SDK AndroidSdkRoot
Android NDK AndroidNdkRoot
JDK JdkPath
Gradle GradlePath

このKeyを使って、各Pathの内容を取得する為のScriptは下記の通りです。

var androidSdkPath = EditorPrefs.GetString("AndroidSdkRoot");
var androidNdkPath = EditorPrefs.GetString("AndroidNdkRoot");
var jdkPath = EditorPrefs.GetString("JdkPath");
var gradlePath = EditorPrefs.GetString("GradlePath");

同様に、各Pathの内容を書き換える為のScriptは下記の通りです。

EditorPrefs.SetString("AndroidSdkRoot", androidSdkPath );
EditorPrefs.SetString("AndroidNdkRoot",androidNdkPath );
EditorPrefs.SetString("JdkPath",jdkPath );
EditorPrefs.SetString("GradlePath",gradlePath);

以上です。

SpriteAtlasとAssetBundleについて検証してみよう!

概要

Unityでは複数のTextureを1枚のTexture(Atlas)に纏めることで、ドローコールを減らし描画パフォーマンスを向上させる効果が期待できます。 このAtlasを作成する為の機能としてLegacy Sprite PackerとUnity2017で追加されたSprite Packerの2種類が存在します。 この2種類の機能は排他的に使用する事が可能でProject Settings > Editor > Sprite Packer > Modeからどちらを使用するか選択することになります。

f:id:kimukats:20210714114127p:plain

ここでは、新旧SpritePackerとAssetBundle化について検証していきますが、ちょっと長くなってしまったので、検証した結果のポイントを先に述べますが、SpriteAtlasはSpriteAtalsを利用しているAssetの依存関係の要因とならないという点に注意して下さい。つまりSpriteAtlasに変更があっても、それを参照しているAssetのAssetBundleは更新の対象となりません。

検証環境

Unity2019.4.28f1

Legacy Sprite Packer

SpriteAtlasを作成するには、TextureImporterでTexture TypeSprite(2D and UI)に設定し、Sprite ModePacking TagにAtlas名を指定します。同じAtlas名を選択したSpriteが同じSpriteAtlasに纏められるという訳です。 f:id:kimukats:20210714134614p:plain

今回0~9とAのTextureを用意し、Packing TagNumbersというTagを設定しました。 SpritePackerWindowで表示するとこのように1枚のAtlasにまとまっていることがわかります。 f:id:kimukats:20210714134118p:plain

さてここで気になる点が一つ。 Sprite Packer Windowで表示されているAtlasは何処に生成されるのでしょうか? 実はLegacy Sprite Packerで生成されたSpriteAtlasはAssetではありません。 キャッシュとしてProject\Assets以下ではなくProject\Library\AtlasCache以下に生成されます。 f:id:kimukats:20210714135450p:plain

Legacy Sprite Packerで生成されたSpriteAtlasはAssetではないというのは非常に重要で、これはLegacy Sprite Packerで生成されたSpriteAtlasが単体ではAssetBundleを作成出来ないことを意味します。

では、SpriteAtlasをAssetBundle化する為にはどうすればよいでしょうか? これは、SpriteAtlasを参照するAssetをAssetBundle化することで実現出来ます。

図のようにSpriteAtlasに含まれる0のTextureを参照するAssetを生成してAssetBundle化を行います。 f:id:kimukats:20210714142153p:plain

尚、AssetBundleを生成するScriptはこんな感じです。

using UnityEditor;

public class AssetBundleBuild
{       
    [MenuItem("Assets/AssetBundle/Build")]
    public static void Build()
    {
    UnityEditor.BuildPipeline.BuildAssetBundles("Assets/AssetBundles", UnityEditor.BuildAssetBundleOptions.ChunkBasedCompression, UnityEditor.BuildTarget.Android);
    }    
}

出来上がったAssetBundleの中身を確認するとこんな感じです。

f:id:kimukats:20210714142810p:plain

サムネイルが小さくてわかりずらいですが、AssetBundleの2番目の要素としてSpriteAtlasが含まれていることが見て取れます。

ちなみに、AssetBundleの中身の表示はテラシュールブログで紹介されているものを使用させて頂いています。

同様にAというTextureを参照するSpriteAを作成し、alphabetabという名前で別のAssetBundleを作成してみます。

f:id:kimukats:20210714144129p:plain

こちらのAssetBundleにも同じSpriteAtlasが含まれていることがわかります。 f:id:kimukats:20210714144749p:plain

これは、SpriteAtlasを単独でAssetBundle化出来ない為、複数のAssetBundleにSpriteAtlasが重複して含まれしまうことを意味しますのでAssetBundleの構成には注意が必要です。alphabetabではTextureAのみ含まれていれば良いのでそれ以外のTextureは無駄でしかありません。TextureAのPacking TagをNumbersからAlphabetに変更してみます。

f:id:kimukats:20210714145808p:plain

SpriteAtlasはNumbersとAlphabetの2枚となりそれぞれ次のようになります。

f:id:kimukats:20210714145838p:plain f:id:kimukats:20210714145857p:plain

AがNumbersからAlphabetに移動していることが確認出来ます。 それではAssetBundleのビルドを行い、AssetBundleの中身を確認してみます。

f:id:kimukats:20210714151713p:plain f:id:kimukats:20210714151743p:plain

alphabetabに含まれるSpriteAtlasは変わっていますが、numbersabは以前のままです。何故でしょうか? ・・・ ・・・ 理由は、SpriteAtlasがAssetBundle化されるSpriteの依存ファイルとならない為です。 Sprite.prefabが参照しているつまり、依存しているAssetは、Texture0・Sprite-Default Material・Sprite-Default.shaderです。 同様にSpriteA.prefabが依存しているAssetはTextureA・Sprite-Default Material・Sprite-Default.shaderです。 先程、TextureAのPacking Tagを変更した為、SpriteA.prefabの構成要素の変更があったと判断されalphabetabの再ビルドが実行されましたが、Sprite.prefabの構成要素には変更が無かった為、AssetBundle numbersの再ビルドは実行されなかったということです。

AssetBundle numbersに含まれるSpriteAtlasを更新したい場合、numbersの再ビルドを実行する必要があります。 いちばん手っ取り早い方法は、AssetBundle numbersを削除することでしょう。 若しくはBuildAssetBundleOptions.ForceRebuildAssetBundleを指定して、AssetBundleをビルドするという方法もあります。

SpritePacker

Legacy Sprite Packerと違い、明示的にProject Viewから右クリックでCreate > Sprite AtlasでSpriteAtlasを生成します。 SpriteAtlasへのSpriteの追加は手動で行います。SpriteAlasのInspectorからObjects for Packingの項目にある+を押して、Spriteを追加していきます。 f:id:kimukats:20210714160125p:plain

Spriteを追加した結果はこんな感じです。 f:id:kimukats:20210714160413p:plain

詳しいワークフローはこちらをご確認下さい。

それではAssetBundleをビルドして中身を確認してみます。

f:id:kimukats:20210714162209p:plain 左がnumbersab,右がalphabetabですがぱっと見た感じどちらも同じものです。

では、Numbers.spriteatlasからAを取り除いてAlphabet.spriteatlasを新たに作成しそちらへAを追加します。

f:id:kimukats:20210714162654p:plain f:id:kimukats:20210714162632p:plain

この状態でAssetBundleをビルドしてみます。 ・・・結果は変わりません。SpritePackerの場合でもSpriteAtlasは依存ファイルとはならないようです。 しかたがないので強制的にAssetBundleをビルドしてみます。

f:id:kimukats:20210714165113p:plain 左がnumbersab,右がalphabetabです、期待道理の結果です。

さて、SpriteAtlas自体をAssetBundleにするとどうなるのでしょうか?

f:id:kimukats:20210714172510p:plain

いい感じです。SpriteAtlasがAssetBundle化され、SpriteAtlasを参照していたPrefabのAssetBundleからは消滅しています。 また、SpriteAtlasに変更を加えた後、AssetBundleをビルドするとSpriteAtlasのAssetBundleはビルドの対象となります。

また、SpriteAtlasをAssetBundle化する場合、include In Buildのチェックは外しておくべきでしょう。 このチェックが有効な場合、SpriteAtlasはアプリケーションに含まれることになる為、2重にSpriteAtlasを持つ事になります。

考察

なぜ、SpriteAtlasは依存関係の対象とならいか考察してみます。 運用中のタイトルで考えてみると、何となくその理由がわかってくる気がします。

ある運営タイトルでSpriteAtlasを参照しているAssetBundle Aがあるとします。 あるイベントで新規のスプライトをSprteAtlasに追加し、そのスプライトを参照するAssetBundleBを追加したとします。 この時、SpriteAtlasが依存関係をもってたとすると、AssetBundleAもビルドの対象となり、ユーザーは更新されたAssetBundleAと新規のAssetBunldeBの両方をダウンロードする必要がありますが、現象の仕組みではAssetBundleAは更新されないので、ユーザーはAssetBundleBのみを更新すればよいことになります。 既存のSpriteAtlasを分割する時は、自動更新されないという不満は残りますが、大きな変更ですので明示的に更新を実行すれば良いですし、最悪更新されなくても描画されないという事故は防げます。

結論

  • SpriteAtlasはそこに含まれるSpriteを参照(使用)するAssetの依存関係とはなり得ないので、SpriteAtlasが変更されても参照先のAssetBundleは更新されない。
  • Legacy Sprite Packerで生成されたSpriteAtlasはSpriteAtlas単体でのAssetBundle化は出来ないが、(新)SpritePackerで生成されたSpriteAtlasはAssetである為、SpriteAtlas単体でAssetBundle化出来る。

以上のことから、これから始まるプロジェクトではLegacy Sprite Packerを使用するメリットはありません。 AssetBundleを使用する場合、SpriteAtlasの特殊性を頭の片隅に覚えておくことで思わぬ事故が防げると思います。