# 平衡团队构成 Time:5 分钟 ![](https://developer.roblox.com/assets/blt506c733356766e41/Team-Balancing-Hero.jpg) 通过将游戏中互加好友的玩家放入同一队伍并确保双方队伍的平衡,可以保证所有玩家都能享受到公平健康的游戏氛围。向游戏添加保持团队平衡的代码后,公平竞争的玩家们会逐渐发展友谊,从而在你的游戏中投入更多的时间。 如果想要实现本文中的代码示例,建议对 Lua `/articles/Table|table(表)` 和 Roblox 的 `BindableEvent|BindableEvent` 有一定了解。 本文中的脚本并不会为游戏自动添加/设置团队。如果对设置团队尚不了解,请参见`/articles/Player Spawns and Teams|玩家生成和团队`一文。 ## 将好友玩家成组分配 如果想要追踪一组好友玩家,需要先在 `ServerScriptService` 中包括 `Script`。在该脚本的代码中,首先需要添加查看游戏中是否有好友的函数,然后将其连接至 `Players/PlayerAdded` 事件。 ServerScriptService 中的 Script ``` local friendGroups = {} -- 将好友加入同一群组,以便其加入同一团队 local function groupFriends(player) local mutualGroups = {} -- 对好友群组进行迭代 for groupIndex, group in ipairs(friendGroups) do -- 对群组中发现的好友进行迭代 for _, user in ipairs(group) do -- 将共同好友放入一个群组中 if player:IsFriendsWith(user.UserId) then if (mutualGroups[group] == nil) then table.insert(mutualGroups, groupIndex) end end end end if #mutualGroups > 0 then local groupIndex = mutualGroups[1] -- 如果玩家拥有多个正在进行游戏的好友群组,则将其合并为一个群组 if #mutualGroups > 1 then groupIndex = mergeGroups(mutualGroups) end table.insert(friendGroups[groupIndex], player) assignInitialTeam(player, friendGroups[groupIndex]) else table.insert(friendGroups, {player}) assignInitialTeam(player) end end -- 将 "PlayerAdded" 事件连接至 "groupFriends()" 函数 game.Players.PlayerAdded:Connect(groupFriends) ``` 此段代码的作用如下: 1. 第一行的 `friendGroups` 表将会储存至少拥有一个共同好友的多个玩家列表。 2. 在查找进入游戏的玩家是否有好友在游戏内时,将会使用 `Player/IsFriendsWith|Player:IsFriendsWith()` 对 `friendGroups` 内各个群组的玩家进行查询。包含进入游戏玩家一名以上好友的群组将会被存储在 `mutualGroups` 表内。 3. 如果找到了多个拥有好友的群组,则首先会将这些群组通过 `mergeGroups()` 辅助函数(在下文合并好友群组章节中进行了定义),然后玩家将会被分配至该群组。如果没有发现任何好友,则玩家将会被分配至新的群组。 4. 当玩家与其线上好友进行分组后,使用辅助函数 `assignInitialTeam()` (在下文分配玩家团队章节中进行了定义)将其分配至团队中。 ### 合并好友群组 如果想要将多个含有共同好友的群组合并为一个更大的群组,请直接在 `groupFriends()` 函数上方添加下列 `mergeGroups()` 辅助函数。该函数将会查看拥有共同好友的群组,对其进行合并,移除旧的群组,然后将合并过的群组返回至 `groupFriends()` 函数。 ``` -- 将所有拥有共同好友的群组合并至一个群组 local function mergeGroups(groups) -- 将其它群组的成员添加至第一个群组 for i = 2, #groups do for _, user in pairs(friendGroups[groups[i]]) do table.insert(friendGroups[groups[1]], user) end -- 移除合并后的废弃群组 table.remove(friendGroups, groups[i]) end return groups[1] end ``` ### 分配玩家团队 如果玩家与好友共同游戏,开始时应该将其放入同一团队中。为了达成此目的,应当: 1. 获取 `TeamService` 并与游戏团队交互。建议将服务放在脚本的最顶端。 ``` local TeamsService = game:GetService("Teams") local friendGroups = {} ``` 2. 在 `mergeGroups()` 函数下添加名为 `assignInitialTeam()` 的函数,其作用为接收玩家及其可能加入群组的参数。如果函数收到了群组参数且群组人数不会因过大而造成游戏不平衡,则好友群组中第一条目的 `Player/Team` 属性将会被用来分配团队。如果没有收到群组参数,则玩家将填入人数最少的团队。 ``` -- 当玩家加入游戏时,决定其应当加入的团队 local function assignInitialTeam(player, group) -- 检查玩家是否属于任何群组且该群组是否并未超过公平团队人数 if group and #group < game.Players.MaxPlayers / 2 then player.Team = group[1].Team else local teams = TeamsService:GetTeams() table.sort(teams, function(a, b) return #a:GetPlayers() < #b:GetPlayers() end) player.Team = teams[1] end end ``` ## 处理玩家退出 当玩家离开游戏后,需要将其从 `friendGroups` 表中移除,否则游戏将会认为好友群组比实际上的人数要多。 如果想要移除玩家的条目,需要对 `friendGroups` 进行迭代,寻找与该玩家相符的用户,并将该玩家的当前索引设为 `nil`。如果玩家之前的群组为空,则将群组也设为 `nil` 。最后需要确保将函数连接至 `Players/PlayerRemoving` 事件。 ``` -- 当玩家离开游戏时,清除其群组 local function removeFromGroup(player) -- 对好友群组进行循环,找到玩家 for groupIndex, group in ipairs(friendGroups) do for userIndex, user in ipairs(group) do if user.Name == player.Name then -- 将其从之前加入的群组中移除 friendGroups[groupIndex][userIndex] = nil -- 如果群组为空,则将其进行移除 if #friendGroups[groupIndex] == 0 then friendGroups[groupIndex] = nil end end end end end -- 将 "PlayerRemoving" 事件连接至 "removeFromGroup()" 函数 game.Players.PlayerRemoving:Connect(removeFromGroup) -- 将 "PlayerAdded" 事件连接至 "groupFriends()" 函数 game.Players.PlayerAdded:Connect(groupFriends) ``` ## 重新平衡团队 决定好友群组之后,在 `ReplicatedStorage` 内添加 `BindableEvent`,以供需要测试及重新平衡时使用。按照 Roblox 传统,将此代码置于脚本顶部。 ``` local TeamsService = game:GetService("Teams") local ReplicatedStorage = game:GetService("ReplicatedStorage") local balanceTeamsEvent = Instance.new("BindableEvent") balanceTeamsEvent.Name = "BalanceTeamsEvent" balanceTeamsEvent.Parent = ReplicatedStorage local friendGroups = {} ``` 接下来,为了保证各团队分配到人数大致相当的好友群组并保持平衡,在 `removeFromGroup()` 下方添加下列 `balanceTeams()` 函数: ``` local function balanceTeams() local teams = TeamsService:GetTeams() -- 对群组进行排序,将更大的群组放在前面 table.sort(friendGroups, function(a, b) return #a > #b end) -- 迭代好友群组(已经将其按从大到小进行排序) for i = 1, #friendGroups do -- 如果群组过大,则将其分开放入两个团队中 if (#friendGroups[i] > game.Players.MaxPlayers / 2) then for _, player in pairs(friendGroups[i]) do table.sort(teams, function(a, b) return #a:GetPlayers() < #b:GetPlayers() end) player.Team = teams[1] end else -- 对团队列表进行排序,最小的团队放在最前 table.sort(teams, function(a, b) return #a:GetPlayers() < #b:GetPlayers() end) local groupTeam = teams[1] -- 将群组中的所有人放入同一团队 for _, player in pairs(friendGroups[i]) do player.Team = groupTeam end end end end ``` 此函数的作用如下: 1. 为了保证各团队分配到人数大致相当的好友群组并保持团队平衡,将各 `friendGroups` 按照从大到小进行排序(第 90 行)。 2. 排序之后,再次对 `friendGroups` 进行迭代。如果任何群组人数大于游戏 `Players/MaxPlayers` 值的一半,则会将该群组内好友分开并分配至人数更少的团队。如果人数小于该值一半,则将会对团队进行升序排序后将该群组分配至最小团队中去。 现在将 `BalanceTeamsEvent` 可绑定`BindableEvent/Event|事件`连接至 `balanceTeams()` 函数,在想要对团队进行重新平衡时`BindableEvent/Fire|触发`该事件即可。建议在两场比赛间触发事件,但也可以自行决定团队平衡的触发条件。 ``` -- 将 "PlayerRemoving" 事件连接至 "removeFromGroup()" 函数 game.Players.PlayerRemoving:Connect(removeFromGroup) -- 将 "PlayerAdded" 事件连接至 "groupFriends()" 函数 game.Players.PlayerAdded:Connect(groupFriends) balanceTeamsEvent.Event:Connect(balanceTeams) ``` ## 完整的脚本 ``` local TeamsService = game:GetService("Teams") local ReplicatedStorage = game:GetService("ReplicatedStorage") local balanceTeamsEvent = Instance.new("BindableEvent") balanceTeamsEvent.Name = "BalanceTeamsEvent" balanceTeamsEvent.Parent = ReplicatedStorage local friendGroups = {} -- 将所有拥有共同好友的群组合并至一个群组 local function mergeGroups(groups) -- 将其它群组的成员添加至第一个群组 for i = 2, #groups do for _, user in pairs(friendGroups[groups[i]]) do table.insert(friendGroups[groups[1]], user) end -- 移除合并后的废弃群组 table.remove(friendGroups, groups[i]) end return groups[1] end -- 当玩家加入游戏时,决定其应当加入的团队 local function assignInitialTeam(player, group) -- 检查玩家是否属于任何群组且该群组是否并未超过公平团队人数 if group and #group < game.Players.MaxPlayers / 2 then player.Team = group[1].Team else local teams = TeamsService:GetTeams() table.sort(teams, function(a, b) return #a:GetPlayers() < #b:GetPlayers() end) player.Team = teams[1] end end -- 将好友加入同一群组,以便其加入同一团队 local function groupFriends(player) local mutualGroups = {} -- 对好友群组进行迭代 for groupIndex, group in ipairs(friendGroups) do -- 对群组中发现的好友进行迭代 for _, user in ipairs(group) do -- 将共同好友放入一个群组中 if player:IsFriendsWith(user.UserId) then if (mutualGroups[group] == nil) then table.insert(mutualGroups, groupIndex) end end end end if #mutualGroups > 0 then local groupIndex = mutualGroups[1] -- 如果玩家拥有多个正在进行游戏的好友群组,则将其合并为一个群组 if #mutualGroups > 1 then groupIndex = mergeGroups(mutualGroups) end table.insert(friendGroups[groupIndex], player) assignInitialTeam(player, friendGroups[groupIndex]) else table.insert(friendGroups, {player}) assignInitialTeam(player) end end -- 当玩家离开游戏时,清除其群组 local function removeFromGroup(player) -- 对好友群组进行循环,找到玩家 for groupIndex, group in ipairs(friendGroups) do for userIndex, user in ipairs(group) do if user.Name == player.Name then -- 将其从之前加入的群组中移除 friendGroups[groupIndex][userIndex] = nil -- 如果群组为空,则将其进行移除 if #friendGroups[groupIndex] == 0 then friendGroups[groupIndex] = nil end end end end end local function balanceTeams() local teams = TeamsService:GetTeams() -- 对群组进行排序,将更大的群组放在前面 table.sort(friendGroups, function(a, b) return #a > #b end) -- 迭代好友群组(已经将其按从大到小进行排序) for i = 1, #friendGroups do -- 如果群组过大,则将其分开放入两个团队中 if (#friendGroups[i] > game.Players.MaxPlayers / 2) then for _, player in pairs(friendGroups[i]) do table.sort(teams, function(a, b) return #a:GetPlayers() < #b:GetPlayers() end) player.Team = teams[1] end else -- 对团队列表进行排序,最小的团队放在最前 table.sort(teams, function(a, b) return #a:GetPlayers() < #b:GetPlayers() end) local groupTeam = teams[1] -- 将群组中的所有人放入同一团队 for _, player in pairs(friendGroups[i]) do player.Team = groupTeam end end end end -- 将 "PlayerRemoving" 事件连接至 "removeFromGroup()" 函数 game.Players.PlayerRemoving:Connect(removeFromGroup) -- 将 "PlayerAdded" 事件连接至 "groupFriends()" 函数 game.Players.PlayerAdded:Connect(groupFriends) balanceTeamsEvent.Event:Connect(balanceTeams) ``` ***__Roblox官方链接__:[平衡团队构成](https://developer.roblox.com/zh-cn/articles/team-balancing)