> ## 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.

# 處理多個 PlayFab 登入

> 使用 PlayFab SDK 執行個體化 API 類別，從單一用戶端或伺服器管理多個同時進行的玩家登入，而不會發生靜態狀態衝突。

根據預設，大多數 PlayFab SDK 都會快取玩家登入結果。在預期只有單一玩家登入的最常見案例中，這種快取很方便。在支援多位同時遊玩玩家的遊戲中，或是在同時管理玩家驗證資料和伺服器認證的伺服器中，這種快取可能會造成妨礙。為了支援這些案例，PlayFab SDK 同時包含靜態和執行個體化的 API 類別。

## 靜態與執行個體化 API 類別的比較

PlayFab 的大部分範例和範例程式碼都是使用靜態 API 類別建置的。例如，在 Unity 中，您可能會看到 **PlayFabClientAPI** 的參考。這些靜態類別會寫入和讀取 SDK 內的靜態狀態。由於依賴靜態狀態，當您在同一個遊戲用戶端中處理多位玩家時，很難使用這些類別。執行個體化 API 類別可避免這些問題，代價是您需要承擔更多的管理和追蹤工作。在需要支援多個同時進行的 PlayFab 登入的情況下，我們建議使用執行個體化 API 類別。

在 Unity SDK 中，**PlayFabClientInstanceAPI** 是 **PlayFabClientAPI** 的執行個體化版本。所有其他 API 類別都遵循類似的命名模式。使用 API 類別的執行個體化版本時，您必須先建立類別的執行個體，才能呼叫任何方法。若要建立執行個體，您必須指定一些額外的內容。對於大多數類別，所需的額外內容只是玩家的驗證內容。在 Unity 中，此內容是 **PlayFabAuthenticationContext**。有些類別包含登入呼叫。對於這些呼叫，您可能還沒有玩家的驗證內容。如果是這種情況，您只需要提供基本設定，例如遊戲識別碼。這些設定是透過 **PlayFabApiSettings** 物件傳入。

## 使用執行個體化 API 類別

使用適當的驗證內容或 API 設定物件建立執行個體化 API 類別之後，就可以像使用靜態類別一樣使用它。所有要求和回應物件都完全相同。唯一的差別在於，您必須負責追蹤執行個體的存留期，並確保為任何可能呼叫 PlayFab 的呼叫端提供適當的執行個體。當您處理多位玩家時，任何 API 類別都需要多個執行個體，每位玩家一個。通常最簡單的做法是將這些執行個體封裝在作為擁有者的玩家物件後面進行管理，但這由您決定。

讓玩家登入的執行個體化類別 (例如 **PlayFabClientInstanceAPI**) 也會在 API 類別執行個體中建立並快取該玩家的驗證內容。此功能可讓您在建立所需的其他類別時，輕鬆參考驗證內容。

## Unity 範例

此範例程式碼示範多位玩家如何登入遊戲，並在個別的類別執行個體中追蹤獨立的狀態。在此範例中，API 類別和基本功能被封裝在一個簡單的 **PlayFabPlayer** 物件後面。遊戲啟動時，我們會讓兩位玩家登入，然後取得 PlayFab 中為每位玩家儲存的任何資料。

