SlideShare a Scribd company logo
1 of 133
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で外部ライブラリを使う
ライブラリインストール後、PythonLibsite-packagesを
EngineBinariesThirdPartyPythonWin64Lib以下に丸ごとコピー
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 = 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
実装例は後日Git Hubなどで公開予定
※あくまで一例として作ったものなので、より実用的にするには改良が必要
※公開した実装例の保守、サポートは致しません
#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
ご清聴ありがとうございました

More Related Content

What's hot

What's hot (20)

Unreal Engine 4.27 ノンゲーム向け新機能まとめ
Unreal Engine 4.27 ノンゲーム向け新機能まとめUnreal Engine 4.27 ノンゲーム向け新機能まとめ
Unreal Engine 4.27 ノンゲーム向け新機能まとめ
 
UE4のシーケンサーをもっともっと使いこなそう!最新情報・Tipsをご紹介!
UE4のシーケンサーをもっともっと使いこなそう!最新情報・Tipsをご紹介!UE4のシーケンサーをもっともっと使いこなそう!最新情報・Tipsをご紹介!
UE4のシーケンサーをもっともっと使いこなそう!最新情報・Tipsをご紹介!
 
メカアクションゲーム『DAEMON X MACHINA』 信念と血と鋼鉄の開発事例
メカアクションゲーム『DAEMON X MACHINA』 信念と血と鋼鉄の開発事例メカアクションゲーム『DAEMON X MACHINA』 信念と血と鋼鉄の開発事例
メカアクションゲーム『DAEMON X MACHINA』 信念と血と鋼鉄の開発事例
 
UE4のマテリアルを もっと楽しもう!~マテリアルでぐっと広がるリアルタイムCG表現の幅~
UE4のマテリアルを もっと楽しもう!~マテリアルでぐっと広がるリアルタイムCG表現の幅~ UE4のマテリアルを もっと楽しもう!~マテリアルでぐっと広がるリアルタイムCG表現の幅~
UE4のマテリアルを もっと楽しもう!~マテリアルでぐっと広がるリアルタイムCG表現の幅~
 
猫でも分かるUE4.22から入ったSubsystem
猫でも分かるUE4.22から入ったSubsystem 猫でも分かるUE4.22から入ったSubsystem
猫でも分かるUE4.22から入ったSubsystem
 
[4.20版] UE4におけるLoadingとGCのProfilingと最適化手法
[4.20版] UE4におけるLoadingとGCのProfilingと最適化手法[4.20版] UE4におけるLoadingとGCのProfilingと最適化手法
[4.20版] UE4におけるLoadingとGCのProfilingと最適化手法
 
UE4 Volumetric Fogで 空間を演出する!
UE4 Volumetric Fogで 空間を演出する!UE4 Volumetric Fogで 空間を演出する!
UE4 Volumetric Fogで 空間を演出する!
 
UE4における大規模背景制作事例 最適化ワークフロー編
UE4における大規模背景制作事例 最適化ワークフロー編UE4における大規模背景制作事例 最適化ワークフロー編
UE4における大規模背景制作事例 最適化ワークフロー編
 
UE4プログラマー勉強会 in 大阪 -エンジンの内部挙動について
UE4プログラマー勉強会 in 大阪 -エンジンの内部挙動についてUE4プログラマー勉強会 in 大阪 -エンジンの内部挙動について
UE4プログラマー勉強会 in 大阪 -エンジンの内部挙動について
 
[CEDEC2018] UE4で多数のキャラクターを生かすためのテクニック
[CEDEC2018] UE4で多数のキャラクターを生かすためのテクニック[CEDEC2018] UE4で多数のキャラクターを生かすためのテクニック
[CEDEC2018] UE4で多数のキャラクターを生かすためのテクニック
 
UE4のローカライズ機能紹介 (UE4 Localization Deep Dive)
UE4のローカライズ機能紹介 (UE4 Localization Deep Dive)UE4のローカライズ機能紹介 (UE4 Localization Deep Dive)
UE4のローカライズ機能紹介 (UE4 Localization Deep Dive)
 
