【UE5】SVNのリビジョン番号をゲームに埋め込む方法

プログラマーの尾関です。
今回は SVN のリビジョン番号や最終更新時間を ゲーム内で表示する方法について紹介します。
リビジョン番号を表示する理由
リビジョン番号を表示する理由としては、現在動作している実行ファイルが「どのバージョンで動いているのか」ということを知るために必要です。
例えばデバッグ会社さんの実行ファイルを提出し、デバッグ確認中に不具合が発生したバージョンをゲーム内で確認できると便利です。
事前準備 (SVNコマンドを使えるようにする)
これはプロジェクトで使用しているバージョン管理システムによりますが、今回は SVN (Subversion) を使用します。
svnコマンドが使用可能かどうかは、コマンドプロンプトから「svn --version」を実行すると確認できます。

このとき、”svn” というコマンドが見つかりません、という表示が出る場合には、svnコマンドのインストールが必要です。
svnコマンドのインストール方法
svnコマンドのインストール方法はいくつかあると思いますが、ここでは一例として、Windows環境の “TortoiseSVN” での手順を紹介します。
インストーラーを実行して「Modify」を選択 (インストール済みの場合)。

“command line client tools > Will be installed on local hard drive” を選ぶと、コマンドラインツールをインストールできます。

Build.cs を編集
svnコマンドからリビジョン番号や最終更新時間を取得してゲーム内に埋め込むには Build.cs での実装が必要です。
以下、Build.csでの実装例です。
using UnrealBuildTool;
using System;
using System.Diagnostics;
using System.IO;
public class RevTest : ModuleRules
{
public RevTest(ReadOnlyTargetRules Target) : base(Target)
{
PCHUsage = PCHUsageMode.UseExplicitOrSharedPCHs;
PublicDependencyModuleNames.AddRange(new string[] { "Core", "CoreUObject", "Engine", "InputCore", "EnhancedInput" });
// uprojectのある場所を取得.
string ProjectDir = Path.GetDirectoryName(Target.ProjectFile.FullName);
// "svn" コマンドでSVNのバージョンを取得する
string rev = RunAndReadCommand("svn", "info --show-item last-changed-revision", ProjectDir) ?? "0";
// "svn" コマンドで最終更新時間を取得する.
string iso = RunAndReadCommand("svn", "info --show-item last-changed-date", ProjectDir);
// C++ 側に文字列として渡す
// (ダブルクォートで囲んで、さらに \" にエスケープする)
PublicDefinitions.Add($"SVN_REVISION_STR=\"{rev}\"");
PublicDefinitions.Add($"SVN_LAST_CHANGED_STR=\"{iso}\"");
}
/// <summary>
/// コマンドを実行して結果を読み取る.
/// </summary>
/// <param name="command">コマンド文字列</param>
/// <param name="arg">引数文字列</param>
/// <param name="workingDir">作業ディレクトリ</param>
/// <returns>実行結果</returns>
static string RunAndReadCommand(string command, string args, string workingDir = null)
{
try {
var p = new Process();
p.StartInfo.FileName = command;
p.StartInfo.Arguments = args;
p.StartInfo.UseShellExecute = false;
p.StartInfo.RedirectStandardOutput = true;
p.StartInfo.RedirectStandardError = true;
p.StartInfo.CreateNoWindow = true;
if (!string.IsNullOrEmpty(workingDir))
{ // 作業ディレクトリを指定がある場合.
p.StartInfo.WorkingDirectory = workingDir;
}
p.Start();
string stdout = p.StandardOutput.ReadToEnd().Trim();
p.WaitForExit();
return string.IsNullOrWhiteSpace(stdout) ? null : stdout;
}
catch {
return null;
}
}
}
RunAndReadCommand() がコマンドを実行するための関数です。この関数に対して以下のコマンドを発行するとそれぞれの情報が取得できます。
- "svn info --show-item last-changed-revision": リビジョン番号の出力
- "svn info --show-item last-changed-date": 最終更新時間 (UTC) を出力
少しの注意点として、コマンドを発行する際はどのフォルダで実行するかという情報が必要になることがあります。svnコマンドはプロジェクト (*.uproject) が存在するフォルダで実行したいので「Path.GetDirectoryName(Target.ProjectFile.FullName)」によってプロジェクトがあるフォルダを取得できます。
ビルドが成功すると「[プロジェクトフォルダ]\Intermediate\Build\Win64\x64\UnrealEditor\DebugGame\[プロジェクト名]」に "Definitions.[プロジェクト名].h" が出力されます。

