Apparaître et réapparaître

*Ce contenu est traduit en utilisant l'IA (Beta) et peut contenir des erreurs. Pour consulter cette page en anglais, clique ici.

La génération est le processus de création d'un objet ou d'un personnage dans une expérience, et la réapparition est le processus d'ajouter un objet ou un personnage dans une expérience après qu'ils aient atteint une condition de suppression, comme la santé d'un personnage atteignant zéro ou tombant de la carte.Les deux processus sont importants car ils garantissent que les joueurs peuvent rejoindre votre expérience et poursuivre leur jeu pour améliorer leurs compétences.

En utilisant l'expérience de balise laser de échantillon comme référence, cette section du tutoriel vous apprend à utiliser et à personnaliser les fonctionnalités intégrées de Roblox pour gérer la génération et la réapparition, y compris les directives de script sur :

  • Configuration des points d'apparition afin que les joueurs ne puissent apparaître que dans la zone d'apparition de leur équipe.
  • Ajouter de nouveaux joueurs et leur personnage à la manche au fur et à mesure qu'ils rejoignent l'expérience.
  • Personnalisation des champs de force qui empêchent les dommages que les joueurs apparaissent et réapparaissent.
  • Gérer l'état du client afin que le jeu fonctionne correctement au bon moment.
  • Réapparition des personnages après qu'ils aient été exclus de la manche.
  • Exécution de petites actions diverses qui sont cruciales pour définir les paramètres de jeu et les personnages.

Cette section inclut beaucoup de contenu de script, mais au lieu d'écrire tout à partir de zéro lors de la création d'une expérience, elle vous encourage à tirer parti des composants existants, à itérer rapidement et à déterminer les systèmes qui ont besoin d'une implémentation personnalisée pour correspondre à votre vision.Une fois que vous aurez terminé cette section, vous apprendrez à mettre en œuvre un jeu en plusieurs manches qui suit des points, surveille l'état du joueur et affiche les résultats des manches.

Configurer les emplacements d'apparition

Si vous deviez tester l'expérience en ce moment, tous les joueurs apparaîtraient au hasard à l'objet SpawnLocation dans la zone de génération de l'équipe verte, ou à l'objet SpawnLocation dans la zone de génération de l'équipe rose.Cela présente un problème de gameplay où les joueurs pourraient se marquer les uns les autres dans chaque zone de génération dès que le champ de force de leur adversaire disparaît.

Pour combattre ce problème, l'expérience de balise laser de test configure les deux emplacements de génération avec une propriété définie sur faux pour restreindre les joueurs de l'équipe adverse de se générer dans la mauvaise zone de génération, et une propriété définie sur la valeur correspondante de Assigner les couleurs d'équipe dans la section précédente du tutoriel :

  • TeamASpawn – Le lieu d'apparition dans la zone d'apparition de l'équipe verte avec une propriété TeamColor définie sur Mint .
  • TeamBSpawn – Le lieu d'apparition dans la zone d'apparition de l'équipe rose avec une propriété TeamColor définie sur Carnation Rose .

TeamASpawn
>

Générateur d'équipeBSpawn
>

Lorsqu'un joueur rejoint l'expérience, ServerScriptService > Gameplay > Rounds > spawnPlayersInMap vérifie combien de joueurs sont déjà dans chaque équipe, puis retourne l'équipe avec le moins de joueurs.

spawnPlayersInMap

local function getSmallestTeam(): Team
local teams = Teams:GetTeams()
-- Trier les équipes dans l'ordre croissant du plus petit au plus grand
table.sort(teams, function(teamA: Team, teamB: Team)
return #teamA:GetPlayers() < #teamB:GetPlayers()
end)
-- Retourner l'équipe la plus petite
return teams[1]
end

Une fois qu'il connaît l'équipe avec le moins de joueurs, il trie le joueur dans cette équipe, définit sa propriété à faux afin que le joueur ne puisse apparaître et réapparaître que dans l'emplacement de génération de son équipe, puis définit sa propriété à , dont vous apprendrez plus tard dans le tutoriel.

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

Si vous examinez Espace de travail > Monde > Carte > Spawns , vous pouvez voir qu'il y a un autre lieu d'apparition sur la carte : NeutralSpawn .Ce lieu d'apparition est unique des autres parce qu'il n'a pas de propriété TeamColor définie sur l'une des deux équipes de l'expérience ; au lieu de cela, ce lieu d'apparition a une propriété Neutral qui change en fonction de si une ronde est active.

