関連ブログ
- [UE4][UE5]開発環境の容量を少しでも減らす 2024.08.14UE
- [UE5] PushModel型のReplicationを使い、ネットワーク最適化を図る 2024.05.29UE
- [UE5]マテリアルでメッシュをスケールする方法 2024.01.17UE
CATEGORY
2019.01.30UE4
執筆バージョン: Unreal Engine 4.21 |
こんにちは!突然ですが、みなさんはこんなメッセージに出会ったことはありますか?
そう、UEで作っているゲームがクラッシュしたのです。
なんて簡潔なメッセージ。
…ヤバイ。早く解決したい。クラッシュはイヤだ!
解決するためには情報が欲しい!
情報は少しでも多い方がいい!
そう、C++のコールスタックだけじゃなくて、呼び出し元ブループリントのコールスタックもログ出力したいんだ!
というわけで、出力できるようにしましょう。
今回は、C++で追加した GameInstance に実装してみました。
やることは、「FCoreDelegates::OnHandleSystemError デリゲートに割り当てた関数内で、FFrame::GetScriptCallstack() の戻り値を UE_LOG() する」です。
MyGameInstance.h
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 |
#pragma once #include "CoreMinimal.h" #include "Engine/GameInstance.h" #include "MyGameInstance.generated.h" UCLASS() class SAMPLEGAME_API UMyGameInstance : public UGameInstance { GENERATED_BODY() public: virtual void Init() override; virtual void Shutdown() override; // 実行中のBlueprintのコールスタックをログ出力します。 // ※この関数をブループリント上からも使いたい場合は、下記 UFUNCTION() の行を有効にしてください。 // UFUNCTION(BlueprintCallable, Category = "Debug") static void PrintLogBlueprintCallstack(); private: // OnHandleSystemError デリゲート登録時のハンドルです。デリゲート登録を解除するために保持します。 FDelegateHandle OnSystemErrorDelegateHandle; // システムエラー時に呼ばれる関数です。C++コールスタックが出力される前に呼ばれます。 void OnSystemError(); }; |
MyGameInstance.cpp
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 |
#include "MyGameInstance.h" #include "CoreUObject/Public/CoreUObject.h" #include "EngineLogs.h" void UMyGameInstance::Init() { Super::Init(); // システムエラー時に呼んでほしい関数を、デリゲートに登録します。 OnSystemErrorDelegateHandle = FCoreDelegates::OnHandleSystemError.AddUObject(this, &UMyGameInstance::OnSystemError); } void UMyGameInstance::Shutdown() { // デリゲート登録を解除します。 FCoreDelegates::OnHandleSystemError.Remove(OnSystemErrorDelegateHandle); Super::Shutdown(); } void UMyGameInstance::PrintLogBlueprintCallstack() { // Blueprintのコールスタックを取得します。 FString Callstack = FFrame::GetScriptCallstack(); // コールスタックをログ出力します。 UE_LOG(LogTemp, Error, TEXT("--------------------------------------")); UE_LOG(LogTemp, Error, TEXT(" Blueprint Callstack:\n%s"), *Callstack); UE_LOG(LogTemp, Error, TEXT("--------------------------------------")); } void UMyGameInstance::OnSystemError() { PrintLogBlueprintCallstack(); } |
テストのため、意図的にクラッシュを発生させるためのクラスも用意しましょう。
C++でアクタを作って、void Crash() 関数を追加するだけです。
MyActor.h
1 2 3 4 5 6 7 8 9 10 11 12 |
// (中略) UCLASS() class SAMPLEGAME_API AMyActor : public AActor { // (中略) public: // ゲームを意図的にクラッシュさせます。 UFUNCTION(BlueprintCallable, Category = "Debug") void Crash(); }; |
MyActor.cpp
1 2 3 4 5 6 7 |
// (中略) void AMyActor::Crash() { AActor* SomeActor = nullptr; SomeActor->Destroy(); } |
テスト用に用意したレベルのレベルブループリントで、以下のようにブループリント関数を作って、BeginPlay から呼び出しましょう。
このゲームをパッケージして実行すると、3秒後に Fatal error! メッセージが表示されます。
メッセージを閉じてゲームを終了し、(パッケージフォルダ)/(ゲーム名)/Saved/Logs 以下を見ると、(ゲーム名).log というログファイルが出力されています。
ログファイルを開くと、以下のように、呼び出し元ブループリントのコールスタックが出力されていることが分かります。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
// (中略) [2019.xx.xx-11.51.00:227][185]LogTemp: Error: -------------------------------------- [2019.xx.xx-11.51.00:227][185]LogTemp: Error: Blueprint Callstack: Minimal_Default_C.CallCrash Minimal_Default_C.ExecuteUbergraph_Minimal_Default [2019.xx.xx-11.51.00:227][185]LogTemp: Error: -------------------------------------- [2019.xx.xx-11.51.00:227][185]LogWindows: Error: === Critical error: === [2019.xx.xx-11.51.00:227][185]LogWindows: Error: [2019.xx.xx-11.51.00:227][185]LogWindows: Error: Fatal error! [2019.xx.xx-11.51.00:227][185]LogWindows: Error: [2019.xx.xx-11.51.00:227][185]LogWindows: Error: Unhandled Exception: EXCEPTION_ACCESS_VIOLATION reading address 0x0000008c // (中略) |
C++で作られた機能を、ブループリントのいろいろな場所で呼び出している場合の不具合箇所特定には、特に有用です。
クラッシュ時のデバッグに、ぜひご活用ください。