Créez un jeu de plateforme en Python avec Arcade – Real Python

By | avril 28, 2021

trouver un expert Python

Pour de nombreux joueurs de jeux vidéo, l'attrait des jeux d'écriture est une des principales raisons d'apprendre la programmation informatique. Cependant, la création d'un jeu de plate-forme 2D tel que Lode Runner, Pitfall !, ou Super Mario Bros. sans outils ou conseils appropriés peut vous frustrer. Heureusement, le Python arcade La bibliothèque rend la création d'un jeu 2D en Python accessible à de nombreux programmeurs!

Si vous n'en avez pas déjà entendu parler, le arcade Library est un framework Python moderne pour créer des jeux avec des graphismes et un son convaincants. Orienté objet et construit pour Python 3.6 et supérieur, arcade vous propose un ensemble d'outils modernes pour créer de superbes expériences de jeu, y compris des jeux de plateforme.

À la fin de ce didacticiel, vous serez en mesure de:

  • Installez le Python arcade bibliothèque
  • Créer un basique Structure de jeu 2D
  • Trouver un jeu utilisable ouvrages d'art et autre les atouts
  • Créez des cartes de plate-forme à l'aide du Carrelé éditeur de carte
  • Définir le joueur Actions, Jeu récompenses, et obstacles
  • Contrôlez votre lecteur avec clavier et manette contribution
  • Jouer effets sonores pour les actions de jeu
  • Faites défiler l'écran de jeu avec fenêtres pour garder votre joueur en vue
  • Ajouter Titre, instruction, et pause écrans
  • Bouge toi éléments de jeu non joueurs sur l'écran

Ce didacticiel suppose que vous avez une compréhension de base de l'écriture de programmes Python. Vous devez également être à l'aise avec arcade bibliothèque et familiarisé avec Python orienté objet, qui est largement utilisé dans arcade.

Vous pouvez télécharger tout le code, les images et les sons de ce didacticiel en cliquant sur le lien ci-dessous:

Installer Python arcade

Vous pouvez installer arcade et ses dépendances en utilisant pépin:

$ python -m pip installer arcade

Des instructions d'installation complètes sont disponibles pour Windows, Mac et Linux. Vous pouvez même installer arcade directement à partir de la source si vous préférez.

Ce tutoriel utilise Python 3.9 et arcade 2.5.5 partout.

Concevoir le jeu

Avant de commencer à écrire un code, il est avantageux d’avoir un plan en place. Puisque votre objectif est d'écrire un jeu de plateforme 2D, ce serait une bonne idée de définir exactement ce qui fait d'un jeu un jeu de plateforme.

Qu'est-ce qu'un jeu de plateforme?

Il y a quelques caractéristiques qui séparent les jeux de plateforme des autres types de jeux:

  • Le joueur saute et grimpe entre différentes plates-formes sur le terrain de jeu.
  • Les plates-formes présentent souvent un terrain inégal et des emplacements en hauteur inégaux.
  • Des obstacles sont placés sur le chemin du joueur et doivent être surmontés pour atteindre un objectif.

Ce ne sont là que les exigences minimales pour un jeu de plateforme, et vous êtes libre d'ajouter d'autres fonctionnalités à votre guise, notamment:

  • Plusieurs niveaux de difficulté croissante
  • Récompenses disponibles tout au long du jeu
  • Vie de plusieurs joueurs
  • Capacité à détruire les obstacles du jeu

Le plan de jeu développé dans ce didacticiel comprend une difficulté et des récompenses croissantes.

Histoire du jeu

Tous les bons jeux ont une histoire, même si elle est simple:

Votre jeu bénéficie d'une histoire qui relie les actions entreprises par le joueur à un objectif primordial.

Pour ce didacticiel, l'histoire du jeu concerne un voyageur de l'espace nommé Roz, qui s'est écrasé sur un monde extraterrestre. Avant que leur vaisseau ne s'écrase, Roz a été projeté à l'écart et doit maintenant trouver son vaisseau spatial, le réparer et rentrer chez lui.

Pour ce faire, Roz doit voyager de son emplacement actuel à la sortie de chaque niveau, ce qui les rapproche du navire. En cours de route, Roz peut collecter des pièces, qui sont utilisées pour réparer l'engin endommagé. Depuis que Roz a été éjecté du vaisseau, ils n'ont pas d'armes et doivent donc éviter tout obstacle dangereux sur le chemin.

Bien que cette histoire puisse sembler idiote, elle sert le but important de informer la conception de vos niveaux et personnages. Cela vous aide à prendre des décisions lors de la mise en œuvre des fonctionnalités:

  • Puisque Roz n'a pas d'armes, il n'y a aucun moyen de tirer sur les ennemis qui peuvent apparaître.
  • Roz s'est écrasé sur un monde extraterrestre, de sorte que les ennemis peuvent être n'importe où et n'importe quoi.
  • Parce que la planète est extraterrestre, la gravité peut être différente, ce qui peut affecter les capacités de saut et de mouvement de Roz.
  • Roz doit réparer son vaisseau spatial endommagé, ce qui nécessite de collecter des objets pour le faire. À l'heure actuelle, les pièces sont disponibles, mais d'autres objets peuvent être disponibles plus tard.

Lors de la conception d'un jeu, vous pouvez rendre l'histoire aussi simple ou complexe que vous le souhaitez.

Mécanique de jeu

Avec une conception approximative à l'esprit, vous pouvez également commencer à planifier la façon dont vous contrôlerez le gameplay. Déplacer Roz sur le terrain de jeu nécessite un moyen de contrôler plusieurs mouvements différents:

  • La gauche et Droite se déplacer sur une plate-forme
  • En haut et Vers le bas grimper aux échelles entre les plates-formes
  • Sautez pour collecter des pièces, éviter les ennemis ou vous déplacer entre les plates-formes

Traditionnellement, les joueurs sont contrôlés à l'aide des quatre touches fléchées pour le mouvement directionnel, ainsi que Espace pour sauter. Vous pouvez également utiliser des clés telles que IJKL, IJKM ou WASD si vous le souhaitez.

Vous n'êtes pas limité non plus à une simple saisie au clavier. le arcade La bibliothèque inclut la prise en charge des manettes de jeu et des contrôleurs de jeu, que vous découvrirez plus tard. Une fois qu'un joystick est connecté à votre ordinateur, vous pouvez déplacer Roz en vérifiant la position des axes X et Y du bâton et sauter en vérifiant les pressions de bouton spécifiques.

Actifs du jeu

Maintenant que vous avez une idée de la façon dont le jeu devrait fonctionner, vous devez prendre des décisions sur l'apparence et le son du jeu. Les images, les sprites, les sons et même le texte utilisés pour afficher la partition sont collectivement appelés les atouts. Ils définissent votre jeu aux yeux de vos joueurs. Leur création peut être un défi, prenant autant de temps, sinon plus, que d'écrire le code du jeu.

Plutôt que de créer vos propres actifs, vous pouvez télécharger des actifs gratuits ou à faible coût à utiliser dans votre jeu. De nombreux artistes et concepteurs fournissent des sprites, des arrière-plans, des polices, des sons et d'autres contenus que les créateurs de jeux peuvent utiliser. Voici quelques sources de musique, de son et d'art dans lesquelles vous pouvez rechercher du contenu utile:

Pour le jeu décrit dans ce didacticiel, vous utiliserez des images de tuiles de carte disponibles gratuitement et des sprites créés par Kenney.nl. Les effets sonores fournis dans le code source téléchargeable ont été créés par l'auteur à l'aide de MuseScore et Audacity.

La dernière étape avant de pouvoir commencer à écrire du code consiste à décider comment vous allez tout structurer et tout stocker.

Définition de la structure du programme

Les jeux vidéo étant constitués d’actifs graphiques et sonores ainsi que de code, il est important d’organiser votre projet. Garder les ressources et le code du jeu correctement organisés vous permettra d'apporter des modifications ciblées à la conception ou au comportement de votre jeu tout en minimisant l'impact sur d'autres aspects du jeu.

Le projet utilise la structure suivante:

