Comment coder les portes et les serrures

Dans les jeux composés de pièces connectées comme The Legend of Zelda, les portes plus récentes de The Binding of Isaac ou tout type de Roguelike ou même de Metroidvania, les portes jouent un rôle essentiel dans la navigation et la progression du joueur..

Les portes permettent au joueur de voyager d’une pièce ou d’un niveau à l’autre, et ont donc une place importante dans la navigation et la connexion des différentes pièces les unes aux autres, ainsi que dans la définition de la carte comme un monde ouvert ou un sol de donjon. Ils peuvent également agir en tant que barrages routiers temporaires que le joueur devra déverrouiller par un mécanicien spécifique (comme obtenir une clé ou activer un commutateur)..

Dans ce tutoriel, je vais vous présenter plusieurs mécanismes de verrouillage et vous proposer des moyens pour les implémenter dans vos jeux. Celles-ci ne sont en aucun cas destinées à être les seules ou les meilleures implémentations; ce sont des exemples pratiques.

Les démonstrations interactives de ce didacticiel ont été réalisées avec l'outil de construction HTML5 Construct 2 et devraient être compatibles avec sa version gratuite. (Les fichiers CAPX sont disponibles dans le téléchargement source.) Toutefois, ce didacticiel devrait vous aider à apprendre à implémenter la logique des portes et des verrous dans le moteur de votre choix. Une fois que vous avez compris la logique, tout dépend de votre propre connaissance de votre outil de codage / langage et de la manière dont vous souhaitez l'adapter au jeu que vous êtes en train de créer..

Plongeons dedans!

Le mécanicien de base

Une porte est fondamentalement un bloc de paysage qui ne peut pas être passé, empêchant le personnage du joueur de passer à travers tant qu'il n'est pas déverrouillé. La porte peut avoir différents états: verrouillée ou non verrouillée, fermée ou ouverte.

Il doit y avoir une représentation évidente de ce dernier; le joueur doit être capable de dire que la porte est en fait une porte et si elle est verrouillée ou non.

Dans les démos suivantes, les portes sont présentées à travers deux graphiques:


C'est une porte fermée.

C'est une porte ouverte.

J'ai également utilisé différentes couleurs pour représenter les différents matériaux pouvant être utilisés dans les portes. Toutefois, l'aspect graphique dépend de vous, de votre jeu et de son univers. La partie la plus importante est que la porte doit être clairement identifiable comme étant une porte et qu'il soit évident si elle bloque la progression du joueur ou si elle est ouverte et mène au reste du niveau ou du monde..

Lorsqu'elle est fermée ou verrouillée, la porte doit être un bloc d'état solide. Lorsqu’il est ouvert, l’état solide doit être désactivé, ce qui permet aux personnages de le traverser. Assurez-vous que quel que soit votre moteur de collision, il vous permet de modifier cet état à la volée assez facilement..

Du point de vue de la programmation, l’objet porte devrait contenir ou être lié à un est verrouillé Variable booléenne. En fonction de la valeur de cette variable, vous pouvez déterminer quel sprite afficher et si le bloc doit être solide ou non..

Pour déverrouiller la porte, le personnage doit contenir un has_key Variable booléenne elle-même lorsque le joueur a ramassé une clé: vrai s'ils l'ont, faux s'ils ne le font pas.

Dans ce mécanisme de base, la clé fait partie de l'inventaire du personnage et une clé s'ouvre. tout des portes. L'utilisation de la clé sur une porte ne la consomme pas; la clé reste dans l'inventaire du personnage.

Pour le visualiser, nous pouvons simplement afficher une image de la clé dans le HUD pour que le joueur sache qu'il "possède" une clé qui pourrait ouvrir les portes une fois que le personnage l'a saisie (en le déplaçant au-dessus de la clé dans la pièce). ).

Prenons l'exemple de base suivant:

Cliquez sur la démo pour l'activer, puis contrôlez le caractère à l'aide des touches fléchées de votre clavier et effectuez des actions à l'aide de la barre d'espace. (Dans cet exemple, l'action est "ouvrir une porte".)

Les murs sont des blocs solides qui ne permettent pas au personnage de traverser lorsqu'il entre en collision avec eux. Les portes fermées sont également solides.

Pour ouvrir une porte, le personnage doit se situer à moins de 64 pixels de la porte et posséder une clé has_key La variable booléenne qui détermine si le personnage a la clé dans son inventaire doit être vrai). 

