Spawn e respawn

*Este conteúdo é traduzido por IA (Beta) e pode conter erros. Para ver a página em inglês, clique aqui.

Spawn é o processo de criar um objeto ou personagem em um jogo, e respawn é o processo de adicionar um objeto ou personagem de volta ao jogo após atender a uma condição de remoção, como a saúde de um personagem atingir zero ou cair do mapa. Ambos os processos são importantes porque garantem que os jogadores possam entrar no seu jogo e continuar jogando para melhorar suas habilidades.

Usando o jogo de laser tag de exemplo como referência, esta seção do tutorial ensina como usar e personalizar os recursos integrados do Roblox para lidar com spawn e respawn, incluindo orientações de script sobre:

  • Configurar locais de spawn para que os jogadores possam apenas aparecer na zona de spawn de sua equipe.
  • Adicionar novos jogadores e seus personagens à rodada à medida que eles entram no jogo.
  • Personalizar campos de força que previnem danos enquanto os jogadores aparecem e reaparecem.
  • Lidar com o estado do cliente para que a jogabilidade funcione corretamente no momento apropriado.
  • Respawn de personagens após serem eliminados da rodada.
  • Realizar pequenas ações diversas que são cruciais para definir parâmetros de jogabilidade e de personagens.

Esta seção inclui bastante conteúdo de script, mas em vez de escrever tudo do zero ao criar um jogo, ela incentiva você a aproveitar componentes existentes, iterar rapidamente e descobrir quais sistemas precisam de uma implementação personalizada para corresponder à sua visão. Após concluir esta seção, você aprenderá como implementar uma jogabilidade baseada em rodadas que rastreia pontos, monitora o estado dos jogadores e exibe os resultados das rodadas.

Configurar locais de spawn

Se você fosse testar o jogo agora, todos os jogadores apareceriam aleatoriamente no objeto SpawnLocation na zona de spawn da equipe verde, ou no objeto SpawnLocation na zona de spawn da equipe rosa. Isso apresenta um problema de jogabilidade onde os jogadores poderiam se eliminar mutuamente dentro de cada zona de spawn assim que o campo de força de seu oponente desaparecesse.

Para combater esse problema, o jogo de laser tag de exemplo configura ambos os locais de spawn com a propriedade Neutral definida como false para restringir os jogadores da equipe adversária de aparecerem na zona de spawn errada, e uma propriedade TeamColor definida para o valor correspondente de Team.Color de Atribuir Cores de Equipe na seção anterior do tutorial:

  • TeamASpawn – O local de spawn na zona de spawn da equipe verde com a propriedade TeamColor definida como Mint.
  • TeamBSpawn – O local de spawn na zona de spawn da equipe rosa com a propriedade TeamColor definida como Carnation Pink.
TeamASpawn
TeamBSpawn

Quando um jogador entra no jogo, ServerScriptServiceGameplayRoundsspawnPlayersInMap verifica quantos jogadores já estão em cada equipe e, em seguida, retorna a equipe com o menor número de jogadores.

spawnPlayersInMap
local function getSmallestTeam(): Team
local teams = Teams:GetTeams()
-- Classifica as equipes em ordem crescente do menor para o maior
table.sort(teams, function(teamA: Team, teamB: Team)
return #teamA:GetPlayers() < #teamB:GetPlayers()
end)
-- Retorna a menor equipe
return teams[1]
end

Uma vez que sabe qual é a equipe com o menor número de jogadores, ele classifica o jogador nessa equipe, define sua propriedade Player.Neutral como false para que o jogador possa apenas aparecer e reaparecer no local de spawn de sua equipe, e então define seu PlayerState para SelectingBlaster, que você aprenderá mais adiante no tutorial.

spawnPlayersInMap
local function spawnPlayersInMap(players: { Player })
for _, player in players do
player.Team = getSmallestTeam()
player.Neutral = false
player:SetAttribute(PlayerAttribute.playerState, PlayerState.SelectingBlaster)
task.spawn(function()
player:LoadCharacter()
end)
end
end

