Projet de référence sur les plantes

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

Plante est une expérience de référence où les joueurs plantent et arrosent des graines, afin de pouvoir ensuite récolter et vendre les plantes résultantes.

Bannière du projet Plante

Le projet se concentre sur des cas d'utilisation courants que vous pourriez rencontrer lors du développement d'une expérience sur Roblox. Lorsque cela est applicable, vous trouverez des notes sur les compromis, les choix de conception et la motivation derrière les différentes choix d'implémentation, afin que vous puissiez prendre la meilleure décision pour vos propres expériences.

Obtenez le fichier

  1. Accédez à la page de l'expérience Plante.
  2. Cliquez sur le bouton et Modifier dans Studio.

Cas d'utilisation

Plante couvre les cas d'utilisation suivants :

  • Persistance des données de session et des données des joueurs
  • Gestion de la vue de l'interface utilisateur
  • Réseau client-serveur
  • Expérience utilisateur pour la première fois (FTUE)
  • Achats de devises dures et douces

De plus, ce projet résout des ensembles de problèmes plus étroits qui sont applicables à de nombreuses expériences, notamment :

  • Personnalisation d'une zone dans le lieu associée à un joueur
  • Gestion de la vitesse de mouvement du personnage du joueur
  • Création d'un objet qui suit les personnages
  • Détection de la partie du monde dans laquelle un personnage se trouve

Notez qu'il existe plusieurs cas d'utilisation dans cette expérience qui sont trop petits, trop spécifiques ou ne montrent pas une solution à un défi de conception intéressant ; ceux-ci ne sont pas couverts.

Structure du projet

La première décision lors de la création d'une expérience consiste à décider comment structurer le projet, qui comprend principalement où placer des instances spécifiques dans le modèle de données et comment organiser et structurer les points d'entrée pour le code client et serveur.

Modèle de données

Le tableau suivant décrit quels services conteneurs dans le modèle de données contiennent les instances.

ServiceTypes d'instances
Workspace

Contient des modèles statiques représentant le monde 3D, en particulier des parties du monde qui n'appartiennent à aucun joueur. Vous n'avez pas besoin de créer, modifier ou détruire ces instances dynamiquement à l'exécution, il est donc acceptable de les laisser ici.

Il y a également un Folder vide, auquel les modèles de ferme des joueurs seront ajoutés à l'exécution.

Lighting

Effets atmosphériques et d'éclairage.

ReplicatedFirst

Contient le plus petit sous-ensemble possible d'instances nécessaires pour afficher l'écran de chargement et initialiser l'expérience. Plus il y a d'instances dans ReplicatedFirst, plus l'attente pour qu'elles se répliquent avant que le code dans ReplicatedFirst puisse s'exécuter sera longue.

  • Dans le dossier Instances existe l’interface graphique de l'écran de chargement.
  • Dans le dossier Source se trouve le code de l'écran de chargement et le code nécessaire pour attendre que le reste de l'expérience se charge. Le start LocalScript est le point d'entrée pour tout le code côté client dans le projet.
ReplicatedStorage

Agit comme un conteneur de stockage pour toutes les instances dont l'accès est requis à la fois par le client et le serveur.

  • Dans le dossier Dependencies se trouvent certaines librairies tierces utilisées par le projet.
  • Dans le dossier Instances se trouve une large gamme d'instances préfabriquées.
  • Dans le dossier Source se trouve tout code non requis pour le processus de chargement qui doit être accessible à la fois par le client et le serveur.
ServerScriptService

Contient un Script servant de point d'entrée pour tout le code côté serveur dans le projet.

ServerStorage

Agit comme un conteneur de stockage pour toutes les instances qui n'ont pas besoin d'être répliquées au client.

  • Dans le dossier Instances se trouve un modèle de Ferme. Une copie de celui-ci est placée dans Workspace lorsque le joueur rejoint l'expérience, où elle sera répliquée à tous les joueurs.
  • Dans le dossier Source se trouve tout le code qui est exclusif au serveur.
