ゲーム固有のデータをクラウドから取得する

Rajen Kishna、Epic Games テクニカル アカウント マネージャー |
2021年11月30日
前の記事で設定されている当社のゲーム サービス認証を使用すると、Title Storage を使用してゲーム固有のデータをクラウドから安全に取得できるようになります。この記事では次の内容を扱います。
 

Title Storage と Player Data Storage の比較

Epic Online Services では、データ用の暗号化されたクラウド ストレージとしてTitle StoragePlayer Data Storage の 2 つが提供されています。暗号化と復号は、ユーザーが指定した暗号化キーを使用して行われるため、クラウドにあるファイルが読み取られることはありません。Title Storage は、すべてのプレイヤーがアクセスできるデータをクラウドに一元的に格納するために使用できます。Title Storage のデータは、デベロッパー ポータル経由で保存されますが、EOS SDK API 経由で取得できます。Title Storage は、ゲームを更新することなくプレイヤーが利用できるようにしたいコンフィギュレーション データがある場合などに便利です。

Player Data Storage は、その名前が示すように、各プレイヤーに固有のデータであり、プレイヤーがログインしている任意のデバイスから API 経由で保存および取得することができます。Player Data Storage を使用して、クラウド保存 (複数のプラットフォームにまたがることも可能) などを実装することができます。次の記事では Player Data Storage 重点を置いて説明します。

使用上の制限事項

安定性と信頼性の高いエコシステムをすべてのユーザーに確実に提供するために、Epic Online Services にはいくつかの「サービス使用上の制限事項」があります。追加の使用上の制限事項が適用されているサービスもあり、Title Storage もそれに該当します。この記事の作成時点では以下の制限事項がありますが、最新情報についてはこのドキュメントを参照してください。
 
  • 1 つのデプロイメント内のすべてのファイルの総サイズが 10GB 以内
  • 1 つのデプロイメント内のすべてのファイルの総数が 100 以内

クライアント ポリシーを変更する