Se você examinar WorkspaceWorldMapSpawns, você pode ver que há um local de spawn a mais no mapa: NeutralSpawn. Este local de spawn é único em relação aos outros porque não tem uma propriedade TeamColor definida para uma das duas equipes no jogo; em vez disso, este local de spawn tem uma propriedade Neutral que muda dependendo se uma rodada está ativa.

Por exemplo, se a rodada estiver ativa, a propriedade Neutral é definida como false para que spawnPlayersInMap possa classificar os jogadores em equipes e spawná-los na arena. No entanto, se a rodada não estiver ativa, como o tempo entre uma rodada e outra, a propriedade Neutral é definida como true para que os jogadores possam aparecer lá independentemente de seu status de equipe. Esse processo é o que torna o local de spawn Neutral um lobby funcional.

Neutral

Para demonstrar, se você examinar ServerScriptServiceGameplayRoundsSpawnPlayersInLobby, que é executado no final de uma rodada, você pode ver que para cada jogador que é passado para a tabela players: { Player }, o script:

  • Define sua propriedade Player.Neutral como true para redefinir automaticamente sua Player.Team para nil, permitindo que o jogador reapareça no lobby quando uma rodada não está ativa, já que a propriedade Neutral do local de spawn também está definida como true.
  • Altera seu PlayerState para InLobby para remover a blaster do jogador e os visuais da interface do usuário em primeira pessoa.

Para mais informações sobre a zona de spawn neutra e sua funcionalidade para cada rodada, veja Adicionando Rodadas na próxima seção do tutorial.

spawnPlayersInLobby
local function spawnPlayersInLobby(players: { Player })
for _, player in players do
player.Neutral = true
player:SetAttribute(PlayerAttribute.playerState, PlayerState.InLobby)
task.spawn(function()
player:LoadCharacter()
end)
end
end

Conectar novos jogadores

O código Luau no Studio é frequentemente orientado a eventos, o que significa que os scripts escutam eventos de um serviço do Roblox e, em seguida, chamam uma função em resposta. Por exemplo, ao adicionar novos jogadores a um jogo multiplayer, deve haver um evento que lida com tudo o que é necessário para que os jogadores se conectem com sucesso. No jogo de laser tag de exemplo, esse evento correspondente é Players.PlayerAdded:Connect.

Players.PlayerAdded:Connect faz parte de vários scripts no jogo. Se você usar o atalho Ctrl/Cmd+Shift+F e pesquisar por Players.PlayerAdded:Connect, os resultados fornecem um bom ponto de partida para entender a configuração inicial do jogo.

Janela Find All do Studio com os resultados de Players.PlayerAdded destacados.

Para demonstrar, abra ServerScriptServiceSetupHumanoid. A distinção entre Player e Character é fundamental para entender este script:

  • Um jogador é um cliente conectado, e um personagem é um modelo Humanoid.
  • Os jogadores precisam escolher uma blaster e ser adicionados ao placar. Os personagens precisam aparecer e receber uma blaster.

SetupHumanoid verifica imediatamente se o jogador tem um personagem (acabou de entrar) ou não (está reaparecendo). Depois de encontrar um, chama onCharacterAdded(), obtém o modelo Humanoid do personagem e o passa para ServerScriptServiceSetupHumanoidsetupHumanoidAsync para personalização. Após definir esses valores, o script aguarda a saúde do personagem atingir zero. Você aprenderá mais sobre respawn mais adiante nesta seção do tutorial.

setupHumanoidAsync
local function setupHumanoidAsync(player: Player, humanoid: Humanoid)
humanoid.DisplayDistanceType = Enum.HumanoidDisplayDistanceType.Subject
humanoid.NameDisplayDistance = 1000
humanoid.HealthDisplayDistance = 1000
humanoid.NameOcclusion = Enum.NameOcclusion.OccludeAll
humanoid.HealthDisplayType = Enum.HumanoidHealthDisplayType.AlwaysOn
humanoid.BreakJointsOnDeath = false
humanoid.Died:Wait()
onHumanoidDied(player, humanoid)
end

