ブログに戻る

Epic Online ServicesにおけるUnreal Blueprintのログイン失敗とAchievementエラーの修正方法

公開日 2026年7月9日
Epic Online ServicesにおけるUnreal Blueprintのログイン失敗とAchievementエラーの修正方法

要点まとめ

Unreal EngineゲームにおけるEpic Online Services (EOS)のログイン失敗および実績(Achievement)エラーを解決するための実践的なデバッグ方法を解説します。非同期処理フローの正しい設計や、EAIDとPUIDの連携エラーといったアイデンティティマッピングの仕組みを詳述しています。BlueprintとC++での堅牢な実装方法に加え、DefaultEngine.iniの正しい設定テンプレートを提供し、最終的にクロスプラットフォームBackend horizOnによる統合の簡素化を提案しています。

Unreal EngineゲームのShippingビルドをコンパイルし、Epic Games Launcherから起動して、カスタムログインボタンをクリックする。しかし何も起こらないか、ゲームクライアントがフリーズし、ログには原因不明の認証失敗の通知が出力される。さらに悪いことに、実績(Achievement)のロック解除ノードはエラーを表示せずに実行されているにもかかわらず、Epic Games Storeオーバーレイのプレイヤープロファイルは完全に空のままだ。これらのエラーは、ストアフロントのAPIを統合する際によく発生する。幸いなことに、非同期実行フローを正しく修正し、サブシステムがユーザーのアイデンティティをどのようにマッピングしているかを理解することで、これらを解決できる。

EOS認証パイプラインの仕組み:EAID vs PUID

Unreal EngineのOnline Subsystem (OSS)を介したEpic Online Services (EOS)の統合には、2層のアイデンティティシステムを扱う必要がある。Epic Account ID (EAID)はAuth Interfaceによって管理される。これはストアフロントレベルでユーザーを検証し、ソーシャルオーバーレイ、フレンドリスト、Epic Games Storeのライブラリチェックなどの機能を有効にする。

一方、Product User ID (PUID)はConnect Interfaceによって管理される。PUIDはゲーム固有の識別子であり、ユーザーをBackendのゲームサービスに紐付ける。これらのサービスには、Matchmaking、Stats、クラウドセーブ、実績(Achievements)が含まれる。

EAIDとPUIDの不一致によって実績(Achievements)が動作しなくなる理由

よくある失敗パターンは、Auth Interfaceでの認証(EAIDの生成)には成功しているものの、サブシステムがConnect Interfaceへのログインに失敗しているケースだ。実績(Achievements)はPUIDを使用してConnect Interfaceで管理されているため、解決されたPUIDがない状態で実績の進行状況を書き込もうとすると、EOS_InvalidUserエラーで失敗する。

ストアフロントのラッパーなしで起動するゲームでは、通常、中央のユーザーテーブルと照合される資格情報を使用してユーザーがログインする。しかし、Epic Games Storeのような現代のストアフロントエコシステムでは、アイデンティティがフェデレーション(統合)されている。ゲームが起動すると、ランチャーから暗号化されたチケットを受け取る。その後、ゲームはこのチケットをストアフロントの認証サーバーに渡す。SDKはEpic Account IDを返し、これがプラットフォーム上のすべてのゲームでユーザーのパスポートとして機能する。

しかし、プライバシーとクロスプレイドのセキュリティを維持するため、Epicは実績(Achievements)のようなゲーム固有の機能がEpic Account IDを直接読み取ることを許可していない。代わりに、ゲームはConnect Interfaceを初期化し、セカンダリの交換処理を行ってProduct User IDを取得しなければならない。この分離により、万が一ゲームがハッキングされたとしても、プレイヤーのグローバルなEpic Account資格情報が侵害されるのを防ぐことができる。PUIDがない場合、ゲームは事実上、未認証のゲスト状態で動作していることになり、EGS機能へのアクセスがブロックされる。資格情報の設定やクライアントポリシーがEpic Developer Portalで正しい権限を持っていない場合、このリンクは失敗する。Identity Interfaceはログイン成功を報告する(Epicアカウント自体は有効であるため)が、ゲームサービスレイヤーはPUIDがリンクされていないため、実績データを書き込むことができない。

失敗するBlueprintログインシーケンスの構造

