テラシュールブログ

旧テラシュールウェアブログUnity記事。主にUnityのTipsやAR・VR、ニコニコ動画についてのメモを残します。

【Unity】Unityでベクターグラフィック(SVG)を利用する

https://github.com/Unity-Technologies/vector-graphics-samples/blob/master/Documentation/vectorgraphics.md

f:id:tsubaki_t1:20180522235702g:plain

UnityのPackageManagerにSVGを利用するライブラリが追加されました。
Unity 2018.1でプレビュー版を使用することが出来ます。

 

 

どんな事が出来るの?

機能については下の動画を見てもらう方良さそうです。
結構色々なことが出来るみたいです

www.youtube.com

使い方

PackagesのManifest.jsonの中身に以下の行を追加します。

そうするとステージングのPackageManagerにアクセス出来るので、あとはVectorGraphicsをImportするだけです。

f:id:tsubaki_t1:20180523000423j:plain

f:id:tsubaki_t1:20180523000237j:plain

 

パッケージのインポート後は、SVG形式のファイルをSpriteとして使用することが出来るようになります。

f:id:tsubaki_t1:20180523000844j:plain

 

ShaderはUnlit/Vectorという新しいシェーダーを使用して表現しているので、シェーダーを差し替える場合には注意が必要そうです。

またTessellation SettingsのTargetResolutionを高く設定することで、ズームしたときにより滑らかに見えます。
SVGなのでズームしてもボケませんが、低解像度向けは少しカクカクします。逆に高解像度向けは非常にハイポリで表現されます)

f:id:tsubaki_t1:20180523001300j:plain


なおUIには現在使えないみたいです。

関連

現状プレビュー機能でちょくちょく変化してます。要望とかはフォーラムで出すと良さそうです。

https://forum.unity.com/threads/vector-graphics-preview-package.529845/

 

マニュアルです

https://github.com/Unity-Technologies/vector-graphics-samples/blob/master/Documentation/vectorgraphics.md

 

チラっと紹介しました

www.slideshare.net

【Unity】Allocator.TempJobで作ったNativeArrayを自動的に開放する

今回はNativeArrayの開放の方法についてです

 

NativeArrayの削除

NativeArrayでコンテナを作る際、アロケータのモードに「Temp」「TempJob」「Persistent」の3つを選択することが出来ます。

 

Persistentは普通にComponentやComponentSystemのAwake的なタイミングで作ってOnDestroy的なタイミングで消せば良いんですが、TempやTempJobのように一時割当するタイプはどう消せばよいのか。

 

.Tempは普通に開放

.Tempは基本的に自身で開放する必要があります。開放を忘れると「A Native Collection has not been disposed, resulting in a memory leak.」とコンソールに表示されます(エディターならば)

 

で、この開放のタイミングですが自分で開放する必要があるので、フレームの終わりやメソッドの終わりのタイミングでDisposeします。

Allocator.Tempは「ジョブの結果を受け取りたい」みたいな場合に使用します。結果を抽出したら用済みなので始末して下さい。

 

.TempJobは少し楽

TempJobこちらはC# Job Systemで使う前提の一時割当で、実は少し楽に開放が出来ます。
DeallocateOnJobCompletion属性をJobSystemのフィールドにセットしておくと、そのNativeArrayをジョブ終了時に自動的に開放してくれるみたいです。

f:id:tsubaki_t1:20180521100416j:plain

下のサンプルコードでも、Allocator.TempJobで生成したNativeArrayは投げっぱなしで開放してませんが、ちゃんとDeallocateOnJobCompletionが片付けてくれます。

 

コレが使えるのはあくまで.TempJobで生成したNativeArrayのみで、Tempで作ったものは開放出来ません。

またジョブが終わったら速やかに開放されてしまうので、中身をジョブ外で受け取りたい場合にはTempを使うのが良いです。

 

サンプル

gist.github.com

【Unity】構造体の配列(NativeArray)と参照渡し

今回は構造体を配列(NativeArray)にした時の、そのアクセスについてです。

 

 

ECSとJobSystemは構造体の配列を使う