[CEDEC2018] UE4アニメーションシステム総おさらい
[CEDEC2018] UE4アニメーションシステム総おさらい[CEDEC2018] UE4アニメーションシステム総おさらい
[CEDEC2018] UE4アニメーションシステム総おさらい
 
初心者向け UE4 映像制作での シーケンサー と Movie Render Queue の使い方
初心者向け UE4 映像制作での シーケンサー と Movie Render Queue の使い方初心者向け UE4 映像制作での シーケンサー と Movie Render Queue の使い方
初心者向け UE4 映像制作での シーケンサー と Movie Render Queue の使い方
 
第2回UE4勉強会 in 大阪 - マテリアル基礎・初級
第2回UE4勉強会 in 大阪 - マテリアル基礎・初級第2回UE4勉強会 in 大阪 - マテリアル基礎・初級
第2回UE4勉強会 in 大阪 - マテリアル基礎・初級
 
60fpsアクションを実現する秘訣を伝授 基礎編
60fpsアクションを実現する秘訣を伝授 基礎編60fpsアクションを実現する秘訣を伝授 基礎編
60fpsアクションを実現する秘訣を伝授 基礎編
 
Fortniteを支える技術
Fortniteを支える技術Fortniteを支える技術
Fortniteを支える技術
 
非同期ロード画面 Asynchronous Loading Screen
非同期ロード画面 Asynchronous Loading Screen非同期ロード画面 Asynchronous Loading Screen
非同期ロード画面 Asynchronous Loading Screen
 
CEDEC2016: Unreal Engine 4 のレンダリングフロー総おさらい
CEDEC2016: Unreal Engine 4 のレンダリングフロー総おさらいCEDEC2016: Unreal Engine 4 のレンダリングフロー総おさらい
CEDEC2016: Unreal Engine 4 のレンダリングフロー総おさらい
 
UE4でマルチプレイヤーゲームを作ろう
UE4でマルチプレイヤーゲームを作ろうUE4でマルチプレイヤーゲームを作ろう
UE4でマルチプレイヤーゲームを作ろう
 
UE4 アセットロード周り-アセット参照調査-
UE4 アセットロード周り-アセット参照調査-UE4 アセットロード周り-アセット参照調査-
UE4 アセットロード周り-アセット参照調査-
 

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

GTMF 2015: UE4で開発環境は次なるステージへ~才能のコラボーレーションでクリエイティビティはさらなる高みへ~ | エピック・ゲームズ・ジャパ...
GTMF 2015: UE4で開発環境は次なるステージへ~才能のコラボーレーションでクリエイティビティはさらなる高みへ~ |  エピック・ゲームズ・ジャパ...GTMF 2015: UE4で開発環境は次なるステージへ~才能のコラボーレーションでクリエイティビティはさらなる高みへ~ |  エピック・ゲームズ・ジャパ...
GTMF 2015: UE4で開発環境は次なるステージへ~才能のコラボーレーションでクリエイティビティはさらなる高みへ~ | エピック・ゲームズ・ジャパ...
Game Tools & Middleware Forum
 
Intalio japan special cloud workshop
Intalio japan special cloud workshopIntalio japan special cloud workshop
Intalio japan special cloud workshop
Daisuke Sugai
 

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

「速」を落とさないコードレビュー
「速」を落とさないコードレビュー「速」を落とさないコードレビュー
「速」を落とさないコードレビュー
 
【CEDEC2018】一歩先のUnityでのパフォーマンス/メモリ計測、デバッグ術
【CEDEC2018】一歩先のUnityでのパフォーマンス/メモリ計測、デバッグ術【CEDEC2018】一歩先のUnityでのパフォーマンス/メモリ計測、デバッグ術
【CEDEC2018】一歩先のUnityでのパフォーマンス/メモリ計測、デバッグ術
 
