実践 Houdini Engine for Unity

Houdini Engine for Unityでできること

Houdini Engine for Unityを使うと、プロシージャルモデリングによりバリエーション豊かなモデルを作成できます。
動的にステージを生成してレベルデザインをしやすくしたり、大量のバリエーションが必要なモデル(様々な形状のドアや木々など)を生成し、一つひとつモデルを作成するよりも大幅に工数を削減することができます。

それらはUnityのエディタ上で動作するので、変更するために他のモデリングツールとUnityを行き来する必要もありません。

詳しくは公式の紹介動画などを参照してください。

この記事では実際のゲーム開発において、Houdini Engine for Unityを使用した際に発生した要件(やりたいこと)とそれをどの様に実装したのかについて記載します。
「○○したい->実装方法」の逆引き形式となっております。

この記事は以下の環境で確認した内容について書いています。
・Houdini 19.0.455
・Unity 2021.1.28f
・Windows 10

自分が色々と書いておりますが、基本的には公式のドキュメントが一次情報なので以下を読み込むことが大切です。大抵のことは解決します。

ちなみに、Houdini 19.0.455ではUnityのVisual Scripting(2021.1以降の機能)に対応したらしく、Houdini Engine for Unityのunitypackageをインポートしただけでは以下のようなエラーが出るため、別途パッケージマネージャーからVisual Scriptingをインストールする必要があります。(Unity 2021.1+を使用の場合。)

Assets\Plugins\HoudiniEngineUnity\Scripts\VisualScripting\HEU_InstantiateHDA.cs(3,13): error CS0234: The type or namespace name 'VisualScripting' does not exist in the namespace 'Unity' (are you missing an assembly reference?)

GameObjectを分けたい

HDAから出力されるGameObjectは特に考えないでHDAを書くと一つのGameObject(Mesh)として出力されます。

しかし、実際にゲーム開発などしているとGameObjectを分けて出力したいケースも多く遭遇します。

HDAが生成するGameObjectを分けるには以下の方法があります。(これ以外にもあるかもしれません。)

・OBJレベルでGeometryノードを分ける
・Primitive Typeを分ける
・Packed Primitiveを使う
・Instanceノードを使う
・複数のOutputノードを使う
どの方法を使うかはケースバイケースになります。

HDAのオプションで「Split geos by group」を使うという方法もありますが、これは廃止予定になっているので使わない方が良いでしょう。

OBJレベルでGeometryノードを分ける

画像5

Geometryノード毎にGameObjectが分かれます。

Primitive Typeを分ける

これはバッドノウハウっぽい雰囲気ですが、一応分かれます。

画像6
画像7

Packed Primitiveを使う

正攻法の一つはこの方法でしょう。

詳しくはこの辺りを参照。

Packed Primitiveを作る方法はさまざまありますが、Copy to Pointsノードであれば「Pack and Instance」オプションをONにするとコピーされたオブジェクトはPacked Primitiveになり、それぞれが別のGameObjectになります。

画像9
画像8

GameObjectは別々ですが、同じ親GameObjectの中に作成されます。

画像10

Copy to Pointsノードを使わず、For-EachでPackするとこうなります。

画像11

今度は親GameObjectも別々に別れました。

画像12

この辺りの親子関係はもっと明示的/直感的に指定出来たらよいと思うのですが...(方法があったら知りたい)。

Instanceノードを使う

Copy to Pointsノードを使った方が簡単ですが...。

画像13

これも1つの親GameObjectにまとまります。

画像14

複数のOutputノードを使う

これが最も正攻法でわかりやすい方法になるかと思います。
Houdini 19.0.455より前はHDA(in Unity)のOutputは1つしか使えなかったのですが、最新の19.0.455では複数のOutputノードに対応してくれました!

Outputを変えたい

HDAの処理の途中経過を見てデバッグしたい時など。
Outputノードがある場合、ない場合で以下の挙動をします。

・Outputノードがない場合、RenderフラグがONになっているノードが出力となる。
・Outputノードがある場合はOutputノードが出力となる。Outputノードは複数OK。

注意!!!

Houdini 19.0.383ではOutputノードを使うとオブジェクトが重複して生成されてしまう不具合を確認しています。
詳しくは以下参照。

19.0.455では修正されていることを確認しました。

UnityでMaterialを割り当てたい

Unity Materialノードを使います。実態はAttribute Createノードです。

HoudiniでMaterialを生成する方法もあるようですが、そちらはまだ試せていません。

UVを調整したい

UV UnwrapノードなどのUV系のノードを使用します。
(Houdini Engineを使わない場合でも必要な処理です。)

コライダーを付けたい

collision_geo_simple_boxなどのgroupを設定するとCook時にコライダーを付けてくれます。attributeじゃなくてgroupなので注意。

色々な種類のコライダーに対応しています。
rendered_プレフィックスのついたgroup名を使わないとコライダーだけのオブジェクトになってしまいます。

Staticにしたい

unity_static attributeを作成し、1を指定します。

タグをつけたい

unity_tag attributeで指定します。

Cook時に任意のスクリプトを実行したい

例えば照明のモデルを生成した場合に、Lightコンポーネントを付けるなどの任意の処理を実行できます。

EVENTSを使う

HDAのEVENTS以下には各イベントのコールバックを設定できます。CookだけではなくRebuildなどその他のEventもあります。

画像15

unity_script attributeを使う

unity_script attributeを使う事でもCook時にスクリプトを実行することが出来ます。

EVENTSと違い、HDAに手動で指定する必要もなく、引数も自由に指定出来たりするので便利です。
ただしここで指定したスクリプトはCookされたGameObjectにコンポーネントとして付けられるので少し注意が必要です。(Editor Onlyの処理を書いていたりなどの場合。)

