Anzeige
Anzeige

Más contenido relacionado

Presentaciones para ti(20)

Similar a Python / BlueprintによるUnreal Engineの自動化 / GTMF2019(20)

Anzeige

Más de Game Tools & Middleware Forum(20)

Último(20)

Anzeige

Python / BlueprintによるUnreal Engineの自動化 / GTMF2019

  1. Python / Blueprintによる Unreal Engineの自動化 Epic Games Japan / Technical Artist 小林浩之
  2. #UE4 | @UNREALENGINE 自己紹介 小林 浩之 Epic Games Japan / Technical Artist スクウェア・エニックス大阪で背景TAを2年ほど 今年2月からEGJのエンタープライズ分野のサポートとして入社
  3. #UE4 | @UNREALENGINE 目次 • 大規模開発における作業効率化・自動化の需要 • Unreal Engine 4の作業効率化・自動化ツール紹介 • 実装例 • Datasmithインポートの効率化 Blueprint • 命名規則に応じたアセットリネーム Editor Utility Widget & Blueprint • Pythonによるインスタンシング Python • インスタンスを個別のStatic Meshに変換 Python
  4. #UE4 | @UNREALENGINE 目次 • 大規模開発における作業効率化・自動化の需要 • Unreal Engine 4の作業効率化・自動化ツール紹介 • 実装例 • Datasmithインポートの効率化 Blueprint • 命名規則に応じたアセットリネーム Editor Utility Widget & Blueprint • Pythonによるインスタンシング Python • インスタンスを個別のStatic Meshに変換 Python
  5. #UE4 | @UNREALENGINE 大規模開発における作業効率化・自動化の需要 大量に配置されたオブジェクトの整理、膨大なアセットの管理など、 手作業でやっているとコストがかかるりすぎる・・・ 時間が足りない・・・ クオリティアップにコストを割けない・・・
  6. #UE4 | @UNREALENGINE 大規模開発における作業効率化・自動化の需要 オブジェクト整理作業 アセット管理作業 などなど 自動化
  7. #UE4 | @UNREALENGINE McLarenによる事例 Unreal Engineへの CADデータインポートの自動化
  8. #UE4 | @UNREALENGINE McLarenによる事例 インポートフローの改善
  9. #UE4 | @UNREALENGINE 目次 • 大規模開発における作業効率化・自動化の需要 • Unreal Engine 4の作業効率化・自動化ツール紹介 • 実装例 • Datasmithインポートの効率化 Blueprint • 命名規則に応じたアセットリネーム Editor Utility Widget & Blueprint • Pythonによるインスタンシング Python • インスタンスを個別のStatic Meshに変換 Python
  10. #UE4 | @UNREALENGINE Bluetility (Blueprint Utility) Blueprintを使ったスクリプティング
  11. #UE4 | @UNREALENGINE Bluetility プラグイン > Editor Scripting Utilities
  12. #UE4 | @UNREALENGINE Unreal Python Pythonによるエディタスクリプティング
  13. #UE4 | @UNREALENGINE Unreal Python プラグイン > Python Editor Script Pluginにチェックで有効化
  14. #UE4 | @UNREALENGINE Unreal Python アウトプットログから直接入力 実行方法
  15. #UE4 | @UNREALENGINE Unreal Python .pyファイルのパス指定で実行 実行方法
  16. #UE4 | @UNREALENGINE Unreal Python エディタ起動時に実行
  17. #UE4 | @UNREALENGINE Editor Utility Widget UMG&Blueprintでエディタ拡張
  18. #UE4 | @UNREALENGINE Editor Utility Widget コンテンツブラウザで右クリック>Editor Utilities>Editor Widgetから作成
  19. #UE4 | @UNREALENGINE Editor Utility Widget
  20. #UE4 | @UNREALENGINE Editor Utility Widget UIに必要な機能(ボタンやテキストなど)をD&Dで置く
  21. #UE4 | @UNREALENGINE Editor Utility Widget UIからの処理をBlueprintで作成
  22. #UE4 | @UNREALENGINE Editor Utility Widget アセット右クリック>Run Editor Utility WidgetでWindow立ち上げ、実行
  23. #UE4 | @UNREALENGINE Editor Utility Widget EGJ 岡田による解説記事 [UE4]エディタ上で動作するツール・エディタ拡張をUMGで簡単に作れる Editor Utility Widget について https://qiita.com/EGJ-Kaz_Okada/items/9f530db3b53d0fde3f20 [UE4]Editor Utility Widgetでツール・エディタ拡張を作る際のUndo/Redo の実装方法について https://qiita.com/EGJ-Kaz_Okada/items/985b98fb934d751f4f69
  24. #UE4 | @UNREALENGINE 目次 • 大規模開発における作業効率化・自動化の需要 • Unreal Engine 4の作業効率化・自動化ツール紹介 • 実装例 • Datasmithインポートの効率化 Blueprint • 命名規則に応じたアセットリネーム Editor Utility Widget & Blueprint • Pythonによるインスタンシング Python • インスタンスを個別のStatic Meshに変換 Python
  25. #UE4 | @UNREALENGINE 実装例:Datasmithインポートの効率化
  26. #UE4 | @UNREALENGINE Datasmithとは CADソフトなどのデータをUE4用に変換してインポートする機能 (Unreal Studioのみ) Datasmith
  27. #UE4 | @UNREALENGINE Datasmithによって大幅に効率化されるが・・・ CADデータの場合細かいネジなどのパーツまで含んだデータになっている場合が多い 環境によっては処理負荷が高くなってしまう可能性も
  28. #UE4 | @UNREALENGINE Datasmithによって大幅に効率化されるが・・・ リアルタイムエンジンでスムーズに描画するためには ほとんど描画されないような小さいパーツは削除したり、一つにまとめる必要がある
  29. #UE4 | @UNREALENGINE 普通にインポートして後から削除しようとするとする場合 パーツ数が膨大だと作業コストが高くなってしまう インポート 小さいパーツを探す 削除 数百パーツを手作業でとか・・・
  30. #UE4 | @UNREALENGINE BlueprintやPythonを使うことでこれらの作業を自動化できる インポート 小さいパーツを探す 削除 自動化
  31. #UE4 | @UNREALENGINE 小さいパーツを除外してインポート
  32. #UE4 | @UNREALENGINE 小さいパーツを除外してインポート
  33. #UE4 | @UNREALENGINE Blueprint ※拡大して解説していきます
  34. #UE4 | @UNREALENGINE 解説 インポートするデータからDatasmith Sceneを構築
  35. #UE4 | @UNREALENGINE Datasmith Scene Datasmithでは、実際にデータをインポートする前に メモリ上で一度シーン構築を行う インポート Datasmith Scene in Memory メモリ上でシーンを構築 CADデータ
  36. #UE4 | @UNREALENGINE Datasmith Scene Datasmithでは、実際にデータをインポートする前に メモリ上で一度シーン構築を行う インポート Datasmith Scene in Memory メモリ上でシーンを構築 CADデータ
  37. #UE4 | @UNREALENGINE 解説 Datasmith Scene内のアクタを取得
  38. #UE4 | @UNREALENGINE 解説 バウンディングボックスの大きさを評価
  39. #UE4 | @UNREALENGINE 解説 バウンディングボックス オブジェクトを囲む最小の立方体 この立方体の幅、奥行き、高さから オブジェクトの大体の大きさを測る
  40. #UE4 | @UNREALENGINE 解説 条件に当てはまればアクタを削除
  41. #UE4 | @UNREALENGINE 解説 インポートオプションを設定
  42. #UE4 | @UNREALENGINE 解説 実際にインポートし、最後にDatasmith Sceneを削除
  43. #UE4 | @UNREALENGINE 実装例:命名規則に応じたアセットリネーム
  44. #UE4 | @UNREALENGINE 命名規則について アセットの種類や用途に応じて名前の前後に付ける文字列
  45. #UE4 | @UNREALENGINE 命名規則について アセットの種類や用途に応じて名前の前後に付ける文字列 例えば・・・ Static Mesh アセット「Table」があるとしたら Static Meshの省略 SM を付け 「SM_Table」 バリエーションがある場合は 番号やアルファベットを付け「 SM_Table _A」にする
  46. #UE4 | @UNREALENGINE 命名規則について 参考 Unreal Engine Assets Naming Convention https://wiki.unrealengine.com/Assets_Naming_Convention_JP
  47. #UE4 | @UNREALENGINE 命名規則について プロジェクトが大規模化するにつれ、命名規則はより重要に
  48. #UE4 | @UNREALENGINE 命名規則について プロジェクトが大規模化するにつれ、命名規則はより重要に 作業者が自由に名前を付けていると・・・ ● 他の作業者から見たとき用途や種類が判別しにくい ● 特定のアセットを探しずらい
  49. #UE4 | @UNREALENGINE 実装例:命名規則に応じたアセットリネーム
  50. #UE4 | @UNREALENGINE 解説 UI エディットできるText BoxやButtonなどを置いただけ シンプルな構成
  51. #UE4 | @UNREALENGINE 解説 UI エディットできるText BoxやButtonなどを置いただけ シンプルな構成
  52. #UE4 | @UNREALENGINE 解説 Blueprint
  53. #UE4 | @UNREALENGINE 解説 Blueprint ボタンが押されたら選択しているアセットを取得
  54. #UE4 | @UNREALENGINE 解説 Blueprint アセットの種類毎にリネーム処理
  55. #UE4 | @UNREALENGINE 解説 Blueprint 関数Asset Renameの中身
  56. #UE4 | @UNREALENGINE 解説 Blueprint 処理するクラスを設定
  57. #UE4 | @UNREALENGINE 解説 Blueprint リネーム処理
  58. #UE4 | @UNREALENGINE 実装例:Pythonによるインスタンシング
  59. #UE4 | @UNREALENGINE インスタンシング 大量のオブジェクトを描画する際に有効な手法 ドローコールを削減し、描画コストを下げる ドローコール 現在の画面を描画するために必要な情報を呼び出す命令のこと 回数が多いほど処理負荷につながる可能性がある
  60. #UE4 | @UNREALENGINE インスタンス化によるドローコールの削減 非インスタンス インスタンス 9回分のドローコール 1回分のドローコール
  61. #UE4 | @UNREALENGINE Instanced Static Mesh Unreal Engineでのインスタンス化メッシュ
  62. #UE4 | @UNREALENGINE Instanced Static Mesh
  63. #UE4 | @UNREALENGINE Instanced Static Mesh • Static Mesh
  64. #UE4 | @UNREALENGINE Instanced Static Mesh • Static Mesh • インスタンス数分の位置、回転、スケール
  65. #UE4 | @UNREALENGINE Static Meshをインスタンス化 ツールやスクリプトを使わずに 手作業で変換しようとすると・・・ アクタ一つ一つの位置、回転、 スケールをコピーして・・・
  66. #UE4 | @UNREALENGINE Static Meshをインスタンス化 ツールやスクリプトを使わずに 手作業で変換しようとすると・・・ インスタンスに追加
  67. #UE4 | @UNREALENGINE Static Meshをインスタンス化 Merge Actors 選択アクタを一つのインスタンスに変換
  68. #UE4 | @UNREALENGINE Static Meshをインスタンス化 Merge Actors 選択アクタを一つのインスタンスに変換 複数インスタンスを一度に生成はできない
  69. #UE4 | @UNREALENGINE Static Meshをインスタンス化 Merge Actors 多数のアクタがあるとして・・・
  70. #UE4 | @UNREALENGINE Static Meshをインスタンス化 Merge Actors 複数グループに分けてインスタンス化したい場合 インスタンス化したいグループ毎に 選択して変換する作業が必要
  71. #UE4 | @UNREALENGINE インスタンス化によるドローコールの削減 大量のアクタを複数インスタンス化していくのは高コスト なるべく自動で、いい感じのグループに分けてインスタンス化したい・・・
  72. #UE4 | @UNREALENGINE インスタンス化によるドローコールの削減 大量のアクタを複数インスタンス化していくのは高コスト なるべく自動で、いい感じのグループに分けてインスタンス化したい・・・ Python外部ライブラリからK-means法を使ってインスタンシング!
  73. #UE4 | @UNREALENGINE K-means法とは ざっくり解説 クラスタ分析を行う手法の一つ Pythonの外部ライブラリScikit Learnに含まれている
  74. #UE4 | @UNREALENGINE K-means法とは ざっくり解説 バラバラな座標のリストがあるとして・・・
  75. #UE4 | @UNREALENGINE K-means法とは ざっくり解説 クラスタ数=5 設定したクラスタ数に応じて、近い属性同士のグループを作る
  76. #UE4 | @UNREALENGINE Unreal Pythonで外部ライブラリを使う ライブラリインストール後、PythonLibsite-packagesを EngineBinariesThirdPartyPythonWin64Lib以下に丸ごとコピー import 〇〇でインポート出来るようになる
  77. #UE4 | @UNREALENGINE K-means法によるインスタンシング
  78. #UE4 | @UNREALENGINE コード 1/2 import unreal import numpy as np import sklearn from sklearn.cluster import KMeans bp_instance = unreal.EditorAssetLibrary.load_blueprint_class('/Game/BP_Instance.BP_Instance') list_actors = unreal.EditorLevelLibrary.get_selected_level_actors() list_static_mesh_actors = unreal.EditorFilterLibrary.by_class(list_actors,unreal.StaticMeshActor) list_unique = np.array([]) for lsm in list_static_mesh_actors: static_mesh = lsm.get_component_by_class(unreal.StaticMeshComponent).get_editor_property("StaticMesh") list_unique = np.append(list_unique,static_mesh) list_unique = np.unique(list_unique) for lu in list_unique: list_transform = np.array([]) list_locations = np.array([[0,0,0]])
  79. #UE4 | @UNREALENGINE コード 2/2 for lsm in list_static_mesh_actors: if lsm.get_component_by_class(unreal.StaticMeshComponent).get_editor_property("StaticMesh") == lu: list_transform = np.append(list_transform,lsm.get_actor_transform()) location = np.array([[lsm.get_actor_location().x,lsm.get_actor_location().y,lsm.get_actor_location().z]]) list_locations = np.append(list_locations,location,axis=0) list_locations = np.delete(list_locations,0,axis=0) num_clusters = 3 pred = KMeans(n_clusters=num_clusters).fit_predict(list_locations) instanced_components = np.array([]) for i in range(num_clusters): instanced_actor = unreal.EditorLevelLibrary.spawn_actor_from_class(bp_instance,(0,0,0),(0,0,0)) instanced_component = instanced_actor.get_component_by_class(unreal.InstancedStaticMeshComponent) instanced_components = np.append(instanced_components,instanced_component) for j, pd in enumerate(pred): instanced_components[pd].add_instance(list_transform[j]) for k in range(num_clusters): instanced_components[k].set_editor_property("StaticMesh",lu) for lsm in list_static_mesh_actors: lsm.destroy_actor()
  80. #UE4 | @UNREALENGINE 解説 1/5 #ライブラリをインポート import unreal import numpy as np import sklearn from sklearn.cluster import KMeans #インスタンス用のクラスをロード bp_instance = unreal.EditorAssetLibrary.load_blueprint_class('/Game/BP_Instance.BP_Instance') #選択しているアクタを取得 list_actors = unreal.EditorLevelLibrary.get_selected_level_actors() list_static_mesh_actors = unreal.EditorFilterLibrary.by_class(list_actors,unreal.StaticMeshActor)
  81. #UE4 | @UNREALENGINE 解説 1/5 #ライブラリをインポート import unreal import numpy as np import sklearn from sklearn.cluster import KMeans #インスタンス用のクラスをロード bp_instance = unreal.EditorAssetLibrary.load_blueprint_class('/Game/BP_Instance.BP_Instance') #選択しているアクタを取得 list_actors = unreal.EditorLevelLibrary.get_selected_level_actors() list_static_mesh_actors = unreal.EditorFilterLibrary.by_class(list_actors,unreal.StaticMeshActor)
  82. #UE4 | @UNREALENGINE 解説 1/5 #ライブラリをインポート import unreal import numpy as np import sklearn from sklearn.cluster import KMeans #インスタンス用のクラスをロード bp_instance = unreal.EditorAssetLibrary.load_blueprint_class('/Game/BP_Instance.BP_Instance') #選択しているアクタを取得 list_actors = unreal.EditorLevelLibrary.get_selected_level_actors() list_static_mesh_actors = unreal.EditorFilterLibrary.by_class(list_actors,unreal.StaticMeshActor)
  83. #UE4 | @UNREALENGINE 解説 1/5 #ライブラリをインポート import unreal import numpy as np import sklearn from sklearn.cluster import KMeans #インスタンス用のクラスをロード bp_instance = unreal.EditorAssetLibrary.load_blueprint_class('/Game/BP_Instance.BP_Instance') #選択しているアクタを取得 list_actors = unreal.EditorLevelLibrary.get_selected_level_actors() list_static_mesh_actors = unreal.EditorFilterLibrary.by_class(list_actors,unreal.StaticMeshActor) 後で選択アクタから 位置、回転、メッシュ情報などを取得
  84. #UE4 | @UNREALENGINE 解説 2/5 #メッシュの種類毎にクラスタリングするため、アクタのリストからメッシュの種類がいくつあるかを求める list_unique = np.array([]) for lsm in list_static_mesh_actors: static_mesh = lsm.get_component_by_class(unreal.StaticMeshComponent).get_editor_property("StaticMesh") list_unique = np.append(list_unique,static_mesh) list_unique = np.unique(list_unique) #インスタンスに追加するトランスフォームリストと、クラスタリングに使うための位置リストを作成 for lu in list_unique: list_transform = np.array([]) list_locations = np.array([[0,0,0]])
  85. #UE4 | @UNREALENGINE 複数種類のメッシュがあった場合 Instanced Static Meshが 持てるメッシュは一種類のみ
  86. #UE4 | @UNREALENGINE 複数種類のメッシュがあった場合 クラスタ数=3
  87. #UE4 | @UNREALENGINE 複数種類のメッシュがあった場合 種類ごとにクラスタリング クラスタ数=3
  88. #UE4 | @UNREALENGINE 複数種類のメッシュがあった場合 クラスタ数=3 種類ごとにクラスタリング
  89. #UE4 | @UNREALENGINE 解説 3/5 #インスタンスに追加するトランスフォームリストと、クラスタリングに使うための位置リストを作成 for lsm in list_static_mesh_actors: if lsm.get_component_by_class(unreal.StaticMeshComponent).get_editor_property("StaticMesh") == lu: list_transform = np.append(list_transform,lsm.get_actor_transform()) location = np.array([[lsm.get_actor_location().x,lsm.get_actor_location().y,lsm.get_actor_location().z]]) list_locations = np.append(list_locations,location,axis=0) list_locations = np.delete(list_locations,0,axis=0) #クラスタリング num_clusters = 3 pred = KMeans(n_clusters=num_clusters).fit_predict(list_locations)
  90. #UE4 | @UNREALENGINE 解説 3/5 #インスタンスに追加するトランスフォームリストと、クラスタリングに使うための位置リストを作成 for lsm in list_static_mesh_actors: if lsm.get_component_by_class(unreal.StaticMeshComponent).get_editor_property("StaticMesh") == lu: list_transform = np.append(list_transform,lsm.get_actor_transform()) location = np.array([[lsm.get_actor_location().x,lsm.get_actor_location().y,lsm.get_actor_location().z]]) list_locations = np.append(list_locations,location,axis=0) list_locations = np.delete(list_locations,0,axis=0) #クラスタリング num_clusters = 3 pred = KMeans(n_clusters=num_clusters).fit_predict(list_locations)
  91. #UE4 | @UNREALENGINE 解説 3/5 #インスタンスに追加するトランスフォームリストと、クラスタリングに使うための位置リストを作成 for lsm in list_static_mesh_actors: if lsm.get_component_by_class(unreal.StaticMeshComponent).get_editor_property("StaticMesh") == lu: list_transform = np.append(list_transform,lsm.get_actor_transform()) location = np.array([[lsm.get_actor_location().x,lsm.get_actor_location().y,lsm.get_actor_location().z]]) list_locations = np.append(list_locations,location,axis=0) list_locations = np.delete(list_locations,0,axis=0) #クラスタリング num_clusters = 3 pred = KMeans(n_clusters=num_clusters).fit_predict(list_locations) Get_actor_location() ・・・Unreal Vector型を返す
  92. #UE4 | @UNREALENGINE Unreal Vector型 Unreal Engine上でVector型をやり取りするための型 Kmeansでも使えるように
  93. #UE4 | @UNREALENGINE 解説 3/5 #インスタンスに追加するトランスフォームリストと、クラスタリングに使うための位置リストを作成 for lsm in list_static_mesh_actors: if lsm.get_component_by_class(unreal.StaticMeshComponent).get_editor_property("StaticMesh") == lu: list_transform = np.append(list_transform,lsm.get_actor_transform()) location = np.array([[lsm.get_actor_location().x,lsm.get_actor_location().y,lsm.get_actor_location().z]]) list_locations = np.append(list_locations,location,axis=0) list_locations = np.delete(list_locations,0,axis=0) #クラスタリング num_clusters = 3 pred = KMeans(n_clusters=num_clusters).fit_predict(list_locations)
  94. #UE4 | @UNREALENGINE 解説 3/5 #インスタンスに追加するトランスフォームリストと、クラスタリングに使うための位置リストを作成 for lsm in list_static_mesh_actors: if lsm.get_component_by_class(unreal.StaticMeshComponent).get_editor_property("StaticMesh") == lu: list_transform = np.append(list_transform,lsm.get_actor_transform()) location = np.array([[lsm.get_actor_location().x,lsm.get_actor_location().y,lsm.get_actor_location().z]]) list_locations = np.append(list_locations,location,axis=0) list_locations = np.delete(list_locations,0,axis=0) #クラスタリング num_clusters = 3 pred = KMeans(n_clusters=num_clusters).fit_predict(list_locations)
  95. #UE4 | @UNREALENGINE 解説 3/5 #インスタンスに追加するトランスフォームリストと、クラスタリングに使うための位置リストを作成 for lsm in list_static_mesh_actors: if lsm.get_component_by_class(unreal.StaticMeshComponent).get_editor_property("StaticMesh") == lu: list_transform = np.append(list_transform,lsm.get_actor_transform()) location = np.array([[lsm.get_actor_location().x,lsm.get_actor_location().y,lsm.get_actor_location().z]]) list_locations = np.append(list_locations,location,axis=0) list_locations = np.delete(list_locations,0,axis=0) #クラスタリング num_clusters = 5 pred = KMeans(n_clusters=num_clusters).fit_predict(list_locations) n_clusters = 5
  96. #UE4 | @UNREALENGINE 解説 3/5 #インスタンスに追加するトランスフォームリストと、クラスタリングに使うための位置リストを作成 for lsm in list_static_mesh_actors: if lsm.get_component_by_class(unreal.StaticMeshComponent).get_editor_property("StaticMesh") == lu: list_transform = np.append(list_transform,lsm.get_actor_transform()) location = np.array([[lsm.get_actor_location().x,lsm.get_actor_location().y,lsm.get_actor_location().z]]) list_locations = np.append(list_locations,location,axis=0) list_locations = np.delete(list_locations,0,axis=0) #クラスタリング num_clusters = 5 pred = KMeans(n_clusters=num_clusters).fit_predict(list_locations) 0 1 2 3 4 0 0 0 1 1 1 2 2 2 2 3 3 3 4 4 4 fit_predict
  97. #UE4 | @UNREALENGINE 解説 4/5 #コンポーネントのリストを作成 instanced_components = np.array([]) #クラスタ毎にアクタをスポーン、コンポーネントをリストに入れる for i in range(num_clusters): instanced_actor = unreal.EditorLevelLibrary.spawn_actor_from_class(bp_instance,(0,0,0),(0,0,0)) instanced_component = instanced_actor.get_component_by_class(unreal.InstancedStaticMeshComponent) instanced_components = np.append(instanced_components,instanced_component)
  98. #UE4 | @UNREALENGINE 解説 4/5 #コンポーネントのリストを作成 instanced_components = np.array([]) #クラスタ毎にアクタをスポーン、コンポーネントをリストに入れる for i in range(num_clusters): instanced_actor = unreal.EditorLevelLibrary.spawn_actor_from_class(bp_instance,(0,0,0),(0,0,0)) instanced_component = instanced_actor.get_component_by_class(unreal.InstancedStaticMeshComponent) instanced_components = np.append(instanced_components,instanced_component)
  99. #UE4 | @UNREALENGINE 解説 4/5 #コンポーネントのリストを作成 instanced_components = np.array([]) #クラスタ毎にアクタをスポーン、コンポーネントをリストに入れる for i in range(num_clusters): instanced_actor = unreal.EditorLevelLibrary.spawn_actor_from_class(bp_instance,(0,0,0),(0,0,0)) instanced_component = instanced_actor.get_component_by_class(unreal.InstancedStaticMeshComponent) instanced_components = np.append(instanced_components,instanced_component)
  100. #UE4 | @UNREALENGINE 解説 4/5 #コンポーネントのリストを作成 instanced_components = np.array([]) #クラスタ毎にアクタをスポーン、コンポーネントをリストに入れる for i in range(num_clusters): instanced_actor = unreal.EditorLevelLibrary.spawn_actor_from_class(bp_instance,(0,0,0),(0,0,0)) instanced_component = instanced_actor.get_component_by_class(unreal.InstancedStaticMeshComponent) instanced_components = np.append(instanced_components,instanced_component) スポーンするアクタは あらかじめ用意しておく Blueprintを作成し Instanced Static Meshを追加
  101. #UE4 | @UNREALENGINE 解説 4/5 #コンポーネントのリストを作成 instanced_components = np.array([]) #クラスタ毎にアクタをスポーン、コンポーネントをリストに入れる for i in range(num_clusters): instanced_actor = unreal.EditorLevelLibrary.spawn_actor_from_class(bp_instance,(0,0,0),(0,0,0)) instanced_component = instanced_actor.get_component_by_class(unreal.InstancedStaticMeshComponent) instanced_components = np.append(instanced_components,instanced_component)
  102. #UE4 | @UNREALENGINE 解説 5/5 #クラスタ番号を元に親となるインスタンスを選択し、トランスフォームを追加 for j, pd in enumerate(pred): instanced_components[pd].add_instance(list_transform[j]) #スタティックメッシュを割り当て for k in range(num_clusters): instanced_components[k].set_editor_property("StaticMesh",lu) #最初に選択していたアクタを削除 for lsm in list_static_mesh_actors: lsm.destroy_actor()
  103. #UE4 | @UNREALENGINE 解説 5/5 #クラスタ番号を元に親となるインスタンスを選択し、トランスフォームを追加 for j, pd in enumerate(pred): instanced_components[pd].add_instance(list_transform[j]) #スタティックメッシュを割り当て for k in range(num_clusters): instanced_components[k].set_editor_property("StaticMesh",lu) #最初に選択していたアクタを削除 for lsm in list_static_mesh_actors: lsm.destroy_actor()
  104. #UE4 | @UNREALENGINE 解説 5/5 #クラスタ番号を元に親となるインスタンスを選択し、トランスフォームを追加 for j, pd in enumerate(pred): instanced_components[pd].add_instance(list_transform[j]) #スタティックメッシュを割り当て for k in range(num_clusters): instanced_components[k].set_editor_property("StaticMesh",lu) #最初に選択していたアクタを削除 for lsm in list_static_mesh_actors: lsm.destroy_actor() 空のインスタンス
  105. #UE4 | @UNREALENGINE 解説 5/5 #クラスタ番号を元に親となるインスタンスを選択し、トランスフォームを追加 for j, pd in enumerate(pred): instanced_components[pd].add_instance(list_transform[j]) #スタティックメッシュを割り当て for k in range(num_clusters): instanced_components[k].set_editor_property("StaticMesh",lu) #最初に選択していたアクタを削除 for lsm in list_static_mesh_actors: lsm.destroy_actor()
  106. #UE4 | @UNREALENGINE 解説 5/5 #クラスタ番号を元に親となるインスタンスを選択し、トランスフォームを追加 for j, pd in enumerate(pred): instanced_components[pd].add_instance(list_transform[j]) #スタティックメッシュを割り当て for k in range(num_clusters): instanced_components[k].set_editor_property("StaticMesh",lu) #最初に選択していたアクタを削除 for lsm in list_static_mesh_actors: lsm.destroy_actor()
  107. #UE4 | @UNREALENGINE 解説 5/5 #クラスタ番号を元に親となるインスタンスを選択し、トランスフォームを追加 for j, pd in enumerate(pred): instanced_components[pd].add_instance(list_transform[j]) #スタティックメッシュを割り当て for k in range(num_clusters): instanced_components[k].set_editor_property("StaticMesh",lu) #最初に選択していたアクタを削除 for lsm in list_static_mesh_actors: lsm.destroy_actor()
  108. #UE4 | @UNREALENGINE これでインスタンス化できたが・・・ 再調整したい場合は元のバラバラな状態に戻す必要がある
  109. #UE4 | @UNREALENGINE 実装例:インスタンスを個別のStaticMeshに変換
  110. #UE4 | @UNREALENGINE インスタンスを個別のStaticMeshに変換
  111. #UE4 | @UNREALENGINE コード import unreal import numpy as np selected_actors = unreal.EditorLevelLibrary.get_selected_level_actors() for sa in selected_actors: instanced_components = np.array([]) instanced_component = sa.get_components_by_class(unreal.InstancedStaticMeshComponent) instanced_components = np.append(instanced_components,instanced_component) for ic in instanced_components: instance_transform = np.array([]) instance_count = ic.get_instance_count() for j in range(instance_count): instance_transform = np.append(instance_transform,ic.get_instance_transform(j,1)) spawned_actor = unreal.EditorLevelLibrary.spawn_actor_from_class(unreal.StaticMeshActor,(0,0,0),(0,0,0)) spawned_actor.set_actor_transform(instance_transform[j],0,0) smc = spawned_actor.get_component_by_class(unreal.StaticMeshComponent) smc.set_editor_property("StaticMesh",ic.get_editor_property("StaticMesh")) for sa in selected_actors: sa.destroy_actor()
  112. #UE4 | @UNREALENGINE 解説 1/3 #ライブラリをインポート import unreal import numpy as np #選択したアクタを取得 selected_actors = unreal.EditorLevelLibrary.get_selected_level_actors()
  113. #UE4 | @UNREALENGINE 解説 1/3 #ライブラリをインポート import unreal import numpy as np #選択したアクタを取得 selected_actors = unreal.EditorLevelLibrary.get_selected_level_actors()
  114. #UE4 | @UNREALENGINE 解説 1/3 #ライブラリをインポート import unreal import numpy as np #選択したアクタを取得 selected_actors = unreal.EditorLevelLibrary.get_selected_level_actors()
  115. #UE4 | @UNREALENGINE 解説 2/3 #インスタンスコンポーネントを配列に入れる for sa in selected_actors: instanced_components = np.array([]) instanced_component = sa.get_components_by_class(unreal.InstancedStaticMeshComponent) instanced_components = np.append(instanced_components,instanced_component) #インスタンス数を取得 for ic in instanced_components: instance_transform = np.array([]) instance_count = ic.get_instance_count()
  116. #UE4 | @UNREALENGINE 解説 2/3 #インスタンスコンポーネントを配列に入れる for sa in selected_actors: instanced_components = np.array([]) instanced_component = sa.get_components_by_class(unreal.InstancedStaticMeshComponent) instanced_components = np.append(instanced_components,instanced_component) #インスタンス数を取得 for ic in instanced_components: instance_transform = np.array([]) instance_count = ic.get_instance_count()
  117. #UE4 | @UNREALENGINE 解説 2/3 #インスタンスコンポーネントを配列に入れる for sa in selected_actors: instanced_components = np.array([]) instanced_component = sa.get_components_by_class(unreal.InstancedStaticMeshComponent) instanced_components = np.append(instanced_components,instanced_component) #インスタンス数を取得 for ic in instanced_components: instance_transform = np.array([]) instance_count = ic.get_instance_count()
  118. #UE4 | @UNREALENGINE 解説 2/3 #インスタンスコンポーネントを配列に入れる for sa in selected_actors: instanced_components = np.array([]) instanced_component = sa.get_components_by_class(unreal.InstancedStaticMeshComponent) instanced_components = np.append(instanced_components,instanced_component) #インスタンス数を取得 for ic in instanced_components: instance_transform = np.array([]) instance_count = ic.get_instance_count() インスタンスがいくつあるか取得し、 その数分新たにアクタをスポーンしていく
  119. #UE4 | @UNREALENGINE 解説 3/3 #インスタンス数の分だけトランスフォーム値を取得 for j in range(instance_count): instance_transform = np.append(instance_transform,ic.get_instance_transform(j,1)) #スポーンしてトランスフォームを適用 spawned_actor = unreal.EditorLevelLibrary.spawn_actor_from_class(unreal.StaticMeshActor,(0,0,0),(0,0,0)) spawned_actor.set_actor_transform(instance_transform[j],0,0) smc = spawned_actor.get_component_by_class(unreal.StaticMeshComponent) #スタティックメッシュを割り当て smc.set_editor_property("StaticMesh",ic.get_editor_property("StaticMesh")) #最初に選択していたアクタを削除 for sa in selected_actors: sa.destroy_actor()
  120. #UE4 | @UNREALENGINE 解説 3/3 #インスタンス数の分だけトランスフォーム値を取得 for j in range(instance_count): instance_transform = np.append(instance_transform,ic.get_instance_transform(j,1)) #スポーンしてトランスフォームを適用 spawned_actor = unreal.EditorLevelLibrary.spawn_actor_from_class(unreal.StaticMeshActor,(0,0,0),(0,0,0)) spawned_actor.set_actor_transform(instance_transform[j],0,0) smc = spawned_actor.get_component_by_class(unreal.StaticMeshComponent) #スタティックメッシュを割り当て smc.set_editor_property("StaticMesh",ic.get_editor_property("StaticMesh")) #最初に選択していたアクタを削除 for sa in selected_actors: sa.destroy_actor()
  121. #UE4 | @UNREALENGINE 解説 3/3 #インスタンス数の分だけトランスフォーム値を取得 for j in range(instance_count): instance_transform = np.append(instance_transform,ic.get_instance_transform(j,1)) #スポーンしてトランスフォームを適用 spawned_actor = unreal.EditorLevelLibrary.spawn_actor_from_class(unreal.StaticMeshActor,(0,0,0),(0,0,0)) spawned_actor.set_actor_transform(instance_transform[j],0,0) #スタティックメッシュを割り当て smc = spawned_actor.get_component_by_class(unreal.StaticMeshComponent) smc.set_editor_property("StaticMesh",ic.get_editor_property("StaticMesh")) #最初に選択していたアクタを削除 for sa in selected_actors: sa.destroy_actor()
  122. #UE4 | @UNREALENGINE 解説 3/3 #インスタンス数の分だけトランスフォーム値を取得 for j in range(instance_count): instance_transform = np.append(instance_transform,ic.get_instance_transform(j,1)) #スポーンしてトランスフォームを適用 spawned_actor = unreal.EditorLevelLibrary.spawn_actor_from_class(unreal.StaticMeshActor,(0,0,0),(0,0,0)) spawned_actor.set_actor_transform(instance_transform[j],0,0) #スタティックメッシュを割り当て smc = spawned_actor.get_component_by_class(unreal.StaticMeshComponent) smc.set_editor_property("StaticMesh",ic.get_editor_property("StaticMesh")) #最初に選択していたアクタを削除 for sa in selected_actors: sa.destroy_actor()
  123. #UE4 | @UNREALENGINE 解説 3/3 #インスタンス数の分だけトランスフォーム値を取得 for j in range(instance_count): instance_transform = np.append(instance_transform,ic.get_instance_transform(j,1)) #スポーンしてトランスフォームを適用 spawned_actor = unreal.EditorLevelLibrary.spawn_actor_from_class(unreal.StaticMeshActor,(0,0,0),(0,0,0)) spawned_actor.set_actor_transform(instance_transform[j],0,0) #スタティックメッシュを割り当て smc = spawned_actor.get_component_by_class(unreal.StaticMeshComponent) smc.set_editor_property("StaticMesh",ic.get_editor_property("StaticMesh")) #最初に選択していたアクタを削除 for sa in selected_actors: sa.destroy_actor()
  124. #UE4 | @UNREALENGINE おまけ:UIを作る
  125. #UE4 | @UNREALENGINE おまけ:UIを作る
  126. #UE4 | @UNREALENGINE おまけ:UIを作る 特定のアクタを選択
  127. #UE4 | @UNREALENGINE おまけ:UIを作る 特定のアクタを選択
  128. #UE4 | @UNREALENGINE おまけ:UIを作る
  129. #UE4 | @UNREALENGINE 実装例は後日Git Hubなどで公開予定 ※あくまで一例として作ったものなので、より実用的にするには改良が必要 ※公開した実装例の保守、サポートは致しません
  130. #UE4 | @UNREALENGINE 参考 Unreal Python API リファレンス https://api.unrealengine.com/INT/PythonAPI/
  131. #UE4 | @UNREALENGINE 参考 Mclarenによる事例:ホワイトペーパー Unreal Studio を使用した CAD データの準備および リアルタイム ビジュアライゼーションでの自動化 https://cdn2.unrealengine.com/Unreal+Engine%2Fresources%2FMcLaren+W hitepaper%2FADCaV-Whitepaper-JPN-V2- f4fd0b09e8b279171149eef9220370e1b7495092.pdf
  132. #UE4 | @UNREALENGINE ご清聴ありがとうございました

