Visual Studio 2019 で EOS 用に C# ソリューションを設定する方法

Rajen Kishna, Technical Account Manager, Epic Games |
2021年9月28日
このシリーズの前回の記事では、Epic Online Services (EOS)の使用を開始するために、 Epic Games デベロッパーポータルにアクセスするための登録方法を説明しました。 アカウント設定ができましたので、Visual Studio 2019を設定し、C# を使用してWPFソリューションを作成する方法を説明していきます。そしてこれらを使用してすべてのEOSサービスを詳しく見ていきます。前回の記事の説明を念頭に置いて進めていきましょう。今回の記事で説明する内容は以下のとおりです。
 

Visual Studio 2019 のインストール

Visual Studio 2019 がインストールされていない場合は、https://visualstudio.microsoft.com/ にアクセスしてダウンロードしてください。Microsoftは、個人や特定の組織向けに無料のコミュニティ エディションを提供しているため、資格があるかどうかを確認してください(一番下までスクロールしてください)。使用するプログラミング言語に応じて、いくつかのワークロードとコンポーネントが必要になります。
 
  • C++
    • Desktop & Mobile > Desktop development with C++
      • EOS SDKサンプルはこれらのビルドツールを使用するため、右側のサイドバーの[Optional(オプション)]で、必ず「MSVC v141-VS2017 C ++ x64 / x86 build tools(v14.16)」をチェックしてください。
    • Gaming > Game development with C++
  • C#
    • Desktop & Mobile > .NET desktop development
VS2019 Workloads
Visual Studio 2019 ワークロード
 
VS2019 MSVC141 Component
Visual Studio 2019 ワークロード
 

EOS SDK C++ と C# サンプルをコンパイルして実行したいため、上記にリストアップしたワークロードとコンポーネントをすべてインストールしてください。

SDK のダウンロード

Visual Studioがインストールされている間に、デベロッパーポータルからSDKをダウンロードすることができます。この記事では、特に断りのない限り、SDK 1.14.1を使用します。
 
  1. https://dev.epicgames.com/portal/でデベロッパーポータルにログインします。
  2. 左側のメニューからSDKを選択します。ここには、C、C#、iOS、Android の各 SDK のダウンロード、製品の概要(SDKが必要とする認証情報の概要を含む)、SDKの変更履歴が表示されます。
  3. [SDKバージョン]ドロップダウンメニューで C SDK を選択し、[ダウンロード] をクリックします。 C#SDKや、他のプラットフォームについても同じようにします。 このシリーズではC#に焦点を当てますが、他のプラットフォームにも同様に適用できます。
  4. 各SDKを便利な場所に保存して解凍します。

各SDKフォルダには「Samples」「SDK」「ThirdPartyNotices」という3つのサブフォルダがあります。「Samples」サブフォルダには、そのプラットフォーム向けのソリューションやプロジェクトがあり、C SDK は最も包括的なサンプルのセットを提供しています。「SDK」サブフォルダには、SDK自体のバイナリ、ライブラリ、ヘッダー、ソースのほか、デベロッパー認証ツールファイル復号化ツールAnti-Cheat Integrity Tool(Anti-Cheat 整合性ツール)、redistributables(再配布可能)が含まれています。「ThirdPartyNotices」には、SDKが使用しているサードパーティソフトウェアの通知概要をまとめたテキストファイルがあります。

SDK サンプルのコンパイル

SDKをダウンロードし、Visual Studioをインストールしたら、サンプルをコンパイルしてすべてが正確に設定されているか素早く確認し、特定の機能を掘り下げるためのリファレンスを提供します。
 
  1. まず、Visual Studio 2019の [C SDK Samples] フォルダにあるSamples.slnファイルを開きます。
  2. ソリューションを開いた状態で Ctrl+Shift+B を押して、すべてのプロジェクトをビルドします。この時点で、各プロジェクトに以下のようなエラーメッセージがポップアップ表示される可能性があります。 

