EOS SDK in C#

EOS C# SDK に関する概要です

15 分で読めます

Epic Online Services (EOS) には、C SDK の他に C# SDK が備わっています。このドキュメントでは、C# SDK を使用してプロジェクトを EOS に統合する方法と、2 つの実装の違いについて解説します。使用する SDK に関係なく基本的な機能はどちらも同じですが、C# SDK の設計は次の点で異なります。

  • C# SDK は C# のベスト プラクティスに準拠し、非同期の操作を管理するために C スタイルのハンドルではなくハンドル オブジェクトを使用するなど、オブジェクト指向の手順に従います。

  • 命名規則は代表的な C# パターンに一致します。たとえば、C SDK の EOS_Auth_Login は C# SDK で Epic.OnlineServices.Auth.AuthInterface.Login としてアクセス可能です。

  • C SDK のデータ構造体は、互換性を確認するためにマクロベースの API バージョン番号が必要となります。これらの値は C# SDK に事前に入力されています。

EOS C# SDK は、Windows、Linux、Mac、iOS、Android などのデスクトップおよびプラットフォーム用のライブラリを含みます。Xbox、Nintendo Switch、PlayStation などの限られたプラットフォーム用の C# SDK 追加バージョンは承認後に使用可能になります。制限付きの SDK および関連ドキュメントにアクセスするには、組織のアクセスを設定 する必要があります。ファーストパーティの承認後にダウンロードする SDK の中にドキュメントが含まれています。

詳細および使用の可能性については、アカウント担当者にご確認ください。

C# SDK 依存関係

C# SDK を使用するには、以下が必要です。

  • .NET Framework 3.5 以上、または同等の API 互換性
  • 以前のバージョンでも動作する場合がありますが、テストは実施されていません。

C# SDK サンプルを使用するには、以下が必要です。

  • .NET Core 3.1
  • Visual Studio 2019 以上

操作を開始する

開始する前に、C# SDK をダウンロード し、Epic の Developer Portal にセットアップする必要があります。SDK には Developer PortalProduct Settings (製品設定) の次の製品情報が必要になります。

  • 製品 ID
  • サンドボックス ID
  • デプロイメント ID
  • クライアント ID
  • クライアント シークレット

EOS SDK の統合を開始する時、これらの値によって Platform インターフェース の作成が可能になります。これは、他のすべての EOS SDK インターフェース にアクセスするために必要です。必要な情報を取得したら、製品に SDK を統合することができるようになります。

インテグレーション

  1. プロジェクトに C# SDK ソース ファイルをインクルードします。

    • (Unity ユーザー) これらのファイル (Core フォルダと Generated フォルダ) を Assets フォルダに置きます。 2.アプリケーションが対象のプラットフォームに適切なライブラリ バイナリ ファイルにアクセスできることを確認してください。
    • (Unity ユーザー) 適切なライブラリ バイナリ ファイルを、対象のプラットフォームの「Assets」フォルダにコピーします。
    • たとえば、Windows x64 の統合の場合は「EOSSDK-Win64-Shipping.dll」を使用します。
  2. C# SDK は Epic.OnlineServices.Config で対象となるライブラリ バイナリ名を決定します。一部のプラットフォームでは便宜上、正確な名前を示すように事前構成されます。

    • 対象のプラットフォームが事前構成されていない場合、ビルド エラーを避けるために構成とプロジェクトのシンボルに適切な変更を加えます。たとえば、Windows x64 を対象にする場合は EOS_PLATFORM_WINDOWS_64 を設定する必要があり、Windows x86 を対象にする場合は EOS_PLATFORM_WINDOWS_32 を設定する必要があります。
  3. (Unity ユーザー) SDK を制御するための新しいスクリプトを作成します。この例では、名前 EOSSDKComponent を使用しています。

    New script Component
  4. (Unity ユーザー) 必要に応じてコンポーネントを作成するには、[Add Component (コンポーネントを追加)] を選択してエンティティに割り当てます。ここでは、デモ目的で メイン カメラ に追加しています。

    Add Component
  5. このコンポーネントに EOS SDK コードを記述します。例については、サンプルコード を参照してください。

SDK を実装する

製品 を設定して C# SDK を統合したら、コードの記述が可能になります。SDK を初期化し、Tick メソッドを定期的に呼び出して SDK で実行できるかどうか、コールバックが実行できるかどうかを確認します。そして、アプリケーションを終了する時に SDK をシャットダウンしてください。

SDK のライフサイクルを管理する

