More Related Content Similar to UE4プログラマー勉強会 in 大阪 -エンジンの内部挙動について (20) UE4プログラマー勉強会 in 大阪 -エンジンの内部挙動について3. 自己紹介
●マーケットプレイスに出品しました
− ComMaterialTools
● https://www.unrealengine.com/marketplace/commaterialtools
● プロジェクト内のマテリアルノード検索
● ディレクトリ単位でのマテリアル情報リスト化
● 指定マテリアル内で使用されているテクスチャの場所検索
●「 UE4 勉強会 in 大阪」の管理者やってます
− https://ue4study-osaka.connpass.com/
● 奇数月に開催。次回 5 回目。
● 初心者、ベテラン問わず登壇して頂ける人募集しています
● (同じ人ばかりになってしまいそうなので是非お願いします)
●マーケットプレイスに出品しました
− ComMaterialTools
● https://www.unrealengine.com/marketplace/commaterialtools
● プロジェクト内のマテリアルノード検索
● ディレクトリ単位でのマテリアル情報リスト化
● 指定マテリアル内で使用されているテクスチャの場所検索
●「 UE4 勉強会 in 大阪」の管理者やってます
− https://ue4study-osaka.connpass.com/
● 奇数月に開催。次回 5 回目。
● 初心者、ベテラン問わず登壇して頂ける人募集しています
● (同じ人ばかりになってしまいそうなので是非お願いします)
3
5. はじめに
今回のお話し
● PC ベース
● ネットワークは無し
● 調べた際の関数名等も記述しています。追いかけた
い方は参考にしてください。
● UE4 のソースコードは分かりやすいですが難解で
す。間違ってたらごめんなさい。教えてください。
5
9. はじめに
AnswerHub 、 Forum 等で質問する手もあります。
ただ、返答が得られるかは親切な皆様 UE4 ユーザー
次第です。
また業務ベースだと表で質問出来無い事もあります。
有償サポートの UDN も有りますが、契約してない /
アカウントが無い場合は使用できません。
9
11. もくじ
● World と Scene と Level
● 処理順
● Level のロード
● Level の Stream
● Actor の生成 /BeginPlay 順番
● Actor の Tick 順番
● スレッド
● 小ネタ
version: UE4.18.2
11
13. World と Scene と Level
World (UWorld)
● Game 、 Editor 、 PIE とかの単位で World が存在
する
○ EWorldType
● そのまま世界全体を管理している。
● 内部にロードした Level を保持している
○ UWorld::LevelCollections
■ DumpLevelCollections で確認出来る
■ 大体 DynamicSourceLevels に入る
13
14. World と Scene と Level
Scene (FScene)
● UWorld の Renderer 機能
○ 機能的に UWorld と分離しているので、 UWorld を持たな
いエディタでプレビューすることが出来る。らしい
○ ビューやフレームに依存しない描画情報を管理している
■ 描画プリミティブやライトのリスト
● FScene 自体は FRendererModule::AllocateScene で生成、
管理される
14
15. World と Scene と Level
Level (ULevel)
● Editor で使う Level と同じイメージ
● ULevel::Actors で Level に入っている Actor のリ
ストを取得出来る (C++)
15
19. 処理順
● 各 Level に対して処理する(1/2)
○ Tick
■ TG_PrePhysics
■ TG_StartPhysics
■ TG_DuringPhysics
■ TG_EndPhysics
■ TG_PostPhysics
○ CurrentLatentActionManager::ProcessLatentActions
○ GetTimerManager::Tick
○ FTickableGameObject::TickObjects( 同じ World のみ )
19
20. 処理順
● 各 Level に対して処理する(2/2)
○ PlayerController::UpdateCameraManager
○ ProcessLevelStreamingVolumes
○ UpdateStreamingState
○ Tick
■ TG_PostUpdateWork
■ TG_LastDemotable
○ FTickTaskManagerInterface::EndFrame
20
21. 処理順
● UWorld::Tick
○ Level の処理
○ Flush
■ GPhysCommandHandler::Flush
■ FinishAsyncTrace
■ BroadcastTickFlush
■ BroadcastPostTickFlush
○ FXSystem::Tick
○ GEngine->ConditionalCollectGarbage
○ EndTickDrawEvent
21
22. 処理順
● 各 World に対して処理する
○ USkyLightComponent::UpdateSkyCaptureContents
○ UReflectionCaptureComponent::UpdateReflectionCa
ptureContents
22
26. 処理順
大まかな要約
1. TickGroup 処理 ( フィジックス後まで)
2. Actor 外の Latent 系処理
3. TimerEvent 系
4. FTickableGameObject 継承クラス更新
5. Streaming 更新
6. TickGroup 処理 ( アップデート後から)
7. FXSystem 更新
8. ガベージコレクション更新 (ConditionalCollectGarbage)
9. SkyLight/Reflection の Capture 更新
10.描画情報の設定 26
28. Level のロード
起点( OpenLevel で遷移した際)
● UEngine::LoadMap
○ LoadPackage
○ UWorld::InitWorld
○ UWorld::SetGameMode
○ WorldContext.World()->FlushLevelStreaming
○ UWorld::InitializeActorsForPlay
○ UNavigationSystem::InitializeForWorld
○ ULocalPlayer::SpawnPlayActor
○ WorldContext.World()->BeginPlay();
○ GameInstance::LoadComplete 28
29. Level のロード
要約( 1 / 3 )
1. PersistentLevel のロード
● LoadPackage
○ ULevel::Serialize / PostLoad
2. GameMode を生成する
○ UWorld::SetGameMode
■ UGameInstance::CreateGameModeForURL
3. SubLevel のロード
● WorldContext.World()->FlushLevelStreaming
○ ULevel::Serialize / PostLoad
29
30. Level のロード
要約( 2 / 3 )
4. ロードした Level にある Actor を生成する
a. UWorld::InitializeActorsForPlay
i. ULevel::IncrementalUpdateComponents
5. ナビゲーションの初期化
b. UNavigationSystem::InitializeForWorld
6. プレイヤー生成
ULocalPlayer::SpawnPlayActor
UWorld::SpawnPlayActor
a.GameMode::Login 呼ばれる
b.GameMode::PostLogin 呼ばれる 30
31. Level のロード
要約( 3 / 3 )
7. BeginPlay が呼ばれる
GameMode::StartPlay
a.GameMode
b.GameState
c.Actor 達
8. GameInstance::LoadComplete が呼ばれる
31
33. Level の Stream
● [LoadStreamLevel] で SubLevel を動的ロード
● [UnloadStreamLevel] で開放
● [GetStreamingLevel] - [Should be Visible] で動
的ロードした SubLevel の表示 ON / OFF
33
34. Level の Stream
[Should be Visible] で ON にした時の挙動
● UWorld::AddToWorld
○ ULevel::IncrementalUpdateComponents
■ AActor::IncrementalRegisterComponents
■ AActor::BeginPlay
● Level に所属している Actor が順次登録されていく
34
35. Level の Stream
● Actor が登録されていくのはフレーム分割される
○ GLevelStreamingActorsUpdateTimeLimit 単位
■ この時間を超えると、残りの Actor は次のフレームに持ち越し
■ デフォルト 5.0ms
● “s.LevelStreamingActorsUpdateTimeLimit” で変更可能
■ LoadStream した Level を暗転中に表示。とかする場合は時間制
限を長めにしないと暗転時間が長くなる可能性
● ブロッキングする事に注意
35
36. Level の Stream
[Should be Visible] で OFF にした時の挙動
● UWorld::RemoveFromWorld
○ ULevel::IncrementalUnregisterComponents
■ AActor::UnregisterAllComponents
■ AActor::EndPlay(EEndPlayReason::RemovedFromWorld)
● Level に所属している Actor が順次外されていく
36
37. Level の Stream
●Actor を外していくのはフレーム分割される
○ GLevelStreamingUnregisterComponentsTimeLimit
単位
■ この時間を超えると、残りの Actor は次のフレームに持ち越し
■ デフォルト 1.0ms
● “s.UnregisterComponentsTimeLimit” で変更可能
■ こちらも LoadStream した Level を暗転中に非表示。とかする
場合は時間制限を長めにしないと暗転時間が長くなる可能性
● ブロッキングする事に注意
37
38. Level の Stream
● Actor 自体の生成は LoadStreamLevel で読み込ま
れた段階
● [Should be Visible] の ON/OFF では Level に関
連付いている Actor 自体は Destroy されない
38
39. Level の Stream
ここで注意!
● Actor の BeginPlay 、 EndPlay は Shold be
Visible を ON/OFF するだけで再度呼び出される
○ 可能性が有る場合は BeginPlay は2回目突入されても大
丈夫な作りを
■ [DoOnce] で処理すれば初回だけ通る様に出来る
39
41. Actor の生成 /BeginPlay 順番
ULevel 内に保存されている Actor の順番で初期化さ
れる
→ULevel 内の Actor の順番は明確な基準無し
ただし、 ULevel::IsNetActor な Actor は末尾に来る
ようにソートされる
→ULevel::SortActorList で行われる
→IsNetActor は主に UCharacter クラス等が ON
41
42. Actor の生成 /BeginPlay 順番
つまり明示的に Actor の BeginPlay 順番を設定出来な
い
→BeginPlay では Actor 同士の生成順番に依存しないよう
に!
42
43. Actor の生成 /BeginPlay 順番
とはいえ欲しい場合は……
1. 全 Actor の BeginPlay が終わった後で処理する
a. UGameInstance::LoadComplete を override
b. AGameModeBase::StartPlay を override
2. GameMode/GameState 等で順番に Spawn する
3. ULevel::SortActorList でソートする(要エンジン改造)
43
47. Actor の Tick 順番
一列に並んだ Actor 達
左右方向の座標値は同じ値
47
Actor
Actor
Actor ( Player )
Character
49. Actor の Tick 順番
Player だけは、参加者全員の CustomTimeDilation
を 0 にすることが出来る
(= Tick 等の Time スケールをゼロに)
49
50. Actor の Tick 順番
とりあえず実行して停止機能を発動させると……
50
Character だけが後ろに居る!
52. Actor の Tick 順番
Tick で処理を行っている為、 Tick の順番次第で処理
の流れが代わってしまう事が原因
52
53. Actor の Tick 順番
停止させたフレーム内での処理
1. 赤の Actor::Tick 。移動する
2. 緑の Actor::Tick 。移動する
3. Player の Tick 。移動+他の Actor の
CustomTimeDilation をゼロに
4. Character の Tick 。
CustomTimeDilation がゼロなので
移動量がゼロに!
53
1
24
3
54. Actor の Tick 順番
停止させたフレーム内での処理
1. Player の Tick 。移動+他の Actor の
CustomTimeDilation をゼロに
2. 赤の Actor::Tick 。 CustomTimeDilation が
ゼロなので移動量がゼロに!
3. 緑の Actor::Tick 。 CustomTimeDilation が
ゼロなので移動量がゼロに!
4. Character の Tick 。 CustomTimeDilation が
ゼロなので移動量がゼロに!
54
2
3
4
1
55. Actor の Tick 順番
今回のは極端な例ですが、もしこの状態を
SceneCapture 等で撮影して止め絵使う場合。
カットシーンで一部の Actor の処理が1フレームだけ
ズレてしまう場合。
下手すると、こちらのシーンだと正常だけど、そちら
のシーンだとおかしくなる、といった事も発生しえま
す。
55
57. Actor の Tick 順番
FTickTaskSequencer が Tick を管理している
● AllEnabledTickFunctions に Tick が有効なリスト
を保持している
○ FTickFunction::SetTickFunctionEnable で登録
● Actor は生成時に登録する
57
58. Actor の Tick 順番
毎フレーム Tick の処理
1. Tick が有効なリストから、今回実行する
TickGroup をキューに積む
○ TickFunction->QueueTickFunction で行う
○ “tick.AllowAsyncTickDispatch 1” で ASync で積める
2. 積んだキューを実行する
○ GameThread で実行
3. 終了を待つ
○ FTaskGraphInterface::Get().WaitUntilTasksComplete
58
59. Actor の Tick 順番
TickGroup
● このグループ単位で処理されていく
● 大まかな順序付け出来る
● AActor からは PrimaryActorTick::TickGroup
● BP の Actor 設定に有る
59
60. Actor の Tick 順番
Tick の開始と終了
● Tick を始める Group 設定
○ FTickFunction::TickGroup
● Tick の終了を同期する Group 設定
○ FTickFunction::EndTickGroup
○ GameThread が SingleThread で動いていると意味ない
ハズ……?
60
61. Actor の Tick 順番
TickGroup
● TG_PrePhysics
● TG_StartPhysics
● TG_DuringPhysics
● TG_EndPhysics
● TG_PostPhysics
● TG_PostUpdateWork
● TG_LastDemotable
61
62. Actor の Tick 順番
Editor で設定出来るグループ
● TG_PrePhysics (フィジックス前)
● TG_StartPhysics
● TG_DuringPhysics (フィジックス中)
● TG_EndPhysics
● TG_PostPhysics (フィジックス後)
● TG_PostUpdateWork (アップデート後)
● TG_LastDemotable
62
63. Actor の Tick 順番
TG_PrePhysics
● 物理処理前に行う Tick
● ゲームループで真っ先に処理される
● AActor 等、物理行うものとか大体ここ
● USkeletalMeshComponent は物理ありの場合は終了同期は
PostPhysics になる。無しの場合は PrePhysics 。
63
64. Actor の Tick 順番
TG_DuringPhysics
● 物理処理と並行して行う Tick
● 物理処理が重い環境だと、この TickGroup 設定に重い Tick
処理を持って来るとパフォーマンスが改善するかも
● ActorComponent 等、物理を絡まない Component 類
64
65. TG_PostPhysics
● 物理処理が終わった後に処理される Tick
● 物理の結果を拾って何かする時
○ FSkeletalMeshComponentEndPhysicsTickFunction
○ FCharacterMovementComponentPostPhysicsTickFunct
ion
○ FPrimitiveComponentPostPhysicsTickFunction
Actor の Tick 順番
65
66. Actor の Tick 順番
TG_PostUpdateWork
● おおよそ全ての更新処理が終わった後に呼ばれる Tick
66
68. Actor の Tick 順番
TickGroup 内で処理される順番
1. HighPriority 設定優先
2. EndTickGroup 順(終了同期設定)
3. 管理クラスの Tick リストに積まれた順
a. = Level に保存された Actor 順
b. Spawn された Actor 順
※ 例外設定で順番が変わることもある
68
69. Actor の Tick 順番
HighPriority
● bHighPriority フラグが ON になっていると、同一
TickGroup 内でも優先的に処理される
● ただし、 C ++からのみ設定可能
○ FTickFunction::SetPriorityIncludingPrerequisites
69
70. Actor の Tick 順番
EndTickGroup 順(終了同期設定)
● 配列で EndTickGroup 要素順に並んでいるので、
その順番にキューに突っ込まれる
● ここを期待してソートするのはキツい
70
71. Actor の Tick 順番
管理クラスの Tick リストに積まれた順
● Level に保存された Actor 順
○ 改造で順番をソートして保存しない限り意図した実行順に
するのはほぼ無理
● Spawn した順
○ こちらはある程度意図した通りに制御出来そう
■ ただ、内部的に TickGroup が違う等、設定が違うとかがあると狂
う
71
72. Actor の Tick 順番
順番の例外設定
● Attach 等親子関係を持つ Actor 同士は順番を考慮
される。子供が後に処理される
● UE4 で Actor 間の Tick 実行順序に依存関係を持た
せる方法 - ほげたつブログ
○ http://hogetatu.hatenablog.com/entry/2016/06/28/233002
■ AActor::AddTickPrerequisiteActor を使う
72
74. Actor の Tick 順番
分かりやすく Tick の優先付けするとしたら……
1. TickGroup
2. HighPriority
3. 個別の関連付け
くらい
74
75. Actor の Tick 順番
細かい順番には依存させず、依存が必要なら
TickGroup を分ける位の括りが良い
もしくは Tick で処理せずに、管理クラスで一括で処
理する等
75
77. スレッド
● FRunnable を継承しているのがスレッド
● TGraphTask<TaskClass>::CreateTask(NULL).C
onstructAndDispatchWhenReady(Task, *this);
○ のような形で動作しているのはタスクスレッドに積まれ
て実行される。(これも別スレッド動作)
77
80. スレッド
FRenderingThread
● 描画コマンド発行スレッド
● GameThread で変更した RenderState や
RenderTransform を受け取って GPU に流し込むコ
マンドを生成する
○ ENQUEUE_RENDER_COMMAND 系を使用することで
RenderThread に Task を投げる
○ UWorld::SendAllEndOfFrameUpdates で GameThread
から RenderThread へ更新した描画情報を投げている
■ UActorComponent::DoDeferredRenderUpdates_Concurrent
で更新
80
88. InputEvent のタイミング
APlayerController の Tick で呼び出される
● APlayerController::TickActor
○ APlayerController::PlayerTick
■ APlayerController::TickPlayerInput
● APlayerController::ProcessPlayerInput
○ UPlayerInput::ProcessInputStack
88
93. 遅延実行のタイミング
Delay 系のノード
● Actor の Tick 内で処理される
○ AActor::Tick
■ GetLatentActionManager().ProcessLatentActions
● ただし、 TickInterval 等で Actor の Tick がスキッ
プされた場合は UWorld::Tick で処理される
○ CurrentLatentActionManager::ProcessLatentActions
■ ※ 実行順番が変わるので注意!
93
96. ノード右上のアイコンなに?
時計マーク
● [Delay] / [LoadAsset] 等に使用
● Latent ノード
○ UFUNCTION に meta で Latent 指定
■ FBlueprintMetadata::MD_Latent
● ノード内部の処理が終わるまで待機する。次に行か
ない
96
97. ノード右上のアイコンなに?
PC+ 雷マーク
● [AnyDamage] 等に使用
● AuthorityOnly
○ UFUNCTION に BlueprintAuthorityOnly 指定
● “ ネットワーク権限を持つマシン上で ( サーバー、 Dedicated
サーバー、シングル プレイヤー ゲーム ) 実行されている場
合、この関数はブループリントのコードからのみ実行されま
す”
○ 関数 - Document
■ https://docs.unrealengine.com/latest/JPN/Programming/UnrealArchit
ecture/Reference/Functions/index.html 97
98. ノード右上のアイコンなに?
モニター+雷マーク
● [Create Widget] 等に使用
● Cosmetic (ClientEvent)
○ UFUNCTION に BlueprintCosmetic 指定
● “ この関数は表面上のもので、 Dedicated サーバー上では
実行されません。”
○ 関数 - Document
■ https://docs.unrealengine.com/latest/JPN/Programming/UnrealArch
itecture/Reference/Functions/index.html
98
100. ノード右上のアイコンなに?
雷マーク
● AnimationBP 内の AnimGraph で使用
● FastPath
● 繋がれる変数ノードが特定条件の時、
構造化して最適化してくれる
○ アニメーションのファストパスによる最適化 -
Document
■ https://docs.unrealengine.com/latest/JPN/Engine/Animation/Optim
ization/FastPath/index.html
○ スミオ先生の検証ツイートツリー - Twitter
■ https://twitter.com/tempkinder/status/940246489120391169 100
104. UObject な C++ クラスのコンストラクタ一杯くる?
● 3 回位来る
○ アプリケーション起動時の CDO - UClass
○ BP アセットのロード時の CDO - UClass
○ 実体生成時 - UObject
104
105. UObject な C++ クラスのコンストラクタ一杯くる?
CDO
●Class Default Object
○ UClass の実体。初期値を保持。
○ ObjectFlags に RF_ClassDefaultObject |
RF_ArchetypeObject が設定されている
● オブジェクト - Document
■ https://docs.unrealengine.com/latest/JPN/Programming/UnrealArch
itecture/Objects/index.html
105
106. UObject な C++ クラスのコンストラクタ一杯くる?
アプリケーション起動時の CDO - UClass
● FEngineLoop::PreInit から呼ばれる
○ アプリケーション起動直後。ゲームに関する初期化が走る
前。
○ 起動直後に呼び出されるため、未初期化な部分をアクセス
しにいったりするとクラッシュする事も
■ 必要な場合は CDO 生成時だけ処理をスキップする等が必要(後
述)
106
107. UObject な C++ クラスのコンストラクタ一杯くる?
BP アセットのロード時の CDO - UClass
● UClass::Serialize から呼び出される
○ 参照されている Level を読み込んだ時や、 LoadObject
とかで BP をロードした際に CDO を生成する
107
108. UObject な C++ クラスのコンストラクタ一杯くる?
実体生成時 - UObject
● 通常イメージする通りのオブジェクト生成
○ こちらもコンストラクタは通る
108
109. UObject な C++ クラスのコンストラクタ一杯くる?
CDO のコンストラクタか、オブジェクトのコンストラ
クタかの判別
● if (HasAnyFlags(RF_ClassDefaultObject))
○ で CDO
109
111. Stat Unit はどこを計測してる?
● 使用されている Stat は下記
○ STAT_UnitFrame
○ STAT_UnitRender
○ STAT_UnitGame
○ STAT_UnitGPU
● 表示、計算している場所
○ FStatUnitData::DrawStat
111
113. Stat Unit はどこを計測してる?
● STAT_UnitRender
○ GRenderThreadTime を使用
■ FSlateRHIRenderer::DrawWindow_RenderThread で計算し
ている
● ここを通った差分時間 - RenderThread の Idle 時間
● RenderThread の Idle 時間は STAT_RenderingIdleTime
で確認できる
○ RenderThread が動作している時間を計測
113
114. Stat Unit はどこを計測してる?
● STAT_UnitGame
○ GGameThreadTime を使用
■ FViewport::Draw で計算している
● ここを通った差分時間 - GameThread の Idle 時間
● GameThread の Idle 時間は STAT_GameIdleTime で確認
できる
○ Idle 時間の計算は FThreadIdleStats::FScopeIdle
■ UEngine::UpdateTimeAndHandleMaxTickRate
○ GameThread が動作している時間を計測
114
115. Stat Unit はどこを計測してる?
● STAT_UnitGPU
○ RHIGetGPUFrameCycles を使用
■ DirectX11 だと GGPUFrameTime
● // Stat unit GPU time is not accurate anyway with SLI
○ FD3DGPUProfiler::BeginFrame ~ EndFrame
○ プラットフォーム依存。
115