Times no Roblox: o TeamColor do spawn não é a cor do spawn
O serviço Teams já dá cor de nome e separação na lista de jogadores sem script nenhum. O que derruba quem está começando são duas propriedades com nomes parecidos no SpawnLocation — e só uma delas tem a ver com time.
Produzido com IA a partir das fontes citadas abaixo, e conferido por checagens automáticas contra essas mesmas fontes. Nenhuma pessoa leu este texto antes da publicação. Política Editorial

Times no Roblox são um daqueles recursos que entregam muito antes de qualquer código. Configurado o serviço, o jogo já mostra o nome de cada jogador colorido pelo time e já separa a lista de jogadores — sem script nenhum.
E eles têm uma armadilha de nome que derruba quase todo iniciante: uma propriedade chamada TeamColor que não é a cor do objeto, ao lado de duas que são.
O problema
O ponto de partida: dá para dividir os jogadores em vários times usando o serviço Teams. E o retorno é imediato — configurado o serviço e atribuídos os times, os jogadores são diferenciados pela cor do nome em tela e pelo nome na lista de jogadores padrão.
Isso é mais do que parece. A identificação visual de time é um problema de interface que, feito à mão, envolve etiqueta sobre a cabeça, cor por jogador e uma lista própria. O serviço entrega os três de graça.
A primeira coisa a saber é que por padrão nenhum time vem configurado. O serviço existe na hierarquia, vazio. Nada acontece até você criar os objetos.
Passo a passo
1. Crie os times. Na hierarquia do Explorer, passe o mouse sobre o objeto Teams e insira um novo objeto Team sob o serviço.
2. Nomeie e escolha a cor. No objeto selecionado, ajuste o Name e atribua um TeamColor único e correspondente. O nome e a cor aparecem os dois na lista de jogadores do jogo.
A palavra "único" na documentação não é enfeite: o TeamColor é o que identifica o time em outros lugares do sistema — inclusive nos pontos de nascimento. Dois times com a mesma cor produzem ambiguidade que o motor resolve de um jeito que você não escolheu.
3. Repita para os outros times.
4. Decida como os jogadores entram. Por padrão, a Roblox distribui jogador novo automaticamente para o time com menos membros. É um balanceamento razoável e resolve a maior parte dos casos.
Para controlar a distribuição você mesmo, desmarque a caixa AutoAssignable em cada time. Aí a atribuição passa a ser sua: por script, mudando a propriedade Team do jogador, indexando o serviço pelo nome do time.
5. Trave os pontos de nascimento. Objetos SpawnLocation fazem jogadores de times diferentes nascerem em lugares específicos, ao entrar ou renascer.
E aqui está o padrão que precisa ser desfeito: por padrão o SpawnLocation é neutro, e qualquer jogador pode nascer nele. Por isso é preciso travar cada um no time que pode ocupá-lo.
São dois passos por spawn: desmarcar a caixa Neutral nas propriedades, e definir o TeamColor do spawn como o TeamColor de um time existente. Se o time usa a cor Lapis, o spawn também usa Lapis.
Uma observação sobre o balanceamento padrão. Distribuir para o time com menos membros é o comportamento certo na maior parte dos casos, e é bom que seja o padrão. Ele falha em três situações específicas, e vale reconhecê-las antes de desligar por reflexo.
A primeira é partida em andamento: quem entra no meio e cai no time que está perdendo tem uma experiência pior, e o balanceamento por contagem não sabe disso.
A segunda é time com papel diferente — caçador contra sobreviventes, atacantes contra defensores. Aí o equilíbrio não é numérico, e distribuir igual é distribuir errado.
A terceira é grupo de amigos: três pessoas que entram juntas vão para times diferentes, porque cada uma é avaliada sozinha na hora em que entra.
Fora desses três, o padrão resolve — e resolve sem código.
Artefato
O script abaixo tem duas partes, e a segunda é a que rende mais rápido.
A primeira é uma distribuição própria com reequilíbrio — útil quando o padrão não serve: partida em andamento, times com papéis diferentes, amigos que precisam cair juntos. Ela depende do AutoAssignable estar desmarcado; com ele ligado, a Roblox distribui antes e o script vira uma segunda opinião que chega tarde.
A segunda é uma conferência de spawns que roda no Studio e aponta os três defeitos que não dão erro nenhum: spawn neutro num jogo com times, spawn com TeamColor que não corresponde a time algum, e time sem nenhum spawn.
Esse terceiro caso é o mais cruel: o time existe, os jogadores são atribuídos a ele, e eles nascem em lugar nenhum de específico — porque não há spawn travado naquela cor.
Vale ainda notar o que o serviço não faz, para não esperar demais dele. Ele identifica e separa visualmente; ele não impede nada. Jogador de um time pode atacar o próprio time, entrar na base adversária e pegar o item do outro — a menos que o seu código diga o contrário. Tudo o que for regra lê a propriedade Team e decide; o serviço só garante que essa propriedade exista e apareça na tela.
Erro comum
O erro campeão está numa nota que a documentação faz questão de deixar no fim, e ela merece ser lida palavra por palavra: o SpawnLocation.TeamColor é diferente do SpawnLocation.BrickColor e do SpawnLocation.Color, que representam a cor visual do objeto e não têm relação com a funcionalidade de time.
São três propriedades, duas delas cosméticas, com nomes que sugerem a mesma coisa. E o comportamento resultante é o mais confuso possível: você pinta o spawn de azul, o jogo mostra um spawn azul, e ele continua não pertencendo ao time azul.
O inverso também acontece e é pior: você define o TeamColor certo, o spawn funciona, e ele continua cinza na tela. Aí alguém da equipe "conserta" a cor pelo BrickColor, e agora o spawn parece certo e funciona certo — mas ninguém sabe qual das duas ações resolveu.
A regra que evita tudo isso: TeamColor decide quem nasce; BrickColor e Color decidem o que se vê. Ajuste os dois, de propósito, sabendo que fazem coisas diferentes.
O segundo erro é esquecer de desmarcar o Neutral. Um spawn neutro num jogo com times funciona — para todo mundo. Os jogadores do time vermelho nascem no spawn azul, porque ele aceita qualquer um. O sintoma é caótico e intermitente, porque depende de qual spawn o motor escolheu naquele instante.
O terceiro é deixar o AutoAssignable ligado e distribuir por script ao mesmo tempo. Os dois funcionam, na ordem errada: a Roblox atribui o jogador ao entrar, o seu script atribui depois, e o jogador muda de time visivelmente no primeiro segundo de jogo. Não é um bug que quebra nada, e é feio o bastante para o jogador notar.
O quarto é reutilizar a mesma cor em dois times. Como o TeamColor é o identificador usado pelos spawns, dois times com a mesma cor tornam impossível dizer a qual deles um spawn pertence.
Criar o time por script no meio da partida. Funciona, e quase sempre é sinal de desenho errado. Os SpawnLocation do mapa apontam para cores de time que precisam existir antes; um time criado depois não tem onde nascer, e os jogadores atribuídos a ele caem no spawn padrão. Times são estrutura do mapa, não estado de partida — o que muda durante o jogo é quem está em qual, não quais existem.
Vale ainda um cuidado com o nome do time, porque ele é usado em dois lugares com exigências diferentes: ele aparece na lista de jogadores, onde precisa ser legível e curto, e é a chave usada na atribuição por script, onde precisa ser exato. Renomear um time depois de o jogo estar no ar quebra toda atribuição escrita com o nome antigo — e o erro não aparece até alguém entrar e não receber time nenhum.
Próximo passo
Com os times funcionando, o exercício de maior retorno é a conferência de spawn — e ela vale ser feita antes de qualquer teste com gente.
Rode a segunda parte do artefato no Studio e leia os avisos. A lista costuma ter pelo menos um item em qualquer mapa que tenha crescido com o tempo: um spawn antigo que ficou neutro, ou um time novo para o qual ninguém criou ponto de nascimento.
Depois disso, vale pensar no que o time significa além da cor. O serviço entrega identificação visual e separação na lista; tudo o que for regra — dano amigo, porta que só um time abre, placar por equipe — é código seu, e todo ele lê a mesma propriedade Team do jogador.
E, se o seu jogo tem partidas, a pergunta seguinte é o que acontece quando alguém sai no meio. O balanceamento automático da Roblox só atua na entrada; manter os times equilibrados durante a partida é decisão de desenho, e é por isso que o artefato traz um reequilíbrio que move quem entrou por último, em vez de quem está jogando há mais tempo.
Fontes
- Implement teams | Documentation - Roblox Creator Hub — consultada em
Artefato pronto pra usar
-- ServerScriptService: distribuição de times com balanceamento próprio.
--
-- O padrão da Roblox joga o jogador novo no time com MENOS membros, e
-- isso resolve a maior parte dos casos. Este script existe para quando
-- não resolve: partida em andamento, times com papéis diferentes,
-- amigos que precisam cair juntos.
--
-- Para ele funcionar, o AutoAssignable de CADA time precisa estar
-- DESMARCADO. Com ele ligado, a Roblox distribui antes e este script
-- vira uma segunda opinião que chega tarde.
local Teams = game:GetService("Teams")
local Players = game:GetService("Players")
local Distribuicao = {}
local function contar(time)
return #time:GetPlayers()
end
-- O time com menos gente. Empate: o primeiro da lista, que é estável.
local function menor()
local escolhido, menos = nil, math.huge
for _, time in Teams:GetTeams() do
local n = contar(time)
if n < menos then
escolhido, menos = time, n
end
end
return escolhido
end
function Distribuicao.entrar(player)
local time = menor()
if not time then
warn("nenhum Team configurado em Teams: o jogador fica sem time")
return
end
player.Team = time
player.Neutral = false
end
-- Reequilibra quando alguém sai e a diferença fica grande. Move o
-- jogador que entrou POR ÚLTIMO no time cheio -- é o que menos
-- atrapalha quem já está jogando.
function Distribuicao.reequilibrar(diferencaMaxima)
local times = Teams:GetTeams()
if #times < 2 then
return
end
table.sort(times, function(a, b) return contar(a) > contar(b) end)
local cheio, vazio = times[1], times[#times]
if contar(cheio) - contar(vazio) <= (diferencaMaxima or 1) then
return
end
local candidatos = cheio:GetPlayers()
local ultimo = candidatos[#candidatos]
if ultimo then
ultimo.Team = vazio
ultimo:LoadCharacter()
end
end
-- ── CONFERÊNCIA DE SPAWN, no Studio ─────────────────────────────────
-- Roda uma vez e aponta os três defeitos que não dão erro:
-- 1. spawn neutro num jogo com times
-- 2. spawn com TeamColor que não é de nenhum time
-- 3. time sem nenhum spawn
function Distribuicao.conferirSpawns()
local cores = {}
for _, time in Teams:GetTeams() do
cores[time.TeamColor.Name] = time.Name
end
local atendidos = {}
for _, obj in workspace:GetDescendants() do
if obj:IsA("SpawnLocation") then
if obj.Neutral then
warn("spawn NEUTRO: " .. obj:GetFullName())
elseif not cores[obj.TeamColor.Name] then
-- o caso silencioso: TeamColor que não bate com time nenhum
warn("spawn com TeamColor sem time: " .. obj:GetFullName()
.. " (" .. obj.TeamColor.Name .. ")")
else
atendidos[cores[obj.TeamColor.Name]] = true
end
end
end
for _, time in Teams:GetTeams() do
if not atendidos[time.Name] then
warn("time SEM SPAWN: " .. time.Name)
end
end
end
Players.PlayerAdded:Connect(Distribuicao.entrar)
Players.PlayerRemoving:Connect(function()
task.defer(Distribuicao.reequilibrar, 1)
end)
return Distribuicao