Hinweis der Redaktion

  1. 本日はこのような内容でお話しさせていただきます。
  2. まず最初に大規模開発における作業効率化・自動化の需要についてお話したいと思います。
  3. 昨今ではコンテンツ開発の規模が大きくなってきており、それに伴って大量のアセットの管理、製作コストも上がって 手作業だけでは時間がかかりすぎたり、クオリティに影響が出る可能性なども考えられます。
  4. そのようなことが起きないように、大規模開発ではこういった単純作業をスクリプト化し自動、または半自動で行えるようにしていく必要が出てくると思います。 実際AAAタイトルのゲームやゲーム以外の分野でも、大規模開発になると単純作業の自動化は積極的に行われています。 もちろんUnreal Engineの事例も
  5. McLarenによるUnreal Engine内での自動化事例を軽く紹介させていただきたいと思います。
  6. McLarenではUnreal Engineを活用して車のデザインツールを開発しています。 様々な種類の車をデザインする過程で部品などのデータをUnreal Engineに複数回インポートする必要が出てきます。 インポート後にもフォルダ構成を整理したり、複数のパーツをマージしたりなど、更に作業が必要になる場合もあります。 数十個程度のパーツであれば手作業でもさほど苦労はしませんが、車のデータになると数千パーツになることも。
  7. そこでMcLarenではPythonや後程紹介するDatasmithを活用することによってインポートに必要な作業の効率化を図っています。 インポート後にデータの解析、テッセレーションやLODの作成、解析したメタデータを元にマテリアル割り当てやシーンへの配置。 これらを自動化してインポートワークフローを大幅に単純化しました。 このようにプロジェクトが大きくなるにつれ、大量のデータを一度に捌く必要が出てきますが、Unreal Engineにはこういった作業効率化に役立つツールが備わっています。
  8. Unreal Engineに備わっている効率化や自動化に役立つツールを紹介したいと思います。
  9. Bluetility Blueprint Utiltyのことなんですが Blueprint、Unreal Engineを触ったことのある方ならご存じかと思いますが、ノードベースのプログラミング機能です。 これによってプログラマがいなくてもゲームのようなロジックを組むことができます。 主にゲーム上で動作するロジックを作るのものなのですが、エディタ上での作業をスクリプト化することも可能です。
  10. Blueprintに限らず、エディタスクリプティングにはプラグインからEditor Scripting Utilitiesを有効にする必要があります。 これによってエディタ効率化に必要な関数やノードがいくつか公開され、Blueprintからもアクセスすることができるようになります。
  11. Pythonもエディタ上で動かすことができます。 Pythonバージョンは2.7になります。
  12. Unreal Pythonはプラグインになっていますのでプラグインからチェックを入れて有効にする必要があります。 これでUnreal Engine上でPythonを実行できるようになります。
  13. 実行方法は複数ありまして、アウトプットログというログを表示したりコマンドを入力したりできるウィンドウで 左隅を押すとPythonモードに切り替えられますので、その状態で直接入力が可能です。
  14. また、用意しておいたスpyファイルのパスを直接指定することでも実行可能です。 頭にpyとつけてスペースを置いて、パスを指定することでスクリプトが実行されます。 こちらはPythonモードではなく、コマンドモードでないと動きませんのでご注意ください。
  15. 起動時に実行するpyファイルを指定することも可能です。 Project Settings>Plugin>Python>Startup Scriptsにパスを入れることで、起動時に毎回実行されるようになります。
  16. 次にEditor Utility Widgetを紹介したいと思います。 簡単に説明すると、Unreal Engine上でツールやエディタ拡張の作成ができる機能です。 UMGと呼ばれる機能を使うのですが、これは本来ゲーム中のUIを作成する機能なんですが、エディタ上で動作するツールのUIを簡単に作れるようになります。
  17. 新しい機能なので使ったことのない方もいるかと思いますので少し説明させていただきます。 コンテンツブラウザで右クリックEditor Utilities > Editor Widgetで作成できます。
  18. エディタを開くとこんな画面になっています
  19. 左側のメニューから、UIとして必要な機能をD&Dで好きな場所に置くことができます。 UIのデザインはすごく簡単にできます。
  20. そして、配置したボタンを押したときなどの処理をBlueprintで作成していきます。
  21. 出来上がったらアセット右クリックからRun Editor Utility Widgetで作成したツールが立ち上がります。 こんな感じで簡単に独自のUIを持ったツールを作ることができるので、BlueprintやPythonと組み合わせて使うとより効率的に作業ができるようになるかと思います。
  22. Editor Utility Widgetに関しては弊社岡田がQiitaにて解説記事を書いています。 興味のある方はこちらも合わせてご覧ください。
  23. ではこれらを使って何ができるか、プロジェクトによって需要は様々かと思いますが 今回はあくまで一例として実装例をいくつか作ってきました。
  24. ポリゴンへの変換、マテリアルの変換やライトマップ用UV展開までを短時間で行うことができます
  25. 数百パーツとか、数千パーツになると、無駄なパーツを一つ一つ探して削除なりマージ作業を何回も繰り返さないといけません。大半の人はやりたくないと思います。
  26. 実装例として、Datasmithで小さいパーツを除外してインポートというのをBlueprintでやってみました。 こんなかんじのテストシーンをインポートしてみます。
  27. この段階、まだEngine上にインポートされていない段階で Datasmith SceneにBlueprintノードでアクセスして、いろいろな処理を行うことができます。
  28. Get All Mesh Actorsでシーン上のアクタを全取得し、
  29. そのアクタ一つ一つのバウンディングボックスの大きさを見ていきます。
  30. これで大きさを判断して
  31. 今回は大きさを判定して削除、という処理になっていますが、削除ではなく一つのオブジェクトにマージする、とか 大きさで判定するのではなく、パーツの名前から判定するなど、プロジェクトの需要に合わせて色々なパターンが作れると思いますので、ぜひお試しください。
  32. 次はEditor Utility Widgetの実装例になります。
  33. プレフィックスは先頭につける文字列、サフィックスは後ろにつける文字列
  34. Unreal Engineでの命名規則の一例がこちらのサイトに載っていますので参考にしてみてください。 Epicが出しているサンプルプロジェクトやマーケットプレイスのコンテンツの命名も参考になると思います。
  35. なぜ命名規則を決めないておかないといけないのか、これもやはりプロジェクトが大規模化するにつれて重要になっていくからです。
  36. そこで今回はEditor Utility Widgetを使って、アセットの種類を判別して、設定した命名規則に応じたリネーム処理を行う仕組みを作ってみました。 左側にくっついているのがEditor Utility Wdgetで作成したもので、種類毎に名前の頭に何をつけるかを設定してあります。 ちゃんと命名されてないアセットが並んでいますが、すべて選択して適用すると一気にリネーム処理が行われ、命名規則に準じた文字列が付けられます。
  37. UIの方は命名規則を設定するテキスト入力の部分と
  38. 適用するボタン一つを置いてみました。 UIの方はシンプルな構成になっています。 このUIに対して、ボタンを押したらどういった処理をするかというところをBlueprintを使って作っていきます。
  39. Blueprintの方はこんな感じになっています。
  40. 左からボタンを押したときのイベントがあって、ボタンがおされたら選択しているアセットを全部取得し、
  41. この部分でアセットの種類ごとにリネーム処理をしていきます。 Asset Renameというノードは新規作成した関数ノードで、この中に更に色々ノードが入っています。
  42. 関数ノードの中身がこんな感じです。 左側の方から解説していくと
  43. さっき取得した選択状態のアセットリストをFilter by classというノードでフィルタリングします。 スタティックメッシュのみにしたり、マテリアルのみにしたりとか。
  44. それらに対して命名規則が正しいかどうかチェックして、正しければ何もしない。 正しくなければアセットの種類に合わせた文字列を頭に追加する。 こんな感じの実装になっています。 命名規則はアセット管理をする上で重要になってきます。 Editor Utility WidgetやBlueprintでチェックや修正を行うツールを作れば、より効率的にアセット管理ができるようになるかと思います。
  45. 言葉で言ってもわかりにくいかもしれないので、Unreal Engineの画像で説明するとこんな感じです。 左側は同じオブジェクトが9個並んでいるので9回分のドローコールになりますが、右側はインスタンスとして9個並べていて、こちらはドローコールは1回分です。 こんな風に画面内にオブジェクトを描画しなければならないとき、オブジェクトが呼び出される回数が少ない方が処理負荷が低くなる可能性があります。
  46. Unreal Engineではインスタンスを作るためにInstanced Static Meshというものが用意されています。
  47. このInstanced Static Meshは、
  48. このInstanced Static Meshは、一つのスタティックメッシュと
  49. このInstanced Static Meshは、一つのスタティックメッシュと インスタンス数分の位置、回転、スケールを含むトランスフォームを持っているという構成になっていて この追加したトランスフォームの数分複製されるようになっています。
  50. ですが、すでに置かれているStatic Meshをインスタンスに変換しようとすると面倒で・・・
  51. これを変換したいアクタの数分行います。やりたくないです。
  52. 実は変換をやってくれるツールはすでにありまして Merge Actorsというツールを使うことで選択アクタを変換することは可能です。
  53. ただし、あくまで選択したアクタを一つのインスタンスにしてくれるだけです。
  54. 例えば・・・
  55. これを利用してUnreal Pythonでオブジェクトのインスタンス化を行います。 この例では2Dの座標ですが3D座標でも可能なので、Unreal Engine上でのアクタの座標から同じことをやってみます。
  56. 外部ライブラリの話が出てきましたが、Unreal Pythonでも外部ライブラリを使うことができます。 PythonでライブラリをインストールするとPython\Lib以下にsite-packagesというフォルダに色々入ると思いますが、Engine以下にも同じような階層がありますのでこちらに丸ごとコピーします。
  57. 実際にKmeansを使ってインスタンシングしてみた結果がこちらになります。 バラバラなオブジェクトに対して実行すると、似通った位置のアクタ同士でインスタンス化されます。 どんな感じの実装になっているか解説していきます。
  58. コードはこんな感じです。 小さくて申し訳ないですが、後日公開予定ですのでじっくり読みたい方や使ってみたい方は、公開次第見ていただければと思います。 こちらも重要な部分をかいつまんで解説していきます。
  59. 後半はこんな感じです。
  60. まずはライブラリインポート Import unrealでunreal engine用のクラスや関数をインポートします。 配列の操作が多くなるので、配列を扱うのに便利なnumpyもインポート。 今回使うKmeansはSkicit learnというライブラリに含まれているので、そちらもインポートしています。
  61. 次に選択しているアクタを取得。 取得してどうするかというと
  62. 後程、位置や回転、何のメッシュが割り当たっているかなどの情報を取得します。 get_selected_level_actorsという関数を使います。
  63. ここは選択アクタに複数種類のメッシュがあったときの処理になります。
  64. ここの部分でクラスタリング用のアクタの位置を配列にしています。
  65. このget_actor_locationでアクタの位置を取得するのですが、返ってくるのはUnreal Vectorという型です。
  66. 位置などの情報は入っているのですが、この状態ではKmeansでは使えません。 なので下のような扱いやすい配列にします。
  67. それをやっているのがここの部分です。
  68. ここでようやくK-meansによるクラスタリング
  69. ここでようやくK-meansによるクラスタリング N-clustersでクラスタ数を指定、
  70. ここでようやくK-meansによるクラスタリング N-clustersでクラスタ数を指定、 その横のfit_predictは配列のクラスタ番号を返す どのクラスタに配属されたか この値を基準にインスタンスを作っていきます
  71. ここでクラスタの数分、空のインスタンスをスポーンします。
  72. Spawn_actor_from_class()という関数でスポーン処理を行います。 スポーンする対象として指定しているBp_instanceは新規作成したBlueprintです。
  73. こんな感じでInstanced Static Meshを追加してあらかじめ用意しておく必要があります。
  74. ただスポーンしただけだと、インスタンスの位置情報やメッシュの割り当ても無しの空の状態です。 なのでここからインスタンスの位置情報やメッシュの割り当てを行っていきます。
  75. 先ほどKmeansを使って出したクラスタ番号を元に、トランスフォームを追加します。
  76. エンジン上の画像でいうとこんな感じのことをやっています。 スポーンした空のインスタンスに右のようにトランスフォームを追加していきます。
  77. こちらはメッシュの割り当てです。
  78. エンジン上の画像でいうとこんな感じです。
  79. これでインスタンス化の処理は終わったので 最初に選択していたアクタをすべて削除して終わりです。 これでいい感じにインスタンス化してくれるスクリプトが出来上がりました。
  80. まずはライブラリインポート Unrealライブラリと、今回も配列の操作が多いのでnumpyを使いました
  81. 選択したアクタを取得
  82. ここでインスタンスの数を取得します。
  83. エディタ上の画面でいうとこの部分 位置、回転、スケールをすべて取得します
  84. ここでStatic Mesh Actorをスポーンして 今取得したトランスフォームをひとつづつ適用していきます。
  85. スポーンしてトランスフォームを適用しただけで、まだ空の状態なのでスタティックメッシュを割り当てます。 これで変換処理は完了したので
  86. 最後に、元あったインスタンスを削除します。 これでインスタンスとバラバラなスタティックメッシュを行き来しながら調整することができるようになりましたが、
  87. せっかくなのでUIを作って使い勝手を良くしてみます。
  88. UIはこんな感じです。 Break Instanceの方はインスタンスをすべて選択するボタンと、実行ボタン Make Instanceの方はスタティックメッシュをすべて選択するボタンと、実行ボタン で、それぞれ実行するpyスクリプトファイルのパスを入力する感じになってます。
  89. Blueprintの方は、こちらが特定のアクタを選択する処理。 ボタンが押されたらGet All Actors Of Classで特定のアクタだけ取得、Set Selected Level Actorsでそれらのアクタを選択させます。
  90. こちらはPythonを実行する処理。 ボタンが押されたらUIに入力されたテキストのパスを取得して、Excute Console Commandで実行。 こんな感じの実装になっています。 このようなちょっとした機能もサッと作れるのがEditor Utility Widgetのいいところですね。
  91. 実際動作しているところです。 選択してインスタンス化し、また選択してバラバラに戻します。 こんな感じで、PythonとWditor Utility WdgetとBlueprintを組み合わせて使うことで、さらに可能性が広がると思います。 プロジェクトによってツールの需要は多種多様だと思うので、いろいろ試して使っていただければと思います。 実装例は以上になります。
  92. 今日紹介させていただいた実装例は、後日Git Hubなどで公開予定です。 注意事項として、あくまで一例として作ったものなので、より実用的にするには改良が必要になるかと思います。 また、公開した実装例の保守、サポートは致しませんのでご了承ください。
  93. 最後に参考資料を共有させていただきます。 Unrealの専用クラスや関数はこちらにまとまっています。 数が膨大ですが、検索機能もあるのでUnreal Pythonを使う際はこちらを利用することをおすすめします。
  94. 最初に紹介したMclarenの資料はホワイトペーパーとして無料で公開されております。 興味のある方はご覧ください。
Anzeige