太郎Work

Unityとかで困ったこと等を残しておきます

EnterPlayMode時にNativeArrayのエラーが発生する

Unity2019.3.1f1の時点です

再生までの時間が早くなるEnterPlayModeを使用すると
NativeArrayアクセス時に↓このエラーが発生します

InvalidOperationException: The NativeArray has been deallocated, it is not allowed to access it

ExecuteAlwaysを指定しているためOnEnableでnew、OnDisableでDisposeしているのですが、どうも適切に開放、確保ができていないようです。

f:id:tarowork:20200226111055p:plain

ReloadSceneにチェックを付ければエラーは発生しなくなりました

UnityTimelineのTrackをスクリプトから入れ替える

現在Timeline関連の実装をしているのですが、Trackの順序をスクリプトから入れ替えたくなったのでメモ
多分こんなことしたい人いないと思うので需要ゼロです

入れ替えたい理由はPlayableOutputの接続順をプログラム的に変えたかったためです

本来はWindow内でTrackを掴むことで順番を入れ替えられます

f:id:tarowork:20200219134548p:plain
Timeline Window

このTrackを保持しているのはPlayableDirectorに指定しているTimelineAssetですが、このTimelineAssetにはMoveが用意されていません

docs.unity3d.com

悲しいことにCreateTrackとDeleteTrackしかなく、TimelineWindowの実装を追っていくと内部でMove的なメソッドは存在していますが呼び出しやすい感じにはなっていません…

そこでTrackを入れ替えたときのTimelineAssetの差分を見てみると `List m_Tracks` 中身を変更しているだけでした
f:id:tarowork:20200219144759p:plain

なので無理やりSerializedPropertyから書き換えることで入れ替えがうまくいきました。

var so = new SerializedObject(timelineAsset);
var tracks = so.FindProperty("m_Tracks");
tracks.MoveArrayElement(src, dst);
so.ApplyModifiedProperties();
so.Dispose();

これだけだとTimelineWindowの描画が更新されないのでReflectionでTimelineAssetを再読み込みするメソッドを呼び出します
第4引数のboolをtrueにしておくことで実質Reload状態にすることが出来ました

var timelineWindowType =
    typeof(UnityEditor.Timeline.TimelineEditor).Assembly.GetType("UnityEditor.Timeline.TimelineWindow");
var window = EditorWindow.GetWindow(timelineWindowType);
var method = timelineWindowType.GetMethod("SetCurrentTimeline",
    BindingFlags.Instance | BindingFlags.NonPublic,
    null,
    new[] {typeof(TimelineAsset), typeof(PlayableDirector), typeof(TimelineClip), typeof(bool)},
    null);

method?.Invoke(window, new[] {timelineAsset, _director, (object) null, true});
<||

VFXGraphで星空を描画してみた

はじめに

本記事は QualiArts Advent Calender 2019 24日目の記事になります。
昨日は @ogrszk の 「Spineでできること〜エディタのTipsとAPI使用例〜」でした


f:id:tarowork:20191121212257j:plain
星空
Unity2019になり、UniversalRenderPipelineなど新機能が追加されましたが、その中の一つであるVFXGraphを使用して星空を描画してみました

注意:

  • 天文学全くわからない素人なので厳密な計算とかは分からないです
  • 新機能の基本的な使用方法は割愛しています

環境

  • Unity2019.3.0b11
  • UniversalRP 7.1.5

目次

  • はじめに
  • 星のデータを用意
  • プロジェクトの作成
  • 星描画に使うPointCacheを生成
    • テキストからMesh化するプログラム
    • 明るさ
    • MeshからPointCacheAssetを作成
    • 星のテクスチャを作成
  • VFXGraph
    • Spawn
    • PointCacheNode
    • GetAttributeNode: color
    • Output Particle Quad
    • VFXGraph全体
    • 結果
  • レンダリング
  • まとめ
続きを読む

Unity2019時代のパッケージ管理

今までのUnityでは .unitypackage を使用したり Assets 以下にGitのSubmoduleで管理するといった方法がありましたが、やっとPackageManager周りが実用的になったので個人的に良いなと思ったフローを紹介します

今回の記事は2019.1.3で作成しています。今後仕様が変わる可能性があるので注意してください
ここで作成したサンプルはこちらに上げているので気になったらどうぞ
github.com

続きを読む

UnityEditorのGameViewが更新されない時の対処方法

非実行時にEditorスクリプトからHierarchey内のオブジェクトに変更を加えても
UnityEditor全体に対しての更新通知が行かないため、SceneView,GameViewの描画更新が行われない

Viewサイズを変える、Inspectorから変更をする等をすれば更新されるがすぐに確認したい場合

Unity - Scripting API: EditorApplication.QueuePlayerLoopUpdate

遡ると2017.2から追加された模様

これを呼び出せばUnityEditor全体にRepaint通知が行ってすぐに見た目が確認できるようになった

GetComponentの負荷検証

気になる内容の記事があったので検証してみました
qiita.com

気になる点

  • Dictionaryのインデクサを使用している←TryGetValueを使用しないと負荷が高い(ソースは忘れた…)
  • transform.Rotationの実装次第ではあまり検証になっていない…気がする

ちょっと修正版作成

コードそのままお借りしてます+無理やり適当な名前で追加してすみません

極力transform取得の処理負荷が出るようにlocalPositionにしています