```csharp theme={null}
using PlayFab;
using PlayFab.ClientModels;
using PlayFab.DataModels;
using System.Collections.Generic;
using UnityEngine;

public class PlayFabLogin : MonoBehaviour
{
    PlayFabPlayer player1 = new PlayFabPlayer();
    PlayFabPlayer player2 = new PlayFabPlayer();

    // Start is called before the first frame update
    void Start()
    {
        if (string.IsNullOrEmpty(PlayFabSettings.staticSettings.TitleId))
        {
            // Please change the titleId below to your own titleId from PlayFab Game Manager.
            PlayFabSettings.staticSettings.TitleId = "";
        }

        player1.Login("testLogin1");
        player2.Login("testLogin2");
    }

    // Update is called once per frame
    void Update()
    {
        if (player1.loggedIn && !player1.dataLoaded && !player1.dataLoading)
        {
            player1.LoadData();
        }
        if (player2.loggedIn && !player2.dataLoaded && !player2.dataLoading)
        {
            player2.LoadData();
        }
    }
}

class PlayFabPlayer
{
    public bool loggedIn = false;
    public bool dataLoading = false;
    public bool dataLoaded = false;
    public string PlayFabId;
    public Dictionary<string, ObjectResult> playerData;

    private PlayFabClientInstanceAPI clientApi;
    private PlayFabDataInstanceAPI dataApi;

    public void Login(string customId)
    {
        clientApi = new PlayFabClientInstanceAPI(PlayFabSettings.staticSettings);

        var request = new LoginWithCustomIDRequest { CustomId = customId, CreateAccount = true };

        clientApi.LoginWithCustomID(request, result =>
        {
            PlayFabId = result.PlayFabId;
            loggedIn = true;
            dataApi = new PlayFabDataInstanceAPI(clientApi.authenticationContext);
            Debug.Log("Login call succeeded.");
        }, error =>
        {
            Debug.LogWarning("Something went wrong with the login call.");
            Debug.LogError("Here's some debug information:");
            Debug.LogError(error.GenerateErrorReport());
        });
    }

    public void LoadData()
    {
        dataLoading = true;
        var request = new GetObjectsRequest { Entity = new PlayFab.DataModels.EntityKey { Id = clientApi.authenticationContext.EntityId, Type = clientApi.authenticationContext.EntityType } };

        dataApi.GetObjects(request, result =>
        {
            playerData = result.Objects;
            dataLoaded = true;
            dataLoading = false;
            Debug.Log("Player data loaded.");
        }, error =>
        {
            Debug.LogWarning("Something went wrong with the GetObjects call.");
            Debug.LogError("Here's some debug information:");
            Debug.LogError(error.GenerateErrorReport());
        });
    }
}
```

## Unreal 範例

此程式碼範例示範 Unreal 中包含自己的 PlayFab 登入內容的 actor。它說明如何將 PlayFab API 執行個體類別封裝在 **ALoginActor** 類別中。可以將多個 **ALoginActor** 執行個體新增至地圖，為它們指定各自的 CustomId，並獨立執行 PlayFab 作業。

LoginActor.h:

```cpp theme={null}
#pragma once

#include "CoreMinimal.h"
#include "GameFramework/Actor.h"
#include "PlayFab.h"
#include "Core/PlayFabError.h"
#include "Core/PlayFabClientDataModels.h"
#include "Core/PlayFabClientAPI.h"
#include "Core/PlayFabDataAPI.h"
#include "LoginActor.generated.h"

UCLASS()
class MINUE_PF_MARKET_API ALoginActor : public AActor
{
	GENERATED_BODY()
	
public:	
	// Sets default values for this actor's properties
	ALoginActor();

    // Please change the TitleId below to your own TitleId from PlayFab Game Manager.
	UPROPERTY(EditAnywhere, config, Category = Settings)
	FString TitleId = TEXT("");

	UPROPERTY(EditAnywhere, config, Category = Settings)
	FString CustomId = TEXT("ExampleCustomId");

protected:
	// Called when the game starts or when spawned

	virtual void BeginPlay() override;

	void OnLoginSuccess(const PlayFab::ClientModels::FLoginResult& Result);
	void OnGetObjectsSuccess(const PlayFab::DataModels::FGetObjectsResponse& Result);
	void OnError(const PlayFab::FPlayFabCppError& ErrorResult) const;

public:	
	bool LoggedIn = false;

	// Called every frame
	virtual void Tick(float DeltaTime) override;

	PlayFabClientPtr clientAPI = nullptr;
	PlayFabDataPtr dataAPI = nullptr;

	TMap<FString, PlayFab::DataModels::FObjectResult> PlayerData;
	bool DataLoaded = false;
};
```

LoginActor.cpp:

```cpp theme={null}
#include "LoginActor.h"

ALoginActor::ALoginActor()
{
 	// Set this actor to call Tick() every frame.  You can turn this off to improve performance if you don't need it.
	PrimaryActorTick.bCanEverTick = true;
}

void ALoginActor::Tick(float DeltaTime)
{
    Super::Tick(DeltaTime);
}

void ALoginActor::BeginPlay()
{
	Super::BeginPlay();
    GetMutableDefault<UPlayFabRuntimeSettings>()->TitleId = TitleId;

    clientAPI = IPlayFabModuleInterface::Get().GetClientAPI();
    dataAPI = IPlayFabModuleInterface::Get().GetDataAPI();

    PlayFab::ClientModels::FLoginWithCustomIDRequest request;
    request.CustomId = CustomId;
    request.CreateAccount = true; 

    clientAPI->LoginWithCustomID(request,
        PlayFab::UPlayFabClientAPI::FLoginWithCustomIDDelegate::CreateUObject(this, &ALoginActor::OnLoginSuccess),
        PlayFab::FPlayFabErrorDelegate::CreateUObject(this, &ALoginActor::OnError)
    );
}

void ALoginActor::OnLoginSuccess(const PlayFab::ClientModels::FLoginResult& Result)
{
    UE_LOG(LogTemp, Log, TEXT("Login call succeeded."));
    LoggedIn = true;

    PlayFab::DataModels::FGetObjectsRequest dataRequest;
    dataRequest.AuthenticationContext = Result.AuthenticationContext;
    dataRequest.Entity.Id = Result.EntityToken->Entity->Id;
    dataRequest.Entity.Type = Result.EntityToken->Entity->Type;

    dataAPI->GetObjects(
        dataRequest,
        PlayFab::UPlayFabDataAPI::FGetObjectsDelegate::CreateUObject(this, &ALoginActor::OnGetObjectsSuccess),
        PlayFab::FPlayFabErrorDelegate::CreateUObject(this, &ALoginActor::OnError)
    );
}

void ALoginActor::OnError(const PlayFab::FPlayFabCppError& ErrorResult) const
{
    UE_LOG(LogTemp, Error, TEXT("Something went wrong with your API call.\nHere's some debug information:\n%s"), *ErrorResult.GenerateErrorReport());
}

void ALoginActor::OnGetObjectsSuccess(const PlayFab::DataModels::FGetObjectsResponse& Result)
{
    PlayerData = Result.Objects;
    DataLoaded = true;
	UE_LOG(LogTemp, Log, TEXT("Player data loaded."));    
}
```

## 伺服器驗證

與執行個體化 API 類別讓遊戲用戶端能處理多位玩家的方式類似，它們也讓伺服器能處理遊戲和玩家驗證的組合，甚至同時處理多個遊戲。基本模式幾乎相同。具現化 API 類別來處理伺服器登入，為該執行個體提供適當的 **PlayFabApiSettings** 物件，然後呼叫驗證 API。如果您在伺服器上，該 API 通常是 **PlayFabAuthenticationInstanceAPI.GetEntityToken**。就像玩家登入一樣，**GetEntityToken** 呼叫的結果會快取在 API 類別執行個體中，並可透過 **authenticationContext** 執行個體屬性加以參考，以建立更多 API 類別執行個體。


## Related topics

- [在 Unity 中使用 Google Play Games 登入進行 PlayFab 驗證](/zh-TW/services/playfab/identity/player-identity/platform-specific-authentication/google-sign-in-unity.md)
- [從 PlayFab 獨立 SDK v1 移轉至 Unified SDK v2](/zh-TW/services/playfab/sdks/unified-sdk/migrating-from-v1.md)
- [庫存堆疊](/zh-TW/services/playfab/economy-monetization/economy-v2/inventory/stacks.md)
- [套件](/zh-TW/publishing/game-publishing/concepts/game-package-management.md)
- [AcpHalCreate](/zh-TW/reference/audio/acphal/functions/acphalcreate.md)