GTMF 2015: UE4で開発環境は次なるステージへ~才能のコラボーレーションでクリエイティビティはさらなる高みへ~ | エピック・ゲームズ・ジャパ...
GTMF 2015: UE4で開発環境は次なるステージへ~才能のコラボーレーションでクリエイティビティはさらなる高みへ~ |  エピック・ゲームズ・ジャパ...GTMF 2015: UE4で開発環境は次なるステージへ~才能のコラボーレーションでクリエイティビティはさらなる高みへ~ |  エピック・ゲームズ・ジャパ...
GTMF 2015: UE4で開発環境は次なるステージへ~才能のコラボーレーションでクリエイティビティはさらなる高みへ~ | エピック・ゲームズ・ジャパ...
 
UE4のモバイル向け機能や最新情報などを改めて紹介!2019
UE4のモバイル向け機能や最新情報などを改めて紹介!2019UE4のモバイル向け機能や最新情報などを改めて紹介!2019
UE4のモバイル向け機能や最新情報などを改めて紹介!2019
 
Devとopsをつなぐchat ops
Devとopsをつなぐchat opsDevとopsをつなぐchat ops
Devとopsをつなぐchat ops
 
第4回UE4勉強会 in 大阪 UE4でのチーム製作
第4回UE4勉強会 in 大阪   UE4でのチーム製作第4回UE4勉強会 in 大阪   UE4でのチーム製作
第4回UE4勉強会 in 大阪 UE4でのチーム製作
 
An Agile Way As an SET at LINE
An Agile Way As an SET at LINEAn Agile Way As an SET at LINE
An Agile Way As an SET at LINE
 
Intalio japan special cloud workshop
Intalio japan special cloud workshopIntalio japan special cloud workshop
Intalio japan special cloud workshop
 
PFN のオンプレML基盤の取り組み / オンプレML基盤 on Kubernetes 〜PFN、ヤフー〜
PFN のオンプレML基盤の取り組み / オンプレML基盤 on Kubernetes 〜PFN、ヤフー〜PFN のオンプレML基盤の取り組み / オンプレML基盤 on Kubernetes 〜PFN、ヤフー〜
PFN のオンプレML基盤の取り組み / オンプレML基盤 on Kubernetes 〜PFN、ヤフー〜
 
Google Developer Day 2010 Japan: Google エンジニアの日常 (山内 知昭)
Google Developer Day 2010 Japan: Google エンジニアの日常 (山内 知昭)Google Developer Day 2010 Japan: Google エンジニアの日常 (山内 知昭)
Google Developer Day 2010 Japan: Google エンジニアの日常 (山内 知昭)
 
エンタープライズ分野向けUE4最新機能のご紹介
エンタープライズ分野向けUE4最新機能のご紹介エンタープライズ分野向けUE4最新機能のご紹介
エンタープライズ分野向けUE4最新機能のご紹介
 
Azure Machine Learning Build 2020
Azure Machine Learning Build 2020Azure Machine Learning Build 2020
Azure Machine Learning Build 2020
 
Twitter4Jとテスト
Twitter4JとテストTwitter4Jとテスト
Twitter4Jとテスト
 
[CEDEC2017] 最新モバイルゲームの実例からみるUE4のモバイル向け機能・Tipsを全部まるっとご紹介!
[CEDEC2017] 最新モバイルゲームの実例からみるUE4のモバイル向け機能・Tipsを全部まるっとご紹介![CEDEC2017] 最新モバイルゲームの実例からみるUE4のモバイル向け機能・Tipsを全部まるっとご紹介!
[CEDEC2017] 最新モバイルゲームの実例からみるUE4のモバイル向け機能・Tipsを全部まるっとご紹介!
 