A nota importante com este script é que as propriedades são completamente opcionais, o que significa que se você remover as primeiras seis linhas da função, o jogo ainda funcionará corretamente. Em vez de serem requisitos funcionais, cada propriedade permite que você tome decisões de design que atendam aos seus objetivos de jogabilidade. Por exemplo:

  • Se você quiser que os nomes dos personagens sejam exibidos a distâncias mais curtas, reduza o valor de Humanoid.NameDisplayDistance.
  • Se você quiser que a saúde de um personagem seja exibida apenas se estiver abaixo de 100%, defina Humanoid.HealthDisplayType como DisplayWhenDamaged.
  • Se você quiser que os personagens se desfaçam quando sua saúde atingir 0, defina Humanoid.BreakJointsOnDeath como True.

Se você alterar os valores dessas propriedades, é importante testar o jogo para que você possa ver o impacto de suas novas configurações. Você pode recriar o que os jogadores experimentam em uma simulação de múltiplos clientes selecionando pelo menos dois personagens em um teste de Server & Clients a partir do mezanino.

Opção Server & Clients no menu suspenso de modos de teste do mezanino do Studio.

Outro exemplo do evento Players.PlayerAdded:Connect está em ServerScriptServicePlayerStateHandler. Assim como no exemplo anterior, PlayerStateHandler verifica imediatamente se há um personagem. Se o jogador não estiver no lobby, o script define um atributo do jogador para o estado SelectingBlaster, o estado inicial para uma rodada em que os jogadores podem selecionar entre dois tipos diferentes de blaster após aparecerem na arena. Este estado também inclui um campo de força que impede que os jogadores sofram danos enquanto estão fazendo sua seleção.

PlayerStateHandler
local function onPlayerAdded(player: Player)
player.CharacterAdded:Connect(function()
if not player.Neutral then
player:SetAttribute(PlayerAttribute.playerState, PlayerState.SelectingBlaster)
onPlayerStateChanged(player, PlayerState.SelectingBlaster)
end
end)

Uma variável particular em PlayerStateHandler merece discussão: attributeChangedConnectionByPlayer. Esta tabela armazena todos os jogadores e suas Connections para o GetAttributeChangedSignal. A razão para armazenar essa conexão em uma tabela é para que PlayerStateHandler possa desconectá-la quando o jogador sair do jogo. Esse processo serve como uma espécie de gerenciamento de memória para evitar que o número de conexões cresça cada vez mais ao longo do tempo.

PlayerStateHandler
local attributeChangedConnectionByPlayer = {}
local function onPlayerAdded(player: Player)
-- Lida com todas as futuras atualizações do estado do jogador
attributeChangedConnectionByPlayer[player] = player
:GetAttributeChangedSignal(PlayerAttribute.playerState)
:Connect(function()
local newPlayerState = player:GetAttribute(PlayerAttribute.playerState)
onPlayerStateChanged(player, newPlayerState)
end)
end
-- Desconecta da conexão de atributo alterado quando o jogador sai
local function onPlayerRemoving(player: Player)
if attributeChangedConnectionByPlayer[player] then
attributeChangedConnectionByPlayer[player]:Disconnect()
attributeChangedConnectionByPlayer[player] = nil
end
end

Você pode ver que ambas as funções conectadas em onPlayerAdded() chamam onPlayerStateChanged(). Durante a configuração inicial, após um jogador ser classificado em uma equipe, onPlayerAdded() define PlayerState como SelectingBlaster, então a primeira instrução if avalia como falsa e desabilita o BlasterState. Na seção Implementar blasters do tutorial, você aprenderá mais detalhes sobre esse processo.