サンプル アプリで Title Storage インターフェース API を呼び出す前に、以下の手順に従って、このサービスへのアクセスを許可するようにクライアント ポリシーを調整する必要があります。
 
  1. デベロッパー ポータル (https://dev.epicgames.com/portal/) にログインします。
  2. 左側のメニューで [Your Product (あなたの製品)] > [Product Settings (製品の設定)] に移動し、[製品の設定] 画面にある [Clients (クライアント)] タブをクリックします。
  3. 使用しているクライアント ポリシーの横にある [...] をクリックし、[Details (詳細)] をクリックします。
 
Developer Portal Client Policy Details
クライアント ポリシーの詳細
 
  1. [Features (機能)] まで下にスクロールし、[Title Storage] の横にあるトグル ボタンをクリックします。
    • 許可されているアクションの一覧が右側に表示されるので、それらを個別にオンにして、クライアントが利用できることをさらにきめ細かく指定することができます。
  2. 「リスト」と「アクセス」の両方の機能を実装するので、その両方のアクションの横にあるボックスをオンにします。
  3. [Save & Exit (保存して終了)] をクリックして確定します。
 
Developer Portal Client Policy Title Storage
機能とアクションが許可されている Title Storage のクライアント ポリシー

デベロッパー ポータルでファイルを設定する

次に、デベロッパー ポータルでファイルを設定します。前述のとおり、Title Storage の API で可能なのはデータのクエリと取得のみであるため、ファイルのアップロードおよび管理はデベロッパー ポータルで行います。
 
  1. 左側のメニューで [Your Product (あなたの製品)] > [Game Services (ゲーム サービス)] > [Title Storage] に移動します。
  2. 以下の項目が表示されています。
    • 追加済みのファイル (およびファイル数)。この画面には、現時点ではおそらく「No files found (ファイルがありません)」と表示されています。
    • 10GB のクォータの使用割合。
    • 今表示されている [Deployment (デプロイメント)]。ここにはおそらく、デフォルトである「Release in Live Sandbox (ライブ サンドボックスでリリース)」のみが表示されますが、デプロイメントを追加していれば、ここで選択できます。
    • ファイルを新規追加するボタン。
    • ドキュメントへのリンク。
 
Developer Portal Title Storage
デベロッパー ポータルでの Title Storage の設定
 
  1. [Add New File (ファイルを新規追加)] ボタンをクリックして、ファイルを新規追加します。

フライアウト メニューに、暗号化キーを入力する画面が表示されます。このキーは 256 ビットの 16 進数の文字列であり、0 ~ 9 の数字と A ~ F の文字を使用して 64 文字で指定します。同じキーをコード内で使用することで、SDK で暗号化と復号を行うことができるようになります。このキーは Epic では保存されず、ファイルを暗号化および復号するために必要であるため、忘れないようにすることが重要です。後で利用できるように、フライアウト メニューに、キーを .txt ファイルとして保存するオプションもあります。
 
  1. このデモでは次のキーを使用しますが、プロダクション環境で SDK を使用する場合は必ずセキュアなキーを使用してください。1111111111111111111111111111111111111111111111111111111111111111
  2. [Next (次へ)] をクリックして、実際のファイルの詳細に移動します。

ここで、64 文字以内のファイル名を指定し、API 経由でファイルをクエリするときに役立つタグを追加し、実際のファイルをアップロードし、オーバーライドを指定することができます。

タグを使用すると、複数のファイルを一度にクエリすることができ、特定のファイル名をゲームでハードコード化する必要がなくなります。Title Storage では複数のタグを追加することができるので、使用可能なファイルをすべてクエリする場合は、すべてのファイルに共通のタグを付けて、そのタグをクエリするだけで済みます。

オーバーライドは、特定の PUID、ID プロバイダの ID (Epic アカウントの ID など)、または EOS クライアントに基づいてファイルの特定のバリアントを使用する場合に役立ちます。オーバーライドを使用すると、例えば、個別のファイルおよびコードやコンフィギュレーションの違いを維持することなく、ゲーム クライアントおよび信頼済みサーバー用の特定のファイルを取得することができます。別の例として、Android と PC で異なる EOS クライアントを使用していて、各デバイスでそのファイルの異なるバージョンを取得している場合にも対応できます。
 
  1. このサンプル アプリのみで使用するファイルを作成しているので、次のように入力します。
    • ファイル名:test_file
    • タグを追加:test_tag ([入力後に [Add (追加)] をクリックします)
    • ファイルをアップロード:任意のファイルをアップロードでき、少量のテキストが入っている単純な .txt ファイルを作成することもできます
    • オーバーライド:そのままにしておきます
  2. [Save & Close (保存して閉じる)] をクリックして送信します。
 
Developer Portal Title Storage Test File
Title Storage に新規ファイルを追加する
 
  1. 最後に、手順 6 と 7 をもう一度繰り返します。
    • 暗号化キー:1111111111111111111111111111111111111111111111111111111111111111
    • ファイル名:another_test
    • タグ:test_tag
    • タグを追加:second_tag
    • ファイルをアップロード:任意のファイル
    • オーバーライド:そのままにしておきます

両方のファイルを使用して、異なるタグでクエリのロジックを検証します。Title Storage の設定画面に 2 つのファイルが表示されているはずです。
 
Developer Portal Title Storage Two Files Added
Title Storage に追加されている 2 つのファイル

EncryptionKey と CacheDirectory を SDK の初期化に追加する

デベロッパー ポータルでポリシーとファイルを設定したので、コードの実装を開始することができます。最初に行う必要があるのは、暗号化キー (前のセクションで指定した) とキャッシュ ディレクトリを SDK の初期化コードに追加して、ファイルを暗号化および復号する方法と、ダウンロードをキャッシュするために使用できるフォルダを指示することです。

Title Storage と Player Data Storage は、指定されたキャッシュ ディレクトリの個別のサブフォルダにキャッシュされるので、SDK の初期化時に設定する必要があるディレクトリは 1 つだけです。ファイル (MD5 ハッシュが同じである) がすでにキャッシュに存在する場合、その後の読み取りリクエストでは、クラウドからそのファイルを再度ダウンロードするのではなく、そのファイルの内容をキャッシュから取得するだけで済みます。
 
  1. 「ApplicationSettings.cs」を開いて、次の 2 つのメンバーを追加します。ここではデフォルトの「Temp」ディレクトリをキャッシュ ディレクトリとして使用していますが、使用しているアプリケーションに読み取りおよび書き込みのアクセス権がある任意のフォルダを指定することができます。

public string EncryptionKey = "1111111111111111111111111111111111111111111111111111111111111111";
public string CacheDirectory = Path.GetTempPath();

 
  1. 「MainWindow.xaml.cs」を開き、オプション変数の初期化にある「IsServer = false」行の直後に、次の 2 行を追加します。

,
EncryptionKey = App.Settings.EncryptionKey,
CacheDirectory = App.Settings.CacheDirectory


このアプリを実行すると、エラーが発生しないことを確認できます。ただし、機能的にはまだ何も変わっていません。

名前またはタグでファイルをクエリする

  1. 「Views」フォルダに「TitleStorageView」という名前で、以下の内容の新規ユーザー コントロールを作成します。

<Grid>
    <Grid.ColumnDefinitions>
        <ColumnDefinition Width="*" />
        <ColumnDefinition Width="Auto" />
    </Grid.ColumnDefinitions>

    <StackPanel Grid.Column="1">
        <TextBox x:Name="TitleStorageFileNameTextBox" Margin="2" TextChanged="TitleStorageFileNameTextBox_TextChanged" Text="{Binding TitleStorageFileName, UpdateSourceTrigger=PropertyChanged}" />
        <Button Width="100" Height="23" Margin="2" Content="Query file" Command="{Binding TitleStorageQueryFile}" />
        <TextBox x:Name="TitleStorageTagTextBox" Margin="2" TextChanged="TitleStorageTagTextBox_TextChanged" Text="{Binding TitleStorageTag, UpdateSourceTrigger=PropertyChanged}" />
        <Button Width="100" Height="23" Margin="2" Content="Query by tag" Command="{Binding TitleStorageQueryFileList}" />
        <Button Width="100" Height="23" Margin="2" Content="Download file" Command="{Binding TitleStorageReadFile}" />
    </StackPanel>

    <ListView x:Name="TitleStorageFilesListView" Grid.Column="0" Margin="2" ItemsSource="{Binding TitleStorageFiles}" SelectedItem="{Binding SelectedTitleStorageFile}" SelectionChanged="TitleStorageFilesListView_SelectionChanged">
        <ListView.View>
            <GridView>
                <GridViewColumn Header="Filename" Width="150" DisplayMemberBinding="{Binding Filename}">
                    <GridViewColumn.HeaderContainerStyle>
                        <Style TargetType="{x:Type GridViewColumnHeader}">
                            <Setter Property="HorizontalContentAlignment" Value="Left" />
                        </Style>
                    </GridViewColumn.HeaderContainerStyle>
                </GridViewColumn>
                <GridViewColumn Header="MD5Hash" Width="250" DisplayMemberBinding="{Binding MD5Hash}">
                    <GridViewColumn.HeaderContainerStyle>
                        <Style TargetType="{x:Type GridViewColumnHeader}">
                            <Setter Property="HorizontalContentAlignment" Value="Left" />
                        </Style>
                    </GridViewColumn.HeaderContainerStyle>
                </GridViewColumn>
                <GridViewColumn Header="FileSizeBytes" Width="100" DisplayMemberBinding="{Binding FileSizeBytes}">
                    <GridViewColumn.HeaderContainerStyle>
                        <Style TargetType="{x:Type GridViewColumnHeader}">
                            <Setter Property="HorizontalContentAlignment" Value="Left" />
                        </Style>
                    </GridViewColumn.HeaderContainerStyle>
                </GridViewColumn>
                <GridViewColumn Header="UnencryptedDataSizeBytes" Width="150" DisplayMemberBinding="{Binding UnencryptedDataSizeBytes}">
                    <GridViewColumn.HeaderContainerStyle>
                        <Style TargetType="{x:Type GridViewColumnHeader}">
                            <Setter Property="HorizontalContentAlignment" Value="Left" />
                        </Style>
                    </GridViewColumn.HeaderContainerStyle>
                </GridViewColumn>
            </GridView>
        </ListView.View>
    </ListView>
</Grid>

 
  • クエリで返されたファイルを ListView を使用して表示する
  • ファイル名またはタグでクエリするボタンが付いている 2 つの TextBox がある
  • 最後に、ListView で選択した実際のファイルをダウンロードするボタンがある
 
  1. 「TitleStorageView.xaml.cs」を開いて、ViewModel をアタッチし、UI イベントとハンドラを追加します。

public partial class TitleStorageView :UserControl
{
    public TitleStorageViewModel ViewModel { get { return ViewModelLocator.TitleStorage; } }

    public TitleStorageView()
    {
        InitializeComponent();
        DataContext = ViewModel;
    }

    private void TitleStorageFilesListView_SelectionChanged(object sender, SelectionChangedEventArgs e)
    {
    }

    private void TitleStorageFileNameTextBox_TextChanged(object sender, TextChangedEventArgs e)
    {
        ViewModel.TitleStorageQueryFile.RaiseCanExecuteChanged();
    }

    private void TitleStorageTagTextBox_TextChanged(object sender, TextChangedEventArgs e)
    {
        ViewModel.TitleStorageQueryFileList.RaiseCanExecuteChanged();
    }
}

 
  1. 「TitleStorageViewModel.cs」クラスを「ViewModels」フォルダに追加します。

public class TitleStorageViewModel :BindableBase
{
    private ObservableCollection<FileMetadata> _titleStorageFiles;
    public ObservableCollection<FileMetadata> TitleStorageFiles
    {
        get { return _titleStorageFiles; }
        set { SetProperty(ref _titleStorageFiles, value); }
    }

    private FileMetadata _selectedTitleStorageFile;
    public FileMetadata SelectedTitleStorageFile
    {
        get { return _selectedTitleStorageFile; }
        set { SetProperty(ref _selectedTitleStorageFile, value); }
    }

    private string _titleStorageFileName;
    public string TitleStorageFileName
    {
        get { return _titleStorageFileName; }
        set { SetProperty(ref _titleStorageFileName, value); }
    }

    private string _titleStorageTag;
    public string TitleStorageTag
    {
        get { return _titleStorageTag; }
        set { SetProperty(ref _titleStorageTag, value); }
    }
}

 
  1. 「ViewModelLocator.cs」で、TitleStorageViewModel クラスの参照を追加します。

private static TitleStorageViewModel _titleStorage;
public static TitleStorageViewModel TitleStorage
{
    get { return _titleStorage ??= new TitleStorageViewModel(); }
}

 
  1. 「TitleStorageService.cs」クラスを「Services」フォルダに追加して、クエリのロジックを保持します。

public static class TitleStorageService
{
    public static void QueryFile(string fileName)
    {
        var queryFileOptions = new QueryFileOptions()
        {
            LocalUserId = ProductUserId.FromString(ViewModelLocator.Main.ProductUserId),
            Filename = fileName
        };

        ViewModelLocator.Main.StatusBarText = $"Querying title storage file <{queryFileOptions.Filename}>...";

        App.Settings.PlatformInterface.GetTitleStorageInterface()
.QueryFile(queryFileOptions, null, (QueryFileCallbackInfo queryFileCallbackInfo) =>
        {
            Debug.WriteLine($"QueryFile {queryFileCallbackInfo.ResultCode}");

            if (queryFileCallbackInfo.ResultCode == Result.Success)
            {
                var copyFileMetadataByFilenameOptions = new CopyFileMetadataByFilenameOptions()
                {
                    LocalUserId = ProductUserId.FromString(ViewModelLocator.Main.ProductUserId),
                    Filename = fileName
                };
                var result = App.Settings.PlatformInterface.GetTitleStorageInterface()
.CopyFileMetadataByFilename(copyFileMetadataByFilenameOptions, out var metadata);

                if (result == Result.Success)
                {
                    ViewModelLocator.TitleStorage.TitleStorageFiles.Add(metadata);
                }
            }

            ViewModelLocator.Main.StatusBarText = string.Empty;
        });
    }

    public static void QueryFileList(string tag)
    {
        var queryFileListOptions = new QueryFileListOptions()
        {
            LocalUserId = ProductUserId.FromString(ViewModelLocator.Main.ProductUserId),
            ListOfTags = new string[] { tag }
        };

        ViewModelLocator.Main.StatusBarText = $"Querying title storage files by tag <{tag}>...";

        App.Settings.PlatformInterface.GetTitleStorageInterface()
.QueryFileList(queryFileListOptions, null, (QueryFileListCallbackInfo queryFileListCallbackInfo) =>
        {
            Debug.WriteLine($"QueryFileList {queryFileListCallbackInfo.ResultCode}");

            if (queryFileListCallbackInfo.ResultCode == Result.Success)
            {
                for (uint i = 0; i < queryFileListCallbackInfo.FileCount; i++)
                {
                    var copyFileMetadataAtIndexOptions = new CopyFileMetadataAtIndexOptions()
                    {
                        Index = i,
                        LocalUserId = ProductUserId.FromString(ViewModelLocator.Main.ProductUserId)
                    };
                    var result = App.Settings.PlatformInterface.GetTitleStorageInterface()
.CopyFileMetadataAtIndex(copyFileMetadataAtIndexOptions, out var metadata);

                    if (result == Result.Success)
                    {
                        ViewModelLocator.TitleStorage.TitleStorageFiles.Add(metadata);
                    }
                }
            }

            ViewModelLocator.Main.StatusBarText = string.Empty;
        });
    }
}

   
  1. 「TitleStorageQueryFileCommand.cs」クラスを「Commands」フォルダに追加します。

public class TitleStorageQueryFileCommand :CommandBase
{
    public override bool CanExecute(object parameter)
    {
        return !string.IsNullOrWhiteSpace(ViewModelLocator.Main.ProductUserId) && !string.IsNullOrWhiteSpace(ViewModelLocator.TitleStorage
.TitleStorageFileName);
    }

    public override void Execute(object parameter)
    {
        ViewModelLocator.TitleStorage.TitleStorageFiles = new ObservableCollection<FileMetadata>();
        TitleStorageService.QueryFile(ViewModelLocator.TitleStorage
.TitleStorageFileName);
    }
}

 
  1. 「TitleStorageQueryFileListCommand.cs」クラスを「Commands」フォルダに追加します。

public class TitleStorageQueryFileListCommand :CommandBase
{
    public override bool CanExecute(object parameter)
    {
        return !string.IsNullOrWhiteSpace(ViewModelLocator.Main.ProductUserId) && !string.IsNullOrWhiteSpace(ViewModelLocator
.TitleStorage.TitleStorageTag);
    }

    public override void Execute(object parameter)
    {
        ViewModelLocator.TitleStorage.TitleStorageFiles = new ObservableCollection<FileMetadata>();
        TitleStorageService.QueryFileList(ViewModelLocator.TitleStorage
.TitleStorageTag);
    }
}

 
  1. 「TitleStorageViewModel.cs」を開き、2 つのコマンドを宣言し、インスタンス化します。

public TitleStorageQueryFileCommand TitleStorageQueryFile { get; set; }
public TitleStorageQueryFileListCommand TitleStorageQueryFileList { get; set; }

public TitleStorageViewModel()
{
    TitleStorageQueryFile = new TitleStorageQueryFileCommand();
    TitleStorageQueryFileList = new TitleStorageQueryFileListCommand();
}

 
  1. Connect インターフェースを使用して正常にログインした後に Title Storage の API を呼び出すことができるように、「ViewModelLocator.cs」にある RaiseConnectCanExecuteChanged() メソッドに次の 2 行を追加します。

TitleStorage.TitleStorageQueryFile.RaiseCanExecuteChanged();
TitleStorage.TitleStorageQueryFileList.RaiseCanExecuteChanged();

 
  1. 最後に、「MainWindow.xaml」にある TabControl に TitleStorageView を追加します。

<TabItem x:Name="TitleStorage" Header="Title Storage">
    <views:TitleStorageView />
</TabItem>


これで、このサンプル アプリを実行して Auth と Connect で認証されると、[Title Storage] タブに移動して、「test_file」または「another_test」のいずれかのファイル名でファイルをクエリできるようになりました。また、タグでクエリすることもでき、「test_tag」を使用すると両方のファイルが返されます。
 
App Title Storage Queried Files
タグで問い合わせされる Title Storage のファイル

ファイルを読み取る

次に、クラウド ストレージからのファイルの読み取りを実装します。この実装はゲームで何が必要とされるかによって異なりますが、ここでは、キャッシュ ディレクトリとして使用しているのと同じ「Temp」ディレクトリへのファイルの単純なダウンロードを行います。
 
  1. 次のメソッドを TitleStorageService.cs に追加します。

public static void ReadFile(FileMetadata fileMetadata)
{
    var readFileOptions = new ReadFileOptions()
    {
        LocalUserId = ProductUserId.FromString(ViewModelLocator.Main.ProductUserId),
        Filename = fileMetadata.Filename,
        ReadChunkLengthBytes = 1048576,
        ReadFileDataCallback = (ReadFileDataCallbackInfo readFileDataCallbackInfo) =>
        {
            using var fs = new FileStream($"{App.Settings.CacheDirectory}{readFileDataCallbackInfo.Filename}", FileMode.Append, FileAccess.Write);
            fs.Write(readFileDataCallbackInfo.DataChunk, 0, readFileDataCallbackInfo.DataChunk.Length);
            return ReadResult.RrContinuereading;
        },
        FileTransferProgressCallback = (FileTransferProgressCallbackInfo fileTransferProgressCallbackInfo) =>
        {
            var percentComplete = (double)fileTransferProgressCallbackInfo.BytesTransferred / (double)fileTransferProgressCallbackInfo.TotalFileSizeBytes * 100;
            ViewModelLocator.Main.StatusBarText = $"Downloading file <{fileTransferProgressCallbackInfo.Filename}> ({System.Math.Ceiling.(percentComplete)}%)...";
        }
    };

    ViewModelLocator.Main.StatusBarText = $"Downloading file <{readFileOptions.Filename}> (creating buffer)...";

    var fileTransferRequest = App.Settings.PlatformInterface.GetTitleStorageInterface().ReadFile(readFileOptions, null, (ReadFileCallbackInfo readFileCallbackInfo) =>
    {
        Debug.WriteLine($"ReadFile {readFileCallbackInfo.ResultCode}");

        if (readFileCallbackInfo.ResultCode == Result.Success)
        {
            Debug.WriteLine($"Successfully downloaded {readFileCallbackInfo.Filename} to {App.Settings.CacheDirectory}.");
            ViewModelLocator.Main.StatusBarText = string.Empty;
        }
    });

    if (fileTransferRequest == null)
    {
        Debug.WriteLine("Error downloading file:bad handle");
        ViewModelLocator.Main.StatusBarText = string.Empty;
    }
}
 
  • このドキュメントで説明されているように、TitleStorage.ReadFile はファイルをチャンクに分割して読み取ります。TitleStorage.ReadFileOptions では、チャンク サイズだけでなく、データを読み取るコールバック メソッドと進捗状況を報告するもう 1 つのコールバックも指定します。
  • 「Temp」フォルダ (デフォルトでは「C:\Users\<Windows_User>\AppData\Local\Temp\」、つまり「%Temp%」) を使用して、ファイルを格納します。
 
  1. 「TitleStorageReadFileCommand.cs」クラスを「Commands」フォルダに追加します。

public class TitleStorageReadFileCommand :CommandBase
{
    public override bool CanExecute(object parameter)
    {
        return ViewModelLocator.TitleStorage.SelectedTitleStorageFile != null;
    }

    public override void Execute(object parameter)
    {
        TitleStorageService.ReadFile(ViewModelLocator.TitleStorage
.SelectedTitleStorageFile);
    }
}

 
  1. 「TitleStorageViewModel.cs」を開き、新しいコマンドを宣言し、インスタンス化します。

public TitleStorageQueryFileCommand TitleStorageQueryFile { get; set; }
public TitleStorageQueryFileListCommand TitleStorageQueryFileList { get; set; }
public TitleStorageReadFileCommand TitleStorageReadFile { get; set; }

public TitleStorageViewModel()
{
    TitleStorageQueryFile = new TitleStorageQueryFileCommand();
    TitleStorageQueryFileList = new TitleStorageQueryFileListCommand();
    TitleStorageReadFile = new TitleStorageReadFileCommand();
}

 
  1. 最後に、「TitleStorageView.xaml.cs」を開き、TitleStorageFilesListView_SelectionChanged() メソッドに次の行を追加します。

ViewModel.TitleStorageReadFile.RaiseCanExecuteChanged();

これで、このサンプル アプリを実行し。認証され、ファイルをクエリし、ファイルをダウンロードすると、そのファイルがローカル ディスク上の「%Temp%」ディレクトリに置かれるようになりました。この例では、「test_file」や「another_test」に .txt ファイル拡張子を追加してファイル名を変更すれば、そのファイルを開いて内容を確認できるようになります。
 
App Title Storage Downloaded File
Title Storage からダウンロードされたファイル

コードを入手する

この記事に記載しているコードは次の場所から取得できます。GitHub リポジトリに記載されている使用方法に従って、ダウンロードしたコードを設定してください。
Title Storage と Player Data Storage の違いについて説明したので、次の記事では、Player Data Storage を使用してプレイヤー固有のデータの格納と取得を実装します。

このシリーズの記事の一覧については、「シリーズの記事リスト」を参照してください。フィードバックや質問がある場合は、コミュニティ フォーラムにアクセスしてお知らせください。

    デベロッパーみなさんの成功が、Epic の成功です

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