[Okta x Jamf合同新年会] Okta Workflowsによるノーコード業務改善 〜Jamf APIを使ってMac端末情報を自動収集してみよう〜
[Okta x Jamf合同新年会] Okta Workflowsによるノーコード業務改善 〜Jamf APIを使ってMac端末情報を自動収集してみよう〜[Okta x Jamf合同新年会] Okta Workflowsによるノーコード業務改善 〜Jamf APIを使ってMac端末情報を自動収集してみよう〜
[Okta x Jamf合同新年会] Okta Workflowsによるノーコード業務改善 〜Jamf APIを使ってMac端末情報を自動収集してみよう〜
 
今さら聞けない人のためのDevOps超入門 ODC2023編
今さら聞けない人のためのDevOps超入門 ODC2023編今さら聞けない人のためのDevOps超入門 ODC2023編
今さら聞けない人のためのDevOps超入門 ODC2023編
 
今さら聞けない人のためのDevOps超入門
今さら聞けない人のためのDevOps超入門今さら聞けない人のためのDevOps超入門
今さら聞けない人のためのDevOps超入門
 
Pivotal Trackerでアジャイルなプロジェクト管理
Pivotal Trackerでアジャイルなプロジェクト管理Pivotal Trackerでアジャイルなプロジェクト管理
Pivotal Trackerでアジャイルなプロジェクト管理
 
An Agile Way As an SET at LINE ~プロダクトオーナーシップ編~
An Agile Way As an SET at LINE ~プロダクトオーナーシップ編~An Agile Way As an SET at LINE ~プロダクトオーナーシップ編~
An Agile Way As an SET at LINE ~プロダクトオーナーシップ編~
 
GTMF 2017 ノンゲーム分野から学ぶUE4の活用テクニック Epic Games Japan
GTMF 2017 ノンゲーム分野から学ぶUE4の活用テクニック Epic Games JapanGTMF 2017 ノンゲーム分野から学ぶUE4の活用テクニック Epic Games Japan
GTMF 2017 ノンゲーム分野から学ぶUE4の活用テクニック Epic Games Japan
 

More from Game Tools & Middleware Forum

GTMF2016:Unreal Engine 4を利用した先進的なゲーム制作手法 The Unreal Way 2016 Epic Games Japan
GTMF2016:Unreal Engine 4を利用した先進的なゲーム制作手法 The Unreal Way 2016 Epic Games JapanGTMF2016:Unreal Engine 4を利用した先進的なゲーム制作手法 The Unreal Way 2016 Epic Games Japan
GTMF2016:Unreal Engine 4を利用した先進的なゲーム制作手法 The Unreal Way 2016 Epic Games Japan
Game Tools & Middleware Forum
 

More from Game Tools & Middleware Forum (20)

UMLの本当の価値を知っていますか?ツールで引き出すその真価! /GTMF2019
UMLの本当の価値を知っていますか?ツールで引き出すその真価! /GTMF2019UMLの本当の価値を知っていますか?ツールで引き出すその真価! /GTMF2019
UMLの本当の価値を知っていますか?ツールで引き出すその真価! /GTMF2019
 
GTMF2016:Unreal Engine 4を利用した先進的なゲーム制作手法 The Unreal Way 2016 Epic Games Japan
GTMF2016:Unreal Engine 4を利用した先進的なゲーム制作手法 The Unreal Way 2016 Epic Games JapanGTMF2016:Unreal Engine 4を利用した先進的なゲーム制作手法 The Unreal Way 2016 Epic Games Japan
GTMF2016:Unreal Engine 4を利用した先進的なゲーム制作手法 The Unreal Way 2016 Epic Games Japan
 
Webで3Dモデルはどう扱う?PlayCanvas:3Dモデルディープダイブ+新機能紹介!
Webで3Dモデルはどう扱う?PlayCanvas:3Dモデルディープダイブ+新機能紹介!Webで3Dモデルはどう扱う?PlayCanvas:3Dモデルディープダイブ+新機能紹介!
Webで3Dモデルはどう扱う?PlayCanvas:3Dモデルディープダイブ+新機能紹介!
 
