UE4 プログラマー向け勉強会 in 大阪
1
エンジンの内部挙動について
自己紹介
● Twitter: com04
● ゲームプログラマー
● マテリアル / レンダリング / エンジン拡張
● 横浜 × 酒 ×UE4
2
自己紹介
●マーケットプレイスに出品しました
− 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
はじめに
4
はじめに
今回のお話し
● PC ベース
● ネットワークは無し
● 調べた際の関数名等も記述しています。追いかけた
い方は参考にしてください。
● UE4 のソースコードは分かりやすいですが難解で
す。間違ってたらごめんなさい。教えてください。
5
はじめに
今回はこの辺り話しません
● ガベージコレクト
● レンダリング
● 処理負荷
6
はじめに
エンジンの内部まで知ってる必要あるの?
7
はじめに
基本的には知らなくて良い(ハズ)
ただ、クリティカルな問題に当たった時は知りたくなる
時があります
● 何か 1 フレームずれる
● 思った順番で処理されない
● こっちのシーンだと行けたのに、あっちだとおかしい
● エンジン内部のクラッシュ /Assert で詰まる
8
はじめに
AnswerHub 、 Forum 等で質問する手もあります。
ただ、返答が得られるかは親切な皆様 UE4 ユーザー
次第です。
また業務ベースだと表で質問出来無い事もあります。
有償サポートの UDN も有りますが、契約してない /
アカウントが無い場合は使用できません。
9
はじめに
→ エンジンのソースコード配布されてるやん!
(プログラマー脳筋)
10
もくじ
● World と Scene と Level
● 処理順
● Level のロード
● Level の Stream
● Actor の生成 /BeginPlay 順番
● Actor の Tick 順番
● スレッド
● 小ネタ
version: UE4.18.2
11
World と Scene と Level
12
World と Scene と Level
World (UWorld)
● Game 、 Editor 、 PIE とかの単位で World が存在
する
○ EWorldType
● そのまま世界全体を管理している。
● 内部にロードした Level を保持している
○ UWorld::LevelCollections
■ DumpLevelCollections で確認出来る
■ 大体 DynamicSourceLevels に入る
13
World と Scene と Level
Scene (FScene)
● UWorld の Renderer 機能
○ 機能的に UWorld と分離しているので、 UWorld を持たな
いエディタでプレビューすることが出来る。らしい
○ ビューやフレームに依存しない描画情報を管理している
■ 描画プリミティブやライトのリスト
● FScene 自体は FRendererModule::AllocateScene で生成、
管理される
14
World と Scene と Level
Level (ULevel)
● Editor で使う Level と同じイメージ
● ULevel::Actors で Level に入っている Actor のリ
ストを取得出来る (C++)
15
処理順
16
処理順
起点
● FEngineLoop::Tick
○ UGameEngine::Tick
■ WorldList の数だけ UWorld::Tick を回す
17
処理順
● UWorld::Tick
○ NavigationSystem->Tick
○ LevelCollections の数だけ次のページの処理回す
18
処理順
● 各 Level に対して処理する(1/2)
○ Tick
■ TG_PrePhysics
■ TG_StartPhysics
■ TG_DuringPhysics
■ TG_EndPhysics
■ TG_PostPhysics
○ CurrentLatentActionManager::ProcessLatentActions
○ GetTimerManager::Tick
○ FTickableGameObject::TickObjects( 同じ World のみ )
19
処理順
● 各 Level に対して処理する(2/2)
○ PlayerController::UpdateCameraManager
○ ProcessLevelStreamingVolumes
○ UpdateStreamingState
○ Tick
■ TG_PostUpdateWork
■ TG_LastDemotable
○ FTickTaskManagerInterface::EndFrame
20
処理順
● UWorld::Tick
○ Level の処理
○ Flush
■ GPhysCommandHandler::Flush
■ FinishAsyncTrace
■ BroadcastTickFlush
■ BroadcastPostTickFlush
○ FXSystem::Tick
○ GEngine->ConditionalCollectGarbage
○ EndTickDrawEvent
21
処理順
● 各 World に対して処理する
○ USkyLightComponent::UpdateSkyCaptureContents
○ UReflectionCaptureComponent::UpdateReflectionCa
ptureContents
22
処理順
● UGameEngine::Tick
○ FTickableGameObject::TickObjects (全体)
○ GameViewport->Tick
○ RedrawViewports
○ GetRendererModule().PostRenderAllViewports
○ IStreamingManager::Tick
○ GameAudioDeviceManager-
>UpdateActiveAudioDevices
23
処理順
● FEngineLoop::Tick
○ GShaderCompilingManager::ProcessAsyncResults
○ GDistanceFieldAsyncQueue::ProcessAsyncTasks
○ FSlateApplication::Tick
○ RHITick
○ FTicker::GetCoreTicker().Tick
○ FThreadManager::Get().Tick
○ GEngine->TickDeferredCommands
24
処理順
長い
25
処理順
大まかな要約
1. TickGroup 処理 ( フィジックス後まで)
2. Actor 外の Latent 系処理
3. TimerEvent 系
4. FTickableGameObject 継承クラス更新
5. Streaming 更新
6. TickGroup 処理 ( アップデート後から)
7. FXSystem 更新
8. ガベージコレクション更新 (ConditionalCollectGarbage)
9. SkyLight/Reflection の Capture 更新
10.描画情報の設定 26
Level のロード
27
Level のロード
起点( OpenLevel で遷移した際)
● UEngine::LoadMap
○ LoadPackage
○ UWorld::InitWorld
○ UWorld::SetGameMode
○ WorldContext.World()->FlushLevelStreaming
○ UWorld::InitializeActorsForPlay
○ UNavigationSystem::InitializeForWorld
○ ULocalPlayer::SpawnPlayActor
○ WorldContext.World()->BeginPlay();
○ GameInstance::LoadComplete 28
Level のロード
要約( 1 / 3 )
1. PersistentLevel のロード
● LoadPackage
○ ULevel::Serialize / PostLoad
2. GameMode を生成する
○ UWorld::SetGameMode
■ UGameInstance::CreateGameModeForURL
3. SubLevel のロード
● WorldContext.World()->FlushLevelStreaming
○ ULevel::Serialize / PostLoad
29
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
Level のロード
要約( 3 / 3 )
7. BeginPlay が呼ばれる
GameMode::StartPlay
a.GameMode
b.GameState
c.Actor 達
8. GameInstance::LoadComplete が呼ばれる
31
Level の Stream
32
Level の Stream
● [LoadStreamLevel] で SubLevel を動的ロード
● [UnloadStreamLevel] で開放
● [GetStreamingLevel] - [Should be Visible] で動
的ロードした SubLevel の表示 ON / OFF
33
Level の Stream
[Should be Visible] で ON にした時の挙動
● UWorld::AddToWorld
○ ULevel::IncrementalUpdateComponents
■ AActor::IncrementalRegisterComponents
■ AActor::BeginPlay
● Level に所属している Actor が順次登録されていく
34
Level の Stream
● Actor が登録されていくのはフレーム分割される
○ GLevelStreamingActorsUpdateTimeLimit 単位
■ この時間を超えると、残りの Actor は次のフレームに持ち越し
■ デフォルト 5.0ms
● “s.LevelStreamingActorsUpdateTimeLimit” で変更可能
■ LoadStream した Level を暗転中に表示。とかする場合は時間制
限を長めにしないと暗転時間が長くなる可能性
● ブロッキングする事に注意
35
Level の Stream
[Should be Visible] で OFF にした時の挙動
● UWorld::RemoveFromWorld
○ ULevel::IncrementalUnregisterComponents
■ AActor::UnregisterAllComponents
■ AActor::EndPlay(EEndPlayReason::RemovedFromWorld)
● Level に所属している Actor が順次外されていく
36
Level の Stream
●Actor を外していくのはフレーム分割される
○ GLevelStreamingUnregisterComponentsTimeLimit
単位
■ この時間を超えると、残りの Actor は次のフレームに持ち越し
■ デフォルト 1.0ms
● “s.UnregisterComponentsTimeLimit” で変更可能
■ こちらも LoadStream した Level を暗転中に非表示。とかする
場合は時間制限を長めにしないと暗転時間が長くなる可能性
● ブロッキングする事に注意
37
Level の Stream
● Actor 自体の生成は LoadStreamLevel で読み込ま
れた段階
● [Should be Visible] の ON/OFF では Level に関
連付いている Actor 自体は Destroy されない
38
Level の Stream
ここで注意!
● Actor の BeginPlay 、 EndPlay は Shold be
Visible を ON/OFF するだけで再度呼び出される
○ 可能性が有る場合は BeginPlay は2回目突入されても大
丈夫な作りを
■ [DoOnce] で処理すれば初回だけ通る様に出来る
39
Actor の生成 /BeginPlay 順番
40
Actor の生成 /BeginPlay 順番
ULevel 内に保存されている Actor の順番で初期化さ
れる
→ULevel 内の Actor の順番は明確な基準無し
ただし、 ULevel::IsNetActor な Actor は末尾に来る
ようにソートされる
→ULevel::SortActorList で行われる
→IsNetActor は主に UCharacter クラス等が ON
41
Actor の生成 /BeginPlay 順番
つまり明示的に Actor の BeginPlay 順番を設定出来な
い
→BeginPlay では Actor 同士の生成順番に依存しないよう
に!
42
Actor の生成 /BeginPlay 順番
とはいえ欲しい場合は……
1. 全 Actor の BeginPlay が終わった後で処理する
a. UGameInstance::LoadComplete を override
b. AGameModeBase::StartPlay を override
2. GameMode/GameState 等で順番に Spawn する
3. ULevel::SortActorList でソートする(要エンジン改造)
43
Actor の Tick 順番
44
Actor の Tick 順番
そもそも Tick 順って考える必要ある?
45
Actor の Tick 順番
テストケース
プレイヤーが参加者全員の時間を止めることが出来る
46
Actor の Tick 順番
一列に並んだ Actor 達
左右方向の座標値は同じ値
47
Actor
Actor
Actor ( Player )
Character
Actor の Tick 順番
BP は Tick で移動するようなロジック
48
Actor の Tick 順番
Player だけは、参加者全員の CustomTimeDilation
を 0 にすることが出来る
(= Tick 等の Time スケールをゼロに)
49
Actor の Tick 順番
とりあえず実行して停止機能を発動させると……
50
Character だけが後ろに居る!
Actor の Tick 順番
状況次第では……
51
Player 以外が後ろに居る!
Actor の Tick 順番
Tick で処理を行っている為、 Tick の順番次第で処理
の流れが代わってしまう事が原因
52
Actor の Tick 順番
停止させたフレーム内での処理
1. 赤の Actor::Tick 。移動する
2. 緑の Actor::Tick 。移動する
3. Player の Tick 。移動+他の Actor の
CustomTimeDilation をゼロに
4. Character の Tick 。
CustomTimeDilation がゼロなので
移動量がゼロに!
53
1
24
3
Actor の Tick 順番
停止させたフレーム内での処理
1. Player の Tick 。移動+他の Actor の
CustomTimeDilation をゼロに
2. 赤の Actor::Tick 。 CustomTimeDilation が
ゼロなので移動量がゼロに!
3. 緑の Actor::Tick 。 CustomTimeDilation が
ゼロなので移動量がゼロに!
4. Character の Tick 。 CustomTimeDilation が
ゼロなので移動量がゼロに!
54
2
3
4
1
Actor の Tick 順番
今回のは極端な例ですが、もしこの状態を
SceneCapture 等で撮影して止め絵使う場合。
カットシーンで一部の Actor の処理が1フレームだけ
ズレてしまう場合。
下手すると、こちらのシーンだと正常だけど、そちら
のシーンだとおかしくなる、といった事も発生しえま
す。
55
Actor の Tick 順番
→ 順番ってどうなってるの?
56
Actor の Tick 順番
FTickTaskSequencer が Tick を管理している
● AllEnabledTickFunctions に Tick が有効なリスト
を保持している
○ FTickFunction::SetTickFunctionEnable で登録
● Actor は生成時に登録する
57
Actor の Tick 順番
毎フレーム Tick の処理
1. Tick が有効なリストから、今回実行する
TickGroup をキューに積む
○ TickFunction->QueueTickFunction で行う
○ “tick.AllowAsyncTickDispatch 1” で ASync で積める
2. 積んだキューを実行する
○ GameThread で実行
3. 終了を待つ
○ FTaskGraphInterface::Get().WaitUntilTasksComplete
58
Actor の Tick 順番
TickGroup
● このグループ単位で処理されていく
● 大まかな順序付け出来る
● AActor からは PrimaryActorTick::TickGroup
● BP の Actor 設定に有る
59
Actor の Tick 順番
Tick の開始と終了
● Tick を始める Group 設定
○ FTickFunction::TickGroup
● Tick の終了を同期する Group 設定
○ FTickFunction::EndTickGroup
○ GameThread が SingleThread で動いていると意味ない
ハズ……?
60
Actor の Tick 順番
TickGroup
● TG_PrePhysics
● TG_StartPhysics
● TG_DuringPhysics
● TG_EndPhysics
● TG_PostPhysics
● TG_PostUpdateWork
● TG_LastDemotable
61
Actor の Tick 順番
Editor で設定出来るグループ
● TG_PrePhysics (フィジックス前)
● TG_StartPhysics
● TG_DuringPhysics (フィジックス中)
● TG_EndPhysics
● TG_PostPhysics (フィジックス後)
● TG_PostUpdateWork (アップデート後)
● TG_LastDemotable
62
Actor の Tick 順番
TG_PrePhysics
● 物理処理前に行う Tick
● ゲームループで真っ先に処理される
● AActor 等、物理行うものとか大体ここ
● USkeletalMeshComponent は物理ありの場合は終了同期は
PostPhysics になる。無しの場合は PrePhysics 。
63
Actor の Tick 順番
TG_DuringPhysics
● 物理処理と並行して行う Tick
● 物理処理が重い環境だと、この TickGroup 設定に重い Tick
処理を持って来るとパフォーマンスが改善するかも
● ActorComponent 等、物理を絡まない Component 類
64
TG_PostPhysics
● 物理処理が終わった後に処理される Tick
● 物理の結果を拾って何かする時
○ FSkeletalMeshComponentEndPhysicsTickFunction
○ FCharacterMovementComponentPostPhysicsTickFunct
ion
○ FPrimitiveComponentPostPhysicsTickFunction
Actor の Tick 順番
65
Actor の Tick 順番
TG_PostUpdateWork
● おおよそ全ての更新処理が終わった後に呼ばれる Tick
66
Actor の Tick 順番
同一 TickGroup 内での順番は?
67
Actor の Tick 順番
TickGroup 内で処理される順番
1. HighPriority 設定優先
2. EndTickGroup 順(終了同期設定)
3. 管理クラスの Tick リストに積まれた順
a. = Level に保存された Actor 順
b. Spawn された Actor 順
※ 例外設定で順番が変わることもある
68
Actor の Tick 順番
HighPriority
● bHighPriority フラグが ON になっていると、同一
TickGroup 内でも優先的に処理される
● ただし、 C ++からのみ設定可能
○ FTickFunction::SetPriorityIncludingPrerequisites
69
Actor の Tick 順番
EndTickGroup 順(終了同期設定)
● 配列で EndTickGroup 要素順に並んでいるので、
その順番にキューに突っ込まれる
● ここを期待してソートするのはキツい
70
Actor の Tick 順番
管理クラスの Tick リストに積まれた順
● Level に保存された Actor 順
○ 改造で順番をソートして保存しない限り意図した実行順に
するのはほぼ無理
● Spawn した順
○ こちらはある程度意図した通りに制御出来そう
■ ただ、内部的に TickGroup が違う等、設定が違うとかがあると狂
う
71
Actor の Tick 順番
順番の例外設定
● Attach 等親子関係を持つ Actor 同士は順番を考慮
される。子供が後に処理される
● UE4 で Actor 間の Tick 実行順序に依存関係を持た
せる方法 - ほげたつブログ
○ http://hogetatu.hatenablog.com/entry/2016/06/28/233002
■ AActor::AddTickPrerequisiteActor を使う
72
Actor の Tick 順番
ややこしい
73
Actor の Tick 順番
分かりやすく Tick の優先付けするとしたら……
1. TickGroup
2. HighPriority
3. 個別の関連付け
くらい
74
Actor の Tick 順番
細かい順番には依存させず、依存が必要なら
TickGroup を分ける位の括りが良い
もしくは Tick で処理せずに、管理クラスで一括で処
理する等
75
スレッド
76
スレッド
● FRunnable を継承しているのがスレッド
● TGraphTask<TaskClass>::CreateTask(NULL).C
onstructAndDispatchWhenReady(Task, *this);
○ のような形で動作しているのはタスクスレッドに積まれ
て実行される。(これも別スレッド動作)
77
スレッド
主なスレッド処理
● GameThread
● FRenderingThread
● FAudioThread
● FPhysXTask
● FParallelAnimationEvaluationTask
78
スレッド
GameThread
● Tick とか動かしているスレッド
● 基本的にメインスレッド扱いになる筈
79
スレッド
FRenderingThread
● 描画コマンド発行スレッド
● GameThread で変更した RenderState や
RenderTransform を受け取って GPU に流し込むコ
マンドを生成する
○ ENQUEUE_RENDER_COMMAND 系を使用することで
RenderThread に Task を投げる
○ UWorld::SendAllEndOfFrameUpdates で GameThread
から RenderThread へ更新した描画情報を投げている
■ UActorComponent::DoDeferredRenderUpdates_Concurrent
で更新
80
スレッド
FAudioThread
● オーディオ周りの処理を行うスレッド
○ このクラス自体での処理はない
○ 外部の USoundWave や
FAudioDevice 、 UAudioComponent からコマンドを投
げられて処理する為のスレッド
■ FAudioThread::RunCommandOnAudioThread(<lamda 式 >)
でコマンドを投げる
81
スレッド
FPhysXTask
● 物理計算のタスク。スレッドで並列処理される
○ FPhysScene でアクセスされる
○ FStartPhysicsTickFunction で開始
■ TickGroup: StartPhysics
○ FEndPhysicsTickFunction で終了
■ TickGroup: EndPhysics
82
スレッド
FParallelAnimationEvaluationTask
● スレッドで並列処理される
● USkeletalMeshComponent::RefreshBoneTransfor
ms
○ “a.ParallelAnimEvaluation 0” で使わない様にも出来
る
■ USkeletalMeshComponent::PerformAnimationEvaluation が並
列化される
■ UAnimInstance::ParallelUpdateAnimation が並列化。ポーズの
計算が並列化されている
83
スレッド
ほか今回解析出来なかったスレッド類(の一部)
● TAsyncRunnable
● FAssetDataDiscovery
● FAssetDataGatherer
● FAsyncWriter
● FUnrealAudioModule
● FUnrealAudioWasapi
● FUnrealAudioXAudio2
● FSoundFileDecoder
● IAudioMixerPlatformInterface
● FMultichannelTcpReceiver
● FShaderCompileThreadRunnableBase
84
小ネタ
85
小ネタ
InputEvent のタイミング
86
InputEvent のタイミング
Actor で使用できる入力イベントノード
87
InputEvent のタイミング
APlayerController の Tick で呼び出される
● APlayerController::TickActor
○ APlayerController::PlayerTick
■ APlayerController::TickPlayerInput
● APlayerController::ProcessPlayerInput
○ UPlayerInput::ProcessInputStack
88
CollisionEvent のタイミング
89
CollisionEvent のタイミング
Actor で使用できるコリジョンイベント
90
CollisionEvent のタイミング
● UPrimitiveComponent::UpdateOverlaps 等で呼
びされる
○ 場所的には、 [SetActorLocation] 等の移動処理のタイ
ミングで Overlap を計算してイベントを呼び出す
91
小ネタ
遅延実行のタイミング
92
遅延実行のタイミング
Delay 系のノード
● Actor の Tick 内で処理される
○ AActor::Tick
■ GetLatentActionManager().ProcessLatentActions
● ただし、 TickInterval 等で Actor の Tick がスキッ
プされた場合は UWorld::Tick で処理される
○ CurrentLatentActionManager::ProcessLatentActions
■ ※ 実行順番が変わるので注意!
93
遅延実行のタイミング
SetTimer 系のノード
● Actor の Tick ではなく、 UWorld::Tick で呼ばれ
る FTimerManager::Tick から処理される
● TickGroup 的には TG_PostPhysics の
後、 TG_PostUpdateWork の前
94
小ネタ
ノード右上のアイコンなに?
95
ノード右上のアイコンなに?
時計マーク
● [Delay] / [LoadAsset] 等に使用
● Latent ノード
○ UFUNCTION に meta で Latent 指定
■ FBlueprintMetadata::MD_Latent
● ノード内部の処理が終わるまで待機する。次に行か
ない
96
ノード右上のアイコンなに?
PC+ 雷マーク
● [AnyDamage] 等に使用
● AuthorityOnly
○ UFUNCTION に BlueprintAuthorityOnly 指定
● “ ネットワーク権限を持つマシン上で ( サーバー、 Dedicated
サーバー、シングル プレイヤー ゲーム ) 実行されている場
合、この関数はブループリントのコードからのみ実行されま
す”
○ 関数 - Document
■ https://docs.unrealengine.com/latest/JPN/Programming/UnrealArchit
ecture/Reference/Functions/index.html 97
ノード右上のアイコンなに?
モニター+雷マーク
● [Create Widget] 等に使用
● Cosmetic (ClientEvent)
○ UFUNCTION に BlueprintCosmetic 指定
● “ この関数は表面上のもので、 Dedicated サーバー上では
実行されません。”
○ 関数 - Document
■ https://docs.unrealengine.com/latest/JPN/Programming/UnrealArch
itecture/Reference/Functions/index.html
98
ノード右上のアイコンなに?
丸二つマーク
● Replicated
○ UPROPERTY に Replicated 指定
○ BP 上では変数のレプリケーション設定
● ネットワーク上で同期を取ってくれる
99
ノード右上のアイコンなに?
雷マーク
● 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
ノード右上のアイコンなに?
BP +歯車マーク
● BP Interface の関数を実装したカスタムイベント
に表示される
101
ノード右上のアイコンなに?
封筒マーク
● BP Interface の関数をメッセージで呼び出すノー
ドに表示される
102
小ネタ
UObject な C++ クラスのコンストラクタ一杯くる?
103
UObject な C++ クラスのコンストラクタ一杯くる?
● 3 回位来る
○ アプリケーション起動時の CDO - UClass
○ BP アセットのロード時の CDO - UClass
○ 実体生成時 - UObject
104
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
UObject な C++ クラスのコンストラクタ一杯くる?
アプリケーション起動時の CDO - UClass
● FEngineLoop::PreInit から呼ばれる
○ アプリケーション起動直後。ゲームに関する初期化が走る
前。
○ 起動直後に呼び出されるため、未初期化な部分をアクセス
しにいったりするとクラッシュする事も
■ 必要な場合は CDO 生成時だけ処理をスキップする等が必要(後
述)
106
UObject な C++ クラスのコンストラクタ一杯くる?
BP アセットのロード時の CDO - UClass
● UClass::Serialize から呼び出される
○ 参照されている Level を読み込んだ時や、 LoadObject
とかで BP をロードした際に CDO を生成する
107
UObject な C++ クラスのコンストラクタ一杯くる?
実体生成時 - UObject
● 通常イメージする通りのオブジェクト生成
○ こちらもコンストラクタは通る
108
UObject な C++ クラスのコンストラクタ一杯くる?
CDO のコンストラクタか、オブジェクトのコンストラ
クタかの判別
● if (HasAnyFlags(RF_ClassDefaultObject))
○ で CDO
109
小ネタ
Stat Unit はどこを計測してる?
110
Stat Unit はどこを計測してる?
● 使用されている Stat は下記
○ STAT_UnitFrame
○ STAT_UnitRender
○ STAT_UnitGame
○ STAT_UnitGPU
● 表示、計算している場所
○ FStatUnitData::DrawStat
111
Stat Unit はどこを計測してる?
● STAT_UnitFrame
○DrawStat 呼び出し差分時間
■= 1 フレームの時間
112
Stat Unit はどこを計測してる?
● STAT_UnitRender
○ GRenderThreadTime を使用
■ FSlateRHIRenderer::DrawWindow_RenderThread で計算し
ている
● ここを通った差分時間 - RenderThread の Idle 時間
● RenderThread の Idle 時間は STAT_RenderingIdleTime
で確認できる
○ RenderThread が動作している時間を計測
113
Stat Unit はどこを計測してる?
● STAT_UnitGame
○ GGameThreadTime を使用
■ FViewport::Draw で計算している
● ここを通った差分時間 - GameThread の Idle 時間
● GameThread の Idle 時間は STAT_GameIdleTime で確認
できる
○ Idle 時間の計算は FThreadIdleStats::FScopeIdle
■ UEngine::UpdateTimeAndHandleMaxTickRate
○ GameThread が動作している時間を計測
114
Stat Unit はどこを計測してる?
● STAT_UnitGPU
○ RHIGetGPUFrameCycles を使用
■ DirectX11 だと GGPUFrameTime
● // Stat unit GPU time is not accurate anyway with SLI
○ FD3DGPUProfiler::BeginFrame ~ EndFrame
○ プラットフォーム依存。
115
以上!
116