多くの開発者が、Blueprintの実行ピンを線形につなぐことでログイン問題に直面している。Loginノードの実行ピンを直接Query AchievementsWrite Achievement Progressノードにドラッグしてつなぐと、必ず失敗する。

認証リクエストはEpicのBackendに送信される必要があるため、非同期処理となる。エンジンは、Epicサーバーがユーザーの資格情報を返すよりもはるかに前の、まさに次のフレームでBlueprintグラフの実行を継続してしまう。この段階では、プレイヤーのUnique Net IDは依然としてnullのままだ。

「見落とされたノード」の不一致:非同期実行におけるRace Condition

ログインノードの直後に実績(Achievement)ノードを呼び出すと、キャッシュされたユーザー状態が空であるため、サブシステムはサイレントエラー(ログに表示されないエラー)をスローする。正しいワークフローでは、サブシステムの非同期デリゲート(Delegate)にカスタムイベントをバインドする必要がある。

さらに、実績(Achievements)に書き込む前にキャッシュするという重要なステップを見落としている開発者も多い。現在のスキーマをサーバーに事前にQuery(照会)せずに、実績の進行状況を変更することはできない。Query Achievementsを事前に呼び出さずにWrite Achievementsを呼び出すと、ローカルのキャッシュされた実績マップのサイズが0であるため、サイレントに失敗するか、コンソールログにEOS_InvalidUserエラーが発生する。

正しい非同期Blueprintフロー

これを解決するには、ログインのトリガーと実績の更新を分離する必要がある。まず、ログインノードを呼び出し、正しい認証資格情報を指定する。

次に、Identity InterfaceのOn Login Completeデリゲートにイベントをバインドする。このイベントが起動し、成功ステータスが返されたら、ユーザーのUnique Net IDを取得する。

第三に、このIDをQuery Achievementsノードに渡す。最後に、On Query Achievements Completeデリゲートにイベントをバインドし、そのコールバック内でのみWrite Achievement Progressを呼び出すようにする。

C++による堅牢なEOSログイン&実績コントローラーの実装

これらの非同期処理をクリーンに処理するために、C++で認証ループを実装することを強く推奨する。これにより、IOnlineSubsystemとそのインターフェースに直接アクセスできるようになる。

以下は、実稼働環境に対応したログインおよび実績ハンドラーの完全なC++実装例だ。

#include "OnlineSubsystem.h"
#include "Interfaces/OnlineIdentityInterface.h"
#include "Interfaces/OnlineAchievementsInterface.h"
#include "OnlineSubsystemUtils.h"

void UEOSLoginHandler::LoginToEpicGames(APlayerController* PlayerController)

{
    if (!PlayerController) return;

    IOnlineSubsystem* Subsystem = IOnlineSubsystem::Get(TEXT("EOS"));
    if (!Subsystem)
    {
        UE_LOG(LogTemp, Warning, TEXT("EOS Subsystem not found. Make sure the plugin is enabled."));
        return;
    }

    IOnlineIdentityPtr Identity = Subsystem->GetIdentityInterface();
    if (Identity.IsValid())
    {
        FOnlineAccountCredentials Credentials;
        Credentials.Type = TEXT("ExchangeCode");
        Credentials.Id = TEXT("");
        Credentials.Token = TEXT(""); // The launcher passes this via command line arguments

        Identity->AddOnLoginCompleteDelegate_Handle(0, FOnLoginCompleteDelegate::CreateUObject(this, &UEOSLoginHandler::OnLoginComplete));
        Identity->Login(0, Credentials);
    }
}

void UEOSLoginHandler::OnLoginComplete(int32 LocalUserNum, bool bWasSuccessful, const FUniqueNetId& UserId, const FString& Error)
{
    if (!bWasSuccessful)
    { 
        UE_LOG(LogTemp, Error, TEXT("Epic Online Services Login Failed. Error: %s"), *Error);
        return;
    }

    UE_LOG(LogTemp, Log, TEXT("EOS Login Successful. ID resolved: %s"), *UserId.ToString());

    IOnlineSubsystem* Subsystem = IOnlineSubsystem::Get(TEXT("EOS"));
    IOnlineAchievementsPtr Achievements = Subsystem->GetAchievementsInterface();
    if (Achievements.IsValid())
    { 
        Achievements->QueryAchievements(UserId, FOnQueryAchievementsCompleteDelegate::CreateUObject(this, &UEOSLoginHandler::OnQueryAchievementsComplete));
    }
}