SDK のライフサイクルは、主に初期化、ティック (通常操作)、そしてシャットダウンの 3 つに分けられます。

初期化

Platform インターフェース は SDK の入り口です。EOS を使用するためにはこのインターフェースのインスタンスが必要です。インスタンスを作成するには、アプリケーションに関する一部の基本情報とともに Epic.OnlineServices.Platform.PlatformInterface.Initialize を呼び出し、次に Epic.OnlineServices.Platform.PlatformInterface のインスタンスを取得するために Developer Portal から取得した値で Epic.OnlineServices.Platform.PlatformInterface.Create を呼び出します。SDK とのインタラクトに必要になるため、このインスタンスを格納します。 たとえば、認証の実行するために最初に Epic.OnlineServices.Auth.AuthInterface のインスタンスが必要です。これは Epic.OnlineServices.Platform.PlatformInterface インスタンスで GetAuthInterface() を呼び出して取得することができます。

SDK を初期化できるのは一度のみです。その後に Epic.OnlineServices.Platform.PlatformInterface.Initialize を呼び出すと AlreadyInitialized という失敗コードが返されます。アプリケーションの開始時に SDK を初期化したら、アプリケーションをシャットダウンするまで解放しないことを推奨します。

ティック

SDK で操作するためには、プラットフォーム インターフェース インスタンスで定期的に Epic.OnlineServices.Platform.PlatformInterface.Tick を呼び出す必要があります。これらの呼び出しはフレームごとに行う必要はありませんが、高い頻度で行ってください。100 ミリ秒ごとに 1 回の呼び出しが妥当と考えられますが、必要に応じて頻度は調整することができます。SDK のコールバックは Tick を呼び出す時にのみ実行できるため、すべての非同期操作はそれに依存します。

シャットダウン

SDK が必要なくなった時は (通常はアプリケーションの終了時) 、 Epic.OnlineServices.Platform.PlatformInterface.Release を呼び出してプラットフォーム インターフェース インスタンスを解放し、次に Epic.OnlineServices.Platform.PlatformInterface.Shutdown を呼び出してシャットダウン プロセスを完了させて終了します。最後に Epic.OnlineServices.Platform.PlatformInterface.Release を使用して SDK を終了状態にすると、それ以降は別のプラットフォーム インターフェース ハンドルを取得したり、SDK を再初期化することができなくなります。このことから、アプリケーションが終了するまで EOS SDK はシャットダウンしないようにしてください。

Unity を含む一部のエディタ環境では、EOS のような外部ライブラリをエディタの起動時にロードするとエディタがシャットダウンするまでアンロードしません。そのような場合は、エディタ内の再生セッションの最後で Epic.OnlineServices.Platform.PlatformInterface.Release または Epic.OnlineServices.Platform.PlatformInterface.Shutdown を呼び出さないでください。以後のセッションでエディタを再起動しないと SDK を初期化できなくなります。また、これらのエディタ環境では連続する SDK のインスタンスを使用するため、再生セッションが終了する直前に開始した次のセッションの操作が終了し、コールバックがトリガーされます。

結果

ほとんどのコールバック データ構造体と一部の戻り値は、 Epic.OnlineServices.Result を使用して SDK 操作の結果を伝達します。これを使用してエラーを処理したり、操作が予測どおりに実行されていることを確認してください。

ログ出力

SDK では、内部インターフェースを介して便利なデバッグ情報を出力します。この機能を有効にするには、できるだけ早い段階で、なるべく SDK の起動後すぐに、希望する詳細度を示すパラメータを使用して Epic.OnlineServices.Logging.LoggingInterface.SetLogLevel を呼び出して Epic.OnlineServices.Logging.LoggingInterface を設定し、次にコールバック関数を使用して Epic.OnlineServices.Logging.LoggingInterface.SetCallback でログ情報を受け取ります。この機能により内部操作の詳細がわかり、予想外のビヘイビアが発生した場合の原因を特定することができます。

アンマネージド メモリ

C SDK では、EOS_Leaderboards_CopyLeaderboardDefinitionByIndex などの一部の API は手動での解放が必要なデータの構造体を渡します。C# SDK では、これらの構造体はラッパーのマーシャリング コードで自動的に処理されるため、リリースを心配する必要はありません。

C SDK では、EOS_Presence_CreatePresenceModification などの一部の API は SDK ライブラリが所有するメモリにデータを設定できるハンドルを渡します。これらのハンドルは手動で解放する必要があります。C# SDK では、これらのハンドルは基になるタイプが Epic.OnlineServices.Handle のオブジェクトで表され、アンマネージド データを設定することができる関数が含まれています。また、リリース関数も含まれています。終了時に手動で呼び出す必要があります。