SoundService

Contient les objets Sound utilisés pour les effets sonores dans l'expérience. Sous SoundService, ces objets Sound n'ont pas de position et ne sont pas simulés dans l'espace 3D.

Points d'entrée

La plupart des projets organisent le code à l'intérieur de ModuleScripts réutilisables qui peuvent être importés dans l'ensemble du code. Les ModuleScripts sont réutilisables mais ils ne s'exécutent pas d'eux-mêmes ; ils doivent être importés par un Script ou un LocalScript. De nombreux projets Roblox auront un grand nombre d'objets Script et LocalScript, chacun se rapportant à un comportement ou à un système particulier dans l'expérience, créant plusieurs points d'entrée.

Pour le microjeu Plante, une approche différente est mise en œuvre via un seul LocalScript qui est le point d'entrée pour tout le code client, et un unique Script qui est le point d'entrée pour tout le code serveur. L'approche correcte pour votre projet dépend de vos besoins, mais un seul point d'entrée offre un meilleur contrôle sur l'ordre dans lequel les systèmes sont exécutés.

Les listes suivantes décrivent les compromis des deux approches :

  • Un seul Script et un seul LocalScript couvrent respectivement le code serveur et client.
  • Un meilleur contrôle sur l'ordre dans lequel différents systèmes sont démarrés, car tout le code est initialisé à partir d'un seul script.
  • Peut passer des objets par référence entre les systèmes.

Architecture des systèmes de haut niveau

Les systèmes de niveau supérieur dans le projet sont détaillés ci-dessous. Certains de ces systèmes sont substantiellement plus complexes que d'autres et, dans de nombreux cas, leur fonctionnalité est abstraite à travers une hiérarchie d'autres classes.

Diagramme d'architecture des systèmes du projet Plante

Chacun de ces systèmes est un "singleton" en ce sens qu'il s'agit d'une classe non instanciable qui est plutôt initialisée par le script start client ou serveur pertinent. Vous pouvez en savoir plus sur le modèle singleton plus loin dans ce guide.

Serveur

Les systèmes suivants sont associés au serveur.

SystèmeDescription
Réseau
  • Crée toutes les instances de RemoteEvent et RemoteFunction.
  • Expose des méthodes pour envoyer et écouter des messages du client.
  • Validation de type pour les arguments reçus du client à l'exécution.
Serveur de données des joueurs
  • Enregistre et charge les données persistantes des joueurs à l'aide de DataStoreService.
  • Stocke les données des joueurs en mémoire et réplique les mutations au client.
  • Expose des signaux et des méthodes pour se souscrire, interroger et mettre à jour les données des joueurs.
Marché
  • Gère les transactions de devises douces du client.
  • Expose une méthode pour vendre les plantes récoltées.
Gestionnaire de groupes de collisions
  • Assigne les modèles des personnages joueurs à des groupes de collisions.
  • Configure les groupes de collisions afin que les personnages des joueurs ne puissent pas entrer en collision avec les chariots de plantes.
Gestionnaire de ferme serveur
  • Recrée le modèle de ferme d'un joueur à partir de ses données de joueur lorsqu'il rejoint l'expérience.
  • Supprime le modèle de ferme lorsqu'un joueur quitte.
  • Met à jour les données du joueur lorsqu'une ferme d'un joueur est modifiée.
  • Expose une méthode pour accéder à la classe Ferme associée à un joueur donné.
Conteneur d'objets joueurs
  • Crée divers objets associés à la durée de vie d'un joueur et fournit une méthode pour les récupérer.
Taguer les joueurs
  • Ajoute des étiquettes CollectionService à tous les objets de joueur et de personnage.
Gestionnaire FTUE serveur
  • Pendant le FTUE, exécute chaque étape et attend qu'elle soit terminée.
