> ## 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 都可以使用[實體程式設計模型](/zh-TW/services/playfab/live-service-management/game-configuration/entities)來支援您對持久性玩家分組的需求。

群組實體可用來儲存其他實體的集合 (包括玩家或角色)，這些集合可在您的遊戲中用於許多用途。

## 範例

* **氏族/公會** - 實體群組可用來描述一組經常一起遊玩的玩家，無論是基於何種社交連結讓他們長期聚在一起。

* **隊伍** - 實體群組可用於短期群組，這些群組的建立是為了讓個別玩家完成眼前的目標，之後即可輕鬆解散。

* **聊天頻道** - 短期或長期聊天頻道都可以定義為實體群組。

* **遊戲內資訊訂閱** - 您的遊戲中是否有獨一無二的傳奇物品？玩家是否想持續獲得該物品動態的更新？請建立以該物品為中心的實體群組，並將所有對該物品感興趣的玩家實體設為成員。

簡而言之，實體群組可以是*任何*實體集合 (無論是 NPC 或玩家控制、真實或抽象)，只要需要與該群組繫結的持續性狀態即可。

此外，由於實體群組本身也是實體，因此它們將包含實體的所有初始功能：

* **物件資料**
* **檔案資料**
* **設定檔**

> [!NOTE]
>
> 群組預設的上限為每個群組 1000 位成員，且僅支援玩家和角色作為成員

## 使用實體群組

建立群組時，第一個新增至群組的實體會被指派為系統管理員角色 (為了簡單起見，本指南將該實體稱為擁有者)。擁有者接著將能夠邀請新成員、建立具有各種可自訂權限的新角色、修改成員角色、踢除成員等。

此外，實體所具有的相同實體函式*也適用於群組*，因此您可以將 JSON 物件和檔案直接儲存到群組，以儲存任意的遊戲專屬資料。

下方提供的程式碼範例應能讓您快速上手基本的公會互動。

它可讓您建立群組、新增和移除成員，以及刪除群組。它的用意是作為起點，並未示範任何角色或權限。

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

namespace TestGuildController
{
    /// <summary>
    /// Assumptions for this controller:
    /// + Entities can be in multiple groups
    ///   - This is game specific, many games would only allow 1 group, meaning you'd have to perform some additional checks to validate this.
    /// </summary>
    [Serializable]
    public class GuildTestController
    {
        // A local cache of some bits of PlayFab data
        // This cache pretty much only serves this example , and assumes that entities are uniquely identifiable by EntityId alone, which isn't technically true. Your data cache will have to be better.
        public readonly HashSet<KeyValuePair<string, string>> EntityGroupPairs = new HashSet<KeyValuePair<string, string>>();
        public readonly Dictionary<string, string> GroupNameById = new Dictionary<string, string>();

        public static EntityKey EntityKeyMaker(string entityId)
        {
            return new EntityKey { Id = entityId };
        }

        private void OnSharedError(PlayFab.PlayFabError error)
        {
            Debug.LogError(error.GenerateErrorReport());
        }

        public void ListGroups(EntityKey entityKey)
        {
            var request = new ListMembershipRequest { Entity = entityKey };
            PlayFabGroupsAPI.ListMembership(request, OnListGroups, OnSharedError);
        }
        private void OnListGroups(ListMembershipResponse response)
        {
            var prevRequest = (ListMembershipRequest)response.Request;
            foreach (var pair in response.Groups)
            {
                GroupNameById[pair.Group.Id] = pair.GroupName;
                EntityGroupPairs.Add(new KeyValuePair<string, string>(prevRequest.Entity.Id, pair.Group.Id));
            }
        }

        public void CreateGroup(string groupName, EntityKey entityKey)
        {
            // A player-controlled entity creates a new group
            var request = new CreateGroupRequest { GroupName = groupName, Entity = entityKey };
            PlayFabGroupsAPI.CreateGroup(request, OnCreateGroup, OnSharedError);
        }
        private void OnCreateGroup(CreateGroupResponse response)
        {
            Debug.Log("Group Created: " + response.GroupName + " - " + response.Group.Id);

            var prevRequest = (CreateGroupRequest)response.Request;
            EntityGroupPairs.Add(new KeyValuePair<string, string>(prevRequest.Entity.Id, response.Group.Id));
            GroupNameById[response.Group.Id] = response.GroupName;
        }
        public void DeleteGroup(string groupId)
        {
            // A title, or player-controlled entity with authority to do so, decides to destroy an existing group
            var request = new DeleteGroupRequest { Group = EntityKeyMaker(groupId) };
            PlayFabGroupsAPI.DeleteGroup(request, OnDeleteGroup, OnSharedError);
        }
        private void OnDeleteGroup(EmptyResponse response)
        {
            var prevRequest = (DeleteGroupRequest)response.Request;
            Debug.Log("Group Deleted: " + prevRequest.Group.Id);

            var temp = new HashSet<KeyValuePair<string, string>>();
            foreach (var each in EntityGroupPairs)
                if (each.Value != prevRequest.Group.Id)
                    temp.Add(each);
            EntityGroupPairs.IntersectWith(temp);
            GroupNameById.Remove(prevRequest.Group.Id);
        }