PlayerStateHandler
local function onPlayerStateChanged(player: Player, newPlayerState: string)
-- O estado do blaster é 'Pronto' apenas se o estado do jogador for 'Jogando'
local newBlasterState = if newPlayerState == PlayerState.Playing then BlasterState.Ready else BlasterState.Disabled
-- Agenda a lógica de destruição do campo de força quando o jogador começa a jogar
if newPlayerState == PlayerState.Playing then
scheduleDestroyForceField(player)
end
player:SetAttribute(PlayerAttribute.blasterStateServer, newBlasterState)
end

Se você adicionar pontos de interrupção ou até mesmo apenas uma instrução print(), poderá ver que onPlayerStateChanged() é chamado frequentemente ao longo do jogo: como durante a configuração inicial de uma rodada, para se definir no caminho principal do código, após o jogador escolher uma blaster, e quando o jogador retorna ao lobby ou ao local de spawn Neutral. Além disso, após o jogador escolher uma blaster, ServerScriptServiceBlasterSelectedHandler define o PlayerState como Playing, e PlayerStateHandler pode finalmente remover o campo de força chamando scheduleDestroyForceField().

Personalizar campos de força

Em vez de usar uma implementação personalizada, o jogo de laser tag de exemplo usa a classe ForceField integrada do Studio para evitar que os jogadores sofram danos enquanto estão selecionando sua blaster. Isso garante que o único requisito para os jogadores aparecerem com um campo de força seja incluir locais de spawn com uma propriedade SpawnLocation.Duration que seja maior que 0. O exemplo usa um valor arbitrário de 9.999 para habilitar campos de força, e depois lida com a duração real programaticamente em ReplicatedStorageForceFieldClientVisuals.

Semelhante a setupHumanoidAsync, a maioria das linhas em ForceFieldClientVisuals são opcionais. Por exemplo, se você comentar o conteúdo da função como o seguinte script faz, o jogo usará o campo de força padrão em vez do script hexagonal em StarterGuiForceFieldGui.

Commenting Out Properties in ForceFieldClientVisuals
local function onCharacterAddedAsync(character: Model)
-- local forceField = character:WaitForChild("ForceField", 3)
-- if not forceField then
-- return
-- end
-- forceField.Visible = false
-- localPlayer.PlayerGui:WaitForChild("ForceFieldGui").Enabled = true
-- forceField.Destroying:Wait()
-- localPlayer.PlayerGui.ForceFieldGui.Enabled = false
end

Como o campo de força personalizado é uma GUI em vez de um novo ParticleEmitter, o script ForceFieldClientVisuals afeta apenas os visuais em primeira pessoa para cada jogador, não os visuais em terceira pessoa quando os jogadores olham para outros jogadores. Os visuais em terceira pessoa mantêm a aparência padrão do Roblox. Para mais informações sobre como modificar campos de força, veja ForceField.Visible.

Os visuais do campo de força em primeira pessoa incluem uma grade hexagonal futurista na periferia da tela.
Visuais do campo de força em primeira pessoa
Os visuais do campo de força em terceira pessoa incluem um orbe azul brilhante ao redor do jogador que está aparecendo no jogo.
Visuais do campo de força em terceira pessoa

Os campos de força são úteis porque fornecem aos jogadores tempo suficiente entre o spawn e o respawn sem precisar se preocupar com jogadores inimigos, mas eventualmente precisam desaparecer para a jogabilidade principal do laser tag. O script que lida com a remoção do campo de força está em ReplicatedStoragescheduleDestroyForceField, e verifica três condições únicas:

  • Após os jogadores selecionarem uma blaster, os campos de força precisam durar o suficiente para permitir que os jogadores se aclimatem ao seu entorno.
  • Durante esse tempo de aclimatação, os campos de força não podem ser uma vantagem, então precisam desaparecer no momento em que um jogador dispara sua blaster.
  • Os campos de força precisam desaparecer quando os jogadores redefinem seus personagens, seja antes de disparar ou antes que o tempo do campo de força acabe.