Dans ces conditions, lorsque le joueur appuie sur la barre d'espace, l'état de la porte appropriée est modifié. Sa variable booléenne fermé à clef est réglé sur faux, et son état "solide" est désactivé.

En pseudocode, cela ressemblerait à quelque chose comme:

Door.Locked = True Door.AnimationFrame = 0 // Le cadre d'animation qui affiche la porte verrouillée Door.Solid = Enabled // Le statut solide de la porte est activé Door.Locked = False Door.AnimationFrame = 1 // L'animation cadre qui affiche la porte comme étant ouverte Door.Solid = Disabled // l'état solide de la porte est désactivé Keyboard Key "Space" est enfoncé et Distance (Character, Door) <= 64px and Door.Locked = True and Character.Has_Key = True //The player has a key Door.Locked = False Keyboard Key "Space" is pressed and Distance(Character,Door) <= 64px and Door.Locked = True and Character.Has_Key = False //The player does not have a key Text.text = "You don't have a key for that door" 

Rappel: Ce code ne représente pas une langue spécifique; vous devriez pouvoir le mettre en oeuvre dans la langue de votre choix.

Vous pouvez également noter que nous faisons une vérification pour quand le joueur fait ne pas avoir la clé attendue et afficher un message de retour expliquant pourquoi la porte n’a pas été déverrouillée. Vous pouvez gérer des chèques comme ceux-là, mais cela convient mieux à votre jeu. Sachez qu'il est toujours agréable de dire à votre joueur que son action a été enregistrée et d'expliquer pourquoi il n'a pas été terminé..

Ceci est une logique très basique de porte et de verrouillage et comment la mettre en œuvre. Dans la suite du didacticiel, nous examinerons d’autres systèmes de verrouillage qui sont des variantes de ce système de base..

Différents systèmes de verrouillage

Nous avons vu le système de base où la clé fait partie intégrante de l'inventaire du personnage et une clé ouvre toutes les portes et peut être réutilisée pour ouvrir plusieurs portes. Construisons là-dessus.

Exemple de KeyStack

Dans l'exemple suivant, le personnage aura un empiler de clés dans son inventaire. Bien qu'il existe plusieurs couleurs de porte, la différence est strictement graphique: l'objet de porte est logiquement identique à celui de l'exemple de base et un type de clé peut ouvrir n'importe laquelle d'entre elles. Cependant, cette fois, chaque fois que vous utilisez une clé pour ouvrir une porte, cette clé est retirée de la pile..


En ce qui concerne le codage, cette modification est principalement au niveau du personnage. Au lieu d'avoir un has_key Variable booléenne, vous voudrez avoir un numérique variable qui contiendra le nombre de clés que le personnage a "en stock".

A chaque fois que le personnage récupère une clé, ajoutez 1 à cette variable pour représenter la pile en hausse. À chaque fois que le personnage ouvre une porte, soustrayez 1 à partir de cette variable pour représenter l'utilisation d'une clé. (Au pays des jeux vidéo, les clés sont détruites dès qu’elles sont utilisées une fois.)

Une autre modification concerne le moment où le joueur appuie sur la barre d'espace: au lieu de vérifier qu'un has_key La variable booléenne est vrai, nous voulons réellement vérifier que la valeur de KeyStack est plus que zéro, afin que nous puissions consommer une clé après avoir ouvert la porte.

En pseudocode, cela ressemble à quelque chose comme:

Mécanique des portes = identique à l'exemple de base ci-dessus. La touche du clavier "Espace" est enfoncée et Character.KeyStack> 0 et Distance (Character, Door) <= 64 and Door.Locked = True Character.KeyStack = Character.KeyStack - 1 Door.Locked = False 

Quelle clé exemple

Dans ce nouvel exemple, nous allons envisager un scénario dans lequel différents types de portes nécessitent le déverrouillage de différents types de clés.