Git(ソースコード管理ツール)を利用する

上記のドキュメントをよく読んで、Gitに管理させるファイル、管理させない(.gitignoreに指定する)ファイルの判断が必要です。

ここが間違っていると、他人の環境でHoudini Engineがうまく動かなかったり、ベイクしたメッシュが消えていたりなどの様々なエラーが発生します。

Unity-Houdini間でリアルタイムに変更を同期したい

SessionSyncという機能を使って、Unity->Houdini、Houdini->Unityのどちらでも変更を即座に反映することが出来ます。

詳しくはこちらの動画がわかりやすいです。

環境変数を使用したい

unity_houdini.envとうファイルを作成することでUnityでHDAを使うときに環境変数を使用することが出来るようになります。

以下の用途などで利用できます。
・Unityプロジェクト外にあるHDAを使用したい
・共通化したHDAを使用したい

unity_houdini.envで指定した環境変数をスクリプトから読み取れるのかどうかは試していませんがもし読み取れれば何かに使えそうです。

Unityプロジェクト外にあるHDAを使用したい

公式のドキュメントではUnityプロジェクト(Assetsフォルダ)外にあるHDAを使用する例が説明されています。

Unityプロジェクト外にあるHDAはメニューの「HoudiniEngine > Load File > HDA」から読み込むことが出来ます。

画像2

この時、unity_houdini.envに何も指定していないとAsset Pathが以下の様に絶対パスになってしまいます。

画像3

個人開発であればこれで良いですが、複数人で作業している場合などでは絶対パスだとそれぞれの環境でパスが違った場合にHDAが見つからずエラーになってしまいます。

そこでunity_houdini.envに以下の様に記述します。HEU_ENVPATH_COMMON_HDASというキーでHDAが保存されているパスを指定します。(パスは例として自分の環境用なので、各自の環境用に適宜変更する必要があります。)

HEU_ENVPATH_COMMON_HDAS=E:\Workspace\HoudiniEngineForUnityExamples\Houdini\CommonAssets

この設定をした後に、Unityプロジェクト外のHEU_ENVPATH_COMMON_HDAS以下にあるHDAを読み込むと、今度はAsset Pathがこの様になります。絶対パスから環境変数に変わりました。

画像1

これで開発者各自の環境でHEU_ENVPATH_COMMON_HDASのパスが違っていても、unity_houdini.envで指定すれば無事にHDAを読み込むことが出来ます。

この様に開発者毎に違う値を記述する必要があるため、unity_houdini.envはGit(ソースコード管理ツール)には含めないことが推奨されています。

共通化したHDAを使用したい

以下の様なフォルダ構成でHDAを保存していて、Common1.hdaはAsset1.hda、Asset2.hdaの両方で使用されているケースを例にとります。

Assets/Houdini
          |- CommonAssets/
          |           |- Common1.hda
          |- Asset1.hda
          |- Asset2.hda

この時、Asset1.hda、Asset2.hda内で使用されているCommon1.hdaは相対パスではないらしく、他の開発者の環境でAsset1.hdaを使おうとするとエラーが発生します。(HDAへの絶対パスが同じならエラーは発生しない。)

これを防ぐために、unity_houdini.envに
「HOUDINI_OTLSCAN_PATH = {CommonAssetsへの絶対パス}」を指定します。

Bakeされたメッシュを任意のフォルダに移動したい

Bakeして作成されるメッシュはデフォルトでは「HoudiniEngineAssetCache/Baked」以下にユニークな名前で配置されます。
全てのBake結果がこのフォルダに入るので、整理したくなる場合があります。(シーンやモデル毎に分けたり。)

手動で移動することもできますが、プログラムで行う場合は以下のようにしました。例えばCookedEventのコールバック関数内でBakeする場合。
「HEU_PluginSettings.AssetCachePath」というプロパティをスクリプトから変更します。

var assetCachePathBak = HEU_PluginSettings.AssetCachePath; // 現在の設定をバックアップ
var assetCachePath = {任意のパス};
HEU_PluginSettings.AssetCachePath = assetCachePath; // キャッシュフォルダを指定
cookedEventData.Asset.BakeToNewStandalone(); // Bake
HEU_PluginSettings.AssetCachePath = assetCachePathBak; // 元に戻す

「HEU_PluginSettings.AssetCachePath」はメニューのHoudiniEngine > Plugin Settings > Houdini Asset Cache Pathの値です。元に戻しておかないと、ここが変わったままになるので、他のHDAをベイクした時に影響してしまいます。

画像4

Houdini 19 / Curve 2.0の挙動

Houdini 18では発生していなかったのですが、Houdini 19 / Curve 2.0になってから、Unityで作ったCurveとHoudiniのCurveノードの挙動が異なっており、ClosedなCurveを作ってHDAに渡し、PolyExtrudeをすると期待通りに動かない現象を確認しています。(この記事を書いている2021/12/01現在、19.0.383)

詳しくはこちらのフォーラムで議論しておりますので参照ください。

19.0.455では修正されていることを確認しています。

あとがき

上記のテクニックを組み合わせることでかなり柔軟にUnityアセットを作成できるようになりました。
使いこなせばHoudiniの強力な表現力をそのままUnityで使用できるため、とても便利です。
Houdini Engineにはまだまだ使い切れていない機能が盛りだくさんですが今回はこの辺で。


もしこの記事があなたのお役に立てたなら幸いです。 よろしければサポートをお願いします。今後の制作資金にさせていただきます!