void UEOSLoginHandler::OnQueryAchievementsComplete(const FUniqueNetId& PlayerId, bool bWasSuccessful)
{
    if (!bWasSuccessful)
    {
        UE_LOG(LogTemp, Warning, TEXT("Failed to query achievements from Epic Developer Portal."));
        return;
    }

    UE_LOG(LogTemp, Log, TEXT("Achievements queried successfully. Now writing progress."));

    IOnlineSubsystem* Subsystem = IOnlineSubsystem::Get(TEXT("EOS"));
    IOnlineAchievementsPtr Achievements = Subsystem->GetAchievementsInterface();
    if (Achievements.IsValid())
    {
        FOnlineAchievementsWriteRef WriteObject = MakeShared<FOnlineAchievementsWrite>();
        WriteObject->SetFloatStat(TEXT("ACH_FIRST_BLOOD"), 100.0f);

        Achievements->WriteAchievements(PlayerId, WriteObject, FOnAchievementsWrittenDelegate::CreateUObject(this, &UEOSLoginHandler::OnAchievementsWritten));
    }
}

void UEOSLoginHandler::OnAchievementsWritten(const FUniqueNetId& PlayerId, bool bWasSuccessful)
{
    if (bWasSuccessful)
    {
        UE_LOG(LogTemp, Log, TEXT("Achievement successfully unlocked on Epic Games Store!"));
    }
    else
    {
        UE_LOG(LogTemp, Error, TEXT("Failed to write achievement progress. Check sandbox mappings."));
    }
}

このコントローラーは順次コールバックを利用している。これにより、Identity InterfaceがEpic Account ID and Product User IDの両方を正常に解決した後にのみ、実績(Achievement)インターフェースにアクセスするように保証している。

EOS向けのDefaultEngine.iniファイルの設定

Config/DefaultEngine.iniファイルの設定が正しくない場合、Blueprintのログインノードは初期化に失敗する。Online Subsystem EOSプラグインがゲームクライアントと連携するには、特定のパラメータが必要だ。

サブシステムが正しく初期化されるように、以下の設定テンプレートを使用すること:

[OnlineSubsystem]
DefaultPlatformService=EOS

[OnlineSubsystemEOS]
bEnabled=true
bUseEpicConnect=true
ProductId=your_product_id_here
SandboxId=your_sandbox_id_here
DeploymentId=your_deployment_id_here
ClientId=your_client_id_here
ClientSecret=your_client_secret_here

[OnlineSubsystemEOS.Auth]
bUseEpicAccount=true

DefaultEngine.iniファイルの設定は、コンパイルされたゲームバイナリとEpic Developer Portalの間の架け橋として機能する。[OnlineSubsystemEOS]以下のパラメータは、プロダクト設定と完全に一致している必要がある。

よく混乱を招くのは、クライアント資格情報(Client Credentials)とSandbox IDの違いだ。Client IDとClient Secretはゲームクライアントの資格情報を表し、EOSのBackendとの通信を可能にする。Sandbox IDは、クライアントがアクセスする特定の環境(Development、Staging、またはLiveなど)を表す。

ゲームクライアントがクライアント資格情報ポリシーに一致しないSandbox IDで認証を試みると、認証リクエストは拒否される。さらに、Online SubsystemがバックグラウンドでEAIDからPUIDへのマッピングを自動的に処理するようにするために、bUseEpicConnectフラグをtrueに設定する必要がある。

オーバーレイのフォーカス不一致とプラットフォーム統合の管理

UnrealのEOSプラグインは、通知の表示、フレンドの招待の処理、OAuth Web認証の実行をEpic Games Overlayに大きく依存している。ゲームクライアントがAccountPortal資格情報タイプを使用してログインをトリガーすると、オペレーティングシステムはブラウザウィンドウを起動するか、オーバーレイを開く。

このリダイレクトにより、フォーカスがゲームウィンドウから外れる。入力モードがフォーカス喪失を処理するように設定されていない場合、ゲームがロックアップしたり、キーが押しっぱなしとして認識されたりすることがある。