Cada uma dessas verificações no script scheduleDestroyForceField chama endForceField() para essas condições.

scheduleDestroyForceField
-- Finaliza o campo de força se o jogador disparar
local blasterStateAttribute = getBlasterStateAttribute()
attributeChangedConnection = player:GetAttributeChangedSignal(blasterStateAttribute):Connect(function()
local currentBlasterState = player:GetAttribute(blasterStateAttribute)
if currentBlasterState == BlasterState.Blasting then
endForceField()
end
end)
-- Finaliza o campo de força se o jogador redefinir
characterRespawnedConnection = player.CharacterRemoving:Connect(endForceField)
-- Finaliza o campo de força após 8 segundos
task.delay(MAX_FORCE_FIELD_TIME, endForceField)

endForceField() inclui uma instrução if aparentemente estranha em torno do booleano forceFieldEnded. Como as verificações são executadas sequencialmente, o script pode chamar a função endForceField() duas ou até três vezes. O booleano forceFieldEnded garante que a função tente destruir um campo de força apenas uma vez.

scheduleDestroyForceField
local function endForceField()
if forceFieldEnded then
return
end
forceFieldEnded = true
attributeChangedConnection:Disconnect()
characterRespawnedConnection:Disconnect()
destroyForceField(player)
end

Lidar com o estado do cliente

Enquanto a maior parte desta seção se concentra em ServerScriptServicePlayerStateHandler, há outro script com o mesmo nome em ReplicatedStorage. A razão para a divisão é a arquitetura cliente-servidor:

  • O cliente precisa entender as informações do estado do jogador para que possa responder adequadamente em tempo real, como exibindo os elementos corretos da interface do usuário ou permitindo que os jogadores se movam e disparem.

  • O servidor precisa de todas essas mesmas informações para que possa prevenir exploits. Por exemplo, o servidor também precisa do estado do jogador para realizar ações como spawn e equipar personagens, desabilitar campos de força e exibir um placar. É por isso que este script está em ReplicatedStorage e não em um local puramente do lado do cliente.

Para ver essa lógica central, revise o seguinte script em ReplicatedStoragePlayerStateHandler que verifica o estado atual do usuário e, em seguida, chama a função apropriada que lida com as ações correspondentes para esse estado.

PlayerStateHandler
local function onPlayerStateChanged(newPlayerState: string)
if newPlayerState == PlayerState.SelectingBlaster then
onSelectingBlaster()
elseif newPlayerState == PlayerState.Playing then
onPlaying()
elseif newPlayerState == PlayerState.TaggedOut then
onTaggedOut()
elseif newPlayerState == PlayerState.InLobby then
onInLobby()
else
warn(`Estado de jogador inválido ({newPlayerState})`)
end
end

Todas as respostas a eventos estão logicamente agrupadas neste script porque requerem um comportamento semelhante de habilitar ou desabilitar os controles do jogador, o movimento da câmera e qual camada da interface do usuário está visível. Por exemplo, durante a seleção da blaster, os jogadores precisam ser tanto invulneráveis quanto incapazes de se mover. O servidor já lida com o campo de força, mas o cliente lida com o movimento. Para demonstrar, se você verificar a lógica da função onSelectingBlaster(), poderá ver que o cliente desabilita o movimento do jogador enquanto eles estão selecionando uma blaster.

PlayerStateHandler
local function onSelectingBlaster()
togglePlayerCamera(true)
togglePlayerMovement(false)
setGuiExclusivelyEnabled(playerGui.PickABlasterGui)
localPlayer:SetAttribute(PlayerAttribute.blasterStateClient, BlasterState.Disabled)
end

A função onPlaying() é igualmente direta. Ela habilita o movimento, transita para a interface principal (HUD), habilita a blaster e chama a mesma função de campo de força que o servidor.

PlayerStateHandler
local function onPlaying()
togglePlayerMovement(true)
setGuiExclusivelyEnabled(playerGui.HUDGui)
localPlayer:SetAttribute(PlayerAttribute.blasterStateClient, BlasterState.Ready)
scheduleDestroyForceField()
end

