49.3K Views
March 12, 19
スライド概要
2019年03月08日に行われた「第11回UE4勉強会 in 大阪」で登壇した際に使用した資料です。
https://ue4study-osaka.connpass.com/event/120568/
Unreal Engineを開発・提供しているエピック ゲームズ ジャパンによる公式アカウントです。 勉強会や配信などで行った講演資料を公開しています。 公式サイトはこちら https://www.unrealengine.com/ja/
非同期ロード画面 Asynchronous Loading Screen
自己紹介 Axel Riffard (アクセル リファール) ● 国籍:フランス ● Twitter : @AxRiff ● サポート エンジニア ● エピックゲームズジャパンに12月入社 ● 趣味はPerfume、ベイスターズ、ウイスキー、日常最適化 ● 最近の勉強:ASM x86-64 ● 今プレイしてるゲーム:KH3 #UE4 | @UNREALENGINE
このセッションで学ぶこと ● UE4のモジュールの作り方 ● スレッドを分けて、綺麗でヒッチしないロード画面UIの作り方 ● UE4の非同期コードの動き方 スライドは後で公開しますよ!!Twitterで見てね! #UE4 | @UNREALENGINE
初期検討 ● マルチスレッドとは? ● UE4での実装は? #UE4 | @UNREALENGINE
マルチスレッドとは? ● 同じCPUで複数な計算を動かすことです ● プラットフォームによって、別コアで動くものと、 同じコアでロジカルスレッドを作成するものがある。 OSレベルで決めることです。マルチプラットフォー ムゲームを開発したら、注意! #UE4 | @UNREALENGINE Process
UE4内のマルチスレッド ● 何もしなくても、UE4がすでにマルチスレッドを使っている ● GameとDraw以外のスレッドを作れる ● エンジン内の非同期コール機能は、FAsyncTaskという関数です。いろんな所 で使われてるから、検索してみて! 実は… FAsyncTaskは、別スレッドで走っているとは限らない! #UE4 | @UNREALENGINE
ロード画面はどうやって作れる? ● レベルロードはゲームスレッドでしか出来ない ● だからロード画面は別スレッドにすれば、ゲームスレッドの邪魔にならない ● レベルのロードが終わったら、非同期で他のアセットをロードする #UE4 | @UNREALENGINE
スレッドを作って、 画面を表示する #UE4 | @UNREALENGINE メインスレッドで LoadLevelを呼ぶ LoadLevelが終わったら、 作ったスレッドを閉めてメ インスレッドを表示する
ActionRPG ● サンプルプロジェクト「ActionRPG」のロード画面の実装が いいベース ● Googleかアンリアルラウンチャーで無料 #UE4 | @UNREALENGINE
https://youtu.be/FMtNr8xwam0 #UE4 | @UNREALENGINE
Game Engine LoadingModule Implementation IModuleInterface SCompoundWidget FSlateDynamicImageBrush #UE4 | @UNREALENGINE FLoadingScreenModule GameModule Interface Use ILoadingScreenModule C++ BlueprintLibrary SLoadingScreen FLoadingScreenBrush Blueprint Use
使い方 #UE4 | @UNREALENGINE
ブループリント側 BP_GameInstance 実際は、Absoluteパスを必 Ji ず使いましょう! Small text can be placed in at 10pt #UE4 | @UNREALENGINE
ごめん、これからは、 プグラマー向けの話になる #UE4 | @UNREALENGINE
モジュールとは ● モジュールはUE4のビルドブロックです ● エンジンは、モジュールの大きなコレクションとして実装され、ゲームは独 自のモジュールでそれらを拡張する ● モジュールを追加することが可能 #UE4 | @UNREALENGINE
モジュールのアプローチ メリット ● ゲームのロジックと分かれている ● 管理しやすい ● 使いまわせる ● エンジンのアップデートがあっても、マージは楽(多分) ● ヒューマンエラーを防げる:関係ない人からみると実装が隠れているので、 副作用が少ない #UE4 | @UNREALENGINE
#UE4 | @UNREALENGINE
モジュールのロードについて ● ロード画面だから、ゲームの前に作らなきゃいけない ● ELoadingPhase::TypeというEnumで設定する ● ゲームのモジュールはDefaultなので、ゲームとエンジンのinitの前にロードし なきゃいけない。PreLoadingScreenでぴったり #UE4 | @UNREALENGINE
.uprojectの編集 "Modules": [ { "Name": “GameModule", "Type": "Runtime", "LoadingPhase": "Default" }, { "Name": “NewGameLoadingScreen", "Type": "ClientOnly", "LoadingPhase": "PreLoadingScreen" } ], #UE4 | @UNREALENGINE
実装 #UE4 | @UNREALENGINE
インターフェイス #UE4 | @UNREALENGINE
モジュールインターフェイス作成
class INewGameLoadingScreenModule : public IModuleInterface
{
public:
static inline INewGameLoadingScreenModule& Get()
{
// Check if newly created module is correctly Loaded
return FModuleManager::LoadModuleChecked<INewGameLoadingScreenModule>(“NewGameLoadingScreen");
}
virtual void StartInGameLoadingScreen(bool bPlayUntilStopped, float PlayTime) = 0;
virtual void StopInGameLoadingScreen() = 0;
};
#UE4 | @UNREALENGINE
モジュールインターフェイスは完了です! 簡単でしょう? #UE4 | @UNREALENGINE
モジュールインターフェイス実装作成
virtual void StartupModule() override
{
//Load assets to display during the loading screen
LoadObject<UObject>(nullptr, TEXT("/Game/UI/T_ActionRPG_TransparentLogo.T_ActionRPG_TransparentLogo") );
if (IsMoviePlayerEnabled())
{
CreateScreen();
}
}
virtual void CreateScreen()
{
FLoadingScreenAttributes LoadingScreen;
LoadingScreen.bAutoCompleteWhenLoadingCompletes = true;
LoadingScreen.MinimumLoadingScreenDisplayTime = 20.f;
LoadingScreen.WidgetLoadingScreen = SNew(SRPGLoadingScreen);
// Set up created loading screen UI and and movie to display in Slate
GetMoviePlayer()->SetupLoadingScreen(LoadingScreen);
}
};
#UE4 | @UNREALENGINE
UI #UE4 | @UNREALENGINE
Slateとは ● アンリアルのカスタム仕様の UI プログラミングフレームワークの名称です ● エディタ インターフェースはスレートを使ってビルドされる #UE4 | @UNREALENGINE
Slate Widget実装
class SNewGameLoadingScreen : public SCompoundWidget
{
public:
SLATE_BEGIN_ARGS(SNewGameLoadingScreen) {}
SLATE_END_ARGS()
void Construct(const FArguments& InArgs)
{
static const FName LoadingScreenName(TEXT(“ImagePathThere"));
LoadingScreenBrush = MakeShareable(new FLoadingScreenBrush(LoadingScreenName, FVector2D(1024, 256)));
FSlateBrush *BGBrush = new FSlateBrush();
// Creation of the ChildSlot in Slate
ChildSlot[...];
}
}
#UE4 | @UNREALENGINE
ChildSlot実装 ChildSlot [ SNew(SOverlay) + SOverlay::Slot() .HAlign(HAlign_Fill) .VAlign(VAlign_Fill) [ SNew(SBorder) .BorderImage(BGBrush) ] +SOverlay::Slot() .HAlign(HAlign_Center) .VAlign(VAlign_Center) [ SNew(SImage) .Image(LoadingScreenBrush.Get()) ] #UE4 | @UNREALENGINE +SOverlay::Slot() .HAlign(HAlign_Fill) .VAlign(VAlign_Fill) [ SNew(SVerticalBox) +SVerticalBox::Slot() .VAlign(VAlign_Bottom) .HAlign(HAlign_Right) .Padding(FMargin(10.0f)) [ SNew(SThrobber) .Visibility(this, &SRPGLoadingScreen::GetLoadIndicatorVisibility) ] ] ];
ChildSlotについて 書き方がおかしく見えるかもしれませんが、実は、正しいC++ですよ。 +SOverlay::Slot()はオペレターオバーロードです。 なれたら、読みやすくなります! 詳細はhttps://wiki.unrealengine.com/Slate,_Helloまで! #UE4 | @UNREALENGINE
モジュール実装作成
class FActionRPGLoadingScreenModule : public IActionRPGLoadingScreenModule
{
virtual void StopInGameLoadingScreen() override
{
FLoadingScreenAttributes LoadingScreen;
LoadingScreen.bAutoCompleteWhenLoadingCompletes = !bPlayUntilStopped;
LoadingScreen.WidgetLoadingScreen = SNew(SRPGLoadingScreen);
// Set up created loading screen UI and and movie to display in Slate
GetMoviePlayer()->SetupLoadingScreen(LoadingScreen);
}
virtual void CreateScreen()
{
FLoadingScreenAttributes LoadingScreen;
LoadingScreen.bAutoCompleteWhenLoadingCompletes = true;
LoadingScreen.MinimumLoadingScreenDisplayTime = 20.f;
LoadingScreen.WidgetLoadingScreen = SNew(SRPGLoadingScreen);
// Set up created loading screen UI and and movie to display in Slate
GetMoviePlayer()->SetupLoadingScreen(LoadingScreen);
}
};
#UE4 | @UNREALENGINE
あれ?非同期のロジックがなくない??! と思いますよね。 #UE4 | @UNREALENGINE
と思いますよね。 実は、あります。 #UE4 | @UNREALENGINE
Slateは、別スレッドで動いています。 #UE4 | @UNREALENGINE
スレッドと非同期で何をする? ● MoviePlayerがSlateThreadで動いています。 ● 実装はRuntime\MoviePlayer\Private\MoviePlayerThreading.cpp ● 是非、ブレークポイントを入れてフローを詳しく見て下さい。 #UE4 | @UNREALENGINE
void FSlateLoadingSynchronizationMechanism::Initialize()
{
check(IsInGameThread());
ResetSlateDrawPassEnqueued();
SetSlateMainLoopRunning();
MainLoop.Lock();
FString ThreadName = TEXT("SlateLoadingThread");
ThreadName.AppendInt(LoadingThreadInstanceCounter.Increment());
// Create new thread
SlateRunnableTask = new FSlateLoadingThreadTask( *this );
SlateLoadingThread = FRunnableThread::Create(SlateRunnableTask, *ThreadName);
}
#UE4 | @UNREALENGINE
ロード画面完了! #UE4 | @UNREALENGINE
Q&A #UE4 | @UNREALENGINE