「Brushup」が4,000社以上のお客様に活用される理由
「Brushup」が4,000社以上のお客様に活用される理由「Brushup」が4,000社以上のお客様に活用される理由
「Brushup」が4,000社以上のお客様に活用される理由
 
Unreal Engine 4の2019年上半期アップデート情報まとめ / GTMF 2019
Unreal Engine 4の2019年上半期アップデート情報まとめ / GTMF 2019Unreal Engine 4の2019年上半期アップデート情報まとめ / GTMF 2019
Unreal Engine 4の2019年上半期アップデート情報まとめ / GTMF 2019
 
3Dアセット作成とリテイク:制作ツールとしてのVR導入のすゝめ / GTMF2019
3Dアセット作成とリテイク:制作ツールとしてのVR導入のすゝめ / GTMF20193Dアセット作成とリテイク:制作ツールとしてのVR導入のすゝめ / GTMF2019
3Dアセット作成とリテイク:制作ツールとしてのVR導入のすゝめ / GTMF2019
 
Game Server Services ではじめる サーバー開発運用しないゲーム開発 /GTMF2019
Game Server Services ではじめる サーバー開発運用しないゲーム開発 /GTMF2019Game Server Services ではじめる サーバー開発運用しないゲーム開発 /GTMF2019
Game Server Services ではじめる サーバー開発運用しないゲーム開発 /GTMF2019
 
品質と開発スピードの両立と、OSSのリスク低減策のご紹介 / GTMF2019
品質と開発スピードの両立と、OSSのリスク低減策のご紹介 / GTMF2019品質と開発スピードの両立と、OSSのリスク低減策のご紹介 / GTMF2019
品質と開発スピードの両立と、OSSのリスク低減策のご紹介 / GTMF2019
 
イマドキの改善!データ分析SDK導入のポイントとAI活用最新事例 / GTMF2019
イマドキの改善!データ分析SDK導入のポイントとAI活用最新事例 / GTMF2019イマドキの改善!データ分析SDK導入のポイントとAI活用最新事例 / GTMF2019
イマドキの改善!データ分析SDK導入のポイントとAI活用最新事例 / GTMF2019
 
「禍つヴァールハイト」のハイスペックサウンド演出とは? / GTMF2019
「禍つヴァールハイト」のハイスペックサウンド演出とは? / GTMF2019「禍つヴァールハイト」のハイスペックサウンド演出とは? / GTMF2019
「禍つヴァールハイト」のハイスペックサウンド演出とは? / GTMF2019
 
アマゾンのゲーム関連ソリューションを活用してゲームの開発力・商品力の底上げを! / GTMF2019
アマゾンのゲーム関連ソリューションを活用してゲームの開発力・商品力の底上げを! / GTMF2019アマゾンのゲーム関連ソリューションを活用してゲームの開発力・商品力の底上げを! / GTMF2019
アマゾンのゲーム関連ソリューションを活用してゲームの開発力・商品力の底上げを! / GTMF2019
 
CRIWARE 最新情報 ~UE4 Editor × ADX2で加速するサウンドデザイン~ / GTMF2019
CRIWARE 最新情報 ~UE4 Editor × ADX2で加速するサウンドデザイン~ / GTMF2019CRIWARE 最新情報 ~UE4 Editor × ADX2で加速するサウンドデザイン~ / GTMF2019
CRIWARE 最新情報 ~UE4 Editor × ADX2で加速するサウンドデザイン~ / GTMF2019
 
SpriteStudio Ver.6.x 移行事例紹介(2) /GTMF2019
SpriteStudio Ver.6.x 移行事例紹介(2) /GTMF2019SpriteStudio Ver.6.x 移行事例紹介(2) /GTMF2019
SpriteStudio Ver.6.x 移行事例紹介(2) /GTMF2019
 