カスタム メモリ デリゲート

コンソールなど一部のプラットフォームでは、SDK で独自の割り当て、再割り当て、および解放するための関数を実装する必要があります。SDK はこれらの関数をかなり頻繁に呼び出すため、SDK が管理されたコードのデリゲートで送信しなくても直接それらにアクセスできると非常に効率的です。

Epic.OnlineServices.Platform.InitializeOptions でプラットフォームを初期化するときにこれらの関数を SDK にパスすることができます。以下が推奨される手順です。

  1. ネイティブ ライブラリを作成し、3 つのメモリ関数を実装します。
  2. 3 つのメモリ関数のそれぞれに対して、メモリ関数にポインタを返すエクスポート関数を実装します。
  3. C# でプラットフォームを初期化する前に、エクスポート関数を呼び出し、返された IntPtr 値をオプション構造体に設定します。

以下のサンプル コードはネイティブ ライブラリがどのようになっているかを構造体で表しています。

void* Allocate(size_t InSizeInBytes, size_t InAlignment)
{
}
void* Reallocate(void* InPointer, size_t InSizeInBytes, size_t InAlignment)
{
}
void Release(void* InPointer)
{
}
typedef void* (*AllocateFunctionPointer)(size_t InSizeInBytes, size_t InAlignment);
extern "C" __declspec(dllexport) AllocateFunctionPointer GetAllocateFunctionPointer();
{
return Allocate;
}
typedef void* (*ReallocateFunctionPointer)(void* InPointer, size_t InSizeInBytes, size_t InAlignment);
extern "C" __declspec(dllexport) ReallocateFunctionPointer GetReallocateFunctionPointer()
{
return Reallocate;
}
typedef void (*ReleaseFunctionPointer)(void* InPointer);
extern "C" __declspec(dllexport) ReleaseFunctionPointer GetReleaseFunctionPointer()
{
return Release;
}

以下のサンプル コードは、C# バインディングの内容を表しています。

[DllImport("MyNativeLibrary.dll")]
private static extern IntPtr GetAllocateFunctionPointer();
[DllImport("MyNativeLibrary.dll")]
private static extern IntPtr GetReallocateFunctionPointer();
[DllImport("MyNativeLibrary.dll")]
private static extern IntPtr GetReleaseFunctionPointer();

スレッド化

現在、SDK はスレッドセーフではありません。SDK への呼び出しはすべて、アプリケーションのメイン スレッドから行うことを推奨します。現時点では asyncawaitThreadTask、または類似するパターンを使用しないでください。

EOS オーバーレイ

アプリケーションで EOS オーバーレイ を使用するには、グラフィック デバイスを作成する前に次の手順を実行します。

  1. SDK ライブラリをメモリにロードします
  2. EOS_Initialize で初期化します
  3. EOS_Platform_Create を使用してプラットフォームを作成します

Unity でこれを行う場合は、低レベルのネイティブ レンダリング プラグイン を作成します。そして、少なくとも次のことを行う必要があります。

  1. プレフィックスとして GfxPlugin を付けてネイティブ ライブラリを作成します。
  2. SDK ライブラリをロードして EOS_InitializeEOS_Platform_Create を正常に呼び出す UnityPluginLoad(void*) という名前のエクスポート関数を追加します。
  3. エクスポート関数を追加して、作成したプラットフォーム インターフェース ハンドルを必要に応じて C# に戻します。これは、Epic.OnlineServices.Platform.PlatformInterface の新しいインスタンスを作成する場合に使用できます。

EOS オーバーレイは、Unity エディタなどのエディタ環境ではサポートされていません。意図しない動作を防ぐために、エディタ環境ではオーバーレイを明示的に無効にすることをお勧めします。

Unity ユーザーの場合、スタンドアローン ビルドで低レベルのネイティブ レンダリング プラグイン内の SDK の存続期間を制御し、エディタ ビルドでプラグインを除外して、動的バインディングを使用した MonoBehaviour 内の SDK の存続期間を制御することをお勧めします。

動的バインディング

通常、C# SDK は externDllImport バインディングを使用します。しかし、動的バインディングを使用する場合もあります。