このファイルを確認すると PublicDefinitions.Add() で追加した定数の定義が追加されています。
// Generated by UnrealBuildTool (UEBuildModuleCPP.cs) : Shared PCH Definitions for RevTest
#pragma once
#include "G:/rev_test/RevTest/Intermediate/Build/Win64/x64/RevTestEditor/DebugGame/UnrealEd/SharedDefinitions.UnrealEd.Project.NonOptimized.ValApi.Cpp20.h"
#undef REVTEST_API
#define UE_IS_ENGINE_MODULE 0
#define UE_DEPRECATED_FORGAME UE_DEPRECATED
#define UE_DEPRECATED_FORENGINE UE_DEPRECATED
#define UE_VALIDATE_FORMAT_STRINGS 1
#define UE_VALIDATE_INTERNAL_API 1
#define UE_ENABLE_INCLUDE_ORDER_DEPRECATED_IN_5_2 0
#define UE_ENABLE_INCLUDE_ORDER_DEPRECATED_IN_5_3 0
#define UE_ENABLE_INCLUDE_ORDER_DEPRECATED_IN_5_4 0
#define UE_ENABLE_INCLUDE_ORDER_DEPRECATED_IN_5_5 0
#define UE_PROJECT_NAME RevTest
#define UE_TARGET_NAME RevTestEditor
#define SVN_REVISION_STR "2"
#define SVN_LAST_CHANGED_STR "2025-08-20T06:12:34.563386Z"
#define UE_MODULE_NAME "RevTest"
#define UE_PLUGIN_NAME ""
#define IMPLEMENT_ENCRYPTION_KEY_REGISTRATION()
#define IMPLEMENT_SIGNING_KEY_REGISTRATION()
#define REVTEST_API DLLEXPORT
#define ENHANCEDINPUT_API DLLIMPORT
SVNのログを確認するとおおよそ合っていそうです。

ただ最終更新時間が UTC のため、9時間のズレが発生しています。これは別のところで修正します。
C++コードの作成
Build.csは定数ヘッダを書き出しただけなので、このままでは Blueprintで取得できません。
そこでC++コードを作成します。

親クラスは "BlueprintFunctionLibrary" を選択。

クラス名はひとまず「CPP_RevisionInfoFunction」としました。(名前は何でもOKです)

ヘッダファイルの記述
ヘッダファイルには以下のように記述しました。
#pragma once
#include "CoreMinimal.h"
#include "Kismet/BlueprintFunctionLibrary.h"
#include "CPP_RevisionInfoFunction.generated.h"
/**
* リビジョン情報の取得.
*/
UCLASS()
class REVTEST_API UCPP_RevisionInfoFunction : public UBlueprintFunctionLibrary
{
GENERATED_BODY()
public:
/**
* リビジョン情報を取得する関数.
* @return リビジョン情報の文字列.
*/
UFUNCTION(BlueprintCallable, Category = "Revision Info")
static FString GetRevisionInfo();
};
cpp側の実装
cpp側は以下の実装です。
#include "CPP_RevisionInfoFunction.h"
// ----------------------------------------- define(s)
// 定数未定義でもコンパイルエラーにならないようにする
#ifndef SVN_REVISION_STR
#define SVN_REVISION_STR "Unknown"
#endif
#ifndef SVN_LAST_CHANGED_STR
#define SVN_LAST_CHANGED_STR "Unknown"
#endif
// TCHAR* に展開されるように TEXT() をかける
static const TCHAR* SVN_REV = TEXT(SVN_REVISION_STR);
static const TCHAR* SVN_LAST_CHANGED = TEXT(SVN_LAST_CHANGED_STR);
// ----------------------------------------- function(s)
// リビジョン情報を取得する関数
FString UCPP_RevisionInfoFunction::GetRevisionInfo()
{
FDateTime Parsed;
if (FDateTime::ParseIso8601(SVN_LAST_CHANGED, Parsed)) {
// UTC → JST (+9時間)
FDateTime JST = Parsed + FTimespan(0, 9, 0, 0);
// フォーマット修正.
FString Result = JST.ToString(TEXT("%Y-%m-%d %H:%M:%S"));
return FString::Printf(TEXT("Rev: %s [%s]"),SVN_REV, *Result);
}
// 変換失敗.
return FString("----");
}
先ほど UTCのため時間がズレているという問題があったので、日本時間に合わせるため +9時間しています。
また「YYYY-mm-dd HH:MM:SS」が馴染みのある日付時間のフォーマットなのでそのように変更しました。
レベルブループリントで確認
後はレベルブループリントから "Print String" などで文字を表示して動作確認。

無事にリビジョン情報が確認できました。

おしまい
以上、リビジョン情報を Unreal Engineのゲーム内で表示する方法でした。
この情報がゲームのデバッグに役立てれば幸いです。