Par exemple, si la ronde est active, la propriété Neutral est définie sur faux afin que spawnPlayersInMap puisse trier les joueurs en équipes et les faire apparaître dans l'arène.Cependant, si la manche n'est pas active, comme le temps entre une manche et la suivante, la propriété Neutral est définie sur vrai afin que les joueurs puissent apparaître là indépendamment du statut de leur équipe.Ce processus est ce qui fait de la location d'apparition neutre un lieu de lobby fonctionnel.

Néutre

Pour démontrer, si vous examinez ServerScriptService > Gameplay > Rounds > SpawnPlayersInLobby , qui s'exécute à la fin d'une ronde, vous pouvez voir que pour chaque joueur qui est passé dans la table players: { Player }, le script :

  • Définit leur propriété à vrai pour réinitialiser automatiquement leur à , permettant au joueur de réapparaître dans le lobby lorsqu'une ronde n'est pas active, car la propriété de l'emplacement d'apparition est également définie sur vrai .
  • Modifie leur PlayerState à InLobby pour supprimer les visuels de l'interface utilisateur en première personne et le blaster du joueur.

Pour plus d'informations sur la zone d'apparition neutre et sa fonctionnalité pour chaque tour, voir Ajouter des tours dans la section suivante du tutoriel.

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

Connecter de nouveaux joueurs

Le code Luau dans Studio est souvent événementiel, ce qui signifie que les scripts écoutent des événements d'un service Roblox, puis appellent une fonction en réponse.Par exemple, lors de l'ajout de nouveaux joueurs à une expérience multijoueur, il doit y avoir un événement qui gère tout ce qui est nécessaire pour que les joueurs se connectent avec succès.Dans l'expérience de balise laser d'échantillon, cet événement correspondant est Players.PlayerAdded:Connect .

Players.PlayerAdded:Connect fait partie de plusieurs scripts dans l'expérience.Si vous utilisez le raccourci Ctrl/Cmd+Shift+F et recherchez Players.PlayerAdded:Connect, les résultats fournissent un bon point de départ pour comprendre le démarrage initial de l'expérience.

Studio's Find All window with the Players.PlayerAdded results highlighted.

Pour démontrer, ouvrez ServerScriptService > SetupHumanoid .La distinction entre Player et Character est essentielle pour comprendre ce script :

  • Un joueur est un client connecté, et un caractère est un modèlisation.
  • Les joueurs doivent choisir un blaster et être ajoutés au classements. Les personnages doivent apparaître et recevoir un blaster.

SetupHumanoid vérifie immédiatement si le joueur a un personnage (vient de rejoindre) ou non (réapparaît).Après avoir trouvé une, elle appelle onCharacterAdded() , obtient le modèle Humanoid du personnage et le transmet à ServerScriptService > SetupHumanoid > setupHumanoidAsync pour la personnalisation.Après avoir défini ces valeurs, le script attend ensuite que la santé du personnage atteigne zéro.Vous apprendrez plus sur la réapparition plus tard dans cette section du tutoriel.

设置HumanoidAsync

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

La note importante avec ce script est que les propriétés sont complètement optionnelles, ce qui signifie que si vous supprimez les six premières lignes de la fonction, l'expérience fonctionne toujours correctement.Plutôt que d'être des exigences fonctionnelles, chaque propriété vous permet de prendre des décisions de conception qui répondent à vos objectifs de jeu.Par exemple :

  • Si vous voulez que les noms de caractères s'affichent à des distances plus proches, réduisez la valeur de Humanoid.NameDisplayDistance .
  • Si vous voulez seulement que la santé d'un personnage soit affichée si elle est inférieure à 100%, définissez Humanoid.HealthDisplayType Afficher lorsqu'il est endommagé .
  • Si vous voulez que les personnages se brisent lorsque leur santé atteint 0, définissez Humanoid.BreakJointsOnDeath à Vrai .

Si vous changez les valeurs de ces propriétés, il est important de tester le jeu afin que vous puissiez voir l'impact de vos nouvelles paramètres.Vous pouvez recréer l'expérience que les joueurs vivent dans un environnement multijoueur en sélectionnant au moins deux personnages dans la section Clients et serveurs de l'onglet Test .

Studio's Test tab with the the players dropdown highlighted. This setting needs to be at least two players to see the impact of your new settings.

