Skip to content

Latest commit

 

History

History
550 lines (433 loc) · 20.2 KB

File metadata and controls

550 lines (433 loc) · 20.2 KB
title date draft
Partie 1 - Dessiner le caractère ‘@’ et le déplacer
2019-03-30 08:39:15 -0700
false

Bienvenue dans la partie 1 du Tutoriel Roguelike revesité ! Cette série va vous aider à créer votre tout premier jeu roguelike, écrit en Python.

Ce tutoriel est largement inspiré de celui trouvé sur Roguebasin. Une grande partie des décisions de conception furent prises afin de conserver une progression commune avec celui-ci (du moins en ce concerne la composition des chapitres et la direction générale). Ce tutoriel n'aurait pu être possible sans le travail des concepteurs d'origine ainsi que celui des merveilleux contributeurs de libtcod et python-tcod.

Nous supposons dans cette partie que vous avez déjà effectué la partie 0 et être prêt à commencer. Sinon, rendez-vous sur cette page est vérifiez votre installation de Python et de TCOD. Assurez-vous d'avoir un fichier engine.py dans le dossier choisi pour votre travail.

Supposant que vous avez accompli tout ça, commençons. Modifiez (ou créez si vous ne l'avez pas déjà fait) le fichier engine.py pour qu'il ressemble à ça :

{{< highlight py3 >}} import tcod as libtcod

def main(): print('Hello World!')

if name == 'main': main() {{</ highlight >}}

Vous pouvez exécuter le programme comme n'importe quel programme Python mais, pour les novices, vous le faîtes en tapant python engine.py dans le terminal. Si vous avez à la fois Python 2 et 3 d'installé sur votre machine, il se peut que vous deviez taper python3 engine.py pour le lancer (cela dépend de votre python par défaut et vous utilisez un environnement virtuel ou non).

D'accord, ce n'est pas le programme le plus enthousiasmant qui soit, je le reconnais, mais nous avons déjà une différence majeure avec l'autre tutoriel. C'est cette chose étrange ici :

{{< highlight py3 >}} if name == 'main': main() {{< /highlight >}}

Qu'est-ce que ça fait ? Simplement, nous disons que nous n'allons lancer la fonction principale "main" que si nous lançons explicitement le script avec la commande python engine.py. Ce n'est pas fondamental que vous compreniez ça maintenant mais si vous tenez à comprendre, cette réponse sur [Stackoverflow] (https://stackoverflow.com/a/419185) donne un bon aperçu.

Assurez-vous que le programme précédent tourne (sinon, c'est probablement un soucis d'installation de libtcod). Une fois que cela est accompli, nous pouvons allez vers des étapes un peu plus intéressantes. La première étape importante dans la conception d'un roguelike est d'obtenir un caractère '@' à l'écran et de le déplacer aussi allons-y.

Modifiez engine.py pour qu'il ressemble à ça :

{{< highlight py3 >}} import tcod as libtcod

def main(): screen_width = 80 screen_height = 50

libtcod.console_set_custom_font('arial10x10.png', libtcod.FONT_TYPE_GREYSCALE | libtcod.FONT_LAYOUT_TCOD)

libtcod.console_init_root(screen_width, screen_height, 'libtcod tutorial revised', False)

while not libtcod.console_is_window_closed():
    libtcod.console_set_default_foreground(0, libtcod.white)
    libtcod.console_put_char(0, 1, 1, '@', libtcod.BKGND_NONE)
    libtcod.console_flush()

    key = libtcod.console_check_for_keypress()

    if key.vk == libtcod.KEY_ESCAPE:
        return True

if name == 'main': main() {{</ highlight >}}

Exécutez engine.py à nouveau et vous devriez vous un '@' à l'écran. Une fois que vous êtes complètement repu de la gloire provoquée par l'écran devant vous, vous pouvez presser la touche `Esc` pour quitter le programme.

Il se passe beaucoup de choses ici, aussi découpons ligne par ligne.

{{< highlight py3 >}} screen_width = 80 screen_height = 50 {{</ highlight >}}

C'est assez simple. On définit quelques variables pour la dimension de l'écran. Éventuellement, nous pourrions charger ces valeurs depuis un fichier JSON plutôt que de les coder en dur dans les sources, mais nous ne en soucierons pas avant d'avoir plus de variables.

{{< highlight py3 >}} libtcod.console_set_custom_font('arial10x10.png', libtcod.FONT_TYPE_GREYSCALE | libtcod.FONT_LAYOUT_TCOD) {{</ highlight >}}

Ici, nous disons à libtcod quelle police employer. La partie 'arial10x10.png' est le fichier qu'on lit (il devrait exister dans votre dossier de projet). Les deux autres parties disent à libtcod quel type de fichier nous lisons.

{{< highlight py3 >}} libtcod.console_init_root(SCREEN_WIDTH, SCREEN_HEIGHT, 'libtcod tutorial revised', False) {{</ highlight >}}

Cette ligne est ce qui crée l'écran. Nous lui donnons les valeurs screen_width et screen_height définies plus haut (80 et 50, respectivement), ainsi qu'un titre (changez le si vous connaissez déjà le nom de votre jeu) et une valeur booléenne qui indique à libtcod si le jeu est plein écran ou non.

{{< highlight py3 >}} while not libtcod.console_is_window_closed(): {{</ highlight >}}

C'est ce qu'on appelle la 'boucle de jeu'. C'est simplement une boucle qui ne va jamais s'interrompre tant qu'on n'aura pas fermé la fenêtre. Chaque jeu a une boucle de jeu d'une manière ou d'une autre.

{{< highlight py3 >}} libtcod.console_set_default_foreground(0, libtcod.white) {{</ highlight >}}

Cette ligne indique à libtcod de régler la couleur de notre symbole '@'. Si vous souhaitez qu'il soit d'une autre couleur, remplacez libtcod.white par quelque chose comme libtcod.red et voyez ce que cela donne. Le '0' dans cette fonction est la console sur laquelle nous écrivons. Nous en reparlerons plus tard.

{{< highlight py3 >}} libtcod.console_put_char(0, 1, 1, '@', libtcod.BKGND_NONE) {{</ highlight >}}

Le premier argument est '0' (à nouveau, la console sur laquelle nous écrivons). Les deux suivants sont des coordonnées x et y, dans ce cas 1 et 1 (essayez de les remplacer pour voir ce que cela donne). Ensuite nous affichons le symbole '@' et réglons le fond sur 'none' avec libtcod.BKGND_NONE.

{{< highlight py3 >}} libtcod.console_flush() {{</ highlight >}}

C'est la partie qui affiche tout à l'écran. Plutôt directe.

{{< highlight py3 >}} key = libtcod.console_check_for_keypress()

    if key.vk == libtcod.KEY_ESCAPE:
        return True

{{</ highlight >}}

Cette partie nous permet de quitter gracieusement le jeu (sans planter) en pressant la touche Esc. La fonction libtcod.console_check_for_keypress() récupère une saisie clavier du programme que nous stockons dans la variable key. Ensuite, nous vérifions si la touche pressée est Esc ou non. Si c'est le cas, nous quittons la boucle, ce qui termine le programme.

Maintenant que nous avons notre symbole @ dessiné, déplaçons le !

Nous devons connaître la position du joueur, donc nous allons créer deux variables, player_x et player_y.

{{< codetab >}} {{< diff-tab >}} {{< highlight diff >}} ... screen_height = 50 +

  • player_x = int(screen_width / 2)
  • player_y = int(screen_height / 2)
  • libtcod.console_set_custom_font('arial10x10.png', libtcod.FONT_TYPE_GREYSCALE | libtcod.FONT_LAYOUT_TCOD) ... {{</ highlight >}} {{</ diff-tab >}} {{< original-tab >}}
    ...
    screen_height = 50
    
    player_x = int(screen_width / 2)
    player_y = int(screen_height / 2)
    
    libtcod.console_set_custom_font('arial10x10.png', libtcod.FONT_TYPE_GREYSCALE | libtcod.FONT_LAYOUT_TCOD)
    ...

{{</ original-tab >}} {{</ codetab >}}

Remarque: Les trois points désignent une partie omise du code. J'inclurai des lignes autour du code à insérer afin que vous sachiez exactement où écrire les nouvelles parties de code mais je ne montrerai pas le fichier entier à chaque fois. Les lignes vertes indiquent le code que vous devriez ajouter.

Nous plaçons le joueur en plein milieu de l'écran. Que fait la fonction int() ? Et bien, Python 3 ne tronque pas automatiquement la division comme Python 2 aussi nous devons convertir le résultat (un flottant) en entier. Sinon libtcod va renvoyer une erreur.

Nous devons aussi modifier la commande affichant le symbole '@' afin d'employer ces nouvelles coordonnées.

{{< codetab >}} {{< diff-tab >}} {{< highlight diff >}} ... libtcod.console_set_default_foreground(0, libtcod.white)

  •   libtcod.console_put_char(0, 1, 1, '@', libtcod.BKGND_NONE)
    
  •   libtcod.console_put_char(0, player_x, player_y, '@', libtcod.BKGND_NONE)
      libtcod.console_flush()
      ...
    

{{</ highlight >}} {{</ diff-tab >}} {{< original-tab >}}

        ...
        libtcod.console_set_default_foreground(0, libtcod.white)
        libtcod.console_put_char(0, 1, 1, '@', libtcod.BKGND_NONE)
        libtcod.console_put_char(0, player_x, player_y, '@', libtcod.BKGND_NONE)
        libtcod.console_flush()
        ...

{{</ original-tab >}} {{</ codetab >}}

Remarque : les lignes en rouge signalent le code qui a été enlevé.

Exécutez le programme maintenant et vous devriez voir le '@' au centre de l'écran. Occupons nous maintenant de le rendre mobile.

Ajoutez les deux lignes suivantes juste au dessus de la boucle principale.

{{< codetab >}} {{< diff-tab >}} {{< highlight diff >}} ... libtcod.console_init_root(screen_width, screen_height, 'libtcod tutorial revised', False)

  • key = libtcod.Key()

  • mouse = libtcod.Mouse()

    while not libtcod.console_is_window_closed(): ... {{</ highlight >}} {{</ diff-tab >}} {{< original-tab >}}

    ...
    libtcod.console_init_root(screen_width, screen_height, 'libtcod tutorial revised', False)

    key = libtcod.Key()
    mouse = libtcod.Mouse()

    while not libtcod.console_is_window_closed():
    ...

{{</ original-tab >}} {{</ codetab >}}

Ainsi que les noms le suggèrent, ces variables vont contenir nos saisies clavier et souris. Nous n'implémenterons pas la souris immédiatement mais la fonction que nous allons ajouter en tient compte aussi nous pouvons l'ajouter.

{{< codetab >}} {{< diff-tab >}} {{< highlight diff >}} ... while not libtcod.console_is_window_closed():

  •   libtcod.sys_check_for_event(libtcod.EVENT_KEY_PRESS, key, mouse)
    
      libtcod.console_set_default_foreground(0, libtcod.white)
    
    ... {{</ highlight >}} {{</ diff-tab >}} {{< original-tab >}}
    ...
    while not libtcod.console_is_window_closed():
        libtcod.sys_check_for_event(libtcod.EVENT_KEY_PRESS, key, mouse)

        libtcod.console_set_default_foreground(0, libtcod.white)
    ...

{{</ original-tab >}} {{</ codetab >}}

C'est la fonction qui capture les nouveaux "événements" (saisie clavier). Elle va mettre à jour les variables key et mouse avec ce que saisit l'utilisateur. À nouveau seule key nous intéresse pour l'instant.

D'accord, nous mettons key à jour avec la saisie de l'utilisateur. Mais qu'en faisons nous ? Cela va traduire les saisies en action de jeu.

Pour l'instant, ce tutoriel n'a pas vraiment divergé de celui d'origine mais voici un changement radical. Nous sommes sur le point de définir une fonction appelée handle_keys qui va gérer les saisies clavier. Nous pourrions l'ajouter à notre fichier engine.py... mais est-ce la place de cette fonction ? Je pense que non. Le moteur (la boucle principale) capture les saisies et devrait en faire quelque chose mais traduire de l'un vers l'autre ne le concerne pas.

Aussi, plutôt que d'ajouter la fonction handle_keys à engine.py, créez un nouveau fichier, appelé input_handlers.py. Placez le code suivant dans ce nouveau fichier.

{{< highlight py3 >}} import tcod as libtcod

def handle_keys(key): # Movement keys if key.vk == libtcod.KEY_UP: return {'move': (0, -1)} elif key.vk == libtcod.KEY_DOWN: return {'move': (0, 1)} elif key.vk == libtcod.KEY_LEFT: return {'move': (-1, 0)} elif key.vk == libtcod.KEY_RIGHT: return {'move': (1, 0)}

if key.vk == libtcod.KEY_ENTER and key.lalt:
    # Alt+Enter: toggle full screen
    return {'fullscreen': True}

elif key.vk == libtcod.KEY_ESCAPE:
    # Exit the game
    return {'exit': True}

# No key was pressed
return {}

{{</ highlight >}}

C'est un gros morceau à comprendre en une seule fois aussi, à nouveau, découpons le en différentes parties.

{{< highlight py3 >}} def handle_keys(key): {{</ highlight >}}

On crée une fonction appelée handle_keys qui prend un paramètre, key. key désigne cette fois la touche capturée plus tôt.

{{< highlight py3 >}} if key.vk == libtcod.KEY_UP: {{</ highlight >}}

Cette expression if (ainsi que les autres elifs) nous dit quelle touche a été pressée. Pour l'instant, c'est l'une des flèches pour le mouvement. Ce qui est plus intéressant est le code dans cette expression if.

{{< highlight py3 >}} return {'move': (0, -1)} {{</ highlight >}}

Que faisons nous ici ? Quand nous retournons de cette fonction, le moteur va devoir faire quelque chose. Dans ce cas, nous voulons déplacer notre personnage. Mais si nous voulons utiliser une autre touche ? Alors nous ne voulons peut-être pas bouger mais utiliser un item, lancer un sort ou quitter le jeu. Une manière de traiter toutes ces possibilités est de renvoyer un dictionnaire depuis cette fonction. Le moteur le lira et décidera ce qu'il en fera.

Pour ce faire, nous retournons un dictionnaire avec la clé 'move' et pour valeur une paire de nombres. Ces nombres dirons au moteur dans quelle direction déplacer le joueur. Par exemple, la touche 'haut' va déplacer le joueur de '0' selon les x et '-1' selon les y.

{{< highlight py3 >}} if key.vk == libtcod.KEY_ENTER and key.lalt: # Alt+Enter: toggle full screen return {'fullscreen': True} elif key.vk == libtcod.KEY_ESCAPE: # Exit the game return {'exit': True} {{</ highlight >}}

Voici les actions sans mouvement que nous acceptons pour l'instant. Si l'utilisateur presse 'ALT+enter', le jeu va passer en mode plein écran. Si l'utilisateur presse 'Esc', le jeu va quitter.

{{< highlight py3 >}} return {} {{</ highlight >}}

Parce que notre moteur attend un dictionnaire, nous devons renvoyer quelque chose, même si rien ne s'est produit.

Cela peut sembler surprenant mais cela prendra sens dans quelques insants. Retournons à ntore fichier engine.py pour appeler la fonction handle_keys.

{{< codetab >}} {{< diff-tab >}} {{< highlight diff >}} ... libtcod.console_flush()

  •   key = libtcod.console_check_for_keypress()
    
  •   action = handle_keys(key)
    
  •   move = action.get('move')
    
  •   exit = action.get('exit')
    
  •   fullscreen = action.get('fullscreen')
    
  •   if move:
    
  •       dx, dy = move
    
  •       player_x += dx
    
  •       player_y += dy
    
  •   if key.vk == libtcod.KEY_ESCAPE:
    
  •   if exit:
    
  •       return True
    
  •   if fullscreen:
    
  •       libtcod.console_set_fullscreen(not libtcod.console_is_fullscreen())
      ...
    

{{</ highlight >}} {{</ diff-tab >}} {{< original-tab >}}

        ...
        libtcod.console_flush()

        key = libtcod.console_check_for_keypress()
        action = handle_keys(key)

        move = action.get('move')
        exit = action.get('exit')
        fullscreen = action.get('fullscreen')

        if move:
            dx, dy = move
            player_x += dx
            player_y += dy

        if key.vk == libtcod.KEY_ESCAPE:
        if exit:
            return True

        if fullscreen:
            libtcod.console_set_fullscreen(not libtcod.console_is_fullscreen())
        ...

{{</ original-tab >}} {{</ codetab >}}

Remarque : Je ferai apparaître en rouge les lignes effacées. Dans ce cas, retirez les lignes : key = libtcod.console_check_for_keypress() et if key.vk == libtcod.KEY_ESCAPE

Aussi, assurez-vous d'importer les fonctions handle_keys en haut de engine.py

{{< codetab >}} {{< diff-tab >}} {{< highlight diff >}} import tcod as libtcod

+from input_handlers import handle_keys {{</ highlight >}} {{</ diff-tab >}} {{< original-tab >}}

import tcod as libtcod

from input_handlers import handle_keys

{{</ original-tab >}} {{</ codetab >}}

Esperons maintenant que le dictionnaire de handle_keys prenne un peu plus de sens. On capture la valeur de retour de handle_keys dans la variable action (qui devrait être un dictionnaire, quoi qu'on ait pressé) et on vérifie quelle touche quelle touche elle contient. Si elle contient une clé appelée move alors on sait qu'il faut chercher les coordonnées (x, y). Si elle contient 'exit', alors on sait qu'il faut quitter le jeu.

Essayez d'exécuter le fichier engine.py maintenant. Vous devriez pouvoir vous déplacer. Amusant !

Une dernière étape avant d'avancer. Regardez la fonction de dessin. Remarquez le premier argument qui est '0'. Cela représente la console dans laquelle on écrit. 0 est celle par défaut. Plutôt que d'écrire sur celle par défaut nous voulons préciser sur quelle console écrire après en avoir crée une nouvelle. La raison derrière est qu'il sera plus facile d'en créer pour dessiner dessus plus tard. Cela sera particulièrement utile quand nous aborderons la partie relative à l'interface graphique de cette série.

Modifiez le fichier engine.py ainsi :

{{< codetab >}} {{< diff-tab >}} {{< highlight diff >}} ... libtcod.console_init_root(screen_width, screen_height, 'libtcod tutorial revised', False)

  • con = libtcod.console_new(screen_width, screen_height)

    key = libtcod.Key() mouse = libtcod.Mouse()

    while not libtcod.console_is_window_closed(): libtcod.sys_check_for_event(libtcod.EVENT_KEY_PRESS, key, mouse)

  •   libtcod.console_set_default_foreground(con, libtcod.white)
    
  •   libtcod.console_put_char(con, player_x, player_y, '@', libtcod.BKGND_NONE)
    
  •   libtcod.console_blit(con, 0, 0, screen_width, screen_height, 0, 0, 0)
    
  •   libtcod.console_set_default_foreground(0, libtcod.white)
    
  •   libtcod.console_put_char(0, player_x, player_y, '@', libtcod.BKGND_NONE)
      libtcod.console_flush()
    
  •   libtcod.console_put_char(con, player_x, player_y, ' ', libtcod.BKGND_NONE)
    
  •   libtcod.console_put_char(0, player_x, player_y, ' ', libtcod.BKGND_NONE)
    

{{</ highlight >}} {{</ diff-tab >}} {{< original-tab >}}

    ...
    libtcod.console_init_root(screen_width, screen_height, 'libtcod tutorial revised', False)

    con = libtcod.console_new(screen_width, screen_height)

    key = libtcod.Key()
    mouse = libtcod.Mouse()

    while not libtcod.console_is_window_closed():
        libtcod.sys_check_for_event(libtcod.EVENT_KEY_PRESS, key, mouse)
        
        libtcod.console_set_default_foreground(con, libtcod.white)
        libtcod.console_put_char(con, player_x, player_y, '@', libtcod.BKGND_NONE)
        libtcod.console_blit(con, 0, 0, screen_width, screen_height, 0, 0, 0)
        libtcod.console_set_default_foreground(0, libtcod.white)
        libtcod.console_put_char(0, player_x, player_y, '@', libtcod.BKGND_NONE)
        libtcod.console_flush()
        
        libtcod.console_put_char(con, player_x, player_y, ' ', libtcod.BKGND_NONE)
        libtcod.console_put_char(0, player_x, player_y, ' ', libtcod.BKGND_NONE)

{{</ original-tab >}} {{</ codetab >}}

Cela conclut la première partie de ce tutoriel ! Si vous utilisez git ou une autre forme de contrôle de version (et je vous le recommande), vous devriez faire un commit de vos changements maintenant.

Si vous voulez voir le code actuel entièrement, cliquez ici. Les fichiers que vous souhaitez consulter sont engine.py et input_handlers.py.

Cliquez ici pour vous rendre à la partie suivante de ce tutoriel.

<script src="/js/codetabs.js"></script>