動的バインディングを使用するには、次を実行します。

  1. EOS_DYNAMIC_BINDINGS を定義します
  2. SDK ライブラリを動的にロードし、ライブラリ ハンドルを取得します
  3. アプリケーション セッションが開始したら、Epic.OnlineServices.Bindings.Hook を呼び出してライブラリ ハンドルと関数ポインタ ローダー デリゲートを渡します。
  4. アプリケーション セッションが終了したら、Epic.OnlineServices.Bindings.Unhook を呼び出してライブラリを解放し、クリーンアップします。

たとえば、Windows で次のような extern 関数を定義する場合があります。

[DllImport("Kernel32.dll")]
private static extern IntPtr LoadLibrary(string lpLibFileName);
[DllImport("Kernel32.dll")]
private static extern int FreeLibrary(IntPtr hLibModule);
[DllImport("Kernel32.dll")]
private static extern IntPtr GetProcAddress(IntPtr hModule, string lpProcName);

次に、SDK の使用を開始するには、ライブラリをロードし、動的バインディングを次のようにフックします。

var libraryPath = $"Assets/{Config.LibraryName}";
var libraryPointer = LoadLibrary(libraryPath);
if (libraryPointer == IntPtr.Zero)
{
throw new Exception($"Failed to load library {libraryPath}");
}
Bindings.Hook(libraryPointer, GetProcAddress);

アプリケーションが終了したら、動的バインディングのフックを解除し、次のコマンドでライブラリを解放します。

Bindings.Unhook();
FreeLibrary(libraryPointer);

一部のプラットフォームには、プラットフォーム固有の API があります。これらのプラットフォームには、独自のバインディング クラスがあります。

プラットフォーム固有の API を使用するには、基本 Epic.OnlineServices.Bindings クラスに 加えて、対応するプラットフォーム固有のバインディング クラスをフックする必要があります。

1.12 の時点では、C# SDK のデフォルトの動作では、Unity エディタなどのエディタ環境に対して動的バインディングが必要となっています。これは、SDK ライブラリのオンデマンドのロードとアンロードを有効にし、ゲームがエディタで実行されるたびに SDK を初期化できるようにするためです。Unity で動的バインディングを使用する ためのより詳細なサンプルコードを以下に示します。

サンプル コードとプロジェクト

認証、プレゼンス、購入フロー、音声など、さまざまな Epic Online Services (EOS) 機能の実装を示すさまざまな C# SDK サンプルがあります。さらに、プラットフォームのセットアップ、ログイン、および Unity EOS SDK コンポーネントの実装のためのサンプル コードがあります。

サンプル コード

このセクションのサンプル コードはデモンストレーションとして、また C# SDK への理解を深めることを目的に用意しました。

プラットフォームを設定する

// Set these values as appropriate.For more information, see the Developer Portal documentation.
string productName = "MyCSharpApplication";
string productVersion = "1.0";
string productId = "";
string sandboxId = "";
string deploymentId = "";
string clientId = "";
string clientSecret = "";
var initializeOptions = new Epic.OnlineServices.Platform.InitializeOptions()
{
ProductName = productName,
ProductVersion = productVersion
};
var initializeResult = Epic.OnlineServices.Platform.PlatformInterface.Initialize(initializeOptions);
if (initializeResult != Epic.OnlineServices.Result.Success)
{
throw new System.Exception("Failed to initialize platform: " + initializeResult);
}
// The SDK outputs lots of information that is useful for debugging.
// Make sure to set up the logging interface as early as possible: after initializing.
Epic.OnlineServices.Logging.LoggingInterface.SetLogLevel(Epic.OnlineServices.Logging.LogCategory.AllCategories, Epic.OnlineServices.Logging.LogLevel.VeryVerbose);
Epic.OnlineServices.Logging.LoggingInterface.SetCallback((Epic.OnlineServices.Logging.LogMessage logMessage) =>
{
System.Console.WriteLine(logMessage.Message);
});
var options = new Epic.OnlineServices.Platform.Options()
{
ProductId = productId,
SandboxId = sandboxId,
DeploymentId = deploymentId,
ClientCredentials = new Epic.OnlineServices.Platform.ClientCredentials()
{
ClientId = clientId,
ClientSecret = clientSecret
}
};
var platformInterface = Epic.OnlineServices.Platform.PlatformInterface.Create(options);
if (platformInterface == null)
{
throw new System.Exception("Failed to create platform");
}

ログインする

