64.8K Views
July 17, 19
スライド概要
GTMF2019の講演資料を公開します。
Unreal Engineを開発・提供しているエピック ゲームズ ジャパンによる公式アカウントです。 勉強会や配信などで行った講演資料を公開しています。 公式サイトはこちら https://www.unrealengine.com/ja/
Python / Blueprintによる Unreal Engineの自動化 Epic Games Japan / Technical Artist 小林浩之
自己紹介 小林 浩之 Epic Games Japan / Technical Artist スクウェア・エニックス大阪で背景TAを2年ほど 今年2月からEGJのエンタープライズ分野のサポートとして入社 #UE4 | @UNREALENGINE
目次 • 大規模開発における作業効率化・自動化の需要 • Unreal Engine 4の作業効率化・自動化ツール紹介 • 実装例 • Datasmithインポートの効率化 • 命名規則に応じたアセットリネーム • Pythonによるインスタンシング Python • インスタンスを個別のStatic Meshに変換 Python #UE4 | @UNREALENGINE Blueprint Editor Utility Widget & Blueprint
目次 • 大規模開発における作業効率化・自動化の需要 • Unreal Engine 4の作業効率化・自動化ツール紹介 • 実装例 • Datasmithインポートの効率化 • 命名規則に応じたアセットリネーム • Pythonによるインスタンシング Python • インスタンスを個別のStatic Meshに変換 Python #UE4 | @UNREALENGINE Blueprint Editor Utility Widget & Blueprint
大規模開発における作業効率化・自動化の需要 大量に配置されたオブジェクトの整理、膨大なアセットの管理など、 手作業でやっているとコストがかかるりすぎる・・・ 時間が足りない・・・ クオリティアップにコストを割けない・・・ #UE4 | @UNREALENGINE
大規模開発における作業効率化・自動化の需要 オブジェクト整理作業 アセット管理作業 などなど 自動化 #UE4 | @UNREALENGINE
McLarenによる事例 Unreal Engineへの CADデータインポートの自動化 #UE4 | @UNREALENGINE
McLarenによる事例 インポートフローの改善 #UE4 | @UNREALENGINE
目次 • 大規模開発における作業効率化・自動化の需要 • Unreal Engine 4の作業効率化・自動化ツール紹介 • 実装例 • Datasmithインポートの効率化 • 命名規則に応じたアセットリネーム • Pythonによるインスタンシング Python • インスタンスを個別のStatic Meshに変換 Python #UE4 | @UNREALENGINE Blueprint Editor Utility Widget & Blueprint
Bluetility (Blueprint Utility) Blueprintを使ったスクリプティング #UE4 | @UNREALENGINE
Bluetility プラグイン > Editor Scripting Utilities #UE4 | @UNREALENGINE
Unreal Python Pythonによるエディタスクリプティング #UE4 | @UNREALENGINE
Unreal Python プラグイン > Python Editor Script Pluginにチェックで有効化 #UE4 | @UNREALENGINE
Unreal Python 実行方法 アウトプットログから直接入力 #UE4 | @UNREALENGINE
Unreal Python 実行方法 .pyファイルのパス指定で実行 #UE4 | @UNREALENGINE
Unreal Python エディタ起動時に実行 #UE4 | @UNREALENGINE
Editor Utility Widget UMG&Blueprintでエディタ拡張 #UE4 | @UNREALENGINE
Editor Utility Widget コンテンツブラウザで右クリック>Editor Utilities>Editor Widgetから作成 #UE4 | @UNREALENGINE
Editor Utility Widget #UE4 | @UNREALENGINE
Editor Utility Widget UIに必要な機能(ボタンやテキストなど)をD&Dで置く #UE4 | @UNREALENGINE
Editor Utility Widget UIからの処理をBlueprintで作成 #UE4 | @UNREALENGINE
Editor Utility Widget アセット右クリック>Run Editor Utility WidgetでWindow立ち上げ、実行 #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 #UE4 | @UNREALENGINE
目次 • 大規模開発における作業効率化・自動化の需要 • Unreal Engine 4の作業効率化・自動化ツール紹介 • 実装例 • Datasmithインポートの効率化 • 命名規則に応じたアセットリネーム • Pythonによるインスタンシング Python • インスタンスを個別のStatic Meshに変換 Python #UE4 | @UNREALENGINE Blueprint Editor Utility Widget & Blueprint
実装例:Datasmithインポートの効率化 #UE4 | @UNREALENGINE
Datasmithとは CADソフトなどのデータをUE4用に変換してインポートする機能 (Unreal Studioのみ) Datasmith #UE4 | @UNREALENGINE
Datasmithによって大幅に効率化されるが・・・ CADデータの場合細かいネジなどのパーツまで含んだデータになっている場合が多い 環境によっては処理負荷が高くなってしまう可能性も #UE4 | @UNREALENGINE
Datasmithによって大幅に効率化されるが・・・ リアルタイムエンジンでスムーズに描画するためには ほとんど描画されないような小さいパーツは削除したり、一つにまとめる必要がある #UE4 | @UNREALENGINE
普通にインポートして後から削除しようとするとする場合 パーツ数が膨大だと作業コストが高くなってしまう インポート 小さいパーツを探す 数百パーツを手作業でとか・・・ #UE4 | @UNREALENGINE 削除
BlueprintやPythonを使うことでこれらの作業を自動化できる インポート 小さいパーツを探す 自動化 #UE4 | @UNREALENGINE 削除
小さいパーツを除外してインポート #UE4 | @UNREALENGINE
小さいパーツを除外してインポート #UE4 | @UNREALENGINE
Blueprint ※拡大して解説していきます #UE4 | @UNREALENGINE
解説 インポートするデータからDatasmith Sceneを構築 #UE4 | @UNREALENGINE
Datasmith Scene Datasmithでは、実際にデータをインポートする前に メモリ上で一度シーン構築を行う CADデータ #UE4 | @UNREALENGINE Datasmith Scene in Memory メモリ上でシーンを構築 インポート
Datasmith Scene Datasmithでは、実際にデータをインポートする前に メモリ上で一度シーン構築を行う CADデータ #UE4 | @UNREALENGINE Datasmith Scene in Memory メモリ上でシーンを構築 インポート
解説 Datasmith Scene内のアクタを取得 #UE4 | @UNREALENGINE
解説 バウンディングボックスの大きさを評価 #UE4 | @UNREALENGINE
解説 バウンディングボックス オブジェクトを囲む最小の立方体 この立方体の幅、奥行き、高さから オブジェクトの大体の大きさを測る #UE4 | @UNREALENGINE
解説 条件に当てはまればアクタを削除 #UE4 | @UNREALENGINE
解説 インポートオプションを設定 #UE4 | @UNREALENGINE
解説 実際にインポートし、最後にDatasmith Sceneを削除 #UE4 | @UNREALENGINE
実装例:命名規則に応じたアセットリネーム #UE4 | @UNREALENGINE
命名規則について アセットの種類や用途に応じて名前の前後に付ける文字列 #UE4 | @UNREALENGINE
命名規則について アセットの種類や用途に応じて名前の前後に付ける文字列 例えば・・・ Static Mesh アセット「Table」があるとしたら Static Meshの省略 SM を付け 「SM_Table」 バリエーションがある場合は 番号やアルファベットを付け「 SM_Table _A」にする #UE4 | @UNREALENGINE
命名規則について 参考 Unreal Engine Assets Naming Convention https://wiki.unrealengine.com/Assets_Naming_Convention_JP #UE4 | @UNREALENGINE
命名規則について プロジェクトが大規模化するにつれ、命名規則はより重要に #UE4 | @UNREALENGINE
命名規則について プロジェクトが大規模化するにつれ、命名規則はより重要に 作業者が自由に名前を付けていると・・・ ● 他の作業者から見たとき用途や種類が判別しにくい ● 特定のアセットを探しずらい #UE4 | @UNREALENGINE
実装例:命名規則に応じたアセットリネーム #UE4 | @UNREALENGINE
解説 UI エディットできるText BoxやButtonなどを置いただけ シンプルな構成 #UE4 | @UNREALENGINE
解説 UI エディットできるText BoxやButtonなどを置いただけ シンプルな構成 #UE4 | @UNREALENGINE
解説 Blueprint #UE4 | @UNREALENGINE
解説 Blueprint ボタンが押されたら選択しているアセットを取得 #UE4 | @UNREALENGINE
解説 Blueprint アセットの種類毎にリネーム処理 #UE4 | @UNREALENGINE
解説 Blueprint 関数Asset Renameの中身 #UE4 | @UNREALENGINE
解説 Blueprint 処理するクラスを設定 #UE4 | @UNREALENGINE
解説 Blueprint リネーム処理 #UE4 | @UNREALENGINE
実装例:Pythonによるインスタンシング #UE4 | @UNREALENGINE
インスタンシング 大量のオブジェクトを描画する際に有効な手法 ドローコールを削減し、描画コストを下げる ドローコール 現在の画面を描画するために必要な情報を呼び出す命令のこと 回数が多いほど処理負荷につながる可能性がある #UE4 | @UNREALENGINE
インスタンス化によるドローコールの削減 非インスタンス インスタンス 9回分のドローコール 1回分のドローコール #UE4 | @UNREALENGINE
Instanced Static Mesh Unreal Engineでのインスタンス化メッシュ #UE4 | @UNREALENGINE
Instanced Static Mesh #UE4 | @UNREALENGINE
Instanced Static Mesh • Static Mesh #UE4 | @UNREALENGINE
Instanced Static Mesh • Static Mesh • インスタンス数分の位置、回転、スケール #UE4 | @UNREALENGINE
Static Meshをインスタンス化 ツールやスクリプトを使わずに 手作業で変換しようとすると・・・ アクタ一つ一つの位置、回転、 スケールをコピーして・・・ #UE4 | @UNREALENGINE
Static Meshをインスタンス化 ツールやスクリプトを使わずに 手作業で変換しようとすると・・・ インスタンスに追加 #UE4 | @UNREALENGINE
Static Meshをインスタンス化 Merge Actors 選択アクタを一つのインスタンスに変換 #UE4 | @UNREALENGINE
Static Meshをインスタンス化 Merge Actors 選択アクタを一つのインスタンスに変換 複数インスタンスを一度に生成はできない #UE4 | @UNREALENGINE
Static Meshをインスタンス化 Merge Actors 多数のアクタがあるとして・・・ #UE4 | @UNREALENGINE
Static Meshをインスタンス化 Merge Actors 複数グループに分けてインスタンス化したい場合 インスタンス化したいグループ毎に 選択して変換する作業が必要 #UE4 | @UNREALENGINE
インスタンス化によるドローコールの削減 大量のアクタを複数インスタンス化していくのは高コスト なるべく自動で、いい感じのグループに分けてインスタンス化したい・・・ #UE4 | @UNREALENGINE
インスタンス化によるドローコールの削減 大量のアクタを複数インスタンス化していくのは高コスト なるべく自動で、いい感じのグループに分けてインスタンス化したい・・・ Python外部ライブラリからK-means法を使ってインスタンシング! #UE4 | @UNREALENGINE
K-means法とは ざっくり解説 クラスタ分析を行う手法の一つ Pythonの外部ライブラリScikit Learnに含まれている #UE4 | @UNREALENGINE
K-means法とは ざっくり解説 バラバラな座標のリストがあるとして・・・ #UE4 | @UNREALENGINE
K-means法とは ざっくり解説 設定したクラスタ数に応じて、近い属性同士のグループを作る クラスタ数=5 #UE4 | @UNREALENGINE
Unreal Pythonで外部ライブラリを使う ライブラリインストール後、Python¥Lib¥site-packagesを Engine¥Binaries¥ThirdParty¥Python¥Win64¥Lib以下に丸ごとコピー import 〇〇でインポート出来るようになる #UE4 | @UNREALENGINE
K-means法によるインスタンシング #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]]) #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() #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) #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) #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) #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) #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]]) #UE4 | @UNREALENGINE
複数種類のメッシュがあった場合 Instanced Static Meshが 持てるメッシュは一種類のみ #UE4 | @UNREALENGINE
複数種類のメッシュがあった場合 クラスタ数=3 #UE4 | @UNREALENGINE
複数種類のメッシュがあった場合 種類ごとにクラスタリング クラスタ数=3 #UE4 | @UNREALENGINE
複数種類のメッシュがあった場合 種類ごとにクラスタリング クラスタ数=3 #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) #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) #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) Get_actor_location() ・・・Unreal Vector型を返す list_locations = np.delete(list_locations,0,axis=0) #クラスタリング num_clusters = 3 pred = KMeans(n_clusters=num_clusters).fit_predict(list_locations) #UE4 | @UNREALENGINE
Unreal Vector型 Unreal Engine上でVector型をやり取りするための型 Kmeansでも使えるように #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) #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) n_clusters = 5 #クラスタリング num_clusters = 5 pred = KMeans(n_clusters=num_clusters).fit_predict(list_locations) #UE4 | @UNREALENGINE
解説 3/5 1 1 3 3 1 3 #インスタンスに追加するトランスフォームリストと、クラスタリングに使うための位置リストを作成 0 1 for lsm in list_static_mesh_actors: 0 0 3 if lsm.get_component_by_class(unreal.StaticMeshComponent).get_editor_property("StaticMesh") == lu: 0 list_transform = np.append(list_transform,lsm.get_actor_transform()) 2 2 2 location = np.array([[lsm.get_actor_location().x,lsm.get_actor_location().y,lsm.get_actor_location().z]]) 4 4 list_locations = np.append(list_locations,location,axis=0) 2 list_locations = np.delete(list_locations,0,axis=0) #クラスタリング num_clusters = 5 2 fit_predict pred = KMeans(n_clusters=num_clusters).fit_predict(list_locations) #UE4 | @UNREALENGINE 4 4
解説 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) #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) #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) #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を追加 #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) #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() #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() #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() 空のインスタンス #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() #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() #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() #UE4 | @UNREALENGINE
これでインスタンス化できたが・・・ 再調整したい場合は元のバラバラな状態に戻す必要がある #UE4 | @UNREALENGINE
実装例:インスタンスを個別のStaticMeshに変換 #UE4 | @UNREALENGINE
インスタンスを個別のStaticMeshに変換 #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() #UE4 | @UNREALENGINE
解説 1/3 #ライブラリをインポート import unreal import numpy as np #選択したアクタを取得 selected_actors = unreal.EditorLevelLibrary.get_selected_level_actors() #UE4 | @UNREALENGINE
解説 1/3 #ライブラリをインポート import unreal import numpy as np #選択したアクタを取得 selected_actors = unreal.EditorLevelLibrary.get_selected_level_actors() #UE4 | @UNREALENGINE
解説 1/3 #ライブラリをインポート import unreal import numpy as np #選択したアクタを取得 selected_actors = unreal.EditorLevelLibrary.get_selected_level_actors() #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() #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() #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() #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() インスタンスがいくつあるか取得し、 その数分新たにアクタをスポーンしていく #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() #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() #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() #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() #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() #UE4 | @UNREALENGINE
おまけ:UIを作る #UE4 | @UNREALENGINE
おまけ:UIを作る #UE4 | @UNREALENGINE
おまけ:UIを作る 特定のアクタを選択 #UE4 | @UNREALENGINE
おまけ:UIを作る 特定のアクタを選択 #UE4 | @UNREALENGINE
おまけ:UIを作る #UE4 | @UNREALENGINE
実装例は後日Qiitaで公開予定 ※あくまで一例として作ったものなので、より実用的にするには改良が必要 ※公開した実装例の保守、サポートは致しません #UE4 | @UNREALENGINE
参考 Unreal Python API リファレンス https://api.unrealengine.com/INT/PythonAPI/ #UE4 | @UNREALENGINE
参考 Mclarenによる事例:ホワイトペーパー Unreal Studio を使用した CAD データの準備および リアルタイム ビジュアライゼーションでの自動化 https://cdn2.unrealengine.com/Unreal+Engine%2Fresources%2FMcLaren+W hitepaper%2FADCaV-Whitepaper-JPN-V2f4fd0b09e8b279171149eef9220370e1b7495092.pdf #UE4 | @UNREALENGINE
#UE4 | @UNREALENGINE
ご清聴ありがとうございました #UE4 | @UNREALENGINE