UE4プログラマー勉強会 in 大阪 -エンジンの内部挙動について

  • 1.
    UE4 プログラマー向け勉強会 in大阪 1 エンジンの内部挙動について
  • 2.
    自己紹介 ● Twitter: com04 ●ゲームプログラマー ● マテリアル / レンダリング / エンジン拡張 ● 横浜 × 酒 ×UE4 2
  • 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
  • 4.
  • 5.
    はじめに 今回のお話し ● PC ベース ●ネットワークは無し ● 調べた際の関数名等も記述しています。追いかけた い方は参考にしてください。 ● UE4 のソースコードは分かりやすいですが難解で す。間違ってたらごめんなさい。教えてください。 5
  • 6.
  • 7.
  • 8.
    はじめに 基本的には知らなくて良い(ハズ) ただ、クリティカルな問題に当たった時は知りたくなる 時があります ● 何か 1フレームずれる ● 思った順番で処理されない ● こっちのシーンだと行けたのに、あっちだとおかしい ● エンジン内部のクラッシュ /Assert で詰まる 8
  • 9.
    はじめに AnswerHub 、 Forum等で質問する手もあります。 ただ、返答が得られるかは親切な皆様 UE4 ユーザー 次第です。 また業務ベースだと表で質問出来無い事もあります。 有償サポートの UDN も有りますが、契約してない / アカウントが無い場合は使用できません。 9
  • 10.
  • 11.
    もくじ ● World とScene と Level ● 処理順 ● Level のロード ● Level の Stream ● Actor の生成 /BeginPlay 順番 ● Actor の Tick 順番 ● スレッド ● 小ネタ version: UE4.18.2 11
  • 12.
    World と Sceneと Level 12
  • 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
  • 16.
  • 17.
    処理順 起点 ● FEngineLoop::Tick ○ UGameEngine::Tick ■WorldList の数だけ UWorld::Tick を回す 17
  • 18.
    処理順 ● UWorld::Tick ○ NavigationSystem->Tick ○LevelCollections の数だけ次のページの処理回す 18
  • 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
  • 23.
    処理順 ● UGameEngine::Tick ○ FTickableGameObject::TickObjects(全体) ○ GameViewport->Tick ○ RedrawViewports ○ GetRendererModule().PostRenderAllViewports ○ IStreamingManager::Tick ○ GameAudioDeviceManager- >UpdateActiveAudioDevices 23
  • 24.
    処理順 ● FEngineLoop::Tick ○ GShaderCompilingManager::ProcessAsyncResults ○GDistanceFieldAsyncQueue::ProcessAsyncTasks ○ FSlateApplication::Tick ○ RHITick ○ FTicker::GetCoreTicker().Tick ○ FThreadManager::Get().Tick ○ GEngine->TickDeferredCommands 24
  • 25.
  • 26.
    処理順 大まかな要約 1. TickGroup 処理( フィジックス後まで) 2. Actor 外の Latent 系処理 3. TimerEvent 系 4. FTickableGameObject 継承クラス更新 5. Streaming 更新 6. TickGroup 処理 ( アップデート後から) 7. FXSystem 更新 8. ガベージコレクション更新 (ConditionalCollectGarbage) 9. SkyLight/Reflection の Capture 更新 10.描画情報の設定 26
  • 27.
  • 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
  • 32.
  • 33.
    Level の Stream ●[LoadStreamLevel] で SubLevel を動的ロード ● [UnloadStreamLevel] で開放 ● [GetStreamingLevel] - [Should be Visible] で動 的ロードした SubLevel の表示 ON / OFF 33
  • 34.
    Level の Stream [Shouldbe 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 [Shouldbe 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
  • 40.
  • 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
  • 44.
    Actor の Tick順番 44
  • 45.
    Actor の Tick順番 そもそも Tick 順って考える必要ある? 45
  • 46.
    Actor の Tick順番 テストケース プレイヤーが参加者全員の時間を止めることが出来る 46
  • 47.
    Actor の Tick順番 一列に並んだ Actor 達 左右方向の座標値は同じ値 47 Actor Actor Actor ( Player ) Character
  • 48.
    Actor の Tick順番 BP は Tick で移動するようなロジック 48
  • 49.
    Actor の Tick順番 Player だけは、参加者全員の CustomTimeDilation を 0 にすることが出来る (= Tick 等の Time スケールをゼロに) 49
  • 50.
    Actor の Tick順番 とりあえず実行して停止機能を発動させると…… 50 Character だけが後ろに居る!
  • 51.
    Actor の Tick順番 状況次第では…… 51 Player 以外が後ろに居る!
  • 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
  • 56.
    Actor の Tick順番 → 順番ってどうなってるの? 56
  • 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
  • 67.
    Actor の Tick順番 同一 TickGroup 内での順番は? 67
  • 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
  • 73.
    Actor の Tick順番 ややこしい 73
  • 74.
    Actor の Tick順番 分かりやすく Tick の優先付けするとしたら…… 1. TickGroup 2. HighPriority 3. 個別の関連付け くらい 74
  • 75.
    Actor の Tick順番 細かい順番には依存させず、依存が必要なら TickGroup を分ける位の括りが良い もしくは Tick で処理せずに、管理クラスで一括で処 理する等 75
  • 76.
  • 77.
    スレッド ● FRunnable を継承しているのがスレッド ●TGraphTask<TaskClass>::CreateTask(NULL).C onstructAndDispatchWhenReady(Task, *this); ○ のような形で動作しているのはタスクスレッドに積まれ て実行される。(これも別スレッド動作) 77
  • 78.
    スレッド 主なスレッド処理 ● GameThread ● FRenderingThread ●FAudioThread ● FPhysXTask ● FParallelAnimationEvaluationTask 78
  • 79.
    スレッド GameThread ● Tick とか動かしているスレッド ●基本的にメインスレッド扱いになる筈 79
  • 80.
    スレッド FRenderingThread ● 描画コマンド発行スレッド ● GameThreadで変更した RenderState や RenderTransform を受け取って GPU に流し込むコ マンドを生成する ○ ENQUEUE_RENDER_COMMAND 系を使用することで RenderThread に Task を投げる ○ UWorld::SendAllEndOfFrameUpdates で GameThread から RenderThread へ更新した描画情報を投げている ■ UActorComponent::DoDeferredRenderUpdates_Concurrent で更新 80
  • 81.
    スレッド FAudioThread ● オーディオ周りの処理を行うスレッド ○ このクラス自体での処理はない ○外部の USoundWave や FAudioDevice 、 UAudioComponent からコマンドを投 げられて処理する為のスレッド ■ FAudioThread::RunCommandOnAudioThread(<lamda 式 >) でコマンドを投げる 81
  • 82.
    スレッド FPhysXTask ● 物理計算のタスク。スレッドで並列処理される ○ FPhysSceneでアクセスされる ○ FStartPhysicsTickFunction で開始 ■ TickGroup: StartPhysics ○ FEndPhysicsTickFunction で終了 ■ TickGroup: EndPhysics 82
  • 83.
    スレッド FParallelAnimationEvaluationTask ● スレッドで並列処理される ● USkeletalMeshComponent::RefreshBoneTransfor ms ○“a.ParallelAnimEvaluation 0” で使わない様にも出来 る ■ USkeletalMeshComponent::PerformAnimationEvaluation が並 列化される ■ UAnimInstance::ParallelUpdateAnimation が並列化。ポーズの 計算が並列化されている 83
  • 84.
    スレッド ほか今回解析出来なかったスレッド類(の一部) ● TAsyncRunnable ● FAssetDataDiscovery ●FAssetDataGatherer ● FAsyncWriter ● FUnrealAudioModule ● FUnrealAudioWasapi ● FUnrealAudioXAudio2 ● FSoundFileDecoder ● IAudioMixerPlatformInterface ● FMultichannelTcpReceiver ● FShaderCompileThreadRunnableBase 84
  • 85.
  • 86.
  • 87.
  • 88.
    InputEvent のタイミング APlayerController のTick で呼び出される ● APlayerController::TickActor ○ APlayerController::PlayerTick ■ APlayerController::TickPlayerInput ● APlayerController::ProcessPlayerInput ○ UPlayerInput::ProcessInputStack 88
  • 89.
  • 90.
  • 91.
    CollisionEvent のタイミング ● UPrimitiveComponent::UpdateOverlaps等で呼 びされる ○ 場所的には、 [SetActorLocation] 等の移動処理のタイ ミングで Overlap を計算してイベントを呼び出す 91
  • 92.
  • 93.
    遅延実行のタイミング Delay 系のノード ● Actorの Tick 内で処理される ○ AActor::Tick ■ GetLatentActionManager().ProcessLatentActions ● ただし、 TickInterval 等で Actor の Tick がスキッ プされた場合は UWorld::Tick で処理される ○ CurrentLatentActionManager::ProcessLatentActions ■ ※ 実行順番が変わるので注意! 93
  • 94.
    遅延実行のタイミング SetTimer 系のノード ● Actorの Tick ではなく、 UWorld::Tick で呼ばれ る FTimerManager::Tick から処理される ● TickGroup 的には TG_PostPhysics の 後、 TG_PostUpdateWork の前 94
  • 95.
  • 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
  • 99.
    ノード右上のアイコンなに? 丸二つマーク ● Replicated ○ UPROPERTYに Replicated 指定 ○ BP 上では変数のレプリケーション設定 ● ネットワーク上で同期を取ってくれる 99
  • 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
  • 101.
    ノード右上のアイコンなに? BP +歯車マーク ● BPInterface の関数を実装したカスタムイベント に表示される 101
  • 102.
    ノード右上のアイコンなに? 封筒マーク ● BP Interfaceの関数をメッセージで呼び出すノー ドに表示される 102
  • 103.
    小ネタ UObject な C++クラスのコンストラクタ一杯くる? 103
  • 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
  • 110.
  • 111.
    Stat Unit はどこを計測してる? ●使用されている Stat は下記 ○ STAT_UnitFrame ○ STAT_UnitRender ○ STAT_UnitGame ○ STAT_UnitGPU ● 表示、計算している場所 ○ FStatUnitData::DrawStat 111
  • 112.
    Stat Unit はどこを計測してる? ●STAT_UnitFrame ○DrawStat 呼び出し差分時間 ■= 1 フレームの時間 112
  • 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
  • 116.