Ici, comme dans le premier exemple de base, les clés feront partie de l'inventaire du personnage. Nous reviendrons sur l'utilisation de variables booléennes pour déterminer si le personnage a ramassé les clés requises. Et puisque nous aurons différentes clés, nous aurons également différents types de portes (porte noire, porte rouge, porte dorée), ce qui nécessitera également une clé appropriée pour pouvoir les ouvrir (clé noire, clé rouge, clé dorée, etc.). ).

Les objets door utiliseront différents sprites pour afficher leur matériel et contiendront une variable numérique nommée Quelle clé cela indiquera le type de clé attendu ainsi que le type de graphique à afficher. Les différentes valeurs de clé sont contenues sous forme de variables constantes, pour une meilleure lisibilité..


En pseudocode:

CONSTANT BLACK_KEY = 0 CONSTANT RED_KEY = 1 CONSTANT GOLD_KEY = 2 Les mécanismes de la porte sont les mêmes que dans l'exemple de base. La touche du clavier "Espace" est enfoncée // La porte nécessite une clé noire, mais le personnage n'en porte pas Si Door.Locked = True et Door.WhichKey = BLACK_KEY et Character.Has_Black_Key = False et Distance (Door, Character) <= 64 Text.text="You need a black key for this door" //The door requires a red key but the character doesn't have one Else If Door.Locked = True and Door.WhichKey = RED_KEY and Character.Has_Red_Key = False and Distance(Door,Character) <= 64 Text.text="You need a red key for this door" //The door requires a gold key but the character doesn't have one Else If Door.Locked = True and Door.WhichKey = GOLD_KEY and Character.Has_Gold_Key = False and Distance(Door,Character) <= 64 Text.text="You need a red key for this door" //The door requires a black key and the character has one Else If Door.Locked = True and Door.WhichKey = BLACK_KEY and Character.Has_Black_Key = True and Distance(Door,Character) <= 64 Door.Locked = False //The door requires a red key and the character has one Else If Door.Locked = True and Door.WhichKey = RED_KEY and Character.Has_Red_Key = True and Distance(Door,Character) <= 64 Door.Locked = False //The door requires a gold key and the character has one Else If Door.Locked = True and Door.WhichKey = GOLD_KEY and Character.Has_Gold_Key = True and Distance(Door,Character) <= 64 Door.Locked = False

Ceci est une variante de l'exemple de base qui autorise plusieurs types de clés et de portes et qui ne consomme pas de clé pour ouvrir des portes. Une fois que vous avez la clé, cela fait partie de votre inventaire, de la "statistique" du personnage..

Exemple de commutation

Cette fois, au lieu d'agir directement sur les portes, le joueur doit activer un interrupteur spécifique pour ouvrir ou fermer une porte spécifique..

Les portes ici sont essentiellement le même objet que dans l'exemple de base. Ils pourraient afficher des graphiques différents, mais la logique de l'objet est toujours la même. Il y a un ajout cependant: nous ajoutons deux variables numériques DoorID et SwitchID, que nous utilisons pour savoir quel commutateur est lié à quelle porte.

Interrupteurs sont un nouveau type d’objets que j’ai choisi de rendre solides (mais ce n’est pas obligatoire). Ils contiennent une variable booléenne, Activé, et variables numériques DoorID et SwitchID que, comme vous pouvez le deviner, nous utilisons pour déterminer quel commutateur est lié à quelle porte.

Alors, quand un interrupteur a Activé: vrai, la porte "liée" est définie pour avoir Verrouillé: Faux. Notre action avec la barre d'espace va alors se produire quand à côté d'un commutateur, plutôt que près d'une porte. Notez l’absence de clé dans cet exemple, car les commutateurs servent de clés:

Nous pourrions simplement utiliser un simple code vérifiant les liaisons des interrupteurs de porte dans la même pièce (puisque cet exemple affiche trois portes et deux interrupteurs dans la même pièce), mais nous verrons plus tard que des interrupteurs peuvent agir sur les portes un autre afin que leur action ne se produise pas au moment exact où le joueur active le commutateur; il se produira plus tard, lorsque la nouvelle salle sera chargée.

Pour cette raison, nous avons besoin de persistance. Une option pour cela consiste à utiliser des tableaux pour garder une trace des données telles que l'état des commutateurs (c'est-à-dire que chaque commutateur est activé ou non)..

En pseudocode:

CONSTANT SWITCH_DOORID = 0 CONSTANT SWITCH_ACTIVATION = 1 // Ces constantes nous permettront de garder un rappel lisible des coordonnées du tableau. 
// Définir un tableau // La coordonnée X du tableau correspondra à la valeur SwitchID // La coordonnée Y-0 sera le DoorID // La coordonnée Y-1 sera l'état d'activation aSwitch (nombre de commutateurs, 2) // 2 est le nombre de hauteur (Y), souvent basé sur 0.
Exécuter une association des SwitchID avec DoorID Le mécanisme de la porte est toujours le même que dans l'exemple de base. // Affichage du graphique de commutateur correct en fonction de leur état d'activation Switch.Activated = True Affiche le cadre d'animation Switch_ON Switch.Activated = False Affiche le cadre d'animation Switch_OFF La touche "Space" est enfoncée et Distance (Caractère, commutateur) <= 64 Switch.Toggle(Activated) //A function that will set the value to either True or False) aSwitch(Switch.SwitchID,SWITCH_ACTIVATION) = Switch.Activated //It can depend on your coding language, but the idea is to set the value in the array where X is the SwitchID and where Y is the state of activation of the switch. The value itself is supposed to be the equivalent of the Switch.Activated boolean value. Door.DoorID = aSwitch(Switch.SwitchID,SWITCH.DOORID) //Allows us to make sure we're applying/selecting the correct door instance //Now according to the activation value, we lock or unlock the door aSwitch(Switch.SwitchID,SWITCH.ACTIVATION) = True Door.Locked = False aSwitch(Switch.SwitchID,SWITCH.ACTIVATION) = False Door.Locked = True

Pour cet exemple spécifique, où les commutateurs sont dans la même pièce que les portes auxquelles ils sont liés, l'utilisation de la technique de la matrice est excessive. Si votre jeu est configuré de manière à ce que chaque commutateur agissant sur une porte soit placé dans la même pièce, alors optez pour la méthode la plus simple, supprimez le tableau et vérifiez si des objets sont allumés. écran seulement.

Exemple de commutateur de plaque

Les commutateurs à plaques sont similaires aux commutateurs, en ce sens qu'ils sont activés ou non et que nous pouvons les relier aux portes pour les verrouiller ou les déverrouiller. La différence réside dans la façon dont un interrupteur à plaque est activé, ce qui se fait par pression.

Dans cet exemple de vue de haut en bas, le commutateur de plaque sera activé chaque fois que le personnage le chevauche. Vous pouvez appuyer sur la barre d'espace pour déposer une pierre sur l'interrupteur de la plaque, la laissant activée même lorsque le personnage ne se tient pas dessus..

L'implémentation de ceci est similaire à l'exemple précédent, avec deux petites modifications:

  • Vous devez activer le commutateur de plaque quand un personnage ou une pierre est dessus..
  • Vous devez faire tomber une pierre (de l'inventaire) sur la plaque de la barre d'espace.
// La plupart des implémentations sont identiques à celles de l'exemple précédent. Remplacez l'objet Switch par un objet PlateSwitch. // Le caractère mécanique Plate-Switch ou la roche ne chevauche PAS PlateSwitch PlateSwitch.Activated = False aSwitch (PlateSwitch.SwitchID, SWITCH_ACTIVATION) = PlateSwitch.Activated. Door.DoorID = aSwitch (PlateSwitch.SwitchID, SWITCH.DOORID) // Nous permet de nous assurer que nous appliquons / sélectionnons la bonne instance de porte Door.Locked = True Le caractère OU La pierre chevauche PlateSwitch PlateSwitch.Activated = True aSwitch (PlateSwitch .SwitchID, SWITCH_ACTIVATION) = PlateSwitch.Activated Door.DoorID = aSwitch (PlateSwitch.SwitchID, SWITCH.DOORID) // Nous permet de nous assurer que nous appliquons / sélectionnons la bonne instance de porte Door.Locked = False Keyboard Key "Space" est pressé et le personnage chevauche PlateSwitch Spawn Rock dans PlateSwitch.position 

Exemple de mobs

Un autre mécanisme de verrouillage possible consiste à obliger le joueur à se débarrasser de tous les ennemis (également appelés mobs) dans une pièce ou une zone pour déclencher le déverrouillage des portes..

Pour cet exemple, j'ai créé quelques zones dans une seule pièce; chaque zone a une porte et plusieurs foules (bien que ces ennemis ne bougent pas et n'infligent pas de dégâts).
Chaque zone a sa propre couleur.