        public void InviteToGroup(string groupId, EntityKey entityKey)
        {
            // A player-controlled entity invites another player-controlled entity to an existing group
            var request = new InviteToGroupRequest { Group = EntityKeyMaker(groupId), Entity = entityKey };
            PlayFabGroupsAPI.InviteToGroup(request, OnInvite, OnSharedError);
        }
        public void OnInvite(InviteToGroupResponse response)
        {
            var prevRequest = (InviteToGroupRequest)response.Request;

            // Presumably, this would be part of a separate process where the recipient reviews and accepts the request
            var request = new AcceptGroupInvitationRequest { Group = EntityKeyMaker(prevRequest.Group.Id), Entity = prevRequest.Entity };
            PlayFabGroupsAPI.AcceptGroupInvitation(request, OnAcceptInvite, OnSharedError);
        }
        public void OnAcceptInvite(EmptyResponse response)
        {
            var prevRequest = (AcceptGroupInvitationRequest)response.Request;
            Debug.Log("Entity Added to Group: " + prevRequest.Entity.Id + " to " + prevRequest.Group.Id);
            EntityGroupPairs.Add(new KeyValuePair<string, string>(prevRequest.Entity.Id, prevRequest.Group.Id));
        }

        public void ApplyToGroup(string groupId, EntityKey entityKey)
        {
            // A player-controlled entity applies to join an existing group (of which they are not already a member)
            var request = new ApplyToGroupRequest { Group = EntityKeyMaker(groupId), Entity = entityKey };
            PlayFabGroupsAPI.ApplyToGroup(request, OnApply, OnSharedError);
        }
        public void OnApply(ApplyToGroupResponse response)
        {
            var prevRequest = (ApplyToGroupRequest)response.Request;

            // Presumably, this would be part of a separate process where the recipient reviews and accepts the request
            var request = new AcceptGroupApplicationRequest { Group = prevRequest.Group, Entity = prevRequest.Entity };
            PlayFabGroupsAPI.AcceptGroupApplication(request, OnAcceptApplication, OnSharedError);
        }
        public void OnAcceptApplication(EmptyResponse response)
        {
            var prevRequest = (AcceptGroupApplicationRequest)response.Request;
            Debug.Log("Entity Added to Group: " + prevRequest.Entity.Id + " to " + prevRequest.Group.Id);
        }
        public void KickMember(string groupId, EntityKey entityKey)
        {
            var request = new RemoveMembersRequest { Group = EntityKeyMaker(groupId), Members = new List<EntityKey> { entityKey } };
            PlayFabGroupsAPI.RemoveMembers(request, OnKickMembers, OnSharedError);
        }
        private void OnKickMembers(EmptyResponse response)
        {
            var prevRequest= (RemoveMembersRequest)response.Request;
            
            Debug.Log("Entity kicked from Group: " + prevRequest.Members[0].Id + " to " + prevRequest.Group.Id);
            EntityGroupPairs.Remove(new KeyValuePair<string, string>(prevRequest.Members[0].Id, prevRequest.Group.Id));
        }
    }
}
```

## 解析範例

此範例建置為控制器，會將最少量的資料儲存到本機快取 (PlayFab 是權威資料層)，並提供對群組執行 CRUD 作業的方式。

讓我們來看看所提供範例中的一些函式：

* `OnSharedError` - 這是 PlayFab 範例中的典型模式。處理錯誤最簡單的方式就是回報錯誤。您的遊戲用戶端可能會有更複雜的錯誤處理邏輯。

* `ListMembership` - 這會呼叫 `ListMembership` 以判斷指定實體所屬的所有群組。玩家會想知道他們已加入的群組。

* `CreateGroup`/`DeleteGroup` - 大致上不言自明。此範例示範在這些呼叫成功執行時更新本機群組資訊快取。

* `InviteToGroup`/`ApplyToGroup` - 加入群組是一個兩步驟的程序，而且可以從兩個方向啟動：
  * 玩家可以要求加入群組。
  * 群組可以邀請玩家。

* `AcceptGroupInvitation`/`AcceptGroupApplication` - 加入程序的第二個步驟。回應的實體接受邀請，完成讓玩家成為群組一員的程序。

* `RemoveMembers` - 具有權限的成員 (由其角色權限定義) 將能夠將成員踢出群組。

## 伺服器與用戶端

如同所有新的實體 API 方法，伺服器 API 與用戶端 API 之間並無區別。

動作會由呼叫者根據程序的驗證方式來執行。用戶端將被識別為用戶端，並會以遊戲玩家實體的身分呼叫這些方法，而且每次呼叫時都會評估其在群組中的角色和權限，以確保其有權執行此動作。

伺服器會使用相同的 `developerSecretKey` 進行驗證，這會將該程序識別為遊戲實體。遊戲會略過角色檢查，而由遊戲執行的 API 呼叫只有在動作無法執行時才會失敗，例如實體若不是成員就無法被移除。

## 另請參閱

若要為您的群組、公會或氏族儲存資料：

* [物件](/zh-TW/services/playfab/live-service-management/game-configuration/entities/entity-objects)
* [檔案](/zh-TW/services/playfab/live-service-management/game-configuration/entities/entity-files)


## Related topics

- [PlayStream 事件模型參考](/zh-TW/services/xbox-services/player-data/stats-leaderboards/event-based/events/index.md)
- [group_updated](/zh-TW/services/playfab/api-references/events/Groups/group-updated.md)
- [group_created](/zh-TW/services/playfab/api-references/events/Groups/group-created.md)
- [group_members_removed](/zh-TW/services/playfab/api-references/events/Groups/group-members-removed.md)
- [group_members_added](/zh-TW/services/playfab/api-references/events/Groups/group-members-added.md)