最近よく紹介されているECSやC# Job Systemといった機能では、構造体をNativeArrayに乗せて一括制御するアプローチが見られます。
というのもECSやJobSystemはDODの考えでメモリ内にデータが散らばるのを嫌がるので、殆どStructで実装されています。


それ自体は大した問題では無いのですが、基本的に構造体の配列は参照渡し出来ないので少しコードが冗長になる印象があります。

f:id:tsubaki_t1:20180520123709j:plain

出来れば構造体でも参照渡しで直接値を書き換えたい所です。

今回はそれを色々とやってみます。

 

配列からrefやoutで参照を貰う事は出来ない

C#で参照渡しといえばrefやoutです。
構造体などの基本的にコピーを生成する受け渡しであっても、refを指定しておければ参照を渡して中身を書き換えることが出来ます。

ただ残念なことに、通常の構造体なら問題なくとも配列に対してとなると上手くはいきません。配列(インデクサー)は残念ながらoutやrefを使えないのです。

f:id:tsubaki_t1:20180520124204j:plain

 

そうだ、unsafeを使おう

最終手段です。unsafeを使います。ポインタを使用してNativeArrayの各要素に直接語りかける訳です。

gist.github.com 

GetUnsafeReadOnlyPtrを使用してNativeArrayのポインタを取得、あとは要素をズラせば中身が取れます。
ただ、何かの処理を行う場所でもunsafeを要求するのは余り良くないかもしれません。

なおNativeArrayにfixedは必要ありません。

またNativeArrayは連続していますがComponentDataArray等のECSがInjectする要素は連続してるとは限らないので、要素をずらした時に正しく取れない点に注意です。

 

ArrayElementAsRefで要素のrefを取得する

Incremental Compilerを導入すると、C#7.2が使えるようになり要素のrefが使えるようになります。ソレに合わせてArrayElementAsRefというAPIが追加されるので、コレを使って要素をrefで取得してメソッドに受け渡す的な事も出来るようになります。

f:id:tsubaki_t1:20180520131528j:plain

gist.github.com

UnsafeUtilityExって、もう少し良い名前は無かったのか感あるので、今後変わる気もします。

 

関連

Incremental Compilerの効果についてのまとめです。
要するにC# 7.2がつかえるようになる他、コンパイル時間短縮も期待出来ます。
但し、現在は記事で紹介されているような、manifest.jsonを書き換えなくともpackagemanagerから導入出来ます。

baba-s.hatenablog.com

Unsafeの設定方法について

tsubakit1.hateblo.jp

【Unity】ECSで簡単なゲームを作ってみたので、その解説

f:id:tsubaki_t1:20180519211935j:plain

ECSで技術デモ的な物は結構あるのですが、ゲーム的になっているものが余り無いので、ECSで簡単なゲームを作ってみました。

 

 

そうだ、ECSで球転がしを作ろう

ふと思いついてECSで球転がしのゲームを作ってみました。所謂Roll a BallでUnityで多分一番簡単に作れるゲームの一つです。
ルールは単純で「ステージ内に配置した全てのコインを取得すれば勝ち」というもの。
プレイヤーは球を操作してステージ内のコインを全て回収します。残りの個数が左上に表示され、これが0になれば勝ちです。

なお、まだECSは機能が足りない状態なので一部車輪の再開発をします

f:id:tsubaki_t1:20180519221046j:plain

プロジェクトは下のリンクに配置しておきます。
前提としてUnity 2018.1f以降が必要です。ECSを動せるのはそのバージョンからです。

github.com

ステージの作成は今までどおりシーンに作る

まずステージの作成ですが、これは今までどおりです。つまり単純にモデルを置いても良いですし、今ならProBuilderでガガっと作っても良いです。
CameraやLightの設定、ポストプロセス等も普通に配置していきます。

f:id:tsubaki_t1:20180519221723j:plain

球のコントロールはGameObjectをシステムからコントロール

プレイヤーが操作する球のコントロールについてです。これはRigidbodyを使いたいのでGameObjectとECSを連携するアプローチを使用します。