arcade_platformer /
|
├── arcade_platformer /
|
├── actifs /
| |
│ ├── images /
| | |
│ │ ├── ennemis /
| | |
│ │ ├── sol /
| | |
│ │ ├── HUD /
| | |
│ │ ├── éléments /
| | |
│ │ ├── joueur /
| | |
│ │ └── tuiles /
| |
│ └── sons /
|
└── tests /

Sous le dossier racine du projet se trouvent les sous-dossiers suivants:

  • arcade_platformer contient tout le code Python du jeu.
  • les atouts se compose de toutes vos images de jeu, polices, sons et cartes de tuiles.
  • des tests contient tous les tests que vous pouvez choisir d'écrire.

Bien qu'il y ait d'autres décisions de gameplay à prendre, cela suffit pour commencer à écrire du code. Vous commencerez par définir la base arcade structure de code dans laquelle vous pouvez construire votre jeu de plateforme!

Définition de la structure du jeu en Python arcade

Votre jeu utilise toutes les capacités orientées objet de arcade. Pour ce faire, vous définissez une nouvelle classe basée sur arcade.Fenêtre, puis remplacez les méthodes de cette classe pour mettre à jour et rendre les graphismes de votre jeu.

Voici un squelette de base de ce à quoi pourrait ressembler un jeu fini. Vous construirez sur ce squelette au fur et à mesure que le jeu progressera:

    1"" "
    2Plateforme d'arcade
    3
    4Démontrer les capacités de l'arcade dans un jeu de plateforme
    5Soutenir l'article Arcade Platformer
    6à https://realpython.com/platformer-python-arcade/
    7
    8Toutes les illustrations de jeu de www.kenney.nl
    9Sons du jeu et cartes de tuiles par auteur
dix"" "
11
12importer arcade
13
14classer Plateforme(arcade.La fenêtre):
15    def __init__(soi):
16        passe
17
18    def mettre en place(soi):
19        "" "Configure le jeu pour le niveau actuel" ""
20        passe
21
22    def on_key_press(soi, clé: int, modificateurs: int):
23        "" "Traite les pressions sur les touches
24
25                                Arguments:
26                                                key int - Quelle touche a été enfoncée
27                                                modifiers int - Quels modificateurs étaient en panne à ce moment-là
28                                "" "
29
30    def on_key_release(soi, clé: int, modificateurs: int):
31        "" "Traite les versions clés
32
33                                Arguments:
34                                                key int - Quelle clé a été libérée
35                                                modifiers int - Quels modificateurs étaient en panne à ce moment-là
36                                "" "
37
38    def on_update(soi, delta_time: flotter):
39        "" "Met à jour la position de tous les objets du jeu
40
41                                Arguments:
42                                                delta_time float - Combien de temps depuis le dernier appel
43                                "" "
44        passe
45
46    def on_draw(soi):
47        passe
48
49si __Nom__ == "__principale__":
50    la fenêtre = Plateforme()
51    la fenêtre.mettre en place()
52    arcade.Cours()

Cette structure de base fournit presque tout ce dont vous avez besoin pour construire un jeu de plateforme 2D:

  • Ligne 12 importe le arcade bibliothèque.

  • Ligne 14 définit la classe utilisée pour exécuter l'ensemble du jeu. Les méthodes de cette classe sont appelées pour mettre à jour l'état du jeu, traiter les entrées de l'utilisateur et dessiner des éléments à l'écran.

  • Ligne 15 définit .__ init __ (), qui initialise l'objet de jeu. Vous ajoutez du code ici pour gérer les actions qui ne doivent être effectuées que lorsque le jeu démarre pour la première fois.

  • Ligne 18 définit .mettre en place(), qui configure le jeu pour commencer à jouer. Vous ajoutez du code à cette méthode qui devra peut-être être répété tout au long du jeu. Par exemple, c'est un excellent endroit pour initialiser de nouveaux niveaux en cas de succès ou pour réinitialiser le niveau actuel en cas d'échec.

  • Lignes 22 et 30 définir .on_key_press () et .on_key_release (), qui vous permettent de traiter les entrées au clavier indépendamment. arcade traite les pressions sur les touches et les relâchements de touches séparément, ce qui permet d'éviter les problèmes de répétition automatique du clavier.

  • Ligne 38 définit .on_update (), où vous mettez à jour l'état de votre jeu et tous les objets qu'il contient. C'est là que les collisions entre les objets sont gérées, la plupart des effets sonores sont joués, les scores sont mis à jour et les sprites sont animés. Cette méthode est l'endroit où tout dans votre jeu se passe réellement, il y a donc généralement beaucoup de code ici.

  • Ligne 46 définit .on_draw (), où tout ce qui est affiché dans votre jeu est dessiné. Contrairement à .on_update (), cette méthode ne contient généralement que quelques lignes de code.

  • Lignes 49 à 52 définissez le point d'entrée principal de votre jeu. C'est là que vous:

    • Créer l'objet de jeu la fenêtre en fonction de votre classe définie à la ligne 13
    • Configurez le jeu en appelant window.setup ()
    • Lancez la boucle de jeu en appelant arcade.run ()

Cette structure de base fonctionne bien pour la plupart des Python arcade Jeux.

Au fur et à mesure de votre progression dans ce didacticiel, vous découvrirez chacune de ces méthodes et en ajouterez de nouvelles pour implémenter les fonctionnalités de votre jeu.

Ajout de la fonctionnalité de jeu initiale

La première chose à faire lors du démarrage du jeu est d'ouvrir la fenêtre du jeu. À la fin de cette section, votre jeu ressemblera à ceci:

Lancer le jeu pour la première fois.

Vous pouvez voir les modifications apportées au squelette de votre jeu dans arcade_platformer / 02_open_game_window.py:

11importer arcade
12importer pathlib
13
14# Constantes du jeu
15# Dimensions de la fenêtre
16SCREEN_WIDTH = 1000
17SCREEN_HEIGHT = 650
18SCREEN_TITLE = "Arcade Platformer"
19
20# Chemin des actifs
21ASSETS_PATH = pathlib.Chemin(__déposer__).résoudre().parent.parent / "les atouts"
22
23classer Plateforme(arcade.La fenêtre):
24    def __init__(soi) -> Rien:
25        super().__init__(SCREEN_WIDTH, SCREEN_HEIGHT, SCREEN_TITLE)
26
27        # Ces listes contiendront différents ensembles de sprites
28        soi.pièces de monnaie = Rien
29        soi.Contexte = Rien
30        soi.des murs = Rien
31        soi.échelles = Rien
32        soi.buts = Rien
33        soi.ennemis = Rien
34
35        # Un sprite pour le joueur, rien de plus n'est nécessaire
36        soi.joueur = Rien
37
38        # Nous avons également besoin d'un moteur physique
39        soi.moteur_physique = Rien
40
41        # Quelque part pour garder le score
42        soi.But = 0
43
44        # À quel niveau sommes-nous?
45        soi.niveau = 1
46
47        # Chargez nos sons ici
48        soi.coin_sound = arcade.load_sound(
49            str(ASSETS_PATH / "des sons" / "coin.wav")
50        )
51        soi.jump_sound = arcade.load_sound(
52            str(ASSETS_PATH / "des sons" / "jump.wav")
53        )
54        soi.Victoire_Sound = arcade.load_sound(
55            str(ASSETS_PATH / "des sons" / "victoire.wav")
56        )