var loginCredentialType = Epic.OnlineServices.Auth.LoginCredentialType.AccountPortal;
/// These fields correspond to <see cref="Epic.OnlineServices.Auth.Credentials.Id" /> and <see cref="Epic.OnlineServices.Auth.Credentials.Token" />,
/// and their use differs based on the login type.For more information, see <see cref="Epic.OnlineServices.Auth.Credentials" />
/// and the Auth Interface documentation.
string loginCredentialId = null;
string loginCredentialToken = null;
var authInterface = platformInterface.GetAuthInterface();
if (authInterface == null)
{
throw new System.Exception("Failed to get auth interface");
}
var loginOptions = new Epic.OnlineServices.Auth.LoginOptions()
{
Credentials = new Epic.OnlineServices.Auth.Credentials()
{
Type = loginCredentialType,
Id = loginCredentialId,
Token = loginCredentialToken
}
};
// Ensure platform tick is called on an interval, or the following call will never callback.
authInterface.Login(loginOptions, null, (Epic.OnlineServices.Auth.LoginCallbackInfo loginCallbackInfo) =>
{
if (loginCallbackInfo.ResultCode == Epic.OnlineServices.Result.Success)
{
System.Console.WriteLine("Login succeeded");
}
else if (Epic.OnlineServices.Common.IsOperationComplete(loginCallbackInfo.ResultCode))
{
System.Console.WriteLine("Login failed: " + loginCallbackInfo.ResultCode);
}
});

Unity の EOSSDK コンポーネント

注: 以下のコードには SDK 1.15 以上が必要です。

// This code is provided for demonstration purposes and is not intended to represent ideal practices.
using Epic.OnlineServices;
using Epic.OnlineServices.Auth;
using Epic.OnlineServices.Logging;
using Epic.OnlineServices.Platform;
using System;
using System.Runtime.InteropServices;
using UnityEngine;
public class EOSSDKComponent : MonoBehaviour
{
// Set these values as appropriate. For more information, see the Developer Portal documentation.
public string m_ProductName = "MyUnityApplication";
public string m_ProductVersion = "1.0";
public string m_ProductId = "";
public string m_SandboxId = "";
public string m_DeploymentId = "";
public string m_ClientId = "";
public string m_ClientSecret = "";
public LoginCredentialType m_LoginCredentialType = LoginCredentialType.AccountPortal;
// These fields correspond to \<see cref="Credentials.Id" /> and \<see cref="Credentials.Token" />,
// and their use differs based on the login type. For more information, see \<see cref="Credentials" />
// and the Auth Interface documentation.
public string m_LoginCredentialId = null;
public string m_LoginCredentialToken = null;
private static PlatformInterface s_PlatformInterface;
private const float c_PlatformTickInterval = 0.1f;
private float m_PlatformTickTimer = 0f;
// If we're in editor, we should dynamically load and unload the SDK between play sessions.
// This allows us to initialize the SDK each time the game is run in editor.
#if UNITY_EDITOR
[DllImport("Kernel32.dll")]
private static extern IntPtr LoadLibrary(string lpLibFileName);
[DllImport("Kernel32.dll")]
private static extern int FreeLibrary(IntPtr hLibModule);
[DllImport("Kernel32.dll")]
private static extern IntPtr GetProcAddress(IntPtr hModule, string lpProcName);
private IntPtr m_LibraryPointer;
#endif
private void Awake()
{
#if UNITY_EDITOR
var libraryPath = "Assets/" + Config.LibraryName;
m_LibraryPointer = LoadLibrary(libraryPath);
if (m_LibraryPointer == IntPtr.Zero)
{
throw new Exception("Failed to load library" + libraryPath);
}
Bindings.Hook(m_LibraryPointer, GetProcAddress);
#endif
}
private void OnApplicationQuit()
{
if (s_PlatformInterface != null)
{
s_PlatformInterface.Release();
s_PlatformInterface = null;
PlatformInterface.Shutdown();
}
#if UNITY_EDITOR
if (m_LibraryPointer != IntPtr.Zero)
{
Bindings.Unhook();
// Free until the module ref count is 0
while (FreeLibrary(m_LibraryPointer) != 0) { }
m_LibraryPointer = IntPtr.Zero;
}
#endif
}
void Start()
{
var initializeOptions = new InitializeOptions()
{
ProductName = m_ProductName,
ProductVersion = m_ProductVersion
};
var initializeResult = PlatformInterface.Initialize(ref initializeOptions);
if (initializeResult != Result.Success)
{
throw new Exception("Failed to initialize platform: " + initializeResult);
}
// The SDK outputs lots of information that is useful for debugging.
// Make sure to set up the logging interface as early as possible: after initializing.