渋谷ほととぎす通信

新しいこと、枯れたこと問わず大庭が興味を持ったものを調査、生活の効率を求める完全趣味の技術ブログ

UIElementsのStyleSheetインスタンスは保持してはいけない

開発環境をUnity2019にアップグレードしたし、新機能のUIElementsを始めました。

公式のブログを読みながら進めています。

本記事はUIElementsに関する落とし穴共有です。

StyleSheetのフィールド保持

何も気にせず、StyleSheetインスタンスをフィールドに保持してしまいました。
以下のような感じで。

StyleSheet _stylesheet;

void OnEnable()
{
    _stylesheet = AssetDatabase.LoadAssetPath<StyleSheet>("Assets/Editor/Hoge/Hoge.uss");

    var label = new Label("test");
    label.styleSheets.Add(stylesheet);
}

ussファイル更新ができなくなる

ussファイルを変更するとこの通りエラーが起きてしまう。

MissingReferenceException: The object of type 'StyleSheet' has been destroyed but you are still trying to access it.
Your script should either check if it is null or you should not destroy the object.
UnityEngine.Object.get_name () (at /Users/builduser/buildslave/unity/build/Runtime/Export/Scripting/UnityEngineObject.bindings.cs:189)
UnityEngine.UIElements.StyleSheets.StyleSheetCache.GetPropertyID (UnityEngine.UIElements.StyleSheet sheet, UnityEngine.UIElements.StyleRule rule, System.Int32 index) (at /Users/builduser/buildslave/unity/build/Modules/UIElements/StyleSheets/StyleSheetCache.cs:333)
UnityEngine.UIElements.StyleSheets.StyleSheetCache.GetPropertyIDs (UnityEngine.UIElements.StyleSheet sheet, System.Int32 ruleIndex) (at /Users/builduser/buildslave/unity/build/Modules/UIElements/StyleSheets/StyleSheetCache.cs:253)
UnityEngine.UIElements.VisualTreeStyleUpdaterTraversal.ProcessMatchedRules (UnityEngine.UIElements.VisualElement element, System.Collections.Generic.List`1[T] matchingSelectors) (at /Users/builduser/buildslave/unity/build/Modules/UIElements/VisualTreeStyleUpdater.cs:313)
UnityEngine.UIElements.VisualTreeStyleUpdaterTraversal.TraverseRecursive (UnityEngine.UIElements.VisualElement element, System.Int32 depth) (at /Users/builduser/buildslave/unity/build/Modules/UIElements/VisualTreeStyleUpdater.cs:248)
UnityEngine.UIElements.StyleSheets.HierarchyTraversal.Recurse (UnityEngine.UIElements.VisualElement element, System.Int32 depth) (at /Users/builduser/buildslave/unity/build/Modules/UIElements/HierarchyTraversal.cs:24)
UnityEngine.UIElements.VisualTreeStyleUpdaterTraversal.TraverseRecursive (UnityEngine.UIElements.VisualElement element, System.Int32 depth) (at /Users/builduser/buildslave/unity/build/Modules/UIElements/VisualTreeStyleUpdater.cs:271)
UnityEngine.UIElements.StyleSheets.HierarchyTraversal.Recurse (UnityEngine.UIElements.VisualElement element, System.Int32 depth) (at 

StyleSheetはローカル変数で使用

このようにローカル変数に保持しておけば問題は起きません。
ussファイルを変更するとシームレスに見た目の更新が入ります。

void OnEnable()
{
    var stylesheet = AssetDatabase.LoadAssetPath<StyleSheet>("Assets/Editor/Hoge/Hoge.uss");
    var label = new Label("test");
    label.styleSheets.Add(stylesheet);
}

最後に

まだ始めたばかりでよくわからないUIElementsですが、uxml、ussで構造と見た目を作っていく感じは、今までのIMGUIとは全く違うアプローチです。

またHTML/CSSのような馴染みもあるため、自分の武器にすべく完全理解しようと思います。

以上

子階層のSpriteRendererのSpriteが1つでも未設定だったらリストを赤くするUnityのEditor拡張

オブジェクトの設定が正しいかどうかをチェックする方法を考えていて、見た目から分かるアプローチをやってみます。

お題は「SpriteRendererのSprite設定がnullのオブジェクトを分かりやすくする」です。

Hierarchy、Projectそれぞれ処理を書く

  • EditorApplication.hierarchyWindowItemOnGUI
  • EditorApplication.projectWindowItemOnGUI

EditorApplicationの hierarchyWindowItemOnGUIprojectWindowItemOnGUI メソッドでそれぞれEditor拡張を書いていきます。

Hierarchyビューでの見え方

f:id:esakun:20190805035011p:plain:w450

Projectビューでの見え方

f:id:esakun:20190805035039p:plain:w450

Hierarchyビュー、ProjectビューそれぞれSpriteが外れていたら、うっすらですが背景が赤くなるようにしています。

全ソースはこちら

最後に

static bool IsValid(GameObject go)
{
    var arr = go.GetComponentsInChildren<SpriteRenderer>();
    return arr.Any(x => x.sprite == null);
}

IsValidの部分の条件を書き換えれば、使い回せます。
プロダクトの規模が大きくなればなるほどバリデーションやチェックツールは必要になってきます。
そんなときの手段の一つとして手元においておこうと思います。

環境

  • Unity2019.1.9f1

Riderにデフォルトで表示されるUsagesやEventFunctionを消す

f:id:esakun:20190801234928p:plain

PCを変えてRiderを使った時、余計なお節介で不要な表示物がデフォルトで表示されていることがあります。

UsagesとかEventFunctionなどがそれです。 これらの表記は僕にとっては不要なので消したいと思います。

あわせて読みたい
・Riderを使っててコードが畳まれるのを解消する方法を紹介しています
Riderでコードが畳まれるのを開くのがめんどいのでやめる - 渋谷ほととぎす通信
・Riderを使う上で設定しておきたいことをまとめました
これだけはやっておきたいRider設定ランキング - 渋谷ほととぎす通信

Code Vision

これらの表示物はCode Visionと呼ばれます。

f:id:esakun:20190802002034p:plain

Preference > Editor > Code Vision > Enable Code Visitionのチェックを外すだけです。

f:id:esakun:20190802002307p:plain

スッキリ^^。

Dictionary.TryGetValueの書き方がシンプルになってた件

UnityのScripting Runtime Versionを.NET4xに切り替えてから、C#の記述が変わりました。
その中で細かいけど、ちょっとイイと思った部分を共有します。

f:id:esakun:20190612234415p:plain

Scripting Runtime Versionを.NET3.5の時の場合は、Dictionaryの書き方はこうでした。

Object result = null;
dictionary.TryGetValue(key, out result);

1行で書けるようになっている

f:id:esakun:20190612234233p:plain
Scripting Runtime Versionを.NET4.xにすると1行で書けるようになっています。

dictionary.TryGetValue(key, out var result);


以上

Unityのスプライトの単位についてまとめておく

UnityでSpriteRendererを使用して2Dを扱う際、必要知識としてスプライトの単位についてまとめておきます。

Unityの単位はメートル

基礎の基礎から

f:id:esakun:20190604235907p:plain:w250
X, Y, Zのスケールが1のこの立方体は、1立方メートルの立方体ということになります。

また、ポジションが(1, 0, 0)であれば、 原点から1メートルX座標を移動した位置ということになります。

2Dではどうなるのか?

ここからが本題です。

結論から示すと、 単位 = PixelPerUnitの数値 です。

f:id:esakun:20190529005657p:plain

PixelPerUnitは、各テクスチャに設定される値で、テクスチャごとに設定値を変えることができます。

デフォルト100なので、ポジションが(1, 0, 0)であれば原点からX座標100ピクセル移動した位置ということになります。

f:id:esakun:20190529005605p:plain
例えば上記のように幅200×100ピクセル PixelPerUnitが100の画像を2枚用意します。

f:id:esakun:20190529010152p:plain

それぞれの位置を(0, 0, 0)、(0, 1, 0)とすれば、このようにぴったりとくっついた状態になります(当たり前ですが)。

まとめ

2DではPixelPerUnitがとても大事になります。
前述しましたが、PixelPerUnityはテクスチャごとに設定を変更することができます。経験上1プロジェクトで扱うPixelPerUnitはすべて揃えた方がオペミスや事故率が下がると思います。

Unity 板ポリを比率を変えずにスクリーンサイズにフィットさせる

f:id:esakun:20190521024330p:plain:w450

  • カメラ : 位置(0,0,-5)
  • 板ポリ(board) : 位置(0, 0, 0) scale(1, 1, 1)

板ポリをスクリーンに縦横比率を変えずにフィットさせるサンプルです。

// 指定サイズの比率
float targetRatio= targetWidth / targetHeight;

// 画面比率
float screenRatio = (float) Screen.width / (float) Screen.height;

//  Camera.orthographicSizeは高さの半分なので、2倍して高さを算出
var height = _camera.orthographicSize * 2f;
float width = 0;
if (screenRatio > targetRatio)
{
    // 画面の方が指定サイズより横長なので、指定サイズの幅を画面幅にフィットさせる
    width = screenRatio * height;
    height = width / targetRatio;
}
else
{
    // 画面の方が縦長なので縦を画面の高さにフィットさせる
    width = height * targetRatio;
}

board.localScale = new Vector3(width, height, 1f);

f:id:esakun:20190521025552p:plain:w450

f:id:esakun:20190521025704p:plain:w450
するとこのように、めでたくUnityちゃんがスクリーンにフィットするようになりました。

以上

Unityスクリーン座標をワールド座標に変換する

個人的に時々必要になる且つよく忘れるコードなのでメモです。

この記事でも活用してて、

f:id:esakun:20181205152326g:plain
このようなスクリーン座標のマウスに追随してワールド座標にメッシュを描画するときに使用しています。

スクリーン座標

f:id:esakun:20190521012411p:plain:w450
スクリーン座標は左下が原点で、右上にひくほど値が大きくなります。

ゲームビューのサイズ設定との関係性

FreeAspectの場合

f:id:esakun:20190521012828p:plain:w450
Unityのゲームビューで、FreeAspectを選んだ際は、等倍で表示されたことになります。

絶対値でサイズ指定の場合

f:id:esakun:20190521013017p:plain:w450
値指定をしている場合は、その値が座標の最大値になります。

比率でサイズ指定の場合

比率指定の場合の画面サイズは等倍です。

ソースコード

var screenPos = Input.mousePosition;
// zの値をスクリーンの位置(カメラからz離れた位置)
screenPos.z = Mathf.Abs(_camera.transform.position.z);

// スクリーン座標をワールド座標に変換
var worldPos = _camera.ScreenToWorldPoint(screenPos);

ポイントはInput.mousePositionしたタイミングではZ座標が0なので、そのまま使用するとカメラと同じ座標になり、思ったのと違う結果になってしまうため、現在のカメラのz'スクリーンからの距離)を指定することで、思った通りの挙動になると思います。

以前にも同じようなことをまとめていました。

Unity2019.1でAndroidリリースに必要なKeyStoreを作成する

Unity2019.1.0もリリースされ、早速個人的な開発に使用しています。
本記事ではUnity2019.1.0でKeyStoreを作成したログを残します。

f:id:esakun:20190507094646p:plain:w450

Build Settins > Player Settings > Publising Settings > KeyStoreManagerを使用します。

f:id:esakun:20190507095625p:plain:w450
Create New > Anywhereでkeystoreファイルの保存先を選択します。 パスワードも設定します。

f:id:esakun:20190507095250p:plain:w450
New Key Valuesに各情報を記載していきます。公式ページ的にはこの辺↓↓。

公式ページに各記載項目の詳しい情報は無いようですが、この辺は常識の範囲で記載すればよいかと思われます。

項目 説明 備考
Alias KeyStoreのわかり易い名前
Password パスワード
Validity(years) 有効年数(デフォ50年)
First and Last Name 名前
Organization Unit 部署名
Orgnization 会社名, 組織名
City or Locatioin 都市名 例 : Setagaya-ku
State or Province 県名 例 : Ibaragi-ken
Country Code 国番号 例 : 日本なら81

f:id:esakun:20190508094925p:plain:w450
Add KeyをクリックしてKeyStoreが作成されて完了です。

環境

  • Unity2019.1.0f2
  • macOS HighSierra 10.13.6

DOTweenで360度くるくる回転させたい

f:id:esakun:20150825162207p:plain

DOTweenを使って360度オブジェクトを回転させたい場合、少しいつもと違う設定が必要になります。

transform.DOLocalRotate(new Vector3(0, 0, 360f), 6f)
    .SetEase(Ease.Linear)
    .SetLoops(-1, LoopType.Restart);

普通に上のように書いてしまうと、オブジェクトは一切回転しません。 0度と360度は同じ角度と判定されてしまうためです。

RotateMode.FastBeyond360を設定する

transform.DOLocalRotate(new Vector3(0, 0, 360f), 6f, RotateMode.FastBeyond360)
    .SetEase(Ease.Linear)
    .SetLoops(-1, LoopType.Restart);

DOLocalRotateの第3引数にRotateMode.FastBeyond360を設定することで、このように360度回転させることができます。

f:id:esakun:20190505004921g:plain:w450

以上

今週のお題「特大ゴールデンウィークSP」

Unity CRI ADX2LEエラー「CriAtomPlugin is not initialized」の対処

今週のお題「特大ゴールデンウィークSP」
CRI ADX2LEをUnityで使用していて、シーン開始フローの中でACFファイルをロードしたタイミングで、

CriAtomPlugin is not initialized

というエラーに遭遇した共有です。

ACFファイル自体はAtomCraftから正しく出力されて、UnityプロジェクトのStreamingAssetsに格納されていることを確認できています。 ちなみに、以下のようなコードでACFファイルをロードしています。

private IEnumerator LoadAcf()
{
    var path = $"{Application.streamingAssetsPath}/Hoge.acf";
    CriAtomEx.RegisterAcf(null, path);
    yield return null;

    if (CriAtomEx.GetNumGameVariables() == -1)
    {
        // ここでCriAtomPlugin is not initializedエラー
        Debug.LogError(message);
    }
}

CriWareLibraryInitializerとCriWareErrorHandlerがシーンになかった

一条さんの記事を改めて読んで、ADX2LEの導入をやってみて特定できました。

原因は単純で、CriWareLibraryInitializerとCriWareErrorHandlerを予めシーンに配置しておかないといけないということでした。

最後に

CRIのエラーログをググっても解決方法が出てこないことがよくあり困ることが多々あります。。。
少しハマりました。以上。

Unity2019 AndroidのPatchビルドが良い感じに高速

f:id:esakun:20190502033955p:plain:w450

Unity2019.1から搭載されたAndroid向けのPatchビルド。

Android: Added Script Only Patching functionality, which sends only script-related changes to the device instead of repackaging the APK file.

What's new in Unity 2019.1 - Unityより

スクリプトだけを再ビルドしてAPKを書き出してくれます。

So when you’re iterating on your C# code, only recompiled libraries are sent to the device.

Unity 2019.1 リリース – Unity Blogより

さらに、変更されたライブラリだけを再コンパイルしてデバイスに送信するとあります。このライブラリがどのような単位なのかがよく分かりませんが、確実にAndroidのビルド時間を削減してくれそうです。

使用した体感としても今までのビルド時間より短縮されているように思えました。

パッチビルドできない状況

パッチビルド時にこのようなエラーが出るときがあります。

Error building scripts: Data layout for script 'Hoge' has changed. Need to do a complete player export

上記の例だとHogeファイルの内容が大きく変わってしまって、フルビルドが必要という旨だと思われます。 こんなときは、いつものようにBuildボタンをクリックしてフルビルドすると良いのですが、Scripts Only Buildのチェックボックスを外してからBuildボタンを押さないと解消されません。

まとめ

iOS開発の場合、Xcodeプロジェクトを書き出してから再度Xcodeでビルドして初めて実機で確認できますが、Androidの場合は、UnityからAPKを書き出せてとても実機確認が楽です。
今回のパッチビルドが追加されたことで、Patch And Runを使用するとさらに実機確認までを高速化でき、作業効率を大きく上げることができそうです。

今まではiOS端末ベースで開発していたのですが、現在は作業効率的にAndroidでとりあえず開発して、その後でiOS確認という流れになりました。

LogCatもUnity上で確認できるようになり、Android開発が良い感じになってきているように感じます。

以上

環境

  • Unity2019.1f2
  • macOS HighSierra 10.13.6

Rider2019.1アップデートで待望のIL Viwerが実装されている

f:id:esakun:20190501025742p:plain:w500
その昔、UnityをMonoDevelopで開発しているときには身近に存在していたIL Viwer。
Riderに移ってからはそれが無く、不便に感じていましたが、とうとうRider2019.1で実装されました。

ILとは

ILとはC#をコンパイルする際に作られる中間言語です。ILを読むことでコードの最適化やC#よりローレベルな処理の仕組みを理解することができます。

以前ILについて勉強した記事を参考にどうぞ。

RiderのIL Viwer

f:id:esakun:20190501030018p:plain:w300
Tools > IL ViwerでIL Viwerを開きます。

f:id:esakun:20190501030149p:plain:w500
C#にキャレットを合わせると、ILがビルドされてIL Viwerに該当箇所が表示されます。

f:id:esakun:20190501033811p:plain:w450
ILにマウスを合わせると説明が出てきて、ILの勉強に良いです。

まとめ

僕にとっては待望の機能がRiderに実装されて、ますますRiderとは密結合になりつつあります。

参考

Unityエラー「The same field name is serialized multiple times in the class or its parent class」の対処

f:id:esakun:20150730215258g:plain:w450

The same field name is serialized multiple times in the class or its parent class. This is not supported: Base(MonoBehaviour) _hoge UnityEngine.GUIUtility:ProcessEvent(Int32, IntPtr)

エディタで実行中は問題なかったのですが、Android向けにビルドすると上記のエラーが出てしまったので対処してみます。

親子クラス内で同名シリアライズ指定するとダメ

C#という視点から見ると、親クラス、サブクラスで同名private変数を持つことは何ら問題はないのですが、Unity的な視点で見るとSerializeFieldアトリビュートをつけてしまうとよろしくありません。

// 親クラス
class Parent : MonoBehaviour
{
    [SerializeField] int _hoge;
}


// 子クラス
class Child : Parent
{
    [SerializeField] int _hoge;
}

上記のような状態はUnityはサポートしていないということです。

エラー個所を特定する

エラー文の中にThis is not supported: Base(MonoBehaviour) _hogeとフィールド名_hogeが犯人だということを教えてくれるので、全文検索してそれっぽい箇所を修正すれば良いかと思います。

以上

Unity SpriteRendererで表示した画像をドラッグして端から端までピッタリ表示する

f:id:esakun:20190424203414p:plain:w450

結論

  • X座標ドラッグ可能距離 = 画像サイズ / PixelPerUnit - orthographicSize * (Screen.Width / Screen.Height)
  • Y座標ドラッグ可能距離 = 画像サイズ / PixelPerUnit - orthographicSize

で割り出せます。
※平行投影カメラが前提

以下のコードをドラッグ処理部分に記述する想定です。

以上です。

Unity2019 PackageManagerがメニューに表示されなくなった時の対処法

2018.3.7f1からUnity2019にアップデートした際に起こったPackageManager周りのトラブル共有です。

タイトルの通り、WindowからPackageManagerの項目が表示されなくなる現象が発生しました。

Unity2019では使用できないバージョンがmanifestで指定されているという旨のコンパイルエラーが出ていました。
というのも下記のキャプチャの通りUnity2019.1.0f2ではPackageの下位バージョンを指定できなっています。

f:id:esakun:20190417171155p:plain:w450
※指定できました。場所が変わっていました。

解決法

  1. previewパッケージを一時的に外す
  2. Unityを再起動

f:id:esakun:20190417095247p:plain
↑の子たちを一時的に削除して、再起動。

これで僕は解決し、PackageManagerと再開することができました。

以上

環境

  • macOS HighSierra 10.13.6
  • Unity2019.1.0f2