> ## Documentation Index
> Fetch the complete documentation index at: https://devdocs.xbox.com/llms.txt
> Use this file to discover all available pages before exploring further.

# 實體快速入門

> 在 Unity C# 中開始使用 PlayFab 實體：進行驗證以取得 EntityKey，然後透過 SDK 範例讀取和寫入實體物件和實體檔案。

本實體快速入門示範如何使用實體物件和實體檔案。

如需從舊版帳戶和資料系統移轉至 PlayFab 實體的相關資訊，請參閱[實體移轉資訊](/zh-TW/services/playfab/live-service-management/game-configuration/entities/migration-information)。

## 需求

* [PlayFab 開發人員帳戶](https://developer.playfab.com)。
* 已安裝的 Unity 編輯器。如需安裝 Unity 編輯器的相關資訊，請參閱 Unity 文件中的 [Installing Unity](https://docs.unity3d.com/Manual/GettingStartedInstallingUnity.html)。您也可以使用 Visual Studio 功能安裝程式安裝 Unity 版本。

<Note>
  PlayFab Unity3D SDK 支援 Unity 編輯器 5.3 版及更新版本。
</Note>

* Unity 專案。如需建立 Unity 專案的相關資訊，請參閱[快速入門指南](https://docs.unity3d.com/Manual/Quickstart3D.html)。

<Note>
  如果您不熟悉 Unity，最近的安裝套件提供安裝遊戲建立逐步解說的選項。您可以使用其中一個逐步解說來建立範例遊戲，以用於下列快速入門。
</Note>

* PlayFab Unity3D SDK。

本文中的 C# 範例是針對 Unity SDK 撰寫的。Unity SDK 使用事件驅動模型來處理非同步工作。若要使用 C# SDK 執行範例程式碼，您必須修改程式碼以使用非同步 Task 模型。必須修改的方法會在簽章中的方法名稱後附加 Async。例如，Unity SDK 中的 SetObject 在 C# SDK 中會變成 SetObjectAsync。如需詳細資訊，請參閱[使用 async 和 await 進行非同步程式設計](https://learn.microsoft.com/en-us/dotnet/csharp/programming-guide/concepts/async/)。

## 用語

實體是任何可以包含資料的 PlayFab 概念。內建實體類型包括：

* **title** - 遊戲包含可供所有玩家使用的全域資訊。這類似於 [TitleData](xref:titleid.playfabapi.com.client.title-widedatamanagement.gettitledata)。它由遊戲/應用程式的遊戲識別碼 (`TitleId`) 識別。
* **master\_player\_account** - 此實體類型可讓您在命名空間內的多個遊戲之間共用玩家的相關資訊。它由玩家的玩家識別碼 (`PlayFabId`) 識別，此識別碼會在任何登入或任何擷取玩家帳戶之帳戶資訊的呼叫中傳回 (例如 PlayFab Client API [GetAccountInfo](xref:titleid.playfabapi.com.client.accountmanagement.getaccountinfo))。
* **title\_player\_account** - 識別包含目前遊戲之一些資訊的玩家帳戶。它由您在任何登入時，於 [EntityKey](xref:titleid.playfabapi.com.authentication.authentication.getentitytoken#entitykey) 物件內取回的實體識別碼 (`EntityKey.Id`) 識別。
* **character** - 識別玩家擁有的角色，其中包含您可以擷取的資訊。它由角色的角色識別碼 (`CharacterId`) 識別。

如需內建實體類型的詳細資訊，請參閱[可用的內建實體類型](/zh-TW/services/playfab/live-service-management/game-configuration/entities/available-built-in-entity-types)。

## 實體初始化

若要呼叫任何實體 API，您必須取得實體 `ID` 和實體 `Type`。您可以使用 `ID` 和 `Type` 呼叫其他實體 API 方法。這些是 [EntityKey](xref:titleid.playfabapi.com.authentication.authentication.getentitytoken#entitykey) 物件的成員。

您可以呼叫任何登入方法 (例如 `LoginWithCustomID`) 來完成此操作。

```csharp theme={null}
    void Login()
    {
        var request = new PlayFab.ClientModels.LoginWithCustomIDRequest
        {
            CustomId = SystemInfo.deviceUniqueIdentifier,
            CreateAccount = true,
        };
        PlayFabClientAPI.LoginWithCustomID(request, OnLogin, OnSharedFailure);
    }

    void OnLogin(PlayFab.ClientModels.LoginResult result)
    {
        entityId = result.EntityToken.Entity.Id;
        // The expected entity type is title_player_account.
        entityType = result.EntityToken.Entity.Type;
    }
```

您也可以呼叫 [GetEntityToken](xref:titleid.playfabapi.com.authentication.authentication.getentitytoken) 方法來取得實體 `ID` 和實體 `Type`。

```csharp theme={null}
PlayFabAuthenticationAPI.GetEntityToken(new GetEntityTokenRequest(),
(entityResult) =>
{
    var entityId = entityResult.Entity.Id;
    var entityType = entityResult.Entity.Type;
}, OnPlayFabError); // Define your own OnPlayFabError function to report errors
```

從用戶端呼叫時，這通常代表已登入的玩家。從遊戲伺服器呼叫時，這代表您的遊戲。

## 實體物件

實體物件可讓您讀取和寫入附加至實體的小型 JSON 可序列化物件。所有實體類型都支援 `GetObjects` 和 `SetObjects` 方法。

下列程式碼片段示範如何在 `title_player_account` 實體上設定和讀取 `Object`。

使用 [SetObjects](xref:titleid.playfabapi.com.data.object.setobjects) 方法，在玩家或遊戲上設定實體物件。

```csharp theme={null}
var data = new Dictionary<string, object>()
{
    {"Health", 100},
    {"Mana", 10000}
};
var dataList = new List<SetObject>()
{
    new SetObject()
    {
        ObjectName = "PlayerData",
        DataObject = data
    },
    // A free-tier customer may store up to 3 objects on each entity
};

PlayFabDataAPI.SetObjects(new SetObjectsRequest()
{
    Entity = new EntityKey {Id = entityId, Type = entityType}, // Saved from GetEntityToken, or a specified key created from a titlePlayerId, CharacterId, etc
    Objects = dataList,
}, (setResult) => {
    Debug.Log(setResult.ProfileVersion);
}, OnPlayFabError);
```

使用 [GetObjects](xref:titleid.playfabapi.com.data.object.getobjects) 方法，擷取玩家或遊戲上的實體物件。

```csharp theme={null}
var getRequest = new GetObjectsRequest {Entity = new EntityKey {Id = entityId, Type = entityType}};
PlayFabDataAPI.GetObjects(getRequest,
    result => { var objs = result.Objects; },
    OnPlayFabError
);
```

## 實體檔案

實體檔案可讓您讀取和寫入附加至實體的任何格式檔案。

使用 [GetFiles](xref:titleid.playfabapi.com.data.file.getfiles) 方法擷取實體檔案。

```csharp theme={null}
    void LoadAllFiles()
    {
        if (GlobalFileLock != 0)
            throw new Exception("This example overly restricts file operations for safety. Careful consideration must be made when doing multiple file operations in parallel to avoid conflict.");

        GlobalFileLock += 1; // Start GetFiles
        var request = new PlayFab.DataModels.GetFilesRequest { Entity = new PlayFab.DataModels.EntityKey { Id = entityId, Type = entityType } };
        PlayFabDataAPI.GetFiles(request, OnGetFileMeta, OnSharedFailure);
    }
```

使用 [initiatefileuploads](xref:titleid.playfabapi.com.data.file.initiatefileuploads) 方法，起始將檔案上傳至實體設定檔。

```csharp theme={null}
    void UploadFile(string fileName)
    {
        if (GlobalFileLock != 0)
            throw new Exception("This example overly restricts file operations for safety. Careful consideration must be made when doing multiple file operations in parallel to avoid conflict.");

        ActiveUploadFileName = fileName;

        GlobalFileLock += 1; // Start InitiateFileUploads
        var request = new PlayFab.DataModels.InitiateFileUploadsRequest
        {
            Entity = new PlayFab.DataModels.EntityKey { Id = entityId, Type = entityType },
            FileNames = new List<string> { ActiveUploadFileName },
        };
        PlayFabDataAPI.InitiateFileUploads(request, OnInitFileUpload, OnInitFailed);
    }
```

使用 [AbortFileUploads](xref:titleid.playfabapi.com.data.file.abortfileuploads) 方法，中止擱置中的實體設定檔檔案上傳。

```csharp theme={null}
    void OnInitFailed(PlayFabError error)
    {
        if (error.Error == PlayFabErrorCode.EntityFileOperationPending)
        {
            // This is an error you should handle when calling InitiateFileUploads, but your resolution path may vary
            GlobalFileLock += 1; // Start AbortFileUploads
            var request = new PlayFab.DataModels.AbortFileUploadsRequest
            {
                Entity = new PlayFab.DataModels.EntityKey { Id = entityId, Type = entityType },
                FileNames = new List<string> { ActiveUploadFileName },
            };
            PlayFabDataAPI.AbortFileUploads(request, (result) => { GlobalFileLock -= 1; UploadFile(ActiveUploadFileName); }, OnSharedFailure); GlobalFileLock -= 1; // Finish AbortFileUploads
            GlobalFileLock -= 1; // Failed InitiateFileUploads
        }
        else
            OnSharedFailure(error);
    }
```

使用 [FinalizeFileUploads](xref:titleid.playfabapi.com.data.file.finalizefileuploads) 方法，完成實體設定檔的檔案上傳。在不可分割的上傳作業成功完成之前，實體系統不會將檔案上傳視為已完成，也不會向其他呼叫端反映任何變更。

```csharp theme={null}
    void FinalizeUpload(byte[] data)
    {
        GlobalFileLock += 1; // Start FinalizeFileUploads
        var request = new PlayFab.DataModels.FinalizeFileUploadsRequest
        {
            Entity = new PlayFab.DataModels.EntityKey { Id = entityId, Type = entityType },
            FileNames = new List<string> { ActiveUploadFileName },
        };
        PlayFabDataAPI.FinalizeFileUploads(request, OnUploadSuccess, OnSharedFailure);
        GlobalFileLock -= 1; // Finish SimplePutCall
    }
```

下列範例示範完整的實體檔案迴圈，從登入、載入檔案，到上傳新檔案。

步驟如下：

* 登入並擷取實體 `ID` 和實體 `Type`。
* 初始化不可分割的上傳作業。
* 上傳所有檔案。
* 完成不可分割的上傳作業。

為了簡單起見，此範例一次儲存一個檔案，但檔案可以成組地以不可分割的方式上傳。

此範例假設您熟悉 Unity 3D 引擎的使用方式。

```csharp theme={null}
#if !DISABLE_PLAYFABENTITY_API && !DISABLE_PLAYFABCLIENT_API

using PlayFab;
using PlayFab.Internal;
using System;
using System.Collections.Generic;
using System.Text;
using UnityEngine;

public class EntityFileExample : MonoBehaviour
{
    public string entityId; // Id representing the logged in player
    public string entityType; // entityType representing the logged in player
    private readonly Dictionary<string, string> _entityFileJson = new Dictionary<string, string>();
    private readonly Dictionary<string, string> _tempUpdates = new Dictionary<string, string>();
    public string ActiveUploadFileName;
    public string NewFileName;
    // GlobalFileLock provides is a simplistic way to avoid file collisions, specifically designed for this example.
    public int GlobalFileLock = 0;

    void OnSharedFailure(PlayFabError error)
    {
        Debug.LogError(error.GenerateErrorReport());
        GlobalFileLock -= 1;
    }

    // OnGUI provides a way to build a Unity GUI entirely within script.
    // Your GUI will be game-specific.
    void OnGUI()
    {
        if (!PlayFabClientAPI.IsClientLoggedIn() && GUI.Button(new Rect(0, 0, 100, 30), "Login"))
            Login();
        if (PlayFabClientAPI.IsClientLoggedIn() && GUI.Button(new Rect(0, 0, 100, 30), "LogOut"))
            PlayFabClientAPI.ForgetAllCredentials();

        if (PlayFabClientAPI.IsClientLoggedIn() && GUI.Button(new Rect(100, 0, 100, 30), "(re)Load Files"))
            LoadAllFiles();

        if (PlayFabClientAPI.IsClientLoggedIn())
        {
            // Display existing files
            _tempUpdates.Clear();
            var index = 0;
            foreach (var each in _entityFileJson)
            {
                GUI.Label(new Rect(100 * index, 60, 100, 30), each.Key);
                var tempInput = _entityFileJson[each.Key];
                var tempOutput = GUI.TextField(new Rect(100 * index, 90, 100, 30), tempInput);
                if (tempInput != tempOutput)
                    _tempUpdates[each.Key] = tempOutput;
                if (GUI.Button(new Rect(100 * index, 120, 100, 30), "Save " + each.Key))
                    UploadFile(each.Key);
                index++;
            }
            // Apply any changes
            foreach (var each in _tempUpdates)
                _entityFileJson[each.Key] = each.Value;

            // Add a new file
            NewFileName = GUI.TextField(new Rect(100 * index, 60, 100, 30), NewFileName);
            if (GUI.Button(new Rect(100 * index, 90, 100, 60), "Create " + NewFileName))
                UploadFile(NewFileName);
        }
    }

    void Login()
    {
        var request = new PlayFab.ClientModels.LoginWithCustomIDRequest
        {
            CustomId = SystemInfo.deviceUniqueIdentifier,
            CreateAccount = true,
        };
        PlayFabClientAPI.LoginWithCustomID(request, OnLogin, OnSharedFailure);
    }

    void OnLogin(PlayFab.ClientModels.LoginResult result)
    {
        entityId = result.EntityToken.Entity.Id;
        entityType = result.EntityToken.Entity.Type;
    }

    void LoadAllFiles()
    {
        if (GlobalFileLock != 0)
            throw new Exception("This example overly restricts file operations for safety. Careful consideration must be made when doing multiple file operations in parallel to avoid conflict.");

        GlobalFileLock += 1; // Start GetFiles
        var request = new PlayFab.DataModels.GetFilesRequest { Entity = new PlayFab.DataModels.EntityKey { Id = entityId, Type = entityType } };
        PlayFabDataAPI.GetFiles(request, OnGetFileMeta, OnSharedFailure);
    }

    void OnGetFileMeta(PlayFab.DataModels.GetFilesResponse result)
    {
        Debug.Log("Loading " + result.Metadata.Count + " files");

        _entityFileJson.Clear();
        foreach (var eachFilePair in result.Metadata)
        {
            _entityFileJson.Add(eachFilePair.Key, null);
            GetActualFile(eachFilePair.Value);
        }
        GlobalFileLock -= 1; // Finish GetFiles
    }

    void GetActualFile(PlayFab.DataModels.GetFileMetadata fileData)
    {
        GlobalFileLock += 1; // Start Each SimpleGetCall
        PlayFabHttp.SimpleGetCall(fileData.DownloadUrl,
            result => { _entityFileJson[fileData.FileName] = Encoding.UTF8.GetString(result); GlobalFileLock -= 1; }, // Finish Each SimpleGetCall
            error => { Debug.Log(error); }
        );
    }

    void UploadFile(string fileName)
    {
        if (GlobalFileLock != 0)
            throw new Exception("This example overly restricts file operations for safety. Careful consideration must be made when doing multiple file operations in parallel to avoid conflict.");

        ActiveUploadFileName = fileName;

        GlobalFileLock += 1; // Start InitiateFileUploads
        var request = new PlayFab.DataModels.InitiateFileUploadsRequest
        {
            Entity = new PlayFab.DataModels.EntityKey { Id = entityId, Type = entityType },
            FileNames = new List<string> { ActiveUploadFileName },
        };
        PlayFabDataAPI.InitiateFileUploads(request, OnInitFileUpload, OnInitFailed);
    }

    void OnInitFailed(PlayFabError error)
    {
        if (error.Error == PlayFabErrorCode.EntityFileOperationPending)
        {
            // This is an error you should handle when calling InitiateFileUploads, but your resolution path may vary
            GlobalFileLock += 1; // Start AbortFileUploads
            var request = new PlayFab.DataModels.AbortFileUploadsRequest
            {
                Entity = new PlayFab.DataModels.EntityKey { Id = entityId, Type = entityType },
                FileNames = new List<string> { ActiveUploadFileName },
            };
            PlayFabDataAPI.AbortFileUploads(request, (result) => { GlobalFileLock -= 1; UploadFile(ActiveUploadFileName); }, OnSharedFailure); GlobalFileLock -= 1; // Finish AbortFileUploads
            GlobalFileLock -= 1; // Failed InitiateFileUploads
        }
        else
            OnSharedFailure(error);
    }

    void OnInitFileUpload(PlayFab.DataModels.InitiateFileUploadsResponse response)
    {
        string payloadStr;
        if (!_entityFileJson.TryGetValue(ActiveUploadFileName, out payloadStr))
            payloadStr = "{}";
        var payload = Encoding.UTF8.GetBytes(payloadStr);

        GlobalFileLock += 1; // Start SimplePutCall
        PlayFabHttp.SimplePutCall(response.UploadDetails[0].UploadUrl,
            payload,
            FinalizeUpload,
            error => { Debug.Log(error); }
        );
        GlobalFileLock -= 1; // Finish InitiateFileUploads
    }

    void FinalizeUpload(byte[] data)
    {
        GlobalFileLock += 1; // Start FinalizeFileUploads
        var request = new PlayFab.DataModels.FinalizeFileUploadsRequest
        {
            Entity = new PlayFab.DataModels.EntityKey { Id = entityId, Type = entityType },
            FileNames = new List<string> { ActiveUploadFileName },
        };
        PlayFabDataAPI.FinalizeFileUploads(request, OnUploadSuccess, OnSharedFailure);
        GlobalFileLock -= 1; // Finish SimplePutCall
    }
    void OnUploadSuccess(PlayFab.DataModels.FinalizeFileUploadsResponse result)
    {
        Debug.Log("File upload success: " + ActiveUploadFileName);
        GlobalFileLock -= 1; // Finish FinalizeFileUploads
    }
}
#endif
```

<Note>
  每個檔案動作都需要許多步驟和多次 API 呼叫，因此請勿嘗試同時以多種方式存取同一個檔案。如果您非常謹慎，可能不需要鎖定機制。如果您想進行複雜的操作，您的鎖定機制可能會複雜得多。
</Note>

## Game Manager 和實體

Game Manager 可讓您操作玩家的物件和檔案。玩家概觀會同時顯示遊戲玩家帳戶和主要玩家帳戶資訊。

<img src="https://mintcdn.com/microsoft-4404708b/3hg2JQs0m7qmDqay/images/playfab/live-service-management/game-configuration/entities/tutorials/game-manager-entities-player-overview.png?fit=max&auto=format&n=3hg2JQs0m7qmDqay&q=85&s=ceac36d3b1e7fd248caeedbf4bea8055" alt="Game Manager - 實體 - 玩家概觀" width="1800" height="1406" data-path="images/playfab/live-service-management/game-configuration/entities/tutorials/game-manager-entities-player-overview.png" />

此外，檔案和物件現在在 **Players** 索引標籤中各有自己的區段。

<img src="https://mintcdn.com/microsoft-4404708b/3hg2JQs0m7qmDqay/images/playfab/live-service-management/game-configuration/entities/tutorials/game-manager-entities-player-files.png?fit=max&auto=format&n=3hg2JQs0m7qmDqay&q=85&s=70b96669460d8bb673ec25fdbcd1bd41" alt="Game Manager - 實體 - 玩家檔案和物件" width="1800" height="969" data-path="images/playfab/live-service-management/game-configuration/entities/tutorials/game-manager-entities-player-files.png" />

## 另請參閱

PlayFab 部落格上的 [Introducing Entities, Objects and Files](https://blog.playfab.com/blog/introducing-entities-objects-and-files)。


## Related topics

- [排行榜快速入門](/zh-TW/services/playfab/community/leaderboards/quickstart-leaderboards.md)
- [實體移轉資訊](/zh-TW/services/playfab/live-service-management/game-configuration/entities/migration-information.md)
- [PlayFab 即時服務管理文件](/zh-TW/services/playfab/live-service-management/index.md)
- [快速入門](/zh-TW/services/playfab/economy-monetization/economy-v2/quickstart.md)
- [Party 快速入門](/zh-TW/services/playfab/multiplayer/networking/quickstart.md)