Spawneur de personnages
  • Respawn des personnages lorsqu'ils meurent. Notez que Players.CharacterAutoLoads a été désactivé afin que le spawn soit suspendu jusqu'à ce que les données du joueur aient été chargées.

Client

Les systèmes suivants sont associés au client.

SystèmeDescription
Réseau
  • Attend que le serveur crée toutes les instances de RemoteEvent et RemoteFunction.
  • Expose des méthodes pour envoyer et écouter des messages vers et depuis le serveur.
  • Applique une validation de type des paramètres à l'exécution.
  • Exécute pcall() sur les fonctions distantes.
Données des joueurs client
  • Stocke les données du joueur local en mémoire.
  • Expose des méthodes et des signaux pour interroger et s'abonner aux changements dans les données des joueurs.
MarchéClient
  • Expose une méthode pour demander au serveur d'acheter un article pour une devise douce.
Gestionnaire de marche et saut local
  • Expose des méthodes pour modifier la WalkSpeed ou la JumpHeight d'un personnage via des multiplicateurs pour éviter les conflits lors de la modification de ces valeurs depuis plusieurs endroits.
Gestionnaire de ferme client
  • Écoute les étiquettes CollectionService spécifiques appliquées aux instances et crée des "composants" ajoutant un comportement à ces instances. Un "composant" fait référence à une classe qui est créée lorsqu'une étiquette CollectionService est ajoutée à une instance et détruite lorsqu'elle est supprimée ; celles-ci sont utilisées pour les invites CTA dans la ferme et diverses classes transmettant l'état de la ferme au joueur.
Configuration de l'interface utilisateur
  • Initialise toutes les couches de l'interface utilisateur.
  • Configure certaines couches pour n'être visibles que dans des sections physiques du monde.
  • Connecte un effet de caméra spécial pour lorsque les menus sont activés.
Gestionnaire FTUE client
  • Configure les étapes FTUE sur le client.
Sprint de personnage
  • Utilise Gestionnaire de marche et saut local pour augmenter la WalkSpeed lorsque le personnage d'un joueur se trouve en dehors de sa ferme.

Communication client-serveur

La plupart des expériences Roblox impliquent un élément de communication entre le client et le serveur. Cela peut inclure le client demandant au serveur d'effectuer une certaine action et le serveur répliquant des mises à jour au client.

Dans ce projet, la communication client-serveur est maintenue aussi générique que possible en limitant l'utilisation des objets RemoteEvent et RemoteFunction afin de réduire le nombre de règles spéciales à suivre. Ce projet utilise les méthodes suivantes, par ordre de préférence :

Réplication via le système de données des joueurs

Le système de données des joueurs permet d'associer des données au joueur qui persistent entre les sessions de sauvegarde. Ce système fournit une réplication du client au serveur et un ensemble d'APIs qui peuvent être utilisées pour interroger des données et s'abonner à des changements, ce qui le rend idéal pour répliquer les changements d'état du joueur du serveur au client.

Par exemple, plutôt que d'envoyer un UpdateCoins RemoteEvent sur mesure pour dire au client combien de pièces il a, vous pouvez appeler ce qui suit et laisser le client s'y abonner via l'événement PlayerDataClient.updated.


PlayerDataServer.setValue(player, "coins", 5)

Bien sûr, cela n'est utile que pour la réplication du serveur au client et pour les valeurs que vous souhaitez persister entre les sessions, mais cela s'applique à un nombre surprenant de cas dans le projet, notamment :

  • L'étape FTUE actuelle
  • L'inventaire du joueur
  • Le montant de pièces que le joueur a
  • L'état de la ferme du joueur

Réplication via attributs

Dans les situations où le serveur doit réplica un valeur personnalisée au client qui est spécifique à une Instance donnée, vous pouvez utiliser attributs. Roblox réplique automatiquement les valeurs d'attributs, donc vous n'avez pas besoin de maintenir des chemins de code pour répliquer l'état associé à un objet. Un autre avantage est que cette réplication se produit en même temps que l'instance elle-même.