GameObjectEntityを使用すると、ECSのSystemからGameObjectの各コンポーネントへアクセスすることが可能になります
この機能を使用して、球の持つRigidbodyへアクセスしていきます。

f:id:tsubaki_t1:20180519222325j:plain

システムが球がプレイヤーかどうかを識別する方法は、ラベルとしてPlayerコンポーネントをもたせる事で実現します。Systemの要求するグループに「Rigidbody」と「Player」を指定する訳です。
この仕組は中々に楽で、Inspectorでオブジェクトをドラッグ&ドロップ等をしなくてもシーン内の特定条件のオブジェクトを見つけ出してくれます。

f:id:tsubaki_t1:20180519223114j:plain

f:id:tsubaki_t1:20180519223735j:plain

このGameObjectをECSの文脈で制御するアプローチは、ECSのメモリレイアウト的にはそれ程メリットを生みませんが、そもそも数が少なければ問題ないという判断です。
それよりInjectによるバインドの簡略化、システム単位で制御がコントロール出来る点(後述)、Rigidbodyの挙動を作るのが面倒くさい、そして何よりシーン上で調整出来る点などの方が楽で良いです。

GameObjectの情報はECSにアップロードしておく

上で球の操作はRigidbody&Playerラベルという二つの要素でコントロールしていました。これは前述下通り数が少なければ問題無いのですが、関係するデータの数が増えてくる場合や並列処理したい場合にはComponentDataとして登録しておきたいです。
なのでEntityに球のPositionを毎フレームアップロードしておきます。

その役割はPositionComponentCopyTransformFromGameObjectComponentが果たします。

f:id:tsubaki_t1:20180519224955j:plain

CopyTransformFromGameObjectComponentは、PositionにTransformの現在値を毎フレーム渡してくれるシステムを呼び出します。
要するに他のSystemからアクセスする際にTransform(ComponentArray)ではなくPosition(ComponentDataArray)経由でアクセス可能になる訳です。これでC#JobSystemなどで使いやすくなります

 

また今回はRigidbody(毎フレーム座標を更新される)を使用しているので値の同期にCopyTransformFromGameObjectComponentを使用していますが、もし単純に初期座標が欲しい(GameObjectも使う)という場合にはCopyInitialTransformFromGameObjectComponentを使用するのが良さそうです。例えばAnimatorが必要だからGameObjectを使うが、座標を毎フレーム更新しない…みたいな場合です。

 

カメラが球を追跡するシステムでは、二つのグループを要求

カメラの追跡です。カメラの追跡システムは「カメラ」と「追跡対象(プレイヤー)」の二つで成り立つので、システムも二つのグループを要求します。
なおCameraコンポーネントはラベル的な用途です。

f:id:tsubaki_t1:20180519230318j:plain

なおPositionコンポーネントはVector3ではなくfloat3が格納されているので、加算する座標データもfloat3で指定しています。

 

あ、Entityに登録するのでGameObjectEntityを忘れずに!
(自分は忘れて30分近く悩んでしまった)

f:id:tsubaki_t1:20180519230932j:plain

 

コインの生成はPrefabをEntity化して配置

球が回収するコインを生成していきます。このコインはソコソコの量があるので、出来ればピュアなECSのような形で生成したい所ですが、コードで全部制御するのは正直面倒くさいので避けたいです。
そこで、PrefabからEntityを取得して生成するアプローチを取ります。

Entityの生成はPrefabSpawnerで行っています。

f:id:tsubaki_t1:20180519231852j:plain

まずはEntityとしてPrefabを作成します*1。PrefabにはEntityに含めるコンポーネントを設定していきます。

例えばコインの識別のためにItemComponent、描画のためにMeshInstanceRenderComponent、座標のアクセスがマトリクスなのは面倒なのでpositionとrotationComponentなどです。
これらのコンポーネントの幾つかは、今までのUnityと同じようにInspectorから内容を調整が出来ます。例えばMeshInstanceRendererComponentで何を描画するか・描画設定などを調整しています。

f:id:tsubaki_t1:20180519231907j:plain