public class PerformanceTest : MonoBehaviour {
	private readonly Dictionary<Type, object> _componentCache = new Dictionary<Type, object>();

	public new T GetComponent<T>() where T : Component
	{
		var type = typeof(T);
		if (_componentCache.ContainsKey(type) == false)
		{
			var component = base.GetComponent<T>();
			if (component == null) component = gameObject.AddComponent<T>();

			_componentCache.Add(type, component);
		}

		return (T) _componentCache[type];
	}

	private readonly Dictionary<Type, Component> _componentCacheFast = new Dictionary<Type, Component>();
	public T GetComponentFast<T>() where T : Component
	{
		var type = typeof(T);
		Component component = null;
		if (_componentCacheFast.TryGetValue(type, out component) == false)
		{
			component = base.GetComponent<T>();
			if (component == null) component = gameObject.AddComponent<T>();

			_componentCacheFast.Add(type, component);
		}

		return (T) component;
	}

	private readonly int max = 10000;
	private CustomSampler samplerNormal;
	private CustomSampler samplerDefaultGetComponent;
	private CustomSampler samplerOverrideGetComponent;
	private CustomSampler samplerGetComponentFast;
	private CustomSampler samplerCache;
	private Transform cacheTransform;

	private void Awake()
	{
		samplerNormal = CustomSampler.Create("transform");
		samplerDefaultGetComponent = CustomSampler.Create("base.GetComponent");
		samplerOverrideGetComponent = CustomSampler.Create("GetComponent");
		samplerGetComponentFast = CustomSampler.Create ("GetComponentFast");
		samplerCache = CustomSampler.Create("cacheTransform");
		cacheTransform = base.GetComponent<Transform>();
	}

	private void Update()
	{
		Vector3 pos;
		//普通の使い方
		samplerNormal.Begin();
		for (var i = 0; i < max; ++i)
			pos = transform.localPosition;
		samplerNormal.End();

		//GetComponentおじさん
		samplerDefaultGetComponent.Begin();
		for (var i = 0; i < max; ++i)
			pos = base.GetComponent<Transform> ().localPosition;
		samplerDefaultGetComponent.End();

		//貼るだけで速くなるコード(?)
		samplerOverrideGetComponent.Begin();
		for (var i = 0; i < max; ++i)
			pos = GetComponent<Transform> ().localPosition;
		samplerOverrideGetComponent.End();

		//貼るだけで速くなるコードTryGetValue版
		samplerGetComponentFast.Begin();
		for (var i = 0; i < max; ++i)
			pos = GetComponentFast<Transform> ().localPosition;
		samplerGetComponentFast.End();

		//メンバ変数でキャッシュ
		samplerCache.Begin();
		for (var i = 0; i < max; ++i)
			pos = cacheTransform.localPosition;
		samplerCache.End();
	}
}

結果

丁度2018が正式リリースされていたので2018.1.0f2で検証
f:id:tarowork:20180503033428p:plain

transform 0.58ms
GetComponent 0.846ms
DictionaryIndexer 3.17ms
DictionaryTryGetValue 2.06ms
transformCache 0.399ms

TryGetValueにするだけで約65%軽減(どっちにしろ重いけど、、)
キャッシュとの違いは10倍近く

結論

何が言いたいかというとキャッシュはしたほうがいいよ
Dictionaryのインデクサは使わないほうがいいよ
という話でした

Unityを使って水っぽい表現作ってみた

今GravityDAZE2をやっているのですが、水表現が面白いことになっていたので自分でUnityを使って実装してみました


背景が殆ど無いので分かりにくいと思いますが…

シェーダ

モデルの裏側をキャプチャしたものを使用して法線で歪ませて描画する方法で実装しました

GrabPass { "_WaterGrab" }

GrabPassを記述すると描画直前のバッファを_GrabTextureにコピーしてくれますが、上のようにTexture名を指定することでこのGrabPassが初めて呼ばれたときのみコピーされるみたいです。

例えばこのシェーダで4個メッシュを描画すると1個めが描画されたときのみGrabPassが走り、2個め以降は1個めで使用したGrabTextureを再利用する
これで負荷を最小限にできます!

あとは一般的なスクリーン投影計算とリム計算ぐらいです。

half kScale = 1.7777;
half2 enc;
enc = i.viewNormal.xy / (i.viewNormal.z+1);
enc /= kScale;
i.viewNormal.xy = enc * 0.5;

フラグメントシェーダ内のこの処理は法線方向をスクリーンでいい感じに見えるように変換する計算式です
UnityCG.cgincのEncodeViewNormalStereo関数を持ってきました
Unityの法線テクスチャ描画時に呼ばれている関数なのですが、使ってみたら結構いい感じになったのでこれにしました。

Cloth

水の揺れ表現にはClothを使ってみました
f:id:tarowork:20170130022406p:plain

パラメータはちょこちょこっと変えていい感じにして外部から少しだけ力を加えて動くようになっています。
頂点設定は一部の頂点を動きにくい設定にして凸凹感が出やすいようにしました。
多分ここをきれいに設定するともっと柔らかい歪みになると思います。
0.1だと動かなすぎるし大きい値だと動きすぎて本来の一から離れすぎてしまうので0.15~0.2ぐらいがちょうどよかったです

Cloth工夫次第でいろいろできそうな予感がします


これ使って何か作りたい