Un autre exemple de l'événement Players.PlayerAdded:Connect est dans ServerScriptService > PlayerStateHandler .Tout comme dans l'exemple précédent, PlayerStateHandler vérifie immédiatement un caractère.Si le joueur n'est pas dans le lobby, le script définit une attribut du joueur à l'état SelectingBlaster , l'état initial pour une ronde dans laquelle les joueurs peuvent sélectionner l'un des deux types de blaster différents après leur apparition dans l'arène.Cet état comprend également un champ de force qui empêche les joueurs de subir des dommages pendant qu'ils font leur sélection.

Gestionnaire d'état du joueur

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)

Une variable spécifique dans PlayerStateHandler guerre mérite une discussion : attributeChangedConnectionByPlayer .Cette table stocke tous les joueurs et leurs Connections à la GetAttributeChangedSignal .La raison de stocker cette connexion dans une table est que PlayerStateHandler peut la déconnecter lorsque le joueur quitte l'expérience.Ce processus sert comme une sorte de gestion de la mémoire pour empêcher le nombre de connexions de croître de plus en plus au fil du temps.

Gestionnaire d'état du joueur

local attributeChangedConnectionByPlayer = {}
local function onPlayerAdded(player: Player)
-- Gérer toutes les futures mises à jour de l'état du joueur
attributeChangedConnectionByPlayer[player] = player
:GetAttributeChangedSignal(PlayerAttribute.playerState)
:Connect(function()
local newPlayerState = player:GetAttribute(PlayerAttribute.playerState)
onPlayerStateChanged(player, newPlayerState)
end)
end
-- Déconnexion de la connexion modifiée par l'attribut lorsque le joueur quitte
local function onPlayerRemoving(player: Player)
if attributeChangedConnectionByPlayer[player] then
attributeChangedConnectionByPlayer[player]:Disconnect()
attributeChangedConnectionByPlayer[player] = nil
end
end

Vous pouvez voir que les deux fonctions connectées dans onPlayerAdded() appellent onPlayerStateChanged() .Pendant le paramètre initial après qu'un joueur se soit rangé dans une équipe, onPlayerAdded() définit PlayerState à SelectingBlaster , de sorte que la première déclaration if évaluera à faux et désactivera le BlasterState .Dans la section Implémenter des blasters du tutoriel plus tard, vous apprendrez plus de détails sur ce processus.

Gestionnaire d'état du joueur

local function onPlayerStateChanged(player: Player, newPlayerState: string)
-- L'état du blaster est « prêt » seulement si l'état du joueur est « en train de jouer »
local newBlasterState = if newPlayerState == PlayerState.Playing then BlasterState.Ready else BlasterState.Disabled
-- Programmer la logique du champ de force de destruction lorsque le joueur commence à jouer
if newPlayerState == PlayerState.Playing then
scheduleDestroyForceField(player)
end
player:SetAttribute(PlayerAttribute.blasterStateServer, newBlasterState)
end

Si vous ajoutez des points de rupture ou même simplement une déclaration , vous pouvez voir que est appelée fréquemment tout au long de l'expérience : pendant le démarrage initial d'une ronde, pour se placer sur le chemin du code principal, après que le joueur ait choisi un blaster, et lorsque le joueur revient au lobby, ou à l'emplacement de génération neutre.De plus, après que le joueur ait choisi un blaster, ServerScriptService > BlasterSelectedHandler défini le PlayerState à Playing, et PlayerStateHandler peut enfin supprimer le champ de force en appelant scheduleDestroyForceField() .

Personnaliser les champs de force

Au lieu d'utiliser une implémentation personnalisée, l'expérience de balise laser d'échantillon utilise la classe intégrée de Studio ForceField pour empêcher les joueurs de subir des dommages pendant qu'ils sélectionnent leur blaster.Cela garantit que la seule exigence pour les joueurs de se générer avec un champ de force est d'inclure des emplacements de génération avec une propriété SpawnLocation.Duration supérieure à 0.L'échantillon utilise une valeur arbitraire de 9 999 pour activer les champs de force, puis gère la durée réelle de manière programmatique dans ReplicatedStorage > ForceFieldClientVisuals .

Semblable à setupHumanoidAsync , la plupart des lignes dans ForceFieldClientVisuals sont facultatives.Par exemple, si vous commentez le contenu de la fonction comme le script suivant, l'expérience utilise le champ de force brillant par défaut au lieu du script hexagonal dans StarterGui > ForceFieldGui .

Commenter les propriétés dans ForceFieldClientVisuals

local function onCharacterAddedAsync(character: Model)
-- forceField local = personnage : WaitForChild("ForceField", 3)
-- si ce n'est pas forceField alors
-- renvoyer
-- terminer
-- forceField.Visible = faux
-- localPlayer.PlayerGui : WaitForChild("ForceFieldGui") est activé = true
-- forceField.Destruction : Wait()
-- localPlayer.PlayerGui.ForceFieldGui.Enabled = faux
end

Comme le champ de force personnalisé est une interface graphique plutôt qu'un nouveau ParticleEmitter, le script ForceFieldClientVisuals n'affecte que les visuels en première personne pour chaque joueur, pas les visuels en troisième personne lorsque les joueurs regardent d'autres joueurs.Les visuels en troisième personne conservent l'apparence par défaut de Roblox.Pour plus d'informations sur la modification des champs de force, voir ForceField.Visible .

First-person force field visuals include a futuristic hexagonal grid on the perimeter of the screen.

Visualisations de champ de force en première personne
>

Third-person force field visuals include a blue sparkling orb around the player spawning into the experience.

Visuels de champ de force en troisième personne
>

Les champs de force sont utiles car ils donnent aux joueurs suffisamment de temps entre la génération et la réapparition sans avoir à s'inquiéter des joueurs ennemis, mais ils doivent finalement disparaître pour le partieprincipal de tag laser.Le script qui gère la suppression du champ de force se trouve dans ReplicatedStorage > scheduleDestroyForceField , et il vérifie trois conditions uniques :

  • Après que les joueurs aient sélectionné un blaster, les champs de force doivent durer assez longtemps pour permettre aux joueurs d'acclimater à leur environnement.
  • Pendant ce temps d'acclimatation, les champs de force ne peuvent pas être un avantage, ils doivent donc disparaître au moment où un joueur lance son blaster.
  • Les champs de force doivent disparaître lorsque les joueurs réinitialisent leurs personnages avant de tirer ou avant l'expiration du champ de force.

Chacun de ces contrôles dans l'appel du script pour ces conditions.

calendrierDestroyForceField

-- Fin du champ de force si le joueur explose
local blasterStateAttribute = getBlasterStateAttribute()
attributeChangedConnection = player:GetAttributeChangedSignal(blasterStateAttribute):Connect(function()
local currentBlasterState = player:GetAttribute(blasterStateAttribute)
if currentBlasterState == BlasterState.Blasting then
endForceField()
end
end)
-- Fin du champ de force si le joueur se réinitialise
characterRespawnedConnection = player.CharacterRemoving:Connect(endForceField)
-- Fin du champ de force après 8 secondes
task.delay(MAX_FORCE_FIELD_TIME, endForceField)

endForceField() inclut une déclaration apparemment étrange if autour du forceFieldEnded booléen.Puisque les vérifications se déroulent séquentiellement, le script peut appeler la fonction endForceField() deux ou même trois fois.Le forceFieldEnded boolean garantit que la fonction ne tente de détruire un champ de force qu'une seule fois.

calendrierDestroyForceField

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

Gérer l'état du client

Bien que la plupart de cette section se concentre sur ServerScriptService > PlayerStateHandler , il y a un autre script du même nom dans ReplicatedStorage .La raison de la division est l'architecture client-serveur :

  • Le client doit comprendre l'information sur l'état du joueur afin qu'il puisse réagir de manière appropriée en temps réel, comme afficher les éléments d'interface utilisateur appropriés ou permettre aux joueurs de se déplacer et de tirer.

  • Le serveur a besoin de toutes ces mêmes informations pour pouvoir empêcher les exploits.Par exemple, le serveur a également besoin de l'état du joueur pour effectuer des actions comme la génération et l'équipement de personnages, la désactivation des champs de force et l'affichage d'un classements.C'est pourquoi ce script est dans ReplicatedStorage et non pas un emplacement purement côté client.

Pour voir cette logique principale, consultez le script suivant dans ReplicatedStorage > PlayerStateHandler qui vérifie l'état actuel de l'utilisateur, puis appelle la fonction appropriée qui gère les actions correspondantes pour cet état.

Gestionnaire d'état du joueur

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(`Invalid player state ({newPlayerState})`)
end
end