このPrefabをEntityManagerのInsntaitateで複製し、座標を登録していきます。ここでEntityの量が超多いならJobで並列に処理してしまうのも良いかもしれません。

f:id:tsubaki_t1:20180519232536j:plain

なおentityManager.Instantiateで生成されるEntityに含まれるのはComponentDataのみです。ComponentDataWrapperやTransformを始めとしたコンポーネント群はココに含まれないので、そこんとこ注意(安心?)です。

 

なおコインの配置自体はMonobehaviourを継承したコンポーネントで行っています。これはシーン上にコインの情報を焼き付けたかったからです。

f:id:tsubaki_t1:20180519233339j:plain

 

当たり判定は並列化、Mathematicsライブラリも活用

プレイヤーと各コインの当たり判定ですが、これは自前で適当なものを用意しました。これはコインをピュアなECSにした関係上EntityがColliderを持てなくなったのが原因です。
Collider辺りはまだECSに対応していないので、ピュアなECSを使用しようと思ったら車輪を再開発する必要があります。
内容? 単純に総当たりで距離計算ですよハハ

この当たり判定ですが、JobComponentSystemで並列化しています。JobSystemに乗せる事でBurstや新しいMathライブラリの恩恵を受けられる為です。この時プレイヤーの座標はGroupを使ってInjectして持ってくればOKです。

なお接触時にオブジェクトを削除する訳ですが、ここでは行わず一旦ラベルに値を付けて後で行っています。
これはやり方が分からなかったのが本音ですが、削除時にエフェクトを出したかったという部分もあります。

f:id:tsubaki_t1:20180519234959j:plain

Entityの削除はHitcheckとは別に後で行います。この時System間の依存関係を作っておくと安心して使えます。

また削除時にパーティクルも出してみました。DestroyEntityも一緒に呼んでますが、PostUpdateCommandsの名前から即座に実行じゃないと思うんで、まぁ良いかなと。

なおピュアなECSの場合、生成・削除コストはかなり安いので、下手にプーリングするよりは生成・破棄しまくったほうが良いんじゃないかなって意見があります。

 

ゲームの進行はコンポーネント、システムのON/OFF管理も担当

最後にゲームの進行管理ですが、これはコンポーネント側で行いました。何故かといえばコルーチンが使いたかったからです。
ECSには現状コルーチンに該当する機能がないので、その辺りをやろうと思ったらコンポーネント使ったほうが楽です。UniRX的な物でも良いかもしれません。
あとシーン毎に要求するフェーズがあるかもしれないので、その辺りも含めて。

さて、この部分では各フェイズ毎に動かすシステム一覧を決めて制御しています。
例えばプレイヤーの入力システムなどはPLAY時には有効ですが、それ以外のINITとCLEAR時に停止させたりしています。

f:id:tsubaki_t1:20180520000629j:plain

またゲームの進行把握にどうしてもEntityの情報がほしかったので、グループを作って観測するようにしました。Disposeし忘れは、普通に忘れてました。

f:id:tsubaki_t1:20180520001136j:plain

 

感想

簡単な玉転がしをECSにしてみました。

どうしてもピュアなECSを使用すると書くコードが多くなるので今は少しシンドイ所がありますが、割と今までどおりの感じで作れるなという印象でもあります。

むしろInjectをガンガン使うとSceneでやってたドラッグ&ドロップとか、Singleton的なアレがスッキリするんで使いやすいと思った部分もあります。

まぁ、もう少し機能が増えてきたら楽になりそうですが…Collider! Animator! SpriteRendere! SphereCheck!

 

なお、この作り方が良いかどうかは自分の胸に聞いて下さい。真実はいつも一つ以上!

関連

ECSの概念的な説明

tsubakit1.hateblo.jp

日本語で分かりやすく詳しいECS&JobSystemの説明

esprog.hatenablog.com

今回散々使ったECSハイブリッドについて

tsubakit1.hateblo.jp

 

サバイバルシューターのECS版。かなりハイブリッド

http://www.davidpol.com/

 

*1:正確には別にPrefabではなくても良いですが、理解しやすさのためPrefabとします