Ceci est particulièrement utile pour les instances créées à l'exécution, car les attributs définis sur une nouvelle instance avant qu'elle ne soit parentée au modèle de données se répliqueront de manière atomique avec l'instance elle-même. Cela contourne tout besoin d'écrire du code pour "attendre" que des données supplémentaires soient répliquées via un RemoteEvent ou StringValue.

Vous pouvez également lire directement les attributs du modèle de données, que ce soit depuis le client ou le serveur, avec la méthode GetAttribute() et vous abonner aux changements avec la méthode GetAttributeChangedSignal(). Dans le projet Plante, cette approche est utilisée pour, entre autres choses, répliquer l'état actuel des plantes aux clients.

Réplication via étiquettes

CollectionService vous permet d'appliquer une étiquette de chaîne à une Instance. Ceci est utile pour catégoriser les instances et répliquer cette catégorisation au client.

Par exemple, l'étiquette CanPlant est appliquée au serveur pour signifier au client qu'un pot donné est capable de recevoir une plante.

Messaging directement via le module réseau

Pour les situations où aucune des options précédentes ne s'applique, vous pouvez utiliser des appels réseau personnalisés via le module Réseau. C'est la seule option dans le projet qui permet la communication client-serveur et est donc la plus utile pour transmettre des requêtes du client et recevoir une réponse du serveur.

Plante utilise des appels réseau directs pour une variété de requêtes client, y compris :

  • Arroser une plante
  • Planter une graine
  • Acheter un article

L'inconvénient de cette approche est que chaque message individuel nécessite une configuration sur mesure qui peut augmenter la complexité du projet, bien que cela ait été évité autant que possible, notamment pour la communication du serveur au client.

Classes et singletons

Les classes dans le projet Plante, comme les instances sur Roblox, peuvent être créées et détruites. Sa syntaxe de classe est inspirée de l'approche idiomatique de Lua pour la programmation orientée objet avec plusieurs modifications pour permettre la prise en charge de la vérification stricte des types.

Instanciation

De nombreuses classes dans le projet sont associées à une ou plusieurs Instances. Des objets d'une classe donnée sont créés à l'aide d'une méthode new(), conforme à comment les instances sont créées sur Roblox en utilisant Instance.new().

Ce modèle est généralement utilisé pour des objets où la classe a une représentation physique dans le modèle de données, et la classe étend sa fonctionnalité. Un bon exemple est BeamBetween, qui crée un objet Beam entre deux objets Attachment donnés et garde ces attachements orientés afin que le faisceau soit toujours dirigé vers le haut. Ces instances pourraient être clonées à partir d'une version préfabriquée dans ReplicatedStorage ou passées dans new() en tant qu'argument et stockées à l'intérieur de l'objet sous self.

Instances correspondantes

Comme noté ci-dessus, de nombreuses classes dans ce projet ont une représentation dans le modèle de données, une instance qui correspond à la classe et est manipulée par elle.

Plutôt que de créer ces instances lorsque l'objet de classe est instancié, le code opte généralement pour Clone() une version préfabriquée de la Instance stockée sous ReplicatedStorage ou ServerStorage. Bien qu'il serait possible de sérialiser les propriétés de ces instances et de les créer à partir de zéro dans les fonctions new() de la classe, cela rendrait l'édition des objets très lourde et les rendrait plus difficiles à comprendre pour un lecteur. De plus, cloner une instance est généralement une opération plus rapide que de créer une nouvelle instance et de personnaliser ses propriétés à l'exécution.

Composition

Bien que l'héritage soit possible en Luau en utilisant les métatables, le projet choisit plutôt de permettre aux classes de s'étendre les unes aux autres par le biais de composition. Lorsque vous combinez des classes par composition, l'objet "enfant" est instancié dans la méthode new() de la classe et est inclus comme membre sous self.