Respawn de personagens

O jogo de laser tag de exemplo lida com o respawn de personagens de volta a uma rodada através do estado onTaggedOut() em ReplicatedStoragePlayerStateHandler. Assim como os estados onSelectingBlaster() e onPlaying(), onTaggedOut() aciona um comportamento único de acordo com as mudanças no atributo playerState. Especificamente, ele desabilita o movimento do jogador, apresenta a interface de respawn e desabilita a blaster.

PlayerStateHandler
local function onTaggedOut()
-- Desabilita controles enquanto está eliminado
togglePlayerMovement(false)
togglePlayerCamera(false)
setGuiExclusivelyEnabled(playerGui.OutStateGui)
-- Desabilita blaster enquanto está eliminado
localPlayer:SetAttribute(PlayerAttribute.blasterStateClient, BlasterState.Disabled)
end

Se você quiser testar esse comportamento, pode pressionar Esc, navegar até a aba Configurações e, em seguida, clicar no botão Redefinir Personagem. Note que quando você aciona a tela de respawn, não pode se mover, rotacionar a câmera ou disparar sua blaster.

Menu de configurações do Roblox com o botão Redefinir Personagem destacado.
Botão Redefinir Personagem
A tela de respawn é exibida enquanto um jogador reaparece na rodada.
Tela de Respawn

É importante notar que este script não respawn realmente os personagens, ele apenas os impede de agir e fornece feedback visual aos jogadores de que o servidor está respawnando seus personagens. Para demonstrar, se você examinar ServerScriptServiceSetupHumanoidsetupHumanoidAsynconHumanoidDied, o script define PlayerState como TaggedOut (notificando essencialmente ReplicatedStoragePlayerStateHandler), e adiciona alguns indicadores visuais. A lógica real de respawn é um comportamento integrado do Roblox.

Quando os jogadores reaparecem na rodada, eles reaparecem no local de spawn de sua equipe de acordo com a propriedade SpawnLocation.TeamColor. Para personalizar o tempo de respawn, você pode adicionar a seguinte linha ao topo de SetupHumanoid. Para aprender mais sobre essa técnica, veja Players.RespawnTime.

SetupHumanoid
local Players = game:GetService("Players")
Players.RespawnTime = 10 -- nova linha, em segundos

Configuração diversificada

Como parte da configuração inicial, o jogo de laser tag de exemplo também realiza alguns pequenos, mas críticos passos:

  • O jogo inclui um script vazio chamado StarterPlayerStarterCharacterScriptsHealth que desabilita a regeneração de saúde padrão do Roblox. Para uma explicação do comportamento dessa propriedade, veja Humanoid.Health.

  • O jogo usa uma câmera em primeira pessoa definindo a propriedade StarterPlayer.CameraMode.LockFirstPerson. Note que se você quiser permitir que os usuários mudem entre câmeras em primeira e terceira pessoa, deve alterar a propriedade programaticamente em vez de apenas defini-la uma vez no Studio, e modificar os controles e a interface do usuário para compensar a mudança de perspectiva.

  • O jogo usa o placar integrado do Roblox com a unidade de "pontos", que os jogadores ganham cada vez que eliminam outro jogador. Você pode ver a configuração em ServerScriptServiceSetupLeaderboard, mas Placar em Jogo oferece uma visão completa. Note que onPlayerTagged adiciona pontos ao placar, que você aprenderá em Adicionar rodadas e Detectar acertos.

Agora que os jogadores podem aparecer, escolher uma blaster e mirá-la de um ponto de vista em primeira pessoa, a próxima seção ensina sobre os scripts por trás da criação de jogabilidade baseada em rodadas.

©2026 Roblox Corporation, Roblox, o logotipo Roblox e Powering Imagination estão entre nossas marcas registradas e não registradas nos EUA e em outros países.