オーバーレイに起因する入力問題への対処は、PCゲーム開発者にとって共通の頭痛の種だ。例えば、オーバーレイのインジェクションは生の入力(Raw Input)ドライバーと競合し、キーボードやマウスの状態がフリーズする原因になる。これらのバグがプレイヤー体験を台無しにするのを防ぐために、当社の分析記事である The Marathon Input Issues Fix: オーバーレイの競合に耐えるPCゲームのアーキテクチャ設計 を確認してほしい。

さらに、テスト中の初期化失敗は、ローカル設定におけるソケット接続の誤設定やタイムアウトしきい値が原因であることが多い。セッション起動時のドライバータイムアウトのトラブルシューティングを行っている場合は、当社のガイド UEFN Session Launch Timeout Nightmares: Unreal Engineネットワークドライバーの診断 を参照してほしい。

クロスプラットフォームのストアフロントBackendの簡素化

ストアフロントサブシステムを手動で設定し、C++のデリゲートチェーンを記述し、プラットフォーム固有のオーバーレイフォーカス問題に対処することは、非常に時間がかかる。Steam、EGS、コンソールなどの複数のストアフロントに対してこれを行うことは、開発のオーバーヘッドを何倍にも増加させる。

これらを自前で構築するには、Load Balancing、データベースのシャーディング、SSL証明書の管理などをセットアップする必要があり、優に4〜6週間はかかる作業となる。

horizOn を使用すれば、これらのストアフロントAPIを抽象化する、統一されたクロスプラットフォームのBackendを利用できる。2層のアイデンティティトークン(EAID/PUID)、EpicのSandbox、またはSteamworksの初期化コードなどを個別に処理する代わりに、ユーザープロファイル、実績(Achievements)、Matchmaking、リーダーボードを処理する単一のAPIを使用できる。

horizOn は架け橋として機能し、ユーザーがどのストアフロントから起動したかにかかわらず、実績やプレイヤーの進行状況を自動的に同期する。これにより、開発者はストアフロント用のボイラープレートコードの作成ではなく、ゲームループ自体に集中できる。つまり、プレイヤーはどのプラットフォームやランチャーを使用しているかに関係なく、実績の解除、クラウドセーブへのアクセス、マッチの検索を行うことができ、開発者はストアフロント固有のSDKコードを1行も書く必要がない。

Epic Online Services統合における現代のベストプラクティス

  1. 非同期ノードを直接チェーンしないこと: 統計情報(Stats)や実績(Achievements)の書き込みなどの下流処理を実行する前に、必ず OnLoginCompleteOnQueryAchievementsComplete デリゲートの起動を待つこと。

  2. 開発中に詳細ログを有効にすること: 隠れたEOSの警告を追跡するために、DefaultEngine.ini[Core.Log]の下に以下の行を追加すること:

    [Core.Log]
    LogOnline=VeryVerbose
    LogOnlineGame=VeryVerbose
    LogEOSSDK=VeryVerbose
    
  3. Dev Portalで実績をEOSにマッピングすること: Epic Games Store (EGS)で作成された実績が、Epic Online Services (EOS)のBackendに明示的にマッピングされていることを確認すること。これらがリンクされていない場合、EOS SDKは実績IDを認識しない。

  4. ShippingビルドではExchangeCodeを使用すること: ローカル開発中はDev Auth Toolを使用した Developer ログインタイプが便利だが、Epic Games Launcherから起動するShippingビルドでは資格情報タイプを ExchangeCode に切り替える必要がある。ランチャーから提供される ExchangeCode 認証タイプを使用すると、WebブラウザによるOAuthリダイレクトがスキップされ、ログインシーケンスに要する時間が8〜12秒の手動ユーザー操作から、500ミリ秒未満の自動ハンドシェイクに短縮される。

ストアフロント統合の合理化

Unreal EngineにおけるEpic Online Servicesのログインおよび実績(Achievement)のエラーを解決することは、非同期パイプラインを理解し、設定プロファイルを完全に一致させることに尽きる。アイデンティティインターフェースとゲームサービスインターフェースの間の遷移を適切に管理することで、開発者はサイレントエラーを排除し、シームレスなプレイヤー体験を提供できる。

マルチプレイヤーBackendの規模を拡張する準備はできただろうか? horizOn を無料で試すか、API docs を確認してほしい。


出典: Epic Games Store (Online Services AND Achievements)

このダッシュボードは以下のチームによって愛情を込めて作られています Projectmakers

© 2026 projectmakers.de

unknown-v1.100.2 / unknown-v--