Pour un exemple de cela en action, voyez la classe CloseButton qui enveloppe la classe Button.

Nettoyage

Tout comme une Instance peut être détruite avec la méthode Destroy(), les classes qui peuvent être instanciées peuvent également être détruites. La méthode de destruction pour les classes du projet est destroy() avec un d minuscule pour maintenir la cohérence de camelCase à travers les méthodes de l'ensemble du code, ainsi que pour distinguer les classes du projet et les instances Roblox.

Le rôle de la méthode destroy() est de détruire toutes les instances créées par l'objet, de déconnecter toutes les connexions, et d'appeler destroy() sur tout objet enfant. Ceci est particulièrement important pour les connexions car les instances avec des connexions actives ne sont pas nettoyées par le ramasse-miettes Luau, même s'il ne reste aucune référence à l'instance ou connexions à l'instance.

Singletons

Les singletons, comme leur nom l'indique, sont des classes pour lesquelles un seul objet peut jamais exister. Ce sont l'équivalent du projet des Services de Roblox. Plutôt que de stocker une référence à l'objet singleton et de le passer autour dans le code Luau, Plante profite du fait que le fait de requérir un ModuleScript met en cache sa valeur retournée. Cela signifie que requérir le même ModuleScript singleton depuis différents endroits fournit systématiquement le même objet retourné. La seule exception à cette règle serait si différents environnements (client ou serveur) accédaient au ModuleScript.

Les singletons se distinguent des classes instanciables par le fait qu'ils n'ont pas de méthode new(). Au lieu de cela, l'objet avec ses méthodes et état est retourné directement via le ModuleScript. Comme les singletons ne sont pas instanciés, la syntaxe self n'est pas utilisée et les méthodes sont plutôt appelées avec un point (.) plutôt qu'un deux-points (:).

Vérification stricte des types

Luau prend en charge le typage progressif, ce qui signifie que vous êtes libre d'ajouter des définitions de types optionnelles à une partie ou à l'intégralité de votre code. Dans ce projet, une vérification de type strict est utilisée pour chaque script. C'est l'option la moins permissive pour l'outil Analyse de script de Roblox et donc la plus susceptible de détecter des erreurs de type avant l'exécution.

Syntaxe de classe typée

L'approche établie pour créer des classes en Lua est bien documentée, cependant elle n'est pas bien adaptée à un typage fort Luau. En Luau, l'approche la plus simple pour obtenir le type d'une classe est la méthode typeof() :


type ClassType = typeof(Class.new())

Cela fonctionne mais n'est pas très utile lorsque votre classe est initiée avec des valeurs qui n'existent qu'à l'exécution, par exemple des objets Player. De plus, l'hypothèse faite dans la syntaxe de classe idiomatique Lua est que la déclaration d'une méthode sur une classe self sera toujours une instance de cette classe ; ce n'est pas une hypothèse que le moteur d'inférence de type peut faire.

Afin de soutenir une inférence stricte des types, le projet Plante utilise une solution qui diffère de la syntaxe de classe idiomatique de Lua de plusieurs manières, dont certaines peuvent sembler non intuitives :

  • La définition de self est dupliquée, à la fois dans la déclaration de type et dans le constructeur. Cela introduit un fardeau de maintien, mais des avertissements seront signalés si les deux définitions deviennent désynchronisées.
  • Les méthodes de classe sont déclarées avec un point, de sorte que self peut être explicitement déclaré comme étant de type ClassType. Les méthodes peuvent toujours être appelées avec un deux-points comme prévu.

--!strict
local MyClass = {}
MyClass.__index = MyClass
export type ClassType = typeof(setmetatable(
{} :: {
property: number,
},
MyClass
))
function MyClass.new(property: number): ClassType
local self = {
property = property,
}
setmetatable(self, MyClass)
return self
end
function MyClass.addOne(self: ClassType)
self.property += 1
end
return MyClass

Typage après des gardes logiques

