Python / Blueprintによる
Unreal Engineの自動化
Epic Games Japan / Technical Artist
小林浩之
#UE4 | @UNREALENGINE
自己紹介
小林 浩之
Epic Games Japan / Technical Artist
スクウェア・エニックス大阪で背景TAを2年ほど
今年2月からEGJのエンタープライズ分野のサポートとして入社
#UE4 | @UNREALENGINE
目次
• 大規模開発における作業効率化・自動化の需要
• Unreal Engine 4の作業効率化・自動化ツール紹介
• 実装例
• Datasmithインポートの効率化 Blueprint
• 命名規則に応じたアセットリネーム Editor Utility Widget & Blueprint
• Pythonによるインスタンシング Python
• インスタンスを個別のStatic Meshに変換 Python
#UE4 | @UNREALENGINE
目次
• 大規模開発における作業効率化・自動化の需要
• Unreal Engine 4の作業効率化・自動化ツール紹介
• 実装例
• Datasmithインポートの効率化 Blueprint
• 命名規則に応じたアセットリネーム Editor Utility Widget & Blueprint
• Pythonによるインスタンシング Python
• インスタンスを個別のStatic Meshに変換 Python
#UE4 | @UNREALENGINE
大規模開発における作業効率化・自動化の需要
大量に配置されたオブジェクトの整理、膨大なアセットの管理など、
手作業でやっているとコストがかかるりすぎる・・・
時間が足りない・・・
クオリティアップにコストを割けない・・・
#UE4 | @UNREALENGINE
大規模開発における作業効率化・自動化の需要
オブジェクト整理作業
アセット管理作業
などなど
自動化
#UE4 | @UNREALENGINE
McLarenによる事例
Unreal Engineへの
CADデータインポートの自動化
#UE4 | @UNREALENGINE
McLarenによる事例
インポートフローの改善
#UE4 | @UNREALENGINE
目次
• 大規模開発における作業効率化・自動化の需要
• Unreal Engine 4の作業効率化・自動化ツール紹介
• 実装例
• Datasmithインポートの効率化 Blueprint
• 命名規則に応じたアセットリネーム Editor Utility Widget & Blueprint
• Pythonによるインスタンシング Python
• インスタンスを個別のStatic Meshに変換 Python
#UE4 | @UNREALENGINE
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インポートの効率化 Blueprint
• 命名規則に応じたアセットリネーム Editor Utility Widget & Blueprint
• Pythonによるインスタンシング Python
• インスタンスを個別のStatic Meshに変換 Python
#UE4 | @UNREALENGINE
実装例: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では、実際にデータをインポートする前に
メモリ上で一度シーン構築を行う
インポート
Datasmith Scene in Memory
メモリ上でシーンを構築
CADデータ
#UE4 | @UNREALENGINE
Datasmith Scene
Datasmithでは、実際にデータをインポートする前に
メモリ上で一度シーン構築を行う
インポート
Datasmith Scene in Memory
メモリ上でシーンを構築
CADデータ
#UE4 | @UNREALENGINE
解説
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)
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型を返す
#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)
#クラスタリング
num_clusters = 5
pred = KMeans(n_clusters=num_clusters).fit_predict(list_locations)
n_clusters = 5
#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
#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)
#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-V2-
f4fd0b09e8b279171149eef9220370e1b7495092.pdf
#UE4 | @UNREALENGINE
#UE4 | @UNREALENGINE
ご清聴ありがとうございました

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