Voici une ventilation:

  • Lignes 11 et 12 importer le arcade et pathlib bibliothèques dont vous avez besoin.

  • Lignes 16 à 18 définir plusieurs constantes de fenêtre de jeu qui seront utilisées pour ouvrir la fenêtre de jeu ultérieurement.

  • Ligne 21 enregistre le chemin de votre les atouts dossier, en utilisant le chemin du fichier actuel comme base. Étant donné que vous utiliserez ces ressources tout au long du jeu, il est essentiel de savoir où elles se trouvent. Utilisant pathlib garantit que vos chemins fonctionneront correctement sous Windows, Mac ou Linux.

  • Ligne 25 configure votre fenêtre de jeu en appelant la classe parente » .__ init __ () méthode utilisant super() et les constantes définies ci-dessus aux lignes 16 à 18.

  • Lignes 28 à 33 définir six listes de sprites différentes pour contenir les différents sprites utilisés dans le jeu. Il n’est pas strictement nécessaire de les déclarer et de les définir ici, car ils seront correctement et entièrement définis ultérieurement dans .mettre en place(). La déclaration des propriétés des objets est un héritage de langages comme C ++ ou Java. Chaque niveau aura un ensemble différent d'objets, qui sont remplis dans .mettre en place():

    • pièces de monnaie sont des objets de collection que Roz peut trouver tout au long du jeu.

    • Contexte les objets sont présentés à des fins d'intérêt visuel uniquement et n'interagissent avec rien.

    • des murs sont des objets dans lesquels Roz ne peut pas se déplacer. Il s'agit notamment des murs réels et des plates-formes sur lesquelles Roz marche et saute.

    • échelles sont des objets qui permettent à Roz de monter ou de descendre.

    • buts sont des objets que Roz doit trouver pour passer au niveau suivant.

    • ennemis sont des objets que Roz doit éviter tout au long du jeu. Le contact avec un ennemi mettra fin à la partie.

  • Ligne 36 déclare l'objet joueur, qui sera correctement défini dans .mettre en place().

  • Ligne 39 déclare un moteur physique utilisé pour gérer les mouvements et les collisions.

  • Ligne 42 définit une variable pour suivre le score actuel.

  • Ligne 45 définit une variable pour suivre le niveau de jeu actuel.

  • Lignes 48 à 56 Utilisez le ASSETS_PATH constante définie précédemment pour localiser et charger les fichiers son utilisés pour collecter des pièces, sauter et terminer chaque niveau.

Vous pouvez en ajouter plus ici si vous le souhaitez, mais rappelez-vous que .__ init __ () n'est exécuté que lorsque le jeu démarre pour la première fois.

Roz doit pouvoir marcher, sauter et grimper dans le monde du jeu. Gérer quand et comment cela se produit est le travail du moteur physique.

Qu'est-ce qu'un moteur physique?

Dans la plupart des plateformes, l'utilisateur déplace le joueur à l'aide d'un joystick ou du clavier. Ils peuvent faire sauter le joueur ou le faire sortir d'une plate-forme. Une fois que le joueur est dans les airs, l'utilisateur n'a rien d'autre à faire pour le faire tomber sur une plate-forme inférieure. Contrôler où un joueur peut marcher et comment il tombe après avoir sauté ou quitté une plate-forme est géré par le moteur physique.

Dans un jeu, le moteur physique fournit une approximation des forces physiques qui agissent sur les joueurs et autres objets du jeu. Ces forces peuvent communiquer ou avoir un impact sur le mouvement des objets du jeu, y compris les mouvements de saut, d'escalade, de chute et de blocage.

Il y a trois moteurs physiques inclus dans Python arcade:

  1. arcade.PhysicsEngineSimple est un moteur très basique qui gère le mouvement et les interactions d'un sprite solo et d'une liste de sprites de murs. Ceci est utile pour les jeux descendants, où la gravité n'est pas un facteur.

  2. arcade.PhysicsEnginePlatformer est un moteur plus complexe conçu pour être utilisé dans les jeux de plateforme. En plus du mouvement de base, il fournit une force de gravité qui tire les objets vers le bas de l'écran. Il fournit également au joueur un moyen de sauter et de gravir des échelles.

  3. arcade.PymunkPhysiqueMoteur est construit sur Pymunk, une bibliothèque de physique 2D qui utilise la bibliothèque Chipmunk. Pymunk met à disposition des calculs physiques extrêmement réalistes arcade applications.

Pour ce didacticiel, vous utiliserez le arcade.PhysicsEnginePlatformer.

Afin de configurer correctement le arcade.PhysicsEnginePlatformer, vous devez fournir le sprite du joueur ainsi que deux listes de sprites contenant les murs et les échelles avec lesquels le joueur interagit. Étant donné que les murs et les échelles varient en fonction du niveau, vous ne pouvez pas définir formellement le moteur physique tant que le niveau n'est pas configuré, ce qui se produit dans .mettre en place().

En parlant de niveaux, comment les définissez-vous de toute façon? Comme pour la plupart des choses, il existe plusieurs façons de faire le travail.

Niveaux de jeu de construction

À l'époque où les jeux vidéo étaient encore distribués sur des disquettes, il était difficile de stocker toutes les données de niveau de jeu nécessaires à un jeu. De nombreux fabricants de jeux ont eu recours à l'écriture de code pour créer des niveaux. Bien que cette méthode économise de l'espace disque, l'utilisation de impératif le code pour générer des niveaux de jeu limite votre capacité à les modifier ou à les augmenter plus tard.

L'espace de stockage devenant moins coûteux, les jeux en ont profité pour stocker davantage de leurs actifs dans des fichiers de données, qui ont été lus et traités par le code. Les niveaux de jeu pouvaient désormais être créés et modifiés sans changer le code du jeu, ce qui permettait aux artistes et aux concepteurs de jeux de contribuer sans avoir à comprendre le code sous-jacent. Cette déclaratif La méthode de conception de niveau permet une plus grande flexibilité lors de la conception et du développement de jeux.

L'inconvénient de la conception déclarative au niveau du jeu est la nécessité non seulement de définir les données, mais également de les stocker. Heureusement, il existe un outil disponible qui peut faire les deux, et il fonctionne extrêmement bien avec arcade.

Tiled est un éditeur de niveau de jeu 2D open source qui produit des fichiers pouvant être lus et utilisés par Python arcade. Tiled vous permet de créer une collection d'images appelée ensemble de tuiles, qui est utilisé pour créer une carte de tuiles définissant chaque niveau de votre jeu. Vous pouvez utiliser Tiled pour créer des cartes de tuiles pour les jeux de haut en bas, isométriques et à défilement latéral, y compris les niveaux de votre jeu:

Conception de base pour le niveau un du jeu de plateforme d'arcade

Tiled est livré avec un excellent ensemble de documents et un excellent didacticiel d'introduction. Pour vous aider à démarrer et, espérons-le, vous ouvrir l'appétit pour plus, vous passerez ensuite par les étapes de création de votre premier niveau de carte.

Téléchargement et démarrage de Tiled

Avant d'exécuter Tiled, vous devez le télécharger. La version actuelle au moment de la rédaction était la version 1.4.3 de Tiled, qui était disponible pour Windows, Mac et Linux dans une variété de formats. Lors du téléchargement, pensez à soutenir sa maintenance continue en faisant également un don.

Une fois que vous avez téléchargé Tiled, vous pouvez le démarrer pour la première fois. Vous verrez la fenêtre suivante:

Tiled, l'éditeur de plateforme, au premier démarrage

Cliquez sur Nouvelle carte pour créer la carte de tuiles pour votre premier niveau. La boîte de dialogue suivante apparaîtra:

Créer une nouvelle carte de tuiles dans Tiled