Linux も動く Microsoft Azure HoloLens にも対応した次世代マルチプレイミドルウェア〜モノビットエンジンクラウド〜にて採用した...
Linux も動く Microsoft Azure HoloLens にも対応した次世代マルチプレイミドルウェア〜モノビットエンジンクラウド〜にて採用した...Linux も動く Microsoft Azure HoloLens にも対応した次世代マルチプレイミドルウェア〜モノビットエンジンクラウド〜にて採用した...
Linux も動く Microsoft Azure HoloLens にも対応した次世代マルチプレイミドルウェア〜モノビットエンジンクラウド〜にて採用した...
 
『Brushup』で2D、3D、音楽の爆速フィードバック! - Brushup - GTMF 2018 OSAKA / TOKYO
『Brushup』で2D、3D、音楽の爆速フィードバック! - Brushup - GTMF 2018 OSAKA / TOKYO『Brushup』で2D、3D、音楽の爆速フィードバック! - Brushup - GTMF 2018 OSAKA / TOKYO
『Brushup』で2D、3D、音楽の爆速フィードバック! - Brushup - GTMF 2018 OSAKA / TOKYO
 
最新事例で学ぶ!ユーザをファンにするカスタマーサポート運用術 - 株式会社ラクス - GTMF 2018 OSAKA / TOKYO
最新事例で学ぶ!ユーザをファンにするカスタマーサポート運用術 - 株式会社ラクス - GTMF 2018 OSAKA / TOKYO最新事例で学ぶ!ユーザをファンにするカスタマーサポート運用術 - 株式会社ラクス - GTMF 2018 OSAKA / TOKYO
最新事例で学ぶ!ユーザをファンにするカスタマーサポート運用術 - 株式会社ラクス - GTMF 2018 OSAKA / TOKYO
 
IncrediBuildでビルド時間を最大90%短縮! - インクレディビルドジャパン株式会社 - GTMF 2018 OSAKA
IncrediBuildでビルド時間を最大90%短縮! - インクレディビルドジャパン株式会社 - GTMF 2018 OSAKAIncrediBuildでビルド時間を最大90%短縮! - インクレディビルドジャパン株式会社 - GTMF 2018 OSAKA
IncrediBuildでビルド時間を最大90%短縮! - インクレディビルドジャパン株式会社 - GTMF 2018 OSAKA
 
IncrediBuildでビルド時間を最大90%短縮! - インクレディビルドジャパン株式会社 - GTMF 2018 TOKYO
IncrediBuildでビルド時間を最大90%短縮! - インクレディビルドジャパン株式会社 - GTMF 2018 TOKYOIncrediBuildでビルド時間を最大90%短縮! - インクレディビルドジャパン株式会社 - GTMF 2018 TOKYO
IncrediBuildでビルド時間を最大90%短縮! - インクレディビルドジャパン株式会社 - GTMF 2018 TOKYO
 
ミドルウェア「AXIP」ロードマップ2018 - AXIP - GTMF 2018 OSAKA / TOKYO
ミドルウェア「AXIP」ロードマップ2018 - AXIP - GTMF 2018 OSAKA / TOKYOミドルウェア「AXIP」ロードマップ2018 - AXIP - GTMF 2018 OSAKA / TOKYO
ミドルウェア「AXIP」ロードマップ2018 - AXIP - GTMF 2018 OSAKA / TOKYO
 
これからのカスタマーサポートのカタチ ~AIチャットボットの仕組と実績のご紹介~ - 株式会社SHIFT PLUS - GTMF 2018 OSAKA /...
これからのカスタマーサポートのカタチ ~AIチャットボットの仕組と実績のご紹介~ - 株式会社SHIFT PLUS - GTMF 2018 OSAKA /...これからのカスタマーサポートのカタチ ~AIチャットボットの仕組と実績のご紹介~ - 株式会社SHIFT PLUS - GTMF 2018 OSAKA /...
これからのカスタマーサポートのカタチ ~AIチャットボットの仕組と実績のご紹介~ - 株式会社SHIFT PLUS - GTMF 2018 OSAKA /...
 

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

Editor's Notes

  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の資料はホワイトペーパーとして無料で公開されております。 興味のある方はご覧ください。