Error    MSB8036    The Windows SDK version 10.0.17763.0 was not found. Install the required version of Windows SDK or change the SDK version in the project property pages or by right-clicking the solution and selecting "Retarget solution".
 
  1. これらのエラーを解消するには、[Solution Explorer] のソリューションノードを右クリックし、[Retarget solution] をクリックします。ここでは、インストールした Windows SDK のバージョン(この例の場合は10.0.19041.0)を選択し、オプションでプラットフォームツールセットを v142 にアップグレードすることができます。この例ではすでにツールセットの v141 を既にインストールしているため、アップグレードしないようにします。
VS2019 Retarget Solution
C SDK サンプルのリターゲット ソリューション
 
  1. ソリューションのリターゲットにより、Ctrl+Shift+B でのビルドが成功するはずです。
サンプルを実行する前に、認証情報を入力する必要があります。C SDK サンプルプロジェクトでは、認証情報は各プロジェクトの Source\SampleConstants.h ファイルに入力されています。これらの値は、デベロッパーポータルのSDKページで、製品の隣にある「Get Credentials(認証情報の取得)」ボタンをクリックすると、すぐに見つけることができます。
 
VS2019 SDK Credentials
SDK ページの認証情報取得ボタン


もしくは、製品にアクセスして左メニューの [Product Settings(製品設定)]をクリックすると、これらの値を確認できます。その際、Product ID、Sandbox ID、Deployment ID、Client ID、Client Secret が必要になります。

次に、C# SDK サンプルをコンパイルしてみましょう。

  1. まず、Visual Studio 2019 の [C SDK Samples] フォルダにあるSamples.sln ファイルを開きます。
  2. ここではすべて準備できているはずなので、Ctrl+Shift+B を押してすべてのプロジェクトをビルドします。

C# Samplesプロジェクトはすべて Common プロジェクトの Settings.cs にある認証情報を共有しています。

C# ソリューションの設定

