Cours Python en ligne
- 10 meilleurs plugins, addons et extensions Gutenberg
- Épisode 8: Enseigner le python dans les salles de classe et d'apprentissage de Grok
- Traitement du langage naturel avec spaCy en Python – Real Python
- Épisode # 49 Projet Python basé sur Microsoft JIT: Pyjion
- Simplifiez le développement de l'interface graphique Python avec PySimpleGUI – Real Python
Les jeux informatiques sont un excellent moyen d'initier les gens au codage et à l'informatique. Depuis que j'étais un joueur dans ma jeunesse, l'attrait d'écrire des jeux vidéo a été la raison pour laquelle j'ai appris à coder. Bien sûr, quand j'ai appris Python, mon premier réflexe a été d'écrire un jeu Python.
Alors que Python rend l'apprentissage du code plus accessible à tous, les choix pour l'écriture de jeux vidéo peuvent être limités, surtout si vous voulez écrire jeux d'arcade avec de superbes graphismes et des effets sonores accrocheurs. Pendant de nombreuses années, les programmeurs de jeux Python se sont limités à pygame
cadre. Maintenant, il y a un autre choix.
le arcade
La bibliothèque est un framework Python moderne pour créer des jeux avec des graphismes et des sons convaincants. Orienté objet et construit pour Python 3.6 et supérieur, arcade
fournit au programmeur un ensemble moderne d'outils pour créer de grandes expériences de jeu Python.
Dans ce didacticiel, vous apprendrez à:
- Installer le
arcade
bibliothèque - Dessiner éléments à l'écran
- Travail avec le
arcade
Boucle de jeu Python - Gérer éléments graphiques à l'écran
- Manipuler entrée utilisateur
- Jouer effets sonores et musique
- Décris comment la programmation de jeux Python avec
arcade
diffère depygame
Ce tutoriel suppose que vous avez une compréhension de l'écriture de programmes Python. Puisque arcade
est une bibliothèque orientée objet, vous devez également être familiarisé avec la programmation orientée objet. Tous les codes, images et sons de ce didacticiel sont disponibles en téléchargement sur le lien ci-dessous:
Contexte et configuration
le arcade
bibliothèque a été écrite par Paul Vincent Craven, professeur d'informatique au Simpson College dans l'Iowa, aux États-Unis. Comme il est construit au-dessus du pyglet
fenêtrage et médiathèque, arcade
propose diverses améliorations, modernisations et améliorations par rapport à pygame
:
- Présente des graphismes OpenGL modernes
- Prend en charge les indications de type Python 3
- A un meilleur support pour les sprites animés
- Intègre des noms de commandes, de fonctions et de paramètres cohérents
- Encourage la séparation de la logique du jeu du code d'affichage
- Nécessite moins de code passe-partout
- Maintient plus de documentation, y compris des exemples de jeux Python complets
- Dispose d'un moteur physique intégré pour les jeux de plateforme
À installer arcade
et ses dépendances, utilisez les pépin
commander:
$ python -m pip install arcade
Sur Mac, vous devez également installer PyObjC
:
$ python -m pip installe l'arcade PyObjC
Des instructions d'installation complètes basées sur votre plate-forme sont disponibles pour Windows, Mac, Linux et même Raspberry Pi. Vous pouvez même installer arcade
directement depuis la source si vous préférez.
Ce didacticiel suppose que vous utilisez arcade
2.1 et Python 3.7 partout.
De base arcade
Programme
Avant de creuser, examinons un arcade
programme qui ouvrira une fenêtre, la remplira de blanc et dessinera un cercle bleu au milieu:
1 # Programme d'arcade de base
2 # Affiche une fenêtre blanche avec un cercle bleu au milieu
3
4 # Importations
5 importation arcade
6
sept # Constantes
8 SCREEN_WIDTH = 600
9 SCREEN_HEIGHT = 800
dix SCREEN_TITLE = "Bienvenue dans Arcade"
11 RAYON = 150
12
13 # Ouvrez la fenêtre
14 arcade.fenêtre ouverte(SCREEN_WIDTH, SCREEN_HEIGHT, SCREEN_TITLE)
15
16 # Définissez la couleur d'arrière-plan
17 arcade.set_background_color(arcade.Couleur.BLANC)
18
19 # Videz l'écran et commencez à dessiner
20 arcade.start_render()
21
22 # Dessinez un cercle bleu
23 arcade.draw_circle_filled(
24 SCREEN_WIDTH / 2, SCREEN_HEIGHT / 2, RAYON, arcade.Couleur.BLEU
25 )
26
27 # Terminer le dessin
28 arcade.finish_render()
29
30 # Tout afficher
31 arcade.courir()
Lorsque vous exécutez ce programme, vous verrez une fenêtre qui ressemble à ceci:
Décomposons cette ligne par ligne:
- Ligne 5 importe le
arcade
bibliothèque. Sans cela, rien d'autre ne fonctionne. - Lignes 8 à 11 définissez certaines constantes que vous utiliserez un peu plus tard, pour plus de clarté.
- Ligne 14 ouvre la fenêtre principale. Vous fournissez la largeur, la hauteur et le texte de la barre de titre, et
arcade
fait le reste. - Ligne 17 définit la couleur d'arrière-plan à l'aide d'une constante de la
arcade.color
paquet. Vous pouvez également spécifier une couleur RVB à l'aide d'une liste ou d'un tuple. - Ligne 20 ensembles
arcade
en mode dessin. Tout ce que vous dessinez après cette ligne sera affiché à l'écran. - Lignes 23 à 25 dessinez le cercle en fournissant les coordonnées centrales X et Y, le rayon et la couleur à utiliser.
- Ligne 28 termine le mode dessin.
- Ligne 31 affiche votre fenêtre pour que vous puissiez la voir.
Si vous connaissez pygame
, vous remarquerez que certaines choses sont différentes:
- Il n'y a pas
pygame.init ()
. Toute l'initialisation est gérée lorsque vous exécutezimportation d'arcade
. - Il n'y a pas de boucle d'affichage explicitement définie. Il est géré en
arcade.run ()
. - Il n'y a pas de boucle d'événement ici non plus. Encore,
arcade.run ()
gère les événements et fournit certains comportements par défaut, tels que la possibilité de fermer la fenêtre. - Vous pouvez utiliser des couleurs prédéfinies pour le dessin plutôt que de les définir vous-même.
- Vous devez commencer et terminer le dessin dans l'arcade en utilisant
start_render ()
etfinish_render ()
.
Examinons de près les principes fondamentaux arcade
concepts derrière ce programme.
arcade
Concepts
Comme pygame
, arcade
le code s'exécute sur presque toutes les plateformes prenant en charge Python. Cela nécessite arcade
pour traiter les abstractions pour diverses différences matérielles sur ces plates-formes. Comprendre ces concepts et abstractions vous aidera à concevoir et à développer vos propres jeux tout en comprenant comment arcade
diffère de pygame
vous aidera à vous adapter à son point de vue unique.
Initialisation
Puisqu'il traite d'une variété de plateformes, arcade
doit effectuer une étape d'initialisation avant de pouvoir l'utiliser. Cette étape est automatique et se produit chaque fois que vous importez arcade
, il n'y a donc pas de code supplémentaire à écrire. Lorsque vous l'importez, arcade
fait ce qui suit:
- Vérifier que vous exécutez sur Python 3.6 ou supérieur.
- Importation le
pyglet_ffmeg2
bibliothèque pour la gestion du son, si elle est disponible. - Importation le
pyglet
bibliothèque pour la gestion des fenêtres et du multimédia. - Installer constantes pour les couleurs et les mappages de touches.
- Importation le reste
arcade
bibliothèque.
Comparez cela avec pygame
, ce qui nécessite une étape d'initialisation distincte pour chaque module.
Fenêtres et coordonnées
Tout en arcade
se passe dans une fenêtre, avec vous créez en utilisant fenêtre ouverte()
. Actuellement, arcade
ne prend en charge qu'une seule fenêtre d'affichage. Vous pouvez rendre la fenêtre redimensionnable lorsque vous l'ouvrez.
arcade
utilise le même système de coordonnées cartésiennes que vous avez peut-être appris en classe d'algèbre. La fenêtre vit dans le quadrant I, avec le point d'origine (0, 0) situé dans le coin inférieur gauche de l'écran. La coordonnée x augmente lorsque vous vous déplacez vers la droite et la coordonnée y augmente lorsque vous vous déplacez vers le haut:
Il est important de noter que ce comportement est le contraire de pygame
et de nombreux autres frameworks de jeu Python. Il vous faudra peut-être un certain temps pour vous adapter à cette différence.
Dessin
Hors de la boîte, arcade
a des fonctions pour dessiner diverses formes géométriques, notamment:
- Arcs
- Cercles
- Ellipses
- Lignes
- Paraboles
- Points
- Des polygones
- Rectangles
- Triangles
Toutes les fonctions de dessin commencent par dessiner_
et suivez un modèle de nom et de paramètres cohérent. Il existe différentes fonctions pour dessiner des formes remplies et délimitées:
Les rectangles étant courants, il existe trois fonctions distinctes pour les dessiner de différentes manières:
draw_rectangle ()
attend les coordonnées x et y du centre du rectangle, la largeur et la hauteur.draw_lrtb_rectangle ()
attend les coordonnées x gauche et droite, suivies des coordonnées y supérieure et inférieure.draw_xywh_rectangle ()
utilise les coordonnées x et y du coin inférieur gauche, suivies de la largeur et de la hauteur.
Notez que chaque fonction nécessite quatre paramètres. Vous pouvez également dessiner chaque forme à l'aide des fonctions de dessin tamponnées, qui utilisent des tampons de sommet pour tout pousser directement sur la carte graphique pour des améliorations de performances incroyables. Toutes les fonctions de dessin tamponnées commencent par créer_
et suivre des modèles de noms et de paramètres cohérents.
Conception orientée objet
En son coeur, arcade
est une bibliothèque orientée objet. Comme pygame
, tu peux écrire arcade
code de procédure, comme vous l'avez fait dans l'exemple ci-dessus. Cependant, le vrai pouvoir de arcade
s'affiche lorsque vous créez complètement programmes orientés objet.
Quand tu as appelé arcade.open_window ()
dans l'exemple ci-dessus, le code crée un arcade.Window
objet dans les coulisses pour gérer cette fenêtre. Plus tard, vous créerez votre propre classe basée sur arcade.Window
pour écrire un jeu Python complet.
Tout d'abord, jetez un œil à l'exemple de code d'origine, qui utilise désormais des concepts orientés objet, pour mettre en évidence les principales différences:
1 # Programme d'arcade de base utilisant des objets
2 # Affiche une fenêtre blanche avec un cercle bleu au milieu
3
4 # Importations
5 importation arcade
6
sept # Constantes
8 SCREEN_WIDTH = 600
9 SCREEN_HEIGHT = 800
dix SCREEN_TITLE = "Bienvenue dans Arcade"
11 RAYON = 150
12
13 # Des classes
14 classe Bienvenue(arcade.Fenêtre):
15 "" "Fenêtre d'accueil principale
16 "" "
17 def __init__(soi):
18 "" "Initialiser la fenêtre
19 "" "
20
21 # Appelle le constructeur de la classe parent
22 super().__init__(SCREEN_WIDTH, SCREEN_HEIGHT, SCREEN_TITLE)
23
24 # Définissez la fenêtre d'arrière-plan
25 arcade.set_background_color(arcade.Couleur.BLANC)
26
27 def on_draw(soi):
28 "" "Appelé chaque fois que vous avez besoin de dessiner votre fenêtre
29 "" "
30
31 # Videz l'écran et commencez à dessiner
32 arcade.start_render()
33
34 # Dessinez un cercle bleu
35 arcade.draw_circle_filled(
36 SCREEN_WIDTH / 2, SCREEN_HEIGHT / 2, RAYON, arcade.Couleur.BLEU
37 )
38
39 # Point d'entrée du code principal
40 si __Nom__ == "__principale__":
41 app = Bienvenue()
42 arcade.courir()
Jetons un œil à ce code ligne par ligne:
-
Lignes 1 à 11 sont les mêmes que l'exemple procédural précédent.
-
Ligne 15 c'est là que les différences commencent. Vous définissez une classe appelée
Bienvenue
basé sur la classe parentarcade.Window
. Cela vous permet de remplacer les méthodes de la classe parente si nécessaire. -
Lignes 18 à 26 définir les
.__ init __ ()
méthode. Après avoir appelé le parent.__ init __ ()
méthode utilisantsuper()
pour configurer la fenêtre, vous définissez sa couleur d'arrière-plan, comme vous l'avez fait auparavant. -
Lignes 28 à 38 définir
.on_draw ()
. Ceci est l'un des nombreuxFenêtre
méthodes que vous pouvez remplacer pour personnaliser le comportement de votrearcade
programme. Cette méthode est appelée à chaque foisarcade
veut dessiner sur la fenêtre. Cela commence par un appel àarcade.start_render ()
, suivi de tout votre code de dessin. Vous n'avez pas besoin d'appelerarcade.finish_render ()
, cependant, commearcade
appellera cela implicitement quand.on_draw ()
prend fin. -
Lignes 41 à 43 sont le principal point d’entrée de votre code. Après avoir créé un nouveau
Bienvenue
objet appeléapp
, tu appellesarcade.run ()
pour afficher la fenêtre.
Cet exemple orienté objet est la clé pour tirer le meilleur parti de arcade
. Vous avez peut-être remarqué une description de .on_draw ()
. arcade
l'appellera chaque fois qu'il souhaite dessiner sur la fenêtre. Alors, comment arcade
savoir quand dessiner quelque chose? Voyons les implications de cela.
Boucle de jeu
Toute l'action dans à peu près tous les jeux se déroule dans un boucle de jeu. Vous pouvez même voir des exemples de boucles de jeu dans des jeux physiques comme les dames, Old Maid ou le baseball. La boucle de jeu commence après la configuration et l'initialisation du jeu et se termine lorsque le jeu se termine. Plusieurs choses se produisent séquentiellement à l'intérieur de cette boucle. Au minimum, une boucle de jeu effectue les quatre actions suivantes:
- Le programme détermine si le jeu est terminé. Si c'est le cas, la boucle se termine.
- L'entrée utilisateur est traité.
- Les États des objets de jeu sont mis à jour en fonction de facteurs tels que l'entrée de l'utilisateur ou le temps.
- Le jeu affiche des visuels et joue des effets sonores en fonction du nouvel état.
Dans pygame
, vous devez configurer et contrôler explicitement cette boucle. Dans arcade
, la boucle de jeu Python vous est fournie, encapsulée dans le arcade.run ()
appel.
Pendant la boucle de jeu intégrée, arcade
appelle un ensemble de Fenêtre
méthodes pour implémenter toutes les fonctionnalités énumérées ci-dessus. Les noms de ces méthodes commencent tous par sur_
et peut être considéré comme gestionnaires de tâches ou d'événements. Quand le arcade
la boucle de jeu doit mettre à jour l'état de tous les objets de jeu Python, elle appelle .on_update ()
. Lorsqu'il doit vérifier le mouvement de la souris, il appelle .on_mouse_motion ()
.
Par défaut, aucune de ces méthodes ne fait quoi que ce soit d'utile. Lorsque vous créez votre propre classe basée sur arcade.Window
, vous les remplacez si nécessaire pour fournir vos propres fonctionnalités de jeu. Certaines des méthodes fournies sont les suivantes:
- La saisie au clavier:
.on_key_press ()
,.on_key_release ()
- Entrée souris:
.on_mouse_press ()
,.on_mouse_release ()
,.on_mouse_motion ()
- Mise à jour de l'objet de jeu:
.on_update ()
- Dessin:
.on_draw ()
Vous n'avez pas besoin de remplacer toutes ces méthodes, uniquement celles pour lesquelles vous souhaitez fournir un comportement différent. Vous n'avez pas non plus à vous soucier quand on les appelle, juste Que faire quand ils sont appelés. Ensuite, vous découvrirez comment regrouper tous ces concepts pour créer un jeu.
Fondamentaux de la conception de jeux Python
Avant de commencer à écrire du code, c'est toujours une bonne idée d'avoir un design en place. Étant donné que vous allez créer un jeu Python dans ce didacticiel, vous allez également concevoir un gameplay pour celui-ci:
- Le jeu est un jeu d'évitement ennemi à défilement horizontal.
- Le joueur commence sur le côté gauche de l'écran.
- Les ennemies entrez à intervalles réguliers et à des emplacements aléatoires sur la droite.
- Les ennemies déplacez-vous vers la gauche en ligne droite jusqu'à ce qu'ils soient hors de l'écran.
- Le joueur peut se déplacer vers la gauche, la droite, le haut ou le bas pour éviter les ennemis.
- Le joueur ne peut pas quitter l'écran.
- Le jeu se termine lorsque le joueur est touché par un ennemi ou que l'utilisateur ferme la fenêtre.
Lorsqu'il décrivait des projets logiciels, un de mes anciens collègues a dit un jour: "Vous ne savez pas ce que vous faites tant que vous ne savez pas ce que vous ne faites pas." Dans cet esprit, voici certaines choses que vous ne couvrirez pas. dans ce tutoriel:
- Pas de vies multiples
- Pas de tenue de score
- Aucune capacité d'attaque des joueurs
- Aucun niveau avancé
- Pas de personnages «boss»
Vous êtes libre d’essayer d’ajouter ces fonctionnalités et d’autres à votre propre programme.
Importations et constantes
Comme avec tout arcade
programme, vous allez commencer par importer la bibliothèque:
1 # Tireur d'arcade de base
2
3 # Importations
4 importation arcade
5 importation Aléatoire
6
sept # Constantes
8 SCREEN_WIDTH = 800
9 SCREEN_HEIGHT = 600
dix SCREEN_TITLE = "Arcade Space Shooter"
11 MISE À L'ÉCHELLE = 2.0
En plus de arcade
, vous importez également Aléatoire
, car vous utiliserez des nombres aléatoires plus tard. Les constantes définissent la taille et le titre de la fenêtre, mais MISE À L'ÉCHELLE
? Cette constante est utilisée pour agrandir la fenêtre et les objets du jeu afin de compenser les écrans à haute résolution. Vous le verrez utilisé à deux endroits au fur et à mesure que le didacticiel se poursuit. Vous pouvez modifier cette valeur pour l'adapter à la taille de votre écran.
Classe de fenêtre
Pour profiter pleinement de la arcade
Boucle de jeu Python et gestionnaires d'événements, créez une nouvelle classe basée sur arcade.Window
:
35 classe SpaceShooter(arcade.Fenêtre):
36 "" "Jeu de défilement latéral Space Shooter
37 Le joueur démarre à gauche, les ennemis apparaissent à droite
38 Le joueur peut se déplacer n'importe où, mais pas hors de l'écran
39 Les ennemis volent vers la gauche à vitesse variable
40 Les collisions mettent fin au jeu
41 "" "
42
43 def __init__(soi, largeur, la taille, Titre):
44 "" "Initialiser le jeu
45 "" "
46 super().__init__(largeur, la taille, Titre)
47
48 # Configurer les listes de sprites vides
49 soi.ennemis_list = arcade.SpriteList()
50 soi.clouds_list = arcade.SpriteList()
51 soi.all_sprites = arcade.SpriteList()
Votre nouvelle classe démarre exactement comme l'exemple orienté objet ci-dessus. À la ligne 43, vous définissez votre constructeur, qui prend la largeur, la hauteur et le titre de la fenêtre de jeu, et utilisez super()
de les transmettre au parent. Ensuite, vous initialisez des listes de sprites vides sur les lignes 49 à 51. Dans la section suivante, vous en apprendrez plus sur les sprites et les listes de sprites.
Sprites et listes de sprites
La conception de votre jeu Python nécessite un seul joueur qui commence à gauche et peut se déplacer librement autour de la fenêtre. Il appelle également des ennemis (en d'autres termes, plus d'un) qui apparaissent au hasard sur la droite et se déplacent vers la gauche. Bien que vous puissiez utiliser le dessiner_
commandes pour attirer le joueur et chaque ennemi, il deviendrait rapidement difficile de garder tout droit.
Au lieu de cela, la plupart des jeux modernes utilisent des sprites pour représenter des objets à l'écran. Essentiellement, un lutin est une image en deux dimensions d'un objet de jeu avec une taille définie qui est dessinée à une position spécifique sur l'écran. Dans arcade
, les sprites sont des objets de classe arcade.Sprite
, et vous les utiliserez pour représenter votre joueur ainsi que les ennemis. Vous allez même ajouter quelques nuages pour rendre l'arrière-plan plus intéressant.
La gestion de tous ces sprites peut être un défi. Vous allez créer un sprite solo, mais vous créerez également de nombreux ennemis et sprites cloud. Garder une trace de tous est un travail pour une liste de sprites. Si vous comprenez comment fonctionnent les listes Python, vous avez les outils à utiliser arcade
Listes de sprites. Listes de sprites faites plus que simplement vous accrocher à tous les sprites. Ils permettent trois comportements importants:
- Vous pouvez mise à jour tous les sprites de la liste avec un seul appel à
SpriteList.update ()
. - Vous pouvez dessiner tous les sprites de la liste avec un seul appel à
SpriteList.draw ()
. - Vous pouvez vérifier si une seule image-objet est entrée en collision avec une image-objet de la liste.
Vous vous demandez peut-être pourquoi vous avez besoin de trois listes de sprites différentes si vous avez seulement besoin de gérer plusieurs ennemis et nuages. La raison en est que chacune des trois listes de sprites différentes existe parce que vous les utilisez à trois fins différentes:
- Tu utilises
.enemies_list
pour mettre à jour les positions ennemies et vérifier les collisions. - Tu utilises
.clouds_list
pour mettre à jour les positions du cloud. - Enfin, vous utilisez
.all_sprites
pour tout dessiner.
Maintenant, une liste n'est plus utile que les données qu'elle contient. Voici comment remplir vos listes de sprites:
53 def installer(soi):
54 "" "Préparez le jeu à jouer
55 "" "
56
57 # Définissez la couleur d'arrière-plan
58 arcade.set_background_color(arcade.Couleur.BLEU CIEL)
59
60 # Configurer le lecteur
61 soi.joueur = arcade.Lutin("images / jet.png", MISE À L'ÉCHELLE)
62 soi.joueur.center_y = soi.la taille / 2
63 soi.joueur.la gauche = dix
64 soi.all_sprites.ajouter(soi.joueur)
Vous définissez .installer()
pour initialiser le jeu à un point de départ connu. Bien que vous puissiez le faire dans .__ init __ ()
, ayant un .installer()
est utile.
Imaginez que vous souhaitiez que votre jeu Python ait plusieurs niveaux ou que votre joueur ait plusieurs vies. Plutôt que de redémarrer tout le jeu en appelant .__ init __ ()
, tu appelles .installer()
à la place, pour réinitialiser le jeu à un point de départ connu ou configurer un nouveau niveau. Même si ce jeu Python ne possède pas ces fonctionnalités, la configuration de la structure permet de les ajouter plus rapidement.
Après avoir défini la couleur d'arrière-plan sur la ligne 58, vous définissez ensuite l'image-objet du joueur:
-
Ligne 61 crée un nouveau
arcade.Sprite
en spécifiant l'image à afficher et le facteur d'échelle. C'est une bonne idée d'organiser vos images dans un seul sous-dossier, en particulier sur des projets plus importants. -
Ligne 62 définit la position y de l'image-objet sur la moitié de la hauteur de la fenêtre.
-
Ligne 63 définit la position x de l'image-objet en plaçant le bord gauche à quelques pixels du bord gauche de la fenêtre.
-
Ligne 64 utilise enfin
.ajouter()
pour ajouter le sprite à la.all_sprites
liste que vous utiliserez pour dessiner.
Les lignes 62 et 63 montrent deux façons différentes de positionner votre sprite. Examinons de plus près toutes les options de positionnement de sprite disponibles.
Positionnement des sprites
Tous les sprites dans arcade
avoir une taille et une position spécifiques dans la fenêtre:
- La taille, spécifié par
Sprite.width
etSprite.height
, est déterminé par le graphique utilisé lors de la création de l'image-objet. - La position est initialement défini pour avoir le centre du sprite, spécifié par
Sprite.center_x
etSprite.center_y
, à (0,0) dans la fenêtre.
Une fois la .center_x
et .center_y
les coordonnées sont connues, arcade
peut utiliser la taille pour calculer la Sprite.left
, Sprite.right
, Sprite.top
, et Sprite.bottom
bords aussi.
Cela fonctionne également en sens inverse. Par exemple, si vous définissez Sprite.left
à une valeur donnée, puis arcade
recalculera également les attributs de position restants. Vous pouvez utiliser n'importe lequel d'entre eux pour localiser le sprite ou le déplacer dans la fenêtre. C’est une caractéristique extrêmement utile et puissante arcade
sprites. Si vous les utilisez, votre jeu Python nécessitera moins de code que pygame
:
Maintenant que vous avez défini l'image-objet du joueur, vous pouvez travailler sur les images-objets ennemies. La conception vous demande de faire apparaître les sprites ennemis à intervalles réguliers. Comment peux-tu faire ça?
Fonctions de planification
arcade.schedule ()
est conçu exactement à cet effet. Il faut deux arguments:
- Le nom de la fonction à appeler
- L'intervalle de temps attendre entre chaque appel, en quelques secondes
Puisque vous voulez que les ennemis et les nuages apparaissent tout au long du jeu, vous configurez une fonction planifiée pour créer de nouveaux ennemis et une seconde pour créer de nouveaux nuages. Ce code entre dans .installer()
. Voici à quoi ressemble ce code:
66 # Générez un nouvel ennemi toutes les 0,25 seconde
67 arcade.programme(soi.add_enemy, 0,25)
68
69 # Créez un nouveau nuage à chaque seconde
70 arcade.programme(soi.add_cloud, 1.0)
Il ne vous reste plus qu'à définir self.add_enemy ()
et self.add_cloud ()
.
Ajout d'ennemis
De la conception de votre jeu Python, les ennemis ont trois propriétés clés:
- Ils apparaissent à des endroits aléatoires sur le côté droit de la fenêtre.
- Ils se déplacent vers la gauche en ligne droite.
- Ils disparaissent quand ils sortent de l'écran.
Le code pour créer un sprite ennemi est très similaire au code pour créer le sprite du joueur:
93 def add_enemy(soi, delta_time: flotte):
94 "" "Ajoute un nouvel ennemi à l'écran
95
96 Arguments:
97 delta_time float - Combien de temps s'est écoulé depuis le dernier appel
98 "" "
99
100 # Tout d'abord, créez le nouveau sprite ennemi
101 ennemi = arcade.Lutin("images / missile.png", MISE À L'ÉCHELLE)
102
103 # Réglez sa position sur une hauteur aléatoire et hors écran à droite
104 ennemi.la gauche = Aléatoire.randint(soi.largeur, soi.largeur + 80)
105 ennemi.Haut = Aléatoire.randint(dix, soi.la taille - dix)
.add_enemy ()
prend un seul paramètre, delta_time
, qui représente le temps écoulé depuis le dernier appel. Ceci est requis par arcade.schedule ()
et bien que vous ne l'utilisiez pas ici, il peut être utile pour les applications qui nécessitent une synchronisation avancée.
Comme avec le sprite du joueur, vous créez d'abord un nouveau arcade.Sprite
avec une image et un facteur d'échelle. Vous définissez la position à l'aide .la gauche
et .Haut
à une position aléatoire quelque part sur l'écran vers la droite:
Cela permet à l'ennemi de se déplacer sur l'écran en douceur, plutôt que d'apparaître simplement sur l'écran. Maintenant, comment faites-vous bouger?
Déplacement des Sprites
Pour déplacer un sprite, vous devez changer sa position pendant la phase de mise à jour de la boucle de jeu. Bien que vous puissiez le faire vous-même, arcade
a des fonctionnalités intégrées pour réduire votre charge de travail. Chaque arcade.Sprite
a non seulement un ensemble d'attributs de position, mais il a également un ensemble de attributs de mouvement. Chaque fois que le sprite est mis à jour, arcade
utilisera les attributs de mouvement pour mettre à jour la position, conférant un mouvement relatif au sprite.
le Sprite.velocity
L'attribut est un tuple composé du changement des positions x et y. Vous pouvez également accéder Sprite.change_x
et Sprite.change_y
directement. Comme mentionné ci-dessus, chaque fois que le sprite est mis à jour, son .position
est modifié en fonction de la .rapidité
. Tout ce que vous devez faire .add_enemy ()
est réglé la vitesse:
107 # Réglez sa vitesse sur une vitesse aléatoire vers la gauche
108 ennemi.rapidité = (Aléatoire.randint(-20, -5), 0)
109
110 # Ajoutez-le à la liste des ennemis
111 soi.ennemis_list.ajouter(ennemi)
112 soi.all_sprites.ajouter(ennemi)
Après avoir défini la vitesse sur une vitesse aléatoire se déplaçant vers la gauche sur la ligne 108, vous ajoutez le nouvel ennemi aux listes appropriées. Lorsque vous appellerez plus tard sprite.update ()
, arcade
s'occupera du reste:
Dans la conception de votre jeu Python, les ennemis se déplacent en ligne droite de droite à gauche. Étant donné que vos ennemis se déplacent toujours vers la gauche, une fois qu'ils sont hors de l'écran, ils ne reviennent pas. Ce serait bien si vous pouviez vous débarrasser d'un sprite ennemi hors écran pour libérer de la mémoire et accélérer les mises à jour. Heureusement, arcade
vous a couvert.
Suppression des sprites
Parce que vos ennemis se déplacent toujours vers la gauche, leurs positions x deviennent toujours plus petites et leurs positions y sont toujours constantes. Par conséquent, vous pouvez déterminer qu'un ennemi est hors écran lorsque ennemi. droit
est plus petit que zéro, qui est le bord gauche de la fenêtre. Une fois que vous avez déterminé que l'ennemi est hors écran, vous appelez emy.remove_from_sprite_lists ()
de le supprimer de toutes les listes auxquelles il appartient et libérer cet objet de la mémoire:
si ennemi.droite < 0:
ennemi.remove_from_sprite_lists()
Mais quand effectuez-vous cette vérification? Normalement, cela se produit juste après le déplacement du sprite. Cependant, souvenez-vous de ce qui a été dit .all_enemies
liste de sprites:
Tu utilises
.enemies_list
pour mettre à jour les positions ennemies et vérifier les collisions.
Cela signifie que dans SpaceShooter.on_update ()
, vous appellerez ennemis_list.update ()
pour gérer automatiquement le mouvement ennemi, ce qui fait essentiellement ce qui suit:
pour ennemi dans ennemis_list:
ennemi.mise à jour()
Ce serait bien si vous pouviez ajouter la vérification hors écran directement au emy.update ()
appelez, et vous pouvez! Rappelles toi, arcade
est une bibliothèque orientée objet. Cela signifie que vous pouvez créer vos propres classes en fonction de arcade
et remplacez les méthodes que vous souhaitez modifier. Dans ce cas, vous créez une nouvelle classe basée sur arcade.Sprite
et remplacer .mise à jour()
seulement:
17 classe FlyingSprite(arcade.Lutin):
18 "" "Classe de base pour tous les sprites volants
19 Les sprites volants incluent les ennemis et les nuages
20 "" "
21
22 def mise à jour(soi):
23 "" "Mettre à jour la position du sprite
24 Lorsqu'il se déplace hors de l'écran vers la gauche, retirez-le
25 "" "
26
27 # Déplacez le sprite
28 super().mise à jour()
29
30 # Supprimer si hors de l'écran
31 si soi.droite < 0:
32 soi.remove_from_sprite_lists()
Vous définissez FlyingSprite
comme tout ce qui volera dans votre jeu, comme les ennemis et les nuages. Vous remplacez ensuite .mise à jour()
, premier appel super (). update ()
pour traiter la motion correctement. Ensuite, vous effectuez la vérification hors écran.
Étant donné que vous avez une nouvelle classe, vous devrez également apporter une petite modification à .add_enemy ()
:
def add_enemy(soi, delta_time: flotte):
"" "Ajoute un nouvel ennemi à l'écran
Arguments:
delta_time float - Combien de temps s'est écoulé depuis le dernier appel
"" "
# Tout d'abord, créez le nouveau sprite ennemi
ennemi = FlyingSprite("images / missile.png", MISE À L'ÉCHELLE)
Plutôt que de créer un nouveau Lutin
, vous créez un nouveau FlyingSprite
pour profiter de la nouvelle .mise à jour()
.
Ajout de nuages
Pour rendre votre jeu Python plus attrayant visuellement, vous pouvez ajouter des nuages au ciel. Les nuages volent dans le ciel, tout comme vos ennemis, vous pouvez donc les créer et les déplacer de la même manière.
.add_cloud ()
suit le même schéma que .add_enemy ()
, bien que la vitesse aléatoire soit plus lente:
def add_cloud(soi, delta_time: flotte):
"" "Ajoute un nouveau cloud à l'écran
Arguments:
delta_time float - Combien de temps s'est écoulé depuis le dernier appel
"" "
# Tout d'abord, créez le nouveau sprite cloud
nuage = FlyingSprite("images / cloud.png", MISE À L'ÉCHELLE)
# Réglez sa position sur une hauteur aléatoire et hors écran à droite
nuage.la gauche = Aléatoire.randint(soi.largeur, soi.largeur + 80)
nuage.Haut = Aléatoire.randint(dix, soi.la taille - dix)
# Réglez sa vitesse sur une vitesse aléatoire vers la gauche
nuage.rapidité = (Aléatoire.randint(-5, -2), 0)
# Ajoutez-le à la liste des ennemis
soi.clouds_list.ajouter(nuage)
soi.all_sprites.ajouter(nuage)
Les nuages se déplacent plus lentement que les ennemis, vous calculez donc une vitesse aléatoire inférieure sur la ligne 129.
Maintenant, votre jeu Python semble un peu plus complet:
Vos ennemis et nuages sont créés et se déplacent d'eux-mêmes maintenant. Il est temps de faire bouger le joueur aussi en utilisant le clavier.
La saisie au clavier
le arcade.Window
La classe a deux fonctions pour traiter la saisie au clavier. Votre jeu Python appellera .on_key_press ()
chaque fois qu'une touche est enfoncée et .on_key_release ()
chaque fois qu'une touche est relâchée. Les deux fonctions acceptent deux paramètres entiers:
symbole
représente la touche réelle qui a été enfoncée ou relâchée.modificateurs
indique quels modificateurs étaient en panne. Il s'agit notamment de la Décalage, Ctrl, et Alt clés.
Heureusement, vous n'avez pas besoin de savoir quels entiers représentent quelles clés. le arcade.key
Le module contient toutes les constantes clavier que vous pouvez utiliser. Traditionnellement, déplacer un joueur avec le clavier utilise un ou plusieurs des trois jeux de touches différents:
- Les quatre touches fléchées pour En haut, Vers le bas, La gauche, et Droite
- Les clés je, J, K, et L, qui correspondent à Haut, Gauche, Bas et Droite
- Pour la commande à gauche, les touches W, UNE, S, et ré, qui mappent également vers le haut, la gauche, le bas et la droite
Pour ce jeu, vous utiliserez les flèches et je/J/K/L. Chaque fois que l'utilisateur appuie sur une touche de mouvement, le sprite du joueur se déplace dans cette direction. Lorsque l'utilisateur relâche une touche de mouvement, le sprite cesse de se déplacer dans cette direction. Vous fournissez également un moyen de quitter le jeu en utilisant Qet un moyen de mettre le jeu en pause à l'aide de P. Pour ce faire, vous devez répondre aux pressions de touches et aux déclenchements:
- Lorsqu'une touche est enfoncée, appel
.on_key_press ()
. Dans cette méthode, vous vérifiez quelle touche a été enfoncée:- Si c'est Q, puis vous quittez simplement le jeu.
- Si c'est P, puis vous définissez un indicateur pour indiquer que le jeu est en pause.
- S'il s'agit d'une clé de mouvement, vous définissez le
.change_x
ou.change_y
en conséquence. - S'il s'agit d'une autre clé, vous l'ignorez.
- Lorsqu'une clé est relâchée, appel
.on_key_release ()
. Encore une fois, vous vérifiez quelle clé a été libérée:- S'il s'agit d'une clé de mouvement, vous définissez le
.change_x
ou.change_y
à 0 en conséquence. - S'il s'agit d'une autre clé, vous l'ignorez.
- S'il s'agit d'une clé de mouvement, vous définissez le
Voici à quoi ressemble le code:
134 def on_key_press(soi, symbole, modificateurs):
135 "" "Gérer la saisie au clavier utilisateur
136 Q: Quittez le jeu
137 P: Suspendre / Annuler le jeu
138 I / J / K / L: Déplacer vers le haut, gauche, bas, droite
139 Flèches: Déplacer vers le haut, gauche, bas, droite
140
141 Arguments:
142 symbol int - Quelle touche a été enfoncée
143 modifiers int - Quels modificateurs ont été pressés
144 "" "
145 si symbole == arcade.clé.Q:
146 # Quittez immédiatement
147 arcade.Fermer la fenêtre()
148
149 si symbole == arcade.clé.P:
150 soi.en pause = ne pas soi.en pause
151
152 si symbole == arcade.clé.je ou symbole == arcade.clé.UP:
153 soi.joueur.change_y = 5
154
155 si symbole == arcade.clé.K ou symbole == arcade.clé.VERS LE BAS:
156 soi.joueur.change_y = -5
157
158 si symbole == arcade.clé.J ou symbole == arcade.clé.LA GAUCHE:
159 soi.joueur.change_x = -5
160
161 si symbole == arcade.clé.L ou symbole == arcade.clé.DROITE:
162 soi.joueur.change_x = 5
163
164 def on_key_release(soi, symbole: int, modificateurs: int):
165 "" "Annuler les vecteurs de mouvement lorsque les touches de mouvement sont relâchées
166
167 Arguments:
168 symbol int - Quelle touche a été enfoncée
169 modifiers int - Quels modificateurs ont été pressés
170 "" "
171 si (
172 symbole == arcade.clé.je
173 ou symbole == arcade.clé.K
174 ou symbole == arcade.clé.UP
175 ou symbole == arcade.clé.VERS LE BAS
176 ):
177 soi.joueur.change_y = 0
178
179 si (
180 symbole == arcade.clé.J
181 ou symbole == arcade.clé.L
182 ou symbole == arcade.clé.LA GAUCHE
183 ou symbole == arcade.clé.DROITE
184 ):
185 soi.joueur.change_x = 0
Dans .on_key_release ()
, vous ne vérifiez que les clés qui auront un impact sur le mouvement de votre sprite de joueur. Il n'est pas nécessaire de vérifier si les touches Pause ou Quit ont été relâchées.
Vous pouvez maintenant vous déplacer sur l'écran et quitter le jeu immédiatement:
Vous vous demandez peut-être comment fonctionne la fonctionnalité de pause. Pour voir cela en action, vous devez d'abord apprendre à mettre à jour tous vos objets de jeu Python.
Mise à jour des objets du jeu
Ce n'est pas parce que vous avez défini une vitesse sur tous vos sprites qu'ils bougeront. Pour les faire bouger, il faut mise à jour les maintes et maintes fois dans la boucle de jeu.
Puisque arcade
contrôle la boucle de jeu Python, il contrôle également quand des mises à jour sont nécessaires en appelant .on_update ()
. Vous pouvez remplacer cette méthode pour fournir le comportement approprié pour votre jeu, y compris le mouvement du jeu et d'autres comportements. Pour ce jeu, vous devez faire quelques choses pour tout mettre à jour correctement:
- Tu vérifies si le jeu est en pause. Si c'est le cas, vous pouvez simplement quitter, afin qu'aucune autre mise à jour ne se produise.
- Vous mettez à jour tous vos sprites pour les faire bouger.
- Tu vérifies si l'image-objet du joueur a quitté l'écran. Si c'est le cas, déplacez-les simplement à l'écran.
C'est tout pour le moment. Voici à quoi ressemble ce code:
189 def on_update(soi, delta_time: flotte):
190 "" "Mettre à jour les positions et les statuts de tous les objets du jeu
191 En cas de pause, ne faites rien
192
193 Arguments:
194 delta_time float - Temps depuis la dernière mise à jour
195 "" "
196
197 # En cas de pause, ne mettez rien à jour
198 si soi.en pause:
199 revenir
200
201 # Tout mettre à jour
202 soi.all_sprites.mise à jour()
203
204 # Gardez le lecteur à l'écran
205 si soi.joueur.Haut > soi.la taille:
206 soi.joueur.Haut = soi.la taille
207 si soi.joueur.droite > soi.largeur:
208 soi.joueur.droite = soi.largeur
209 si soi.joueur.bas < 0:
210 soi.joueur.bas = 0
211 si soi.joueur.la gauche < 0:
212 soi.joueur.la gauche = 0
La ligne 198 est l'endroit où vous vérifiez si le jeu est en pause et retournez simplement si c'est le cas. Cela saute tout le code restant, donc il n'y aura aucun mouvement. Tout mouvement de sprite est géré par la ligne 202. Cette ligne unique fonctionne pour trois raisons:
- Chaque sprite est membre du
self.all_sprites
liste. - L'appel à
self.all_sprites.update ()
entraîne l'appel.mise à jour()
sur chaque sprite de la liste. - Chaque sprite dans la liste a
.rapidité
(composé de.change_x
et.change_y
attributs) et traitera son propre mouvement lorsque.mise à jour()
est appelé.
Enfin, vous vérifiez si l'image-objet du joueur est hors écran dans les lignes 205 à 212 en comparant les bords des images-objets avec les bords de la fenêtre. Par exemple, aux lignes 205 et 206, si self.player.top
est au-delà du haut de l'écran, vous réinitialisez self.player.top
en haut de l'écran. Maintenant que tout est mis à jour, vous pouvez tout dessiner.
Dessin sur la fenêtre
Depuis les mises à jour des objets de jeu se produisent dans .on_update ()
, il semble approprié que le dessin des objets du jeu se fasse selon une méthode appelée .on_draw ()
. Parce que vous avez tout organisé en listes de sprites, votre code pour cette méthode est très court:
231 def on_draw(soi):
232 "" "Dessine tous les objets du jeu
233 "" "
234 arcade.start_render()
235 soi.all_sprites.dessiner()
Tout dessin commence par l'appel à arcade.start_render ()
à la ligne 234. Tout comme la mise à jour, vous pouvez dessiner tous vos sprites à la fois en appelant simplement self.all_sprites.draw ()
à la ligne 235. Maintenant, il n'y a qu'une dernière partie de votre jeu Python sur laquelle travailler, et c'est la toute dernière partie de la conception initiale:
Lorsque le joueur est touché par un obstacle, ou si l'utilisateur ferme la fenêtre, le jeu se termine.
Ceci est la partie réelle du jeu! En ce moment, les ennemis voleront à travers votre sprite de joueur sans rien faire. Voyons comment vous pouvez ajouter cette fonctionnalité.
Détection de collision
Les jeux sont tous des collisions sous une forme ou une autre, même dans les jeux non informatiques. Without real or virtual collisions, there would be no slap-shot hockey goals, no double-sixes in backgammon, and no way in chess to capture your opponent’s queen on the end of a knight fork.
Collision detection in computer games requires the programmer to detect if two game objects are partially occupying the same space on the screen. You use collision detection to shoot enemies, limit player movement with walls and floors, and provide obstacles to avoid. Depending on the game objects involved and the desired behavior, collision detection logic can require potentially complicated math.
However, you don’t have to write your own collision detection code with arcade
. You can use one of three different Sprite
methods to detect collisions quickly:
Sprite.collides_with_point((x,y))
RetourVrai
if the given point(x,y)
is within the boundary of the current sprite, andFaux
otherwise.Sprite.collides_with_sprite(Sprite)
RetourVrai
if the given sprite overlaps with the current sprite, andFaux
otherwise.Sprite.collides_with_list(SpriteList)
returns a list containing all the sprites in theSpriteList
that overlap with the current sprite. If there are no overlapping sprites, then the list will be empty, meaning it will have a length of zero.
Since you’re interested in whether or not the single-player sprite has collided with any of the enemy sprites, the last method is exactly what you need. You call self.player.collides_with_list(self.enemies_list)
and check if the list it returns contains any sprites. If so, then you end the game.
So, where do you make this call? The best place is in .on_update()
, just before you update the positions of everything:
189 def on_update(soi, delta_time: float):
190 """Update the positions and statuses of all game objects
191 If paused, do nothing
192
193 Arguments:
194 delta_time float -- Time since the last update
195 """
196
197 # If paused, don't update anything
198 si soi.paused:
199 revenir
200
201 # Did you hit anything? If so, end the game
202 si soi.player.collides_with_list(soi.enemies_list):
203 arcade.close_window()
204
205 # Update everything
206 soi.all_sprites.mise à jour()
Lines 202 and 203 check for a collision between the player
and any sprite in .enemies_list
. If the returned list contains any sprites, then that indicates a collision, and you can end the game. Now, why would you check avant updating the positions of everything? Remember the sequence of action in the Python game loop:
- You update the states of the game objects. You do this in
.on_update()
. - You draw all the game objects in their new positions. You do this in
.on_draw()
.
If you check for collisions after you update everything in .on_update()
, then any new positions won’t be drawn if a collision is detected. You’re actually checking for a collision based on sprite positions that haven’t been shown to the user yet. It may appear to the player as though the game ended before there was an actual collision! When you check first, you ensure that what’s visible to the player is the same as the game state you’re checking.
Now you have a Python game that looks good and provides a challenge! Now you can add some extra features to help make your Python game stand out.
There are many more features you can add to your Python game to make it stand out. In addition to the features the game design called out that you didn’t implement, you may have others in mind as well. This section will cover two features that will give your Python game some added impact by adding sound effects and controlling the game speed.
Du son
Sound is an important part of any computer game. From explosions to enemy taunts to background music, your Python game is a little flat without sound. Out of the box, arcade
provides support for WAV files. If the ffmpeg library is installed and available, then arcade
also supports Ogg and MP3 format files. You’ll add three different sound effects and some background music:
- The first sound effect plays as the player moves up.
- The second sound effect plays when the player moves down.
- The third sound effect plays when there is a collision.
- The background music is the last thing you’ll add.
You’ll start with the sound effects.
Sound Effects
Before you can play any of these sounds, you have to load them. You do so in .setup()
:
66 # Spawn a new enemy every 0.25 seconds
67 arcade.programme(soi.add_enemy, 0,25)
68
69 # Spawn a new cloud every second
70 arcade.programme(soi.add_cloud, 1.0)
71
72 # Load your sounds
73 # Sound sources: Jon Fincher
74 soi.collision_sound = arcade.load_sound("sounds/Collision.wav")
75 soi.move_up_sound = arcade.load_sound("sounds/Rising_putter.wav")
76 soi.move_down_sound = arcade.load_sound("sounds/Falling_putter.wav")
Like your sprite images, it’s good practice to place all your sounds in a single sub-folder.
With the sounds loaded, you can play them at the appropriate time. Pour .move_up_sound
et .move_down_sound
, this happens during the .on_key_press()
handler:
134 def on_key_press(soi, symbol, modifiers):
135 """Handle user keyboard input
136 Q: Quit the game
137 P: Pause the game
138 I/J/K/L: Move Up, Left, Down, Right
139 Arrows: Move Up, Left, Down, Right
140
141 Arguments:
142 symbol int -- Which key was pressed
143 modifiers int -- Which modifiers were pressed
144 """
145 si symbol == arcade.key.Q:
146 # Quit immediately
147 arcade.close_window()
148
149 si symbol == arcade.key.P:
150 soi.paused = ne pas soi.paused
151
152 si symbol == arcade.key.je ou symbol == arcade.key.UP:
153 soi.player.change_y = 5
154 arcade.play_sound(soi.move_up_sound)
155
156 si symbol == arcade.key.K ou symbol == arcade.key.DOWN:
157 soi.player.change_y = -5
158 arcade.play_sound(soi.move_down_sound)
Now, whenever the player moves up or down, your Python game will play a sound.
The collision sound will play whenever .on_update()
detects a collision:
def on_update(soi, delta_time: float):
"""Update the positions and statuses of all game objects
If paused, do nothing
Arguments:
delta_time float -- Time since the last update
"""
# If paused, don't update anything
si soi.paused:
revenir
# Did you hit anything? If so, end the game
si len(soi.player.collides_with_list(soi.enemies_list)) > 0:
arcade.play_sound(soi.collision_sound)
arcade.close_window()
# Update everything
soi.all_sprites.mise à jour()
Just before the window closes, a collision sound will play.
Background Music
Adding background music follows the same pattern as adding sound effects. The only difference is when it starts to play. For background music, you normally start it when the level starts, so load and start the sound in .setup()
:
66 # Spawn a new enemy every 0.25 seconds
67 arcade.programme(soi.add_enemy, 0,25)
68
69 # Spawn a new cloud every second
70 arcade.programme(soi.add_cloud, 1.0)
71
72 # Load your background music
73 # Sound source: http://ccmixter.org/files/Apoxode/59262
74 # License: https://creativecommons.org/licenses/by/3.0/
75 soi.background_music = arcade.load_sound(
76 "sounds/Apoxode_-_Electric_1.wav"
77 )
78
79 # Load your sounds
80 # Sound sources: Jon Fincher
81 soi.collision_sound = arcade.load_sound("sounds/Collision.wav")
82 soi.move_up_sound = arcade.load_sound("sounds/Rising_putter.wav")
83 soi.move_down_sound = arcade.load_sound("sounds/Falling_putter.wav")
84
85 # Start the background music
86 arcade.play_sound(soi.background_music)
Now, you not only have sound effects, but also some nifty background music as well!
Sound Limitations
There are some limitations on what arcade
can currently do with sound:
- There is no volume control on any sounds.
- There is no way to repeat a sound, such as looping background music.
- There is no way to tell if a sound is currently playing before you try to stop it.
- Without
ffmpeg
, you are limited to WAV sounds, which can be large.
Despite these limitations, it’s well worth the effort to add sound to your arcade
Python game.
Python Game Speed
The speed of any game is dictated by its fréquence d'images, which is the frequency at which the graphics on the screen are updated. Higher frame rates normally result in smoother gameplay, while lower frame rates give you more time to perform complex calculations.
The frame rate of an arcade
Python game is managed by the game loop in arcade.run()
. The Python game loop calls .on_update()
et .on_draw()
roughly 60 times per second. Therefore, the game has a frame rate of 60 frames per second or 60 FPS.
Notice the description above says that the frame rate is roughly 60 FPS. This frame rate is not guaranteed to be exact. It may fluctuate up or down based on many factors, such as load on the machine or longer-than-normal update times. As a Python game programmer, you want to ensure your Python game acts the same, whether it’s running at 60 FPS, 30 FPS, or any other rate. Donc comment fais-tu cela?
Time-Based Movement
Imagine an object moving in space at 60 kilometers per minute. You can calculate how far the object will travel in any length of time by multiplying that time by the object’s speed:
The object moves 120 kilometers in 2 minutes and 30 kilometers in half a minute.
You can use this same calculation to move your sprites at a constant speed no matter what the frame rate. If you specify the sprite’s speed in terms of pixels per second, then you can calculate how many pixels it moves every frame if you know how much time has passed since the last frame appeared. How do you know that?
Recall that .on_update()
takes a single parameter, delta_time
. This is the amount of time in seconds that have passed since the last time .on_update()
was called. For a game running at 60 FPS, delta_time
will be 1/60 of a second or roughly 0.0167 seconds. If you multiply the time that’s passed by the amount a sprite will move, then you’ll ensure sprite movement is based on the time elapsed and not the frame rate.
Updating Sprite Movement
There’s just one problem—neither Sprite.on_update()
nor SpriteList.on_update()
accept the delta_time
parameter. This means there’s no way to pass this on to your sprites to handle automatically. Therefore, to implement this feature, you need to update your sprite positions manually. Replace the call to self.all_sprites.update()
dans .on_update()
with the following code:
def on_update(soi, delta_time: float):
"""Update the positions and statuses of all game objects
If paused, do nothing
Arguments:
delta_time float -- Time since the last update
"""
# If paused, don't update anything
si soi.paused:
revenir
# Did you hit anything? If so, end the game
si len(soi.player.collides_with_list(soi.enemies_list)) > 0:
arcade.play_sound(soi.collision_sound)
arcade.close_window()
# Update everything
pour sprite dans soi.all_sprites:
sprite.center_x = int(
sprite.center_x + sprite.change_x * delta_time
)
sprite.center_y = int(
sprite.center_y + sprite.change_y * delta_time
)
In this new code, you modify the position of each sprite manually, multiplying .change_x
et .change_y
par delta_time
. This ensures that the sprite moves a constant distance every second, rather than a constant distance every frame, which can smooth out gameplay.
Updating Sprite Parameters
Of course, this also means you should re-evaluate and adjust the initial position and speed of all your sprites as well. Recall the position and .velocity
your enemy sprites are given when they’re created:
93 def add_enemy(soi, delta_time: float):
94 """Adds a new enemy to the screen
95
96 Arguments:
97 delta_time float -- How much time as passed since the last call
98 """
99
100 # First, create the new enemy sprite
101 ennemi = FlyingSprite("images/missile.png", SCALING)
102
103 # Set its position to a random height and off screen right
104 ennemi.la gauche = Aléatoire.randint(soi.width, soi.width + 80)
105 ennemi.Haut = Aléatoire.randint(dix, soi.la taille - dix)
106
107 # Set its speed to a random speed heading left
108 ennemi.velocity = (Aléatoire.randint(-20, -5), 0)
With the new movement calculations based on time, your enemies will now move at a maximum speed of 20 pixels every second. This means that on an 800-pixel-wide window, the fastest enemy will take forty seconds to fly across the screen. Further, if the enemy starts eighty pixels to the right of the window, then the fastest will take four full seconds just to appear!
Adjusting the position and velocity is part of making your Python game interesting and playable. Start by adjusting each by a factor of ten, and readjust from there. The same reevaluation and adjustments should be done with the clouds, as well as the movement velocity of the player.
Tweaks and Enhancements
During your Python game design, there were several features that you didn’t add. To add to that list, here are some additional enhancements and tweaks that you may have noticed during Python gameplay and testing:
- When the game is paused, enemies and clouds are still generated by the scheduled functions. This means that, when the game is unpaused, a huge wave of them are waiting for you. How do you prevent that from happening?
- As mentioned above, due to some limitations of the
arcade
sound engine, the background music does not repeat itself. How do you work around that issue? - When the player collides with an enemy, the game ends abruptly without playing the collision sound. How do you keep the game open for a second or two before it closes the window?
There may be other tweaks you could add. Try to implement some of them as an exercise, and share your results down in the comments!
A Note on Sources
You may have noticed a comment when the background music was loaded, listing the source of the music and a link to the Creative Commons license. This was done because the creator of that sound requires it. The license requirements state that, in order to use the sound, both proper attribution and a link to the license must be provided.
Here are some sources for music, sound, and art that you can search for useful content:
As you make your games and use downloaded content such as art, music, or code from other sources, please be sure that you’re complying with the licensing terms of those sources.
Conclusion
Computer games are a great introduction to coding, and the arcade
library is a great first step. Designed as a modern Python framework for crafting games, you can create compelling Python game experiences with great graphics and sound.
Throughout this tutorial, you learned how to:
- Install the
arcade
library - Draw items on the screen
- Work with the
arcade
Python game loop - Manage on-screen graphic elements
- Handle user input
- Play sound effects and music
- Describe how Python game programming in
arcade
differs frompygame
I hope you give arcade
un essai. If you do, then please leave a comment below, and Happy Pythoning! You can download all the materials used in this tutorial at the link below:
[ad_2]