La barre d'espace fera que le personnage tire des projectiles; trois projectiles vont tuer une foule.

Ce type de mécanisme est utilisé dans The Legend of Zelda et The Binding of Isaac et s’articule autour d’une fonction permettant de contrôler le nombre d’ennemis vivants dans la pièce ou la zone. Dans cet exemple, chaque zone colorée contient un nombre de créatures vivantes, initié lorsque la pièce se charge, et est lié à la porte. La mort de chaque foule soustrait 1 de ce comptoir; une fois qu'il tombe à 0, les portes Fermé à clef l'état est changé en Faux.

// Au début du jeu Pour chaque zone Pour chaque groupe qui se chevauchent Zone Area.AliveMobs = Area.AliveMobs + 1 
Le mécanisme de la porte est le même que dans l'exemple de base
Un appui sur la touche "Espace" génère un projectile à partir de la position du personnage Un projectile entre en collision avec Mob Mob.HP = Mob.HP - 1 Détruit le projectile Mob.HP <=0 //Mob is dead and Mob is overlapping Area Destroy Mob Area.AliveMobs = Area.AliveMobs - 1 Area.AliveMobs <= 0 and Door is linked to Area //By means of an ID, a pointer or whatever Door.Locked = False

Dans cet exemple, un Surface est un sprite de couleur avec une variable numérique associée, AliveMobs, qui compte le nombre de mobs qui chevauchent la zone. Une fois que tous les monstres d'une zone sont vaincus, la porte correspondante est déverrouillée (en utilisant le même mécanisme que nous avons vu depuis l'exemple de base).

Exemple de navigation

Comme je l'ai mentionné dans l'introduction, les portes peuvent servir d'obstacles bloquants, mais peuvent également être utilisées pour permettre au personnage du joueur de naviguer d'une pièce à l'autre..

Dans cet exemple, les portes seront déverrouillées par défaut car nous sommes plus intéressés par l'aspect navigation..

Le mécanisme dépend beaucoup du jeu que vous jouez, ainsi que de la façon dont vous gérez la structure de données de vos sols. Je n'entrerai pas dans les détails du fonctionnement de mon implémentation ici, car il est très spécifique à Construct 2, mais vous pouvez le trouver dans les fichiers source si vous le souhaitez..

Conclusion

Tout au long de cet article, nous avons vu à quel point les portes sont des obstacles temporaires qui nécessitent des clés ou des mécanismes de déverrouillage tels que des commutateurs, des commutateurs à plaques ou même la mort des monstres. Nous avons également vu comment ils peuvent agir en tant que "ponts", permettant de naviguer à travers différentes régions du monde du jeu..

Pour rappel, voici quelques mécanismes de verrouillage possibles:

  • Une clé pour toutes les portes, dans le cadre de l'inventaire.
  • Clés consommables: chaque fois que vous ouvrez une porte, une clé est retirée de votre pile de clés.
  • Des portes différentes nécessitent des clés différentes.
  • Interrupteurs, ou interrupteurs de plaque, où vous n'agissez pas directement sur la porte pour la déverrouiller, mais par le biais d'un périphérique lié distinct.
  • Tuer tous les mobs d'une zone déverrouille automatiquement une porte.

Si vous mélangez tous ces mécanismes dans un jeu, vous pourriez vous retrouver avec quelque chose comme ceci:

Nous avons ici une belle sélection de différents mécanismes de portes et serrures, obligeant le joueur à passer par plusieurs pièces pour déverrouiller les différentes portes. À des fins d’apprentissage, vous voudrez peut-être reproduire cela dans votre propre environnement de programmation, en utilisant toutes les implémentations précédentes que nous avons connues..

J'espère que vous avez apprécié cet article et qu'il vous a été utile, et j'aimerais vous rappeler que vous pouvez trouver la source de toutes les démonstrations sur Github. Vous pouvez les ouvrir et les éditer dans la version gratuite de Construct 2 (version r164.2 ou supérieure).

Références

  • Image d'aperçu: Serrure conçue par João Miranda du projet Noun