すべてのインストールとテストが完了しましたので、いよいよ独自のC#を設定します。
  1. Visual Studio 2019 の新しいインスタンスを開き、[Create a new project] を選択します。
  2. 上部にある「テンプレートの検索」(Alt+S)の検索ボックスに「wpf」と入力し、WPF Application [C#] [Windows] [Desktop] のエントリーをクリックします。[Next(次へ)]をクリックして続けます。
  3. プロジェクト名(例:EOSCSharpSample)、ロケーション、ソリューション名(デフォルトではプロジェクト名)を入力します。次へをクリックして続けます。
  4. Target Framework として.NET Core 3.1 (Long-term support) を選択し、[Create(作成)]をクリックしてソリューションの作成を完了します。
  5. Solution Explorer でソリューションノードを右クリックし、File Explorer で [Open Folder(フォルダを開く)]をクリックします。SDK という新しいフォルダを作成し、ダウンロードした C# SDKのSDK\Source サブフォルダをここにコピーします。
  6. 先ほどコピーしたSDKソースとラッパーファイルを含めるために、プロジェクト ファイルを修正します。Solution Explorer でプロジェクトノードを右クリックし、[Edit Project File(プロジェクトファイルの編集)]をクリックします。
  7. 以下の XML を、XML のプロジェクトノード内の既存の PropertyGroup ノードの隣にコピーします。

<ItemGroup>
    <Compile Include="..\SDK\Source\Core\**">
        <Link>SDK\Core\%(RecursiveDir)%(Filename)%(Extension)</Link>
    </Compile>
    <Compile Include="..\SDK\Source\Generated\**">
        <Link>SDK\Generated\%(RecursiveDir)%(Filename)%(Extension)</Link>
    </Compile>
</ItemGroup>

 
  1. 次に、SDK にターゲットとしているプラットフォームを知らせるために、条件付きコンパイル シンボルを追加する必要があります。Solution Explorer でプロジェクト ノードを右クリックし、[Properties(プロパティ)]をクリックします。
  2. 左側の[Build]タブをクリックし、「Condal compilation symbols」テキストボックスに「EOS_PLATFORM_WINDOWS_32(または「EOS_PLATFORM_WINDOWS_64」)」を入力します。それに応じてプラットフォーム ターゲットを合わせる必要があるため、ドロップダウンから x86(またはx64)を選択します。
  3. 最後に、SDKのバイナリをアプリケーションに含める必要があるため、ダウンロードした C# SDKのSDK\Bin サブフォルダにあるEOSSDK-Win32-Shipping.dll (またはEOSSDK-Win64-Shipping.dll)ファイルを、Solution Explorer のプロジェクトに直接コピーします。
  4. Solution Explorer で追加されたDLLをクリックして、「Copy to Output Directory(出力ディレクトリにコピー)」プロパティを「Copy if newer(新しい場合はコピー)」に変更します。
  5. Ctrl+Shift+B でソリューションをビルドし、すべてがコンパイルされていることを確認します。エラーがあれば修正します。

Model-View-ViewModel の概要

物事を明確にし、モジュール化するために、Model-View-ViewModel アーキテクチャ パターンを使ってソリューションを設定し、各サービスの機能を構築していきます。MVVMを使ったことがない方でも安心してください。わかりやすく説明します。使用するコンセプトの概要は以下のとおりです。
 
  • Commands - コードを実行するためにViews(ビュー)のボタンにバインドする機能
  • Converters - ある型(文字列など)を、必要に応じて異なる型(ブーリアンなど)に変換するヘルパー機能(例:空の文字列を偽のブーリアン値に変換する)。
  • Helpers - - 一般的なヘルパー機能。例えば、ViewModel の基本的な値が変更されたときに UI を更新するための INotifyPropertyChanged の実装など。
  • Services - 機能のメインクラス
  • ViewModels - Viewsで使用される値とコマンドインスタンスのコンテナ。
  • Views - ユーザーに提示するUIコンポーネント

これらのコンセプトを実装することで、それぞれの機能が明確になっていきます。

EOS SDK の初期化

ソリューションのセットアップを完了するために、EOS SDKの初期化コードを実装し、今後の記事でEOSサービスの実装を追加できるようにします。

EOSの機能は、サービスごとに機能を論理的にグループ化したインターフェースにまとめられています。EOSの一次インターフェースは Platform インターフェースと呼ばれ、他のすべてのインターフェースへのアクセスを提供しています。そのため、まず SDK を初期化してアプリケーション全体で使用可能な Platform インターフェースのインスタンスを作成します。
 
  1. まず、プロジェクトのルートに ApplicationSettings.cs という新しいクラスを追加します。各設定には、必ずSDKの認証情報を入力してください。
public class ApplicationSettings
{
    public string ProductId { get; private set; } = "";

    public string ClientId { get; private set; } = "";
    public string ClientSecret { get; private set; } = "";

    public string SandboxId { get; private set; } = "";

    public string DeploymentId { get; private set; } = "";

    public PlatformInterface PlatformInterface { get; set; }

    public void Initialize(Dictionary<string, string> commandLineArgs)
    {
        // Use command line arguments if passed
        ProductId = commandLineArgs.ContainsKey("-productid") ? commandLineArgs.GetValueOrDefault("-productid") : ProductId;
        SandboxId = commandLineArgs.ContainsKey("-sandboxid") ? commandLineArgs.GetValueOrDefault("-sandboxid") : SandboxId;
        DeploymentId = commandLineArgs.ContainsKey("-deploymentid") ? commandLineArgs.GetValueOrDefault("-deploymentid") : DeploymentId;
        ClientId = commandLineArgs.ContainsKey("-clientid") ? commandLineArgs.GetValueOrDefault("-clientid") : ClientId;
        ClientSecret = commandLineArgs.ContainsKey("-clientsecret") ? commandLineArgs.GetValueOrDefault("-clientsecret") : ClientSecret;
    }
}

 
  1. このファイルには、SDK 認証情報、メインの PlatformInterface インスタンス (他のすべてのサービスへのアクセスを提供するSDK のコア) 、およびコマンドラインの初期化コードが含まれます。Epic Games Store にゲームを公開する場合は、ユーザーIDなどの情報をコマンドライン引数でゲームに渡します。今後もこのファイルを使用して、初期化変数 (認証情報のタイプやスコープなど) をトラッキングしていきます。
  2. 不明なタイプは、関連する using ステートメントをファイルに追加して解決します。
  3. 次に、ApplicationResources.xamlという名前の新しい Resource Dictionary(WPF)ファイルをプロジェクトのルートに追加します。ここでは空欄にしておいても構いませんが、今後の記事で使用するコンバーターへの参照を追加するために使用します。App.xaml を開き、以下の XAML でこの新しい ResourceDictionary を追加します。
<Application.Resources>
    <ResourceDictionary>
        <ResourceDictionary.MergedDictionaries>
            <ResourceDictionary Source="/ApplicationResources.xaml"/>
        </ResourceDictionary.MergedDictionaries>
    </ResourceDictionary>
</Application.Resources>

 
  1. App.xaml.cs を開き、以下のコードを挿入して、ApplicationSettings へのスタティックリファレンスを作成し、渡されたコマンドライン引数を解析します。

public partial class App : Application
{
    public static ApplicationSettings Settings { get; set; }

    protected override void OnStartup(StartupEventArgs e)
    {
        // Get command line arguments (if any) to overwrite default settings
        var commandLineArgsDict = new Dictionary<string, string>();
        for (int index = 0; index < e.Args.Length; index += 2)
        {
            commandLineArgsDict.Add(e.Args[index], e.Args[index + 1]);
        }

        Settings = new ApplicationSettings();
        Settings.Initialize(commandLineArgsDict);

        base.OnStartup(e);
    }
}

 
  1. 最後に、MainWindow.xaml.cs を使用して SDK の設定と初期化を行います。まず、ゲームのティックをシミュレートするためのタイマーを追加します。EOS SDK はこのタイマーを使って、サービスコールの呼び出しメソッドを起動します。

private DispatcherTimer updateTimer;
private const float updateFrequency = 1 / 30f;

 
  1. Window Closingイベントのイベント ハンドラを追加して、PlatformInterface インスタンスを解放し、シャットダウン プロセスを完了します。

public MainWindow()
{
    InitializeComponent();

    Closing += MainWindow_Closing;
}

private void MainWindow_Closing(object sender, System.ComponentModel.CancelEventArgs e)
{
    App.Settings.PlatformInterface.Release();
    App.Settings.PlatformInterface = null;

    _ = PlatformInterface.Shutdown();
}

 
  1. InitializeApplication() 関数を定義し、MainWindow がインスタンス化されたときに呼び出すことで、SDKをセットアップします。 

private void InitializeApplication()
{
    var initializeOptions = new InitializeOptions()
    {
        ProductName = "EOSCSharpSample",
        ProductVersion = "1.0.0"
    };

    var result = PlatformInterface.Initialize(initializeOptions);
    Debug.WriteLine($"Initialize: {result}");

    _ = LoggingInterface.SetLogLevel(LogCategory.AllCategories, LogLevel.Info);
    _ = LoggingInterface.SetCallback((LogMessage message) => Debug.WriteLine($"[{message.Level}] {message.Category} - {message.Message}"));

    var options = new Options()
    {
        ProductId = App.Settings.ProductId,
        SandboxId = App.Settings.SandboxId,
        ClientCredentials = new ClientCredentials()
        {
            ClientId = App.Settings.ClientId,
            ClientSecret = App.Settings.ClientSecret
        },
        DeploymentId = App.Settings.DeploymentId,
        Flags = PlatformFlags.DisableOverlay,
        IsServer = false
    };

    PlatformInterface platformInterface = PlatformInterface.Create(options);

    if (platformInterface == null)
    {
        Debug.WriteLine($"Failed to create platform. Ensure the relevant settings are set.");
    }

    App.Settings.PlatformInterface = platformInterface;

    updateTimer = new DispatcherTimer(DispatcherPriority.Render)
    {
        Interval = new TimeSpan(0, 0, 0, 0, (int)(updateFrequency * 1000))
    };

    updateTimer.Tick += (sender, e) => Update(updateFrequency);
    updateTimer.Start();
}


少し紐解いてみましょう。: 
  • 一般的に、EOSのサービスコールは Options クラスのインスタンス化で構成されており、このクラスは呼び出し先の関数に渡されます。このケースでは、InitializeOptions をインスタンス化して、PlatformInterface の Initialize() 関数に渡しています。
  • この関数はすぐに結果を返しますが、多くの場合、サービスコールはパラメータとしてコールバック関数を受け取り、関数が完了したときにそれが起動されるようになっています。その例は、認証に関する次項でご紹介します。
  • 次に、Logging インターフェースを使用して、ログの受信方法を定義します。今回のサンプルアプリケーションでは、すべてのログメッセージをデバッグ出力ウィンドウに出力するようにしていますが、ここでログをファイルやクラウドサービスに書き出すように設定することもできます。
  • そして、PlatformInterface.Creat eで同じ Options -> call パターンを繰り返して PlatformInterface を作成し、そのインスタンスをApp.Settings に保存してアプリ内で取得できるようにします。
  • 最後に、updateFrequency の間隔(ここでは1/30)でタイマーを設定し、tick イベントハンドラを設定して、タイマーを起動します。一般的ガイドラインは、APIコールバックの応答性を確保するために、この間隔をゲームのティックレートと同じに設定することです。なお、SDK はスレッドセーフではないため、すべての SDK の呼び出しは、初期化したのと同じスレッドから行う必要があります。
 
  1. タイマーの Tick イベントハンドラは、PlatformInterface の Tickメソッドを呼び出し、非同期コールバックが正常に呼び出されるようにします。

private void Update(float updateFrequency)
{
    App.Settings.PlatformInterface?.Tick();
}

 
  1. 最後に、MainWindow のコンストラクタに InitializeApplication() の呼び出しを追加します。

この時点で、アプリケーションをコンパイルして実行することができ、デバッグ出力ウィンドウには、初期化が成功したことを示すいくつかのログメッセージが表示されるはずです。

Initialize: Success
[Info] LogEOSOverlay - Overlay will not load, because it was explicitly disabled when creating the platform
[Info] LogEOSAntiCheat - [AntiCheatClient] Anti-cheat client not available. Verify that the game was started with the correct launcher if you intend to use it.
[Info] LogEOS - Updating Platform SDK Config, Time: 0.368525
[Info] LogEOS - SDK Config Platform Update Request Successful, Time: 0.821195
[Info] LogEOSAnalytics - Start Session (User: ...)
[Warning] LogEOSAnalytics - EOS SDK Analytics disabled for route [1].
[Info] LogEOS - Updating Product SDK Config, Time: 0.866974
[Info] LogEOSAnalytics - Start Session (User: ...)
[Info] LogEOS - SDK Config Product Update Request Successful, Time: 1.350656
[Info] LogEOS - SDK Config Data - Watermark: 82749226
[Info] LogEOS - ScheduleNextSDKConfigDataUpdate - Time: 1.350656, Update Interval: 325.699646

MVVM アーキテクチャの構築

今回の記事を終える前に、今後の記事で追加機能を簡単に挿入できるように、MVVMアーキテクチャをもう少し構築する必要があります。
  1. まず、プロジェクトのルートに「Helpers」というフォルダを作成します。
  2. MVVMの実装を支援する2つのHelperクラスを作成します。このクラスの機能については、他の記事を参照してください。「BindableBase.cs」という新しいクラスを作成します。

public abstract class BindableBase : INotifyPropertyChanged
{
    public event PropertyChangedEventHandler PropertyChanged;

    protected bool SetProperty<T>(ref T storage, T value, [CallerMemberName] string propertyName = null)
    {
        if (Equals(storage, value)) return false;

        storage = value;
        OnPropertyChanged(propertyName);
        return true;
    }

    protected void OnPropertyChanged([CallerMemberName] string propertyName = null)
    {
        var eventHandler = PropertyChanged;
        if (eventHandler != null)
            eventHandler(this, new PropertyChangedEventArgs(propertyName));
    }
}

 
  1. 次に、CommandBase.cs というクラスを作成します。

public class CommandBase : ICommand
{
    public virtual bool CanExecute(object parameter)
    {
        throw new NotImplementedException();
    }

    public virtual void Execute(object parameter)
    {
        throw new NotImplementedException();
    }

    public event EventHandler CanExecuteChanged;
    public void RaiseCanExecuteChanged()
    {
        CanExecuteChanged?.Invoke(this, new EventArgs());
    }
}

 

public class MainViewModel : BindableBase
{
    private string _statusBarText;
    public string StatusBarText
    {
        get { return _statusBarText; }
        set { SetProperty(ref _statusBarText, value); }
    }

    public MainViewModel()
    {
    }
}

 
  1. 次に、ViewModelLocator.csというクラスを作成します。

public static class ViewModelLocator
{
    private static MainViewModel _main;
    public static MainViewModel Main
    {
        get { return _main ??= new MainViewModel(); }
    }
}

 
  1. そして、UIの自動化を支援する Converter をいくつか作成します。プロジェクトのルートに Converters というフォルダを作成し、StringToBooleanConverters.cs というクラスを追加します。

[Bindable(BindableSupport.Default)]
public class StringToBooleanConverter : IValueConverter
{
    public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
    {
        if (value == null || !(value is string))
        {
            return false;
        }

        string stringValue = value as string;

        return !string.IsNullOrWhiteSpace(stringValue.Trim());
    }
    public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
    {
        throw new NotImplementedException();
    }
}

 
  1. StringToVisibilityConverter.cs というクラスを追加します。

[Bindable(BindableSupport.Default)]
public class StringToVisibilityConverter : IValueConverter
{

    public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
    {
        if (value == null || !(value is string))
        {
            return Visibility.Collapsed;
        }

        string stringValue = value as string;

        return string.IsNullOrWhiteSpace(stringValue.Trim()) ? Visibility.Collapsed : (object)Visibility.Visible;
    }
    public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
    {
        throw new NotImplementedException();
    }
}

 
  1. 最後に、プロジェクトのルートにある ApplicationResources.xaml を開き、以下の XAML で置き換えます。なお、プロジェクト名がEOSCSharpSample でない場合は、xmlns:c="... "の次の名前空間を変更する必要があります。

<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
                    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
                    xmlns:c="clr-namespace:EOSCSharpSample.Converters">

    <c:StringToVisibilityConverter x:Key="StringToVisibilityConverter" />
    <c:StringToBooleanConverter x:Key="StringToBooleanConverter" />

</ResourceDictionary>

コードの取得

この記事のコードは以下の通りです。  C# ソリューションの設定セクションのステップ 5 と 9 に従って SDK をソリューションに追加し、ApplicationSettings.cs を編集して SDK の認証情報を含めることを確認してください。

次回の記事では、アカウントサービスを使用して Epic アカウントでのログインを開始します。 このシリーズの最初の記事にあるシリーズ記事一覧 も是非ご覧ください。

    あなたが成功するとき、私たちは成功します

    Epic では、オープンで調和のとれたゲーム コミュニティを信じています。オンライン サービスをすべての人に無料で提供することにより、より多くのデベロッパーがそれぞれのプレイヤー コミュニティにサービスを提供できるようにすることが目標です。