Ces propriétés de carte de tuiles par défaut sont idéales pour les jeux de plate-forme et représentent les meilleures options pour un arcade Jeu. Voici une brève description des autres options que vous pouvez sélectionner:

  • Orientation spécifie comment la carte est affichée et modifiée.
    • Orthogonal les cartes sont carrées et sont utilisées pour les jeux de haut en bas et de plate-forme. arcade fonctionne mieux avec les cartes orthogonales.
    • Isométrique les cartes déplacent le point de vue pour être un angle non carré par rapport au champ de jeu, fournissant une vue pseudo-3D du monde 2D. Échelonné les cartes isométriques spécifient que le bord supérieur de la carte est le bord supérieur de la vue.
    • Hexagonal les cartes utilisent des hexagones plutôt que des carrés pour chaque tuile de carte (bien que Tiled affiche des carrés dans l'éditeur).
  • Format de couche de tuile spécifie comment la carte est stockée sur le disque. La compression à l'aide de zlib permet d'économiser de l'espace disque.
  • Ordre de rendu des tuiles spécifie comment les tuiles sont stockées dans le fichier et finalement comment elles sont rendues par le moteur de jeu.
  • Taille de la carte définit la taille de la carte à stocker, en unités de tuiles. Spécifier la carte comme Infini indique à Tiled de déterminer la taille finale en fonction des modifications effectuées.
  • Taille de la tuile spécifie la taille de chaque tuile en pixels. Si vous utilisez une illustration provenant d'une source externe, définissez-la sur la taille des tuiles de cet ensemble. L'illustration fournie pour ce didacticiel utilise des sprites carrés mesurant 128 × 128 pixels. Cela signifie que chaque tuile comprend environ 16 000 pixels et qu'ils peuvent être stockés sur disque et en mémoire de manière à augmenter les performances du jeu si nécessaire.

Cliquez sur Enregistrer sous pour enregistrer le niveau. Puisqu'il s'agit d'un élément du jeu, enregistrez-le sous arcade_platformer / assets / platform_level_01.tmx.

Les cartes de tuiles consistent en un ensemble de tuiles placées sur des couches de carte spécifiques. Pour commencer à définir une carte de mosaïques pour un niveau, vous devez d'abord définir le jeu de mosaïques à utiliser et les couches sur lesquelles ils apparaissent.

Créer un jeu de tuiles

Les tuiles utilisées pour créer votre niveau sont contenues dans un ensemble de tuiles. Le jeu de tuiles est associé à la carte de tuiles et fournit toutes les images de sprite nécessaires pour définir le niveau.

Vous définissez et interagissez avec un jeu de mosaïques à l'aide du Ensembles de tuiles vue, située dans le coin inférieur droit de la fenêtre Mosaïque:

Emplacement du jeu de tuiles dans Tiled

Clique le Nouveau jeu de tuiles pour définir le jeu de mosaïques pour ce niveau. Tiled présente une boîte de dialogue demandant des informations sur le nouveau jeu de mosaïques à créer:

Création d'un nouveau jeu de tuiles dans Tiled

Vous disposez des options suivantes pour votre nouveau jeu de mosaïques:

  • Nom est le nom de votre ensemble de tuiles. Appelez celui-ci arcade_platformer.
  • Taper spécifie comment le jeu de mosaïques sera défini:
    • Collection d'images indique que chaque vignette est contenue dans une seule image distincte sur le disque. Vous devez sélectionner cette option, comme arcade fonctionne mieux avec des images de tuiles individuelles.
    • Basé sur l'image de l'ensemble de tuiles indique que toutes les tuiles sont combinées en une seule grande image que Tiled doit traiter pour localiser chaque image individuelle. Sélectionnez cette option uniquement si les ressources que vous utilisez le nécessitent.
  • Incorporer dans la carte demande à Tiled de stocker l'ensemble de tuiles dans la carte de tuiles. Ne cochez pas cette case, car vous allez enregistrer et utiliser le jeu de mosaïques en tant que ressource distincte dans plusieurs cartes de mosaïques.

Cliquez sur Enregistrer sous et enregistrez-le sous assets / arcade_platformer.tsx. Pour réutiliser ce jeu de mosaïques sur les futures cartes de mosaïques, sélectionnez CarteAjouter un jeu de tuiles externe pour l'inclure.

Définition du jeu de mosaïques

Votre nouvel ensemble de tuiles est initialement vide, vous devez donc le remplir avec des tuiles. Vous faites cela en localisant vos images de tuiles et en les ajoutant à l'ensemble. Chaque image doit avoir les mêmes dimensions que le Taille de la tuile défini lors de la création de la carte de tuiles.

Cet exemple suppose que vous avez téléchargé les ressources du jeu pour ce didacticiel. Vous pouvez le faire en cliquant sur le lien ci-dessous:

Vous pouvez également télécharger le Platformer Pack Redux (360 Assets) et déplacer le contenu du PNG dossier à votre arcade-platformer / actifs / images dossier. Rappelez-vous que votre carte de tuiles se trouve sous arcade-platformer / actifs, car cela sera important plus tard.

Dans la barre d'outils, cliquez sur le signe plus bleu (+) ou sélectionnez Jeu de tuilesAjouter des tuiles pour commencer le processus. La boîte de dialogue suivante vous sera présentée:

Ajout de tuiles à un jeu de tuiles dans Tiled

À partir de là, accédez aux dossiers répertoriés ci-dessous pour ajouter les ressources spécifiées à votre jeu de mosaïques:

Dossier Déposer
arcade-platformer / actifs / images / sol / herbe Tous les fichiers
arcade-platformer / actifs / images / HUD hudHeart_empty.png
hudHeart_full.png
hudHeart_half.png
hudX.png
arcade-platformer / actifs / images / objets coinBronze.png
coinGold.png
coinSilver.png
flagGreen_down.png
flagGreen1.png
flagGreen2.png
arcade-platformer / actifs / images / tuiles doorOpen_mid.png
doorOpen_top.png
grass.png
ladderMid.png
ladderTop.png
signExit.png
signLeft.png
signRight.png
torch1.png
torch2.png
eau.png
waterTop_high.png
waterTop_low.png

Lorsque vous avez terminé d'ajouter des fichiers, votre jeu de mosaïques doit ressembler à ceci:

L'ensemble de tuiles peuplées dans Tiled

Si vous ne voyez pas toutes vos vignettes, cliquez sur le bouton Envelopper dynamiquement les carreaux sur la barre d'outils pour les afficher tous.

Enregistrez votre nouveau jeu de mosaïques avec Ctrl+S ou alors DéposerSauvegarder dans le menu et revenez à votre carte de tuiles. Vous verrez le nouveau jeu de mosaïques en bas à droite de l'interface Mosaïque, prêt à être utilisé pour définir votre carte de mosaïques!

Définition des couches de carte

Chaque élément d'un niveau a un objectif spécifique:

  • Le sol et les murs définissent où et comment votre joueur peut se déplacer.
  • Les pièces et autres objets de collection marquent des points et débloquent des succès.
  • Les échelles permettent au joueur de grimper sur de nouvelles plates-formes mais ne bloquent pas le mouvement autrement.
  • Les éléments d'arrière-plan fournissent un intérêt visuel et peuvent fournir des informations.
  • Les ennemis fournissent des obstacles que le joueur doit éviter.
  • Les objectifs fournissent une raison de se déplacer dans le niveau.

Chacun de ces différents types d'articles nécessite une manipulation différente dans arcade. Par conséquent, il est logique de les garder séparés lors de leur définition dans Tiled. Tiled vous permet de faire exactement cela en utilisant couches de carte. En plaçant différents types d'éléments sur différentes couches de carte et en traitant chaque couche séparément, vous pouvez suivre et gérer chaque type de sprite différemment.

Pour définir un calque, ouvrez d'abord le Couches vue dans le coin supérieur droit de l'écran Mosaïque:

La vue Calques en mosaïque

Le calque par défaut est déjà défini et sélectionné. Renommez ce calque en terre en cliquant sur le calque, puis en modifiant le Nom dans le Propriétés vue à gauche. Vous pouvez également double-cliquer sur le nom pour le modifier directement dans le Couches panneau:

Changer le nom d'un calque dans Tiled

Ce calque contiendra vos tuiles de sol, y compris les murs à travers lesquels le joueur ne peut pas marcher.

La création de nouveaux calques vous oblige à définir non seulement le nom du calque, mais également le type de calque. Tiled propose quatre types de couches:

  1. Couches de tuiles vous permettent de placer des tuiles de votre ensemble de tuiles sur la carte. Le placement est limité aux emplacements de la grille et les tuiles doivent être placées comme défini.
  2. Couches d'objets te permettre de placer objets comme des objets de collection ou des déclencheurs sur la carte. Les objets peuvent être des tuiles de la carte de tuiles ou des formes librement dessinées, et ils peuvent être visibles ou non. Chaque objet peut être librement positionné, mis à l'échelle et pivoté.
  3. Couches d'image vous permettent de placer des images sur la carte pour les utiliser comme image d'arrière-plan ou de premier plan.
  4. Grouper les couches vous permettent de regrouper les couches en groupes pour une gestion simplifiée des cartes.

Pour ce didacticiel, vous utiliserez une couche d'objets pour placer des pièces de monnaie sur la carte et des couches de tuiles pour tout le reste.

Pour créer les nouvelles couches de tuiles, cliquez sur Nouvelle Couche dans le Couches afficher, puis sélectionnez Couche de tuile:

Création d'une nouvelle couche de carte dans Tiled

Créez trois nouvelles couches de tuiles nommées échelles, Contexte, et but.

Ensuite, créez un nouveau calque d'objet appelé pièces de monnaie pour contenir vos objets de collection:

Création d'une nouvelle couche de carte d'objets dans Tiled

Vous pouvez organiser les calques dans l'ordre de votre choix à l'aide des boutons fléchés en bas de la vue des calques. Vous pouvez maintenant commencer à définir votre niveau!

Concevoir un niveau

Dans le livre Classic Game Design, l'auteur et développeur de jeux Franz Lanzinger définit huit règles pour la conception de jeux classiques. Voici les trois premières règles:

  1. Rester simple.
  2. Démarrez le jeu immédiatement.
  3. Rampe de difficulté de facile à difficile.

De même, le développeur de jeux vétéran Steve Goodwin parle de jeux d'équilibrage dans son livre Polished Game Development. Il souligne qu'un bon équilibre du jeu commence avec le niveau 1, qui «devrait être le premier développé et le dernier terminé».

Avec ces idées à l'esprit, voici quelques conseils pour concevoir vos niveaux de plateforme:

  1. Le premier niveau du jeu devrait présenter à l'utilisateur les fonctionnalités et les commandes de base du jeu.
  2. Rendez les premiers obstacles faciles à surmonter.
  3. Rendez les premiers objets de collection impossibles à manquer et les suivants plus difficiles à obtenir.
  4. N'introduisez pas d'obstacles qui nécessitent de la finesse à surmonter tant que l'utilisateur n'a pas appris à naviguer dans le monde.
  5. N'introduisez pas d'ennemis tant que l'utilisateur n'a pas appris à surmonter les obstacles.

Vous trouverez ci-dessous un examen plus approfondi d'un premier niveau conçu avec ces directives à l'esprit. Dans les documents téléchargeables, cette conception de niveau complète se trouve sous assets / platform_level_01.tmx:

Conception de base pour le niveau un du jeu de plateforme d'arcade

Le joueur commence par la gauche et procède vers la droite, indiqué par la flèche pointant vers la droite. Au fur et à mesure que le joueur se déplace à droite, il trouve une pièce de bronze, ce qui augmentera son score. Une deuxième pièce de bronze se trouve plus tard suspendue plus haut dans les airs, ce qui démontre au joueur que les pièces peuvent être n'importe où. Ensuite, le joueur trouve une pièce d'or, qui a une valeur en points différente.

Le joueur monte alors une rampe, ce qui démontre qu'il y a plus de monde au-dessus d'eux. Au sommet de la colline se trouve la dernière pièce d'or, qu'ils doivent sauter pour obtenir. De l'autre côté de la colline se trouve la sortie, qui est également marquée.

Ce niveau simple permet de montrer à l'utilisateur comment se déplacer et sauter. Cela montre qu'il existe des objets de collection dans le monde qui valent des points. Il montre également des éléments informatifs ou décoratifs avec lesquels le joueur n'interagit pas, tels que le signe de la flèche, le panneau de sortie et les touffes d'herbe. Enfin, il leur montre à quoi ressemble l'objectif.

Une fois le travail acharné de conception de votre premier niveau terminé, vous pouvez maintenant le créer dans Tiled.

Construire un niveau

Avant de pouvoir placer des pièces et le but, vous devez savoir comment y arriver. La première chose à définir est donc l'emplacement du sol. Avec votre carte de tuiles sélectionnée dans Tiled, sélectionnez le terre couche à construire.

Dans votre jeu de mosaïques, sélectionnez le herbeCentre tuile. Ensuite, cliquez dans n'importe quelle grille de la rangée inférieure de votre carte de tuiles pour mettre cette tuile en place:

Définition de la première tuile de sol dans Tiled

Avec le premier jeu de tuiles, vous pouvez faire glisser sur la ligne du bas pour tout définir sur herbeCentre. Ensuite, sélectionnez le herbeMid tuile pour dessiner le dessus herbeux du niveau à travers la deuxième rangée:

Placer des tuiles d'herbe dans Tiled

Continuez à construire le niveau en utilisant les tuiles d'herbe pour construire une colline de deux tuiles en commençant environ à mi-chemin à travers le monde. Laissez un espace de quatre tuiles sur le bord droit pour permettre au joueur de descendre la colline et pour le panneau de sortie et le portail de sortie.

Ensuite, passez à la but couche et placez les tuiles du portail de sortie dans une tuile à partir du bord extrême droit:

Placer le but dans Tiled

Une fois la plate-forme de base et l'objectif en place, vous pouvez placer des éléments d'arrière-plan. Passer au Contexte couche, placez une flèche sur le côté gauche pour indiquer au joueur où aller et un panneau de sortie à côté du portail. Vous pouvez également placer des touffes d’herbe à l’endroit de votre choix sur la carte:

Placement des éléments d'arrière-plan dans Tiled

Vous pouvez maintenant définir où placer les pièces. Passez à votre pièces de monnaie couche pour le faire. N'oubliez pas qu'il s'agit d'un calque d'objets, vous n'êtes donc pas limité à placer des pièces de monnaie sur la grille. Sélectionnez la pièce de bronze et placez-la près de la flèche de départ. Placez une deuxième pièce de bronze un peu plus à droite et un peu plus haut:

Placer des pièces de monnaie en bronze sur le niveau dans Tiled

Repeat this process with two gold coins, placing one just before the hill and one on top, at least three tiles above the top of the hill:

Placing gold coin objects on the level in Tiled

The different coins should score different point values when the player collects them. There are a couple of ways you can do this, but for this tutorial you’ll set a custom property to track each coin’s point value.

Defining Custom Properties

One of the benefits of using an object layer is the ability to set custom properties on objects on that layer. Custom properties are defined by you and represent any value you wish. In this case, you’ll use them to specify the point value for each coin on the layer.

With the coins layer selected, press S to begin selecting objects. Then right-click the first bronze coin you placed, and select Object Properties from the context menu to view its properties:

Viewing object properties in Tiled

Predefined object properties are shown at the top of the Object Properties view, while custom properties are shown below. Currently there are no custom properties, so you need to add one. Click the blue plus sign at the bottom of the Object Properties view to add a new custom property:

Adding a new custom property to an object in Tiled

You define both the name and the type of the custom property. In this case, you set the property as an int and the name as point_value.

With the custom property defined, you can set its value in the Object Properties view:

Setting the value of a custom property

Perform these same steps for each of the coins in your level, setting the values to dix for bronze coins and 20 for gold coins. Don’t forget to save the level, because next you’ll learn how to read it into arcade.

Reading Game Levels

Defining a game level in Tiled is great, but unless you can read it into arcade, it’s not very useful. Luckily, arcade natively supports reading Tiled tile maps and processing the layers. Once done, your game will look like this:

First game level with the Roz player shown

Reading your game level is handled completely in .setup(). This code can be found in the file arcade_platformer/03_read_level_one.py.

First, you add a few more constants:

# Game constants
# Window dimensions
SCREEN_WIDTH = 1000
SCREEN_HEIGHT = 650
SCREEN_TITLE = "Arcade Platformer"

# Scaling constants
MAP_SCALING = 1.0

# Player constants
GRAVITY = 1.0
PLAYER_START_X = 65
PLAYER_START_Y = 256

These constants define the scaling factor for your maps as well as the starting position of your player and the strength of gravity in your world. These constants are used to define the level in .setup():

def setup(self) -> Rien:
    """Sets up the game for the current level"""

    # Get the current map based on the level
    map_name = F"platform_level_self.level:02.tmx"
    map_path = ASSETS_PATH / map_name

    # What are the names of the layers?
    wall_layer = "ground"
    coin_layer = "coins"
    goal_layer = "goal"
    background_layer = "background"
    ladders_layer = "ladders"

    # Load the current map
    game_map = arcade.tilemap.read_tmx(str(map_path))

    # Load the layers
    self.background = arcade.tilemap.process_layer(
        game_map, layer_name=background_layer, scaling=MAP_SCALING
    )
    self.goals = arcade.tilemap.process_layer(
        game_map, layer_name=goal_layer, scaling=MAP_SCALING
    )
    self.walls = arcade.tilemap.process_layer(
        game_map, layer_name=wall_layer, scaling=MAP_SCALING
    )
    self.ladders = arcade.tilemap.process_layer(
        game_map, layer_name=ladders_layer, scaling=MAP_SCALING
    )
    self.coins = arcade.tilemap.process_layer(
        game_map, layer_name=coin_layer, scaling=MAP_SCALING
    )

    # Set the background color
    background_color = arcade.color.FRESH_AIR
    si game_map.background_color:
        background_color = game_map.background_color
    arcade.set_background_color(background_color)

    # Create the player sprite if they're not already set up
    si ne pas self.player:
        self.player = self.create_player_sprite()

    # Move the player sprite back to the beginning
    self.player.center_x = PLAYER_START_X
    self.player.center_y = PLAYER_START_Y
    self.player.change_x = 0
    self.player.change_y = 0

    # Load the physics engine for this map
    self.physics_engine = arcade.PhysicsEnginePlatformer(
        player_sprite=self.player,
        platforms=self.walls,
        gravity_constant=GRAVITY,
        ladders=self.ladders,
    )

First, you build the name of the current tile map using the current level. The format string self.level:02 results in a two-digit level number and allows you to define up to ninety-nine different map levels.

Next, using pathlib syntax, define the full path to your maps. This allows arcade to properly locate all your game resources.

Next, define the names of your layers, which you will use shortly. Make sure these match the layer names you defined in Tiled.

Now you open the tile map so you can process the previously named layers. The function arcade.tilemap.process_layer() takes a number of arguments, but you will provide only three of them:

  1. le game_map, which contains the layer to be processed
  2. The name of the layer to read and process
  3. Any scaling to apply to the tiles

arcade.tilemap.process_layer() returns a SpriteList populated with Sprite objects representing the tiles in the layer. Any custom properties defined for a tile, such as point_value for the tiles in the coins layer, are stored with the Sprite in a dictionary called .properties. You’ll see how to access them later.

You also set the background color of the level. You can define your own background color in Tiled using MapMap Properties and defining the Background Color property. If a background color isn’t set in Tiled, you use the predefined .FRESH_AIR color.

Next, check to see if a player is already created. This might be the case if you call .setup() to restart the level or move to the next level. If not, you call a method to create the player sprite (more on that a little later). If there is a player, then you place the player into position and ensure it’s not moving.

Finally, you can define the physics engine to use, passing in the following parameters:

  1. The player sprite
  2. UNE SpriteList containing walls
  3. A constant defining gravity
  4. UNE SpriteList containing ladders

Walls determine where the player can move and when they can jump, and ladders enable climbing. The gravity constant controls how fast or slow the player falls.

Of course, running this code now won’t work, as you still need to define the player.

Defining the Player

The one thing missing from your game so far is a player:

First game level with the Roz player shown

Dans .setup(), you called a method called .create_player_sprite() to define the player if it didn’t already exist. You create the player sprite in a separate method for two main reasons:

  1. It isolates any changes in the player from other code in .setup().
  2. It helps simplify the game setup code.

In any game, sprites can be static ou alors animated. Static sprites don’t change their appearance as the game progresses, such as the sprites that represent your ground tiles, background items, and coins. Animated sprites, by contrast, change their appearance as the game progresses. To add some visual interest, you’ll make your player sprite animated.

In Python arcade, you create an animated sprite by defining a list of images, called textures, for each animation sequence, such as climbing or walking. As the game progresses, arcade picks the next texture to display from the list for the sequence being animated. When the end of the list is reached, arcade starts over again from the beginning. By picking textures carefully, you can create the illusion of movement in your animated sprites:

A selection of textures for the animated Roz character

Because your player sprite performs a number of different activities, you provide texture lists for each of the following:

  • Standing, facing both right and left
  • Walking to the right and to the left
  • Climbing up and down a ladder

You can provide any number of textures for each of these activities. If you don’t want an action animated, you can provide a single texture.

The file arcade_platformer/04_define_player.py contains the definition of .create_player_sprite(), which defines the animated player sprite. Place this method in your Platformer class below .setup():

def create_player_sprite(self) -> arcade.AnimatedWalkingSprite:
    """Creates the animated player sprite

                Retour:
                                The properly set up player sprite
                """
    # Where are the player images stored?
    texture_path = ASSETS_PATH / "images" / "player"

    # Set up the appropriate textures
    walking_paths = [[[[
        texture_path / F"alienGreen_walkX.png" for X dans (1, 2)
    ]
    climbing_paths = [[[[
        texture_path / F"alienGreen_climbX.png" for X dans (1, 2)
    ]
    standing_path = texture_path / "alienGreen_stand.png"

    # Load them all now
    walking_right_textures = [[[[
        arcade.load_texture(texture) for texture dans walking_paths
    ]
    walking_left_textures = [[[[
        arcade.load_texture(texture, mirrored=True)
        for texture dans walking_paths
    ]

    walking_up_textures = [[[[
        arcade.load_texture(texture) for texture dans climbing_paths
    ]
    walking_down_textures = [[[[
        arcade.load_texture(texture) for texture dans climbing_paths
    ]

    standing_right_textures = [[[[arcade.load_texture(standing_path)]

    standing_left_textures = [[[[
        arcade.load_texture(standing_path, mirrored=True)
    ]

    # Create the sprite
    player = arcade.AnimatedWalkingSprite()

    # Add the proper textures
    player.stand_left_textures = standing_left_textures
    player.stand_right_textures = standing_right_textures
    player.walk_left_textures = walking_left_textures
    player.walk_right_textures = walking_right_textures
    player.walk_up_textures = walking_up_textures
    player.walk_down_textures = walking_down_textures

    # Set the player defaults
    player.center_x = PLAYER_START_X
    player.center_y = PLAYER_START_Y
    player.Etat = arcade.FACE_RIGHT

    # Set the initial texture
    player.texture = player.stand_right_textures[[[[0]

    revenir player

For your game, you animate Roz when they walk and climb but not when they are simply standing still. Each animation has two separate images, and your first task is to locate those images. You can download all the assets and source code used in this tutorial by clicking the link below:

Alternatively, you can create a folder called assets/images/player to store the textures used to draw Roz. Then, in the Platformer Pack Redux (360 Assets) archive you downloaded earlier, locate the PNG/Players/128x256/Green folder, and copy all the images there to your new assets/images/player folder.

This new path containing the player textures is defined in texture_path. Using this path, you create full pathnames to each texture resource using list comprehensions and f-string formatting.

Having these paths allows you to create a list of textures with arcade.load_texture() using more list comprehensions. Since Roz can walk left and right, you define different lists for each direction. The images show Roz pointing to the right, so you use the mirrored parameter when defining the textures for Roz walking or standing facing left. Moving up or down a ladder looks the same, so those lists are defined identically.

Even though there is only one standing texture, you still need to place it in a list so arcade can deal with the AnimatedSprite properly.

All the really hard work is done now. You create the actual AnimatedWalkingSprite, specifying the texture lists to use. Next, you set Roz’s initial location and direction as well as the first texture to display. Finally, you return the completely constructed sprite at the end of the method.

Now you have an initial map and a player sprite. If you run this code, you should see the following:

The initial play test results in a black screen.

Well, that’s not very entertaining. That’s because while you’ve created everything, you aren’t currently updating or drawing anything. Time to fix that!

Updating and Drawing

Updating the state of your game occurs in .on_update(), which arcade calls roughly sixty times per second. This method handles the following actions and events:

  • Moving player and enemy sprites
  • Detecting collisions with enemies or collectibles
  • Updating scores
  • Animating sprites

In short, everything that makes your game playable occurs in .on_update(). After everything has been updated, arcade calls .on_draw() to render everything to the screen.

This separation of game logic from game display means you can add or modify features in your game freely without affecting code that displays the game. In fact, because most of the game logic occurs in .on_update(), your .on_draw() method is often very short.

You can find all the code below in arcade_platformer/05_update_and_draw.py in the downloadable materials. Add .on_draw() to your Platformer class:

def on_draw(self) -> Rien:
    arcade.start_render()

    # Draw all the sprites
    self.background.draw()
    self.walls.draw()
    self.coins.draw()
    self.goals.draw()
    self.ladders.draw()
    self.player.draw()

After the obligatory call to arcade.start_render(), you call .draw() on all your sprite lists, followed by the player sprite. Note the order in which items are drawn. You should start with sprites that appear farthest back and proceed forward. Now when you run the code, it should look like this:

The real initial play test screen drawn to the window.

The only thing missing is proper placement of the player sprite. Pourquoi? Because animated sprites need to be updated to select the proper texture to display and proper placement on the screen, and you haven’t updated anything yet. Here’s what that looks like:

def on_update(self, delta_time: float) -> Rien:
    """Updates the position of all game objects

                Arguments:
                                delta_time float -- How much time since the last call
                """

    # Update the player animation
    self.player.update_animation(delta_time)

    # Update player movement based on the physics engine
    self.physics_engine.update()

    # Restrict user movement so they can't walk off screen
    si self.player.la gauche < 0:
        self.player.la gauche = 0

    # Check if we've picked up a coin
    coins_hit = arcade.check_for_collision_with_list(
        sprite=self.player, sprite_list=self.coins
    )

    for coin dans coins_hit:
        # Add the coin score to our score
        self.score += int(coin.properties[[[["point_value"])

        # Play the coin sound
        arcade.play_sound(self.coin_sound)

        # Remove the coin
        coin.remove_from_sprite_lists()

    # Now check if we're at the ending goal
    goals_hit = arcade.check_for_collision_with_list(
        sprite=self.player, sprite_list=self.goals
    )

    si goals_hit:
        # Play the victory sound
        self.victory_sound.jouer()

        # Set up the next level
        self.level += 1
        self.setup()

To make sure your game operates at a constant speed no matter the actual frame rate, .on_update() takes a single float parameter called delta_time, which indicates the time since the last update.

The first thing to do is to animate the player sprite. Based on the player’s movement, .update_animation() automatically selects the correct texture to use.

Next, you update the movement of everything that can move. Since you defined a physics engine in .setup(), it makes sense to let it handle movement. However, the physics engine will let the player run off the left side of the game map, so you also need to take steps to prevent that.

Now that the player has moved, you check if they have collided with a coin. If so, that counts as collecting the coin, so you increment the player’s score using the point_value custom property you defined in Tiled. Then you play a sound and remove the coin from the play field.

You also check if the player has reached the final goal. If so, you play the victory sound, increment the level, and call .setup() again to load the next map and reset the player in it.

But how does the user reach that final goal? The physics engine will make sure Roz doesn’t fall through the floor and can jump, but it doesn’t actually know where to move Roz or when to jump. That’s something the user should decide, and you need to provide a way for them to do that.

Moving the Player Sprite

In the early days of computer gaming, the only input device available was the keyboard. Even today, many games—including this one—still provide keyboard control.

Moving the player using the keyboard can be done in a variety of ways. There are many different popular keyboard arrangements, including:

Of course, there are many other keyboard arrangements to choose from.

Since you need to allow Roz to move in all four directions as well as jump, for this game you’ll use the arrow and IJKL keys for movement and the space bar for jumping:

All keyboard input in arcade is handled by .on_key_press() et .on_key_release(). You can find the code for making Roz move via the keyboard in arcade_platformer/06_keyboard_movement.py.

First, you need two new constants:

23# Player constants
24GRAVITY = 1.0
25PLAYER_START_X = 65
26PLAYER_START_Y = 256
27PLAYER_MOVE_SPEED = dix
28PLAYER_JUMP_SPEED = 20

These constants control how fast Roz moves. PLAYER_MOVE_SPEED controls their movement left, right, and up and down ladders. PLAYER_JUMP_SPEED indicates how high Roz can jump. By setting these values as constants, you can tweak them to dial in the proper gameplay during testing.

You use those constants in .on_key_press():

def on_key_press(self, key: int, modifiers: int) -> Rien:
    """Arguments:
                key -- Which key was pressed
                modifiers -- Which modifiers were down at the time
                """

    # Check for player left or right movement
    si key dans [[[[arcade.key.LEFT, arcade.key.J]:
        self.player.change_x = -PLAYER_MOVE_SPEED
    elif key dans [[[[arcade.key.RIGHT, arcade.key.L]:
        self.player.change_x = PLAYER_MOVE_SPEED

    # Check if player can climb up or down
    elif key dans [[[[arcade.key.UP, arcade.key.je]:
        si self.physics_engine.is_on_ladder():
            self.player.change_y = PLAYER_MOVE_SPEED
    elif key dans [[[[arcade.key.DOWN, arcade.key.K]:
        si self.physics_engine.is_on_ladder():
            self.player.change_y = -PLAYER_MOVE_SPEED

    # Check if player can jump
    elif key == arcade.key.SPACE:
        si self.physics_engine.can_jump():
            self.player.change_y = PLAYER_JUMP_SPEED
            # Play the jump sound
            arcade.play_sound(self.jump_sound)

There are three major components to this code:

  1. You handle horizontal movement by checking for the Left et Right arrows and the J et L keys from your IJKL arrangement. You then set the .change_x property appropriately.

  2. You handle vertical movement by checking for the En haut et Down arrows as well as the je et K keys. However, since Roz can only move up and down on ladders, you verify that using .is_on_ladder() before moving up or down.

  3. You handle jumping via the Space key. To prevent Roz from jumping in midair, you check if Roz can jump using .can_jump(), which returns True only if Roz is standing on a wall. If so, you move the player up and play the jump sound.

When you release a key, Roz should stop moving. You set that up in .on_key_release():

def on_key_release(self, key: int, modifiers: int) -> Rien:
    """Arguments:
                key -- The key which was released
                modifiers -- Which modifiers were down at the time
                """

    # Check for player left or right movement
    si key dans [[[[
        arcade.key.LEFT,
        arcade.key.J,
        arcade.key.RIGHT,
        arcade.key.L,
    ]:
        self.player.change_x = 0

    # Check if player can climb up or down
    elif key dans [[[[
        arcade.key.UP,
        arcade.key.je,
        arcade.key.DOWN,
        arcade.key.K,
    ]:
        si self.physics_engine.is_on_ladder():
            self.player.change_y = 0

This code follows a similar pattern to .on_key_press():

  1. You check if any of the horizontal movement keys were released. If so, then Roz’s change_x is set to 0.
  2. You check if the vertical movement keys were released. Again, since Roz needs to be on a ladder to move up and down, you need to check .is_on_ladder() here as well. If not, a player could jump and then press and release En haut, leaving Roz hanging in midair!

Note that you don’t need to check if the jump key was released.

OK, now you can move Roz around, but why does Roz just walk out of the window to the right? You need a way to keep Roz visible in the game world as they move around, and that’s where viewports come in.

Scrolling the Viewport

Early video games restricted gameplay to a single window, which was the entire world for the player. However, modern video game worlds can be too large to fit in a tiny game window. Most games implement a scrolling view, which shows a portion of game world to the player. In Python arcade, this scrolling view is called a viewport. It is essentially a rectangle that defines which part of the game world you show in the gameplay window:

You can find this code in the downloadable materials under arcade_platformer/07_scrolling_view.py.

To implement the scrolling view, you define the viewport based on Roz’s current location. When Roz travels close to any edge of the gameplay window, you move the viewport in the direction of travel so Roz remains comfortably on screen. You also ensure the viewport doesn’t scroll outside the visible world. To do this, you need to know a few things:

  • How close can Roz travel to the gameplay window edge before the viewport scrolls? This is known as the margin, and it can be different for each window edge.
  • Where is the current viewport now?
  • How wide is your game map?
  • Where is Roz now?

First, you define the margins as constants at the top of the code:

# Player constants
GRAVITY = 1.0
PLAYER_START_X = 65
PLAYER_START_Y = 256
PLAYER_MOVE_SPEED = dix
PLAYER_JUMP_SPEED = 20

# Viewport margins
# How close do we have to be to scroll the viewport?
LEFT_VIEWPORT_MARGIN = 50
RIGHT_VIEWPORT_MARGIN = 300
TOP_VIEWPORT_MARGIN = 150
BOTTOM_VIEWPORT_MARGIN = 150

Note the difference between LEFT_VIEWPORT_MARGIN et RIGHT_VIEWPORT_MARGIN. This allows Roz to get closer to the left edge than the right. This way, as Roz moves right, the user has more time to see and react to obstacles.

The viewport is a rectangle with the same width and height as the gameplay window, which are the constants SCREEN_WIDTH et SCREEN_HEIGHT. Therefore, to fully describe the viewport, you only need to know the location of the bottom-left corner. By changing this corner, the viewport will react to Roz’s movement. You track this corner in your game object and define it in .setup(), right after you move Roz to the start of the level:

# Move the player sprite back to the beginning
self.player.center_x = PLAYER_START_X
self.player.center_y = PLAYER_START_Y
self.player.change_x = 0
self.player.change_y = 0

# Reset the viewport
self.view_left = 0
self.view_bottom = 0

For this tutorial, since every level starts in the same place, the bottom-left corner of the viewport always starts in the same place as well.

You can calculate the width of the game map by multiplying the number of tiles contained in the game map by the width of each tile. You calculate this after you read each map and set the background color in .setup():

# Set the background color
background_color = arcade.color.FRESH_AIR
si game_map.background_color:
    background_color = game_map.background_color
arcade.set_background_color(background_color)

# Find the edge of the map to control viewport scrolling
self.map_width = (
    game_map.map_size.width - 1
) * game_map.tile_size.width

Subtracting 1 from game_map.map_size.width corrects for the tile indexing used by Tiled.

Lastly, you know where Roz is located at any time by inspecting any of the position properties in self.player.

Here’s how you use all this information to scroll the viewport in .update():

  1. After updating Roz’s position, you calculate whether they are within a margin’s distance of any of the four edges.
  2. If so, you move the viewport in that direction by the amount Roz is inside the margin.

You can put this code in a separate method of the Platformer class to make updates easier:

def scroll_viewport(self) -> Rien:
    """Scrolls the viewport when the player gets close to the edges"""
    # Scroll left
    # Find the current left boundary
    left_boundary = self.view_left + LEFT_VIEWPORT_MARGIN

    # Are we to the left of this boundary? Then we should scroll left.
    si self.player.la gauche < left_boundary:
        self.view_left -= left_boundary - self.player.la gauche
        # But don't scroll past the left edge of the map
        si self.view_left < 0:
            self.view_left = 0

    # Scroll right
    # Find the current right boundary
    right_boundary = self.view_left + SCREEN_WIDTH - RIGHT_VIEWPORT_MARGIN

    # Are we to the right of this boundary? Then we should scroll right.
    si self.player.right > right_boundary:
        self.view_left += self.player.right - right_boundary
        # Don't scroll past the right edge of the map
        si self.view_left > self.map_width - SCREEN_WIDTH:
            self.view_left = self.map_width - SCREEN_WIDTH

    # Scroll up
    top_boundary = self.view_bottom + SCREEN_HEIGHT - TOP_VIEWPORT_MARGIN
    si self.player.Haut > top_boundary:
        self.view_bottom += self.player.Haut - top_boundary

    # Scroll down
    bottom_boundary = self.view_bottom + BOTTOM_VIEWPORT_MARGIN
    si self.player.bottom < bottom_boundary:
        self.view_bottom -= bottom_boundary - self.player.bottom

    # Only scroll to integers. Otherwise we end up with pixels that
    # don't line up on the screen.
    self.view_bottom = int(self.view_bottom)
    self.view_left = int(self.view_left)

    # Do the scrolling
    arcade.set_viewport(
        la gauche=self.view_left,
        right=SCREEN_WIDTH + self.view_left,
        bottom=self.view_bottom,
        Haut=SCREEN_HEIGHT + self.view_bottom,
    )

This code can look a little confusing, so it may be useful to look at a concrete example, such as what happens when Roz moves right and you need to scroll the viewport. Here’s the code you’ll walk through:

# Scroll right
# Find the current right boundary
right_boundary = self.view_left + SCREEN_WIDTH - RIGHT_VIEWPORT_MARGIN

# Are we right of this boundary? Then we should scroll right.
si self.player.right > right_boundary:
    self.view_left += self.player.right - right_boundary
    # Don't scroll past the right edge of the map
    si self.view_left > self.map_width - SCREEN_WIDTH:
        self.view_left = self.map_width - SCREEN_WIDTH

Here are some sample values for your key variables:

  • Roz has moved right, setting their self.player.right property to 710.
  • The viewport hasn’t changed yet, so self.view_left is currently 0.
  • The constant SCREEN_WIDTH est 1000.
  • The constant RIGHT_VIEWPORT_MARGIN est 300.

First, calculate the value of right_boundary, which determines if Roz is within the margin of the right edge of the viewport:

  • The right edge of the visible viewport is self.view_left + SCREEN_WIDTH, which is 1000.
  • Subtracting the RIGHT_VIEWPORT_MARGIN from this gives you a right_boundary of 700.

Next, check if Roz has moved beyond the right_boundary. Depuis self.player.right > right_boundary est True, you need to move the viewport, so you calculate how far to move it:

  • Calculate self.player.right - right_boundary as dix, which is how far Roz has moved into the right margin.
  • Since the viewport rectangle is measured from the left, add this to self.view_left to make it dix.

However, you don’t want to move the viewport off the edge of the world. If the viewport were scrolled all the way to the right, its left edge would be a full screen width smaller than the width of the map:

  • Check if self.view_left > self.map_width - SCREEN_WIDTH.
  • If so, simply set self.view_left to that value to restrict the viewport movement.

You do the same sequence of steps for the left boundary. The top and bottom edges are also checked to update self.view_bottom. With both view variables updated, the last thing to do is to set the viewport using arcade.set_viewport().

Since you put this code in a separate method, call it at the end of .on_update():

si goals_hit:
    # Play the victory sound
    self.victory_sound.jouer()

    # Set up the next level
    self.level += 1
    self.setup()

# Set the viewport, scrolling if necessary
self.scroll_viewport()

With this in place, your game view should follow Roz as they move left, right, up, or down, never letting them get off screen!

That’s it—you have a platformer! Now it’s time to add some extras!

Conclusion

The Python arcade library is a modern Python framework, ideal for crafting games with compelling graphics and sound. Object oriented and built for Python 3.6 and up, arcade provides the programmer with a modern set of tools for crafting great game experiences, including platform games. arcade is open source and contributions are always welcome.

After reading this tutorial, you’re now able to:

  • Install the Python arcade library
  • Create a basic 2D game structure
  • Find usable game artwork and other assets
  • Build platform maps using the Tiled map editor
  • Define player actions, game rewards, et obstacles
  • Control your player with keyboard et joystick contribution
  • Play sound effects for game actions
  • Scroll the game screen with viewports to keep your player in view
  • Add Titre, instruction, et pause screens
  • Move nonplayer game elements on the screen

There’s still plenty to do with this game. Here are some feature ideas you can implement:

  • Add a Game Over screen.
  • Animate coins on screen.
  • Add animations when Roz collides with an enemy.
  • Detect when Roz falls off the map.
  • Give Roz multiple lives.
  • Add a high score table.
  • Use the arcade.PymunkPhysicsEngine to provide more realistic physics interactions.

There’s lots more to explore in the arcade library as well. With these techniques, you’re now fully equipped to get out there and make some cool games!

You can download all the code, images, and sounds used in this tutorial by clicking the link below:

[ad_2]