Au moment de la rédaction, le type d'une valeur n'est pas réduit après une instruction conditionnelle de garde. Par exemple, après la garde ci-dessous, le type de optionalParameter n'est pas réduit à number.


--!strict
local function foo(optionalParameter: number?)
if not optionalParameter then
return
end
print(optionalParameter + 1)
end

Pour atténuer cela, de nouvelles variables sont créées après ces gardes avec leur type explicitement casté.


--!strict
local function foo(optionalParameter: number?)
if not optionalParameter then
return
end
local parameter = optionalParameter :: number
print(parameter + 1)
end

Traverser les hiérarchies du modèle de données

Dans certains cas, la base de code doit traverser la hiérarchie du modèle de données d'un arbre d'objets créés à l'exécution. Cela représente un défi intéressant pour la vérification des types. Au moment de la rédaction, il n'est pas possible de définir une hiérarchie générique du modèle de données en tant que type. En conséquence, il y a des cas où les informations de type disponibles pour une structure du modèle de données ne sont que le type de l'instance de niveau supérieur.

Une approche de ce défi consiste à caster à any et ensuite affiner. Par exemple :


local function enableVendor(vendor: Model)
local zonePart: BasePart = (vendor :: any).ZonePart
end

Le problème avec cette approche est qu'elle impacte la lisibilité. Au lieu de cela, le projet utilise un module générique appelé getInstance pour traverser les hiérarchies du modèle de données qui cast à any en interne.


local function enableVendor(vendor: Model)
local zonePart: BasePart = getInstance(vendor, "ZonePart")
end

Au fur et à mesure que la compréhension du moteur de types de la hiérarchie du modèle de données évolue, il est possible que des motifs comme celui-ci ne soient plus nécessaires.

Interface utilisateur

Plante comprend une variété d'interfaces utilisateurs 2D simples et complexes. Celles-ci incluent des éléments de l'affichage tête haute (HUD) non interactifs comme le compteur de pièces et des menus interactifs complexes comme la boutique.

Approche UI

Vous pouvez comparer de manière lâche les UI de Roblox au DOM HTML, car c'est une hiérarchie d'objets décrivant ce que l'utilisateur devrait voir. Les approches pour créer et mettre à jour une interface utilisateur Roblox sont largement divisées en pratiques impératives et déclaratives.

ApprocheAvantages et inconvénients
Impératif

Dans l'approche impérative, l'UI est traitée comme toute autre hiérarchie d'instances sur Roblox. La structure de l'UI est créée avant l'exécution dans le Studio et ajoutée au modèle de données, généralement directement dans StarterGui. Ensuite, à l'exécution, le code manipule des pièces spécifiques de l'UI pour refléter l'état requis par le créateur.

Cette approche présente certains avantages. Vous pouvez créer l'UI depuis zéro dans le Studio et la stocker dans le modèle de données. C'est une expérience d'édition simple et visuelle qui peut accélérer la création d'UI. Comme le code de l'UI impératif ne se soucie que de ce qui doit être changé, il rend également les changements simples d'UI faciles à mettre en œuvre.

Un inconvénient notable est que, puisque les approches UI impératives nécessitent que l'état soit mis en œuvre manuellement sous forme de transformations, des représentations complexes de l'état peuvent devenir très difficiles à trouver et à déboguer. Il est courant que des erreurs émergent lors du développement de code UI impératif, surtout lorsque l'état et l'UI deviennent désynchronisés en raison de plusieurs mises à jour interagissant dans un ordre inattendu.

Un autre défi avec les approches impératives est qu'il est plus difficile de décomposer l'UI en composants significatifs qui peuvent être déclarés une fois et réutilisés. Comme l'ensemble de l'arbre UI est déclaré en temps d'édition, des motifs communs peuvent être répétés dans plusieurs parties du modèle de données.

Déclaratif