Toutes les réponses à des événements sont logiquement regroupées dans ce script car elles nécessitent un comportement similaire pour activer ou désactiver les contrôles du joueur, le mouvement de la caméra et quelle couche d'interface utilisateur est visible.Par exemple, lors de la sélection du blaster, les joueurs doivent être à la fois invulnérables et incapables de se déplacer.Le serveur gère déjà le champ de force, mais le client gère le mouvement.Pour démontrer, si vous vérifiez la logique de la fonction onSelectingBlaster() , vous pouvez voir que le client désactive le mouvement du joueur pendant qu'il sélectionne un blaster.

Gestionnaire d'état du joueur

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

La fonction onPlaying() est également simple.Il permet le mouvement, les transitions vers l'affichage tête principale (HUD), active le blaster et appelle la même fonction de champ de force du serveur.

Gestionnaire d'état du joueur

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

Réapparition des personnages

L'expérience de balise laser d'échantillon gère la réapparition du personnage dans une ronde à travers l'état onTaggedOut() en ReplicatedStorage > PlayerStateHandler .Comme l'état et déclenche un comportement unique selon les modifications de l'attribut .Spécifiquement, il désactive le mouvement du joueur, présente l'interface de réapparition et désactive le blaster.

Gestionnaire d'état du joueur

local function onTaggedOut()
-- Désactiver les contrôles lorsqu'ils sont étiquetés
togglePlayerMovement(false)
togglePlayerCamera(false)
setGuiExclusivelyEnabled(playerGui.OutStateGui)
-- Désactiver le blaster lorsqu'il est étiqueté
localPlayer:SetAttribute(PlayerAttribute.blasterStateClient, BlasterState.Disabled)
end

Si vous voulez tester ce comportement, vous pouvez appuyer sur Esc, naviguer vers l'onglet paramètres puis cliquer sur le bouton réinitialiser le personnage .Remarquez que lorsque vous déclenchez l'écran de réapparition, vous ne pouvez pas vous mouvement, faire pivoter la caméra ou faire exploser votre blaster.

Roblox's settings menu with the Reset Character button highlighted.

Bouton de réinitialisation du personnage
>

The respawn screen displays as a player respawns back into the round.

Écran de réapparition
>

Il est important de noter que ce script ne fait pas réapparaître réellement des personnages, il les empêche simplement d'agir et fournit des commentaires visuels aux joueurs que le serveur réapparaît leurs personnages.Pour démontrer, si vous examinez ServerScriptService > SetupHumanoid > setupHumanoidAsync > onHumanoidDied , le script définit PlayerState à TaggedOut (en essentiellement notifiant ReplicatedStorage > PlayerStateHandler ), et ajoute quelques indicateurs visuels.La logique réelle de réapparition est un comportement Roblox intégré.

Lorsque les joueurs réapparaissent dans la manche, ils réapparaissent à l'emplacement d'apparition de leur équipe selon la propriété SpawnLocation.TeamColor.Pour personnaliser le temps de réapparition, vous pouvez ajouter la ligne suivante en haut de SetupHumanoid .Pour en savoir plus sur cette technique, voir Players.RespawnTime .

Lancement de Humanoid

local Players = game:GetService("Players")
Players.RespawnTime = 10 -- new line, in seconds

Configuration diverses

En tant qu'élément de la configuration initiale, l'expérience de balise laser d'échantillon effectue également quelques petites, mais critiques étapes :

  • L'expérience inclut un script vide nommé StarterPlayer > StarterCharacterScripts > Santé qui désactive la régénération de santé par défaut de Roblox.Pour une explication du comportement de cette propriété, voir Humanoid.Health .

  • L'expérience utilise une caméra à la première personne en définissant la propriété StarterPlayer.CameraMode.LockFirstPerson.Notez que si vous voulez permettre aux utilisateurs de changer entre les caméras en première et en troisième personne, vous devez modifier la propriété de manière programmatique plutôt que de simplement la définir une fois dans Studio, et de modifier les contrôles et l'interface utilisateur pour compenser le changement de perspective.

  • L'expérience utilise le classement intégré de Roblox avec l'unité de «points», que les joueurs gagnent chaque fois qu'ils marquent un autre joueur.Vous pouvez voir la configuration dans ServerScriptService > SetupLeaderboard , mais In-Experience Leaderboards offre une vue d'ensemble complète.Notez que onPlayerTagged ajoute des points au classements, dont vous apprendrez dans Ajouter des tours et Détecter des hits.

Maintenant que les joueurs peuvent régénération, apparition, choisissez un blaster et visez-le d'un point de voiren première personne, la section suivante vous enseigne sur les scripts derrière la création d'un partiede type tour.