Dans l'approche déclarative, l'état désiré des instances UI est déclaré explicitement, et l'implémentation efficace de cet état est abstraite par des bibliothèques telles que Roact ou Fusion.

L'avantage de cette approche est que l'implémentation de l'état devient triviale et il vous suffit de décrire à quoi vous voulez que votre UI ressemble. Cela rend l'identification et la résolution des bugs significativement plus faciles.

L'inconvénient clé est d'avoir à déclarer l'ensemble de l'arbre UI dans le code. Des bibliothèques comme Roact et Fusion ont des syntaxes pour faciliter cela, mais c'est tout de même un processus long et une expérience d'édition moins intuitive lors de la composition de l'UI.

Plante utilise une approche impérative sous l'idée que montrer les transformations directement donne une vue d'ensemble plus efficace de la façon dont l'UI est créée et manipulée sur Roblox. Cela ne serait pas possible avec une approche déclarative. Certaines structures et logiques UI répétées sont également abstraites en composants réutilisables pour éviter un écueil commun dans la conception UI impérative.

Architecture de haut niveau

Diagramme d'architecture de l'UI du projet Plante

Couches et composants

Dans Plante, toutes les structures d'UI sont soit une Couche soit un Composant.

  • Couche est définie comme un singleton de groupement de niveau supérieur qui encapsule les structures UI préfabriquées dans ReplicatedStorage. Une couche peut contenir un certain nombre de composants, ou elle peut encapsuler sa propre logique entièrement. Des exemples de couches sont le menu d'inventaire ou le compteur de pièces dans l'affichage tête haute.
  • Composant est un élément UI réutilisable. Lorsqu'un nouvel objet composant est instancié, il clone un modèle préfabriqué de ReplicatedStorage. Les composants peuvent eux-mêmes contenir d'autres composants. Des exemples de composants sont une classe de bouton générique ou le concept d'une liste d'articles.

Gestion des vues

Un problème courant de gestion d'UI est la gestion des vues. Ce projet a une gamme de menus et d'éléments HUD, dont certains écoutent l'entrée utilisateur, et une gestion soigneuse de leur visibilité ou de leur activation est requise.

Plante aborde ce problème avec son système UIHandler qui gère quand une couche d'UI doit ou ne doit pas être visible. Toutes les couches d'UI de l'expérience sont catégorisées comme HUD ou Menu et leur visibilité est gérée selon les règles suivantes :

  • L'état activé des couches Menu et HUD peut être basculé.
  • Les couches HUD activées ne sont affichées que si aucune couche Menu n'est activée.
  • Les couches Menu activées sont stockées dans une pile, et une seule couche Menu est visible à la fois. Lorsqu'une couche Menu est activée, elle est insérée à l'avant de la pile et affichée. Lorsqu'une couche Menu est désactivée, elle est retirée de la pile et la prochaine couche Menu activée en file d'attente est affichée.

Cette approche est intuitive car elle permet de naviguer dans les menus avec un historique. Si un menu est ouvert depuis un autre menu, fermer le nouveau menu affichera de nouveau l'ancien menu.

Les singletons de couche d'UI s'enregistrent auprès du UIHandler et reçoivent un signal qui se déclenche lorsque sa visibilité doit changer.

Lectures complémentaires

À partir de cet aperçu complet du projet Plante, vous voudrez peut-être explorer les guides suivants qui approfondissent encore plus des concepts et sujets connexes.

  • Modèle Client-Serveur — Un aperçu du modèle client-serveur dans Roblox.
  • Luau — Détails sur Luau, le langage de script créé par Roblox descendant de Lua 5.1.
  • Événements distants et rappels — Tout sur les événements réseau distants et les rappels pour la communication à travers la frontière client-serveur.
  • UI — Détails sur les objets d'interface utilisateur et la conception sur Roblox.
©2026 Société Roblox. Roblox, le logo Roblox et Powering Imagination font partie de nos marques déposées aux États-Unis et dans d'autres pays.