Blog ENI : Toute la veille numérique !
Accès illimité 24h/24 à tous nos livres & vidéos ! 
Découvrez la Bibliothèque Numérique ENI. Cliquez ici
💥 Les 22 & 23 novembre : Accès 100% GRATUIT
à la Bibliothèque Numérique ENI. Je m'inscris !
  1. Livres et vidéos
  2. Pygame
  3. Travailler en 3D avec Pygame
Extrait - Pygame Initiez-vous au développement de jeux vidéo en Python (2e édition)
Extraits du livre
Pygame Initiez-vous au développement de jeux vidéo en Python (2e édition)
1 avis
Revenir à la page d'achat du livre

Travailler en 3D avec Pygame

Introduction

Les jeux développés au cours de cet ouvrage sont exclusivement en deux dimensions (2D). Et, dans les faits, Pygame est plutôt associé au développement de jeux en 2D. Il n’empêche que Pygame permet l’affichage en trois dimensions.

Un certain nombre de jeux vidéo sont en trois dimensions ou utilisent un graphisme qui s’en rapproche. On peut notamment citer les jeux dits FPS (First Person Shooter, jeu vidéo de tir en vue à la première personne, en français). Ce type de jeu consiste en général en l’incarnation d’un personnage muni d’une arme à feu plongé dans une 3D immersive. Le but est d’abattre les ennemis rencontrés. En général, le personnage principal se déplace dans un réseau de couloirs dessinés en trois dimensions.

Par sa base SDL, Pygame est un outil très orienté 2D. Les jeux qui ont été pris en exemple dans cet ouvrage sont tous des jeux en deux dimensions. Que leur faudrait-il alors pour être des jeux 3D ? La réponse peut sembler évidente : un décor en 3D et un affichage en 3D. Autrement dit, leur algorithme reste le même : la logique du jeu, la boucle de jeu elle-même ne sont pas supposées avoir besoin de modifications outre mesure. Mais reste à savoir comment faire de la 3D avec Pygame. La réponse tient en un mot : OpenGL.

La bibliothèque 3D OpenGL

OpenGL (Open Graphics Library) est une bibliothèque qui permet de faire de la 3D. Elle est utilisée dans de nombreux domaines bien au-delà du jeu vidéo : simulation scientifique, CAO, réalité augmentée, etc. Son propos est de permettre de créer des objets 3D auxquels on peut associer des textures. OpenGL se charge ensuite de l’affichage de ces objets 3D en tenant compte de l’orientation, de la distance et des aspects relatifs à la lumière, comme l’ombre projetée, la transparence, etc.

Le but ici n’est certes pas de vous apprendre OpenGL. OpenGL est en effet un monde vaste et complexe et il faudrait bien plus que ces quelques pages pour apprendre à coder de façon autonome avec cet outil. Le but ici est bien de vous accompagner dans vos premiers pas dans la 3D dans un cadre Pygame.

OpenGL en Python/Pygame

1. PyOpenGL

Il existe un module Python qui permet de travailler avec OpenGL. Il s’agit de PyOpenGL. S’il n’est pas installé sur votre machine, cette commande pip lancée dans le terminal vous permet de le faire.

pip install PyOpenGL 

Pour l’utiliser dans un code Python, il suffit d’importer le module comme il est fait habituellement (pour Pygame, par exemple). En général, nous procédons à l’import en écrivant ces deux lignes :

from OpenGL.GL import * 
from OpenGL.GLU import * 

2. Les notions fondamentales : sommet et arête

Pour définir les objets 3D dans OpenGL, nous allons utiliser deux notions fondamentales : celle de sommet (vertex en anglais) et celle d’arête (edge en anglais).

De façon schématique, nous pouvons considérer que les sommets sont des points de l’espace et donc ont leurs coordonnées en trois dimensions. Par exemple : (1, 2, -4). Les arêtes sont les segments qui relient les sommets.

Ainsi, si nous définissons deux sommets :

  • le sommet 1 situé aux coordonnées (1, 0, 0),

  • le sommet 2 situé aux coordonnées (0, -1, 0),

  • alors nous pouvons définir l’arête 1 qui relie le sommet 1 au sommet 2.

Cela revient à considérer que définir les objets 3D dans OpenGL, c’est avant tout définir une collection de points tridimensionnels (les sommets) et de définir ensuite les arêtes qui relient entre eux ces sommets.

3. PyOpenGL et Pygame

Quand nous créons la fenêtre de jeu Pygame, nous spécifions un mode de fonctionnement relatif à OpenGL. La valeur utilisée est : pygame.DOUBLEBUF|pygame.OPENGL.

Nous avons par exemple ce type de code :

import pygame 
pygame.init() 
ZONE = (800,800) 
pygame.display.set_mode(ZONE...

PyOpenGL/Pygame : l’exemple du cube

Prenons l’exemple classique d’un cube affiché en trois dimensions, que nous soumettons à une rotation continue. L’idée est d’afficher uniquement les arêtes du cube ; nous ne définissons ni texture ni coloration des faces.

1. Le code global

Commençons par présenter le code global de l’exemple, avant de procéder à l’explication détaillée du programme.

import pygame  
from OpenGL.GL import *  
from OpenGL.GLU import *  
  
SOMMETS = (  
   (1, -1, -1),  
   (1, 1, -1),  
   (-1, 1, -1),  
   (-1, -1, -1),  
   (1, -1, 1),  
   (1, 1, 1),  
   (-1, -1, 1),  
   (-1, 1, 1)  
   ) 
 
ARETES = ( 
   (0,1), 
   (0,3), 
   (0,4), 
   (2,1), 
   (2,3), 
   (2,7), 
   (6,3), 
   (6,4), 
   (6,7), 
   (5,1), 
   (5,4), 
   (5,7) 
   ) 
 
 
def Cube(): 
   glBegin(GL_LINES) 
   for arete in ARETES: 
       for sommet in arete: 
           glVertex3fv(SOMMETS[sommet]) 
   glEnd() 
 
pygame.init() 
ZONE = (800,800) 
pygame.display.set_mode(ZONE, pygame.DOUBLEBUF|pygame.OPENGL) 
 
gluPerspective(45, (ZONE[0]/ZONE[1]), 0.1, 50.0) 
glTranslatef(0.0,0.0, -5) 
 
while True:  
 for event in pygame.event.get(): 
   if event.type == pygame.QUIT: 
     pygame.quit() 
     quit() ...

Plus loin avec PyOpenGL/Pygame : l’exemple du cube (suite)

Reprenons l’exemple précédent en cherchant à colorer les surfaces de manière à accentuer l’impression 3D.

Un point important à préciser : une couleur RGB, qui se définit en Python/Pygame avec des valeurs comprises entre 0 et 255, est définie côté OpenGL avec des valeurs comprises entre 0 et 1. Si nous prenons l’exemple de la couleur rouge :

  • elle se code (255, 0, 0) en Pygame,

  • elle se code (1, 0, 0) en OpenGL.

1. Le code global

Nous retrouvons largement la trame du code global précédent à laquelle nous avons ajouté la gestion des surfaces et des couleurs.

import pygame 
from OpenGL.GL import * 
from OpenGL.GLU import * 
  
COULEURS = ( 
 (1,0,0), 
 (0,1,0), 
 (0,0,1), 
 (1,0,0), 
 (0,1,0), 
 (0,0,1), 
 (1,0,0), 
 (0,1,0),  
 (0,0,1), 
 (1,0,0), 
 (0,1,0), 
 (0,0,1), 
) 
 
SOMMETS = ( 
 (1, -1, -1), 
 (1, 1, -1), 
 (-1, 1, -1), 
 (-1, -1, -1), 
 (1, -1, 1), 
 (1, 1, 1), 
 (-1, -1, 1), 
 (-1, 1, 1) 
) 
 
ARETES = ( 
 (0,1), 
 (0,3), 
 (0,4), 
 (2,1), 
 (2,3), 
 (2,7), 
 (6,3), 
 (6,4), 
 (6,7), 
 (5,1), 
 (5,4), 
 (5,7) 
) 
 
SURFACES = ( 
 (0,1,2,3), 
 (3,2,7,6), 
 (6,7,5,4), 
 (4,5,1,0), 
 (1,5,7,2), 
 (4,0,3,6) 
) 
 
def Cube(): 
 glBegin(GL_QUADS) 
 for surface in SURFACES: 
   cmp = 0 
   for vertex in surface: 
     cmp+=1 
 ...

OpenGL en Python et macOS

Les codes précédents sont supposés fonctionner sur tous les systèmes d’exploitation Linux, Windows ou macOS, moyennant l’activation d’un environnement virtuel basé sur le fichier requirements.txt (généré en décembre 2022) :

pygame==2.1.2 
PyOpenGL==3.1.6 

Or, un bug touchant certains systèmes macOS exclusivement, Linux et Windows ne sont pas concernés) provoque l’erreur suivante dès lors que l’on utilise OpenGL avec Python :

ImportError: ('Unable to load OpenGL library', 'dlopen(OpenGL, 10): 
image not found', 'OpenGL', None) 

Deux solutions dans l’attente de la résolution de ce bug, côté PyOpenGL :

  • Patcher l’arborescence PyOpenGL, ce qui est plutôt déconseillé.

  • Utiliser dans votre code utilisant PyOpenGL ces quelques lignes de code ci-dessous.

import pygame 
try: 
   import OpenGL as ogl 
   try:  
       import OpenGL.GL 
   except ImportError: 
       print('Patch pour MacOS') 
       from ctypes import util 
       orig_util_find_library = util.find_library 
 
       def new_util_find_library(name): 
           res = orig_util_find_library(name) 
           if res: 
               return res 
           return '/System/Library/Frameworks/'+name+'.framework/'+name 
       util.find_library = new_util_find_library 
except ImportError: 
   pass 
from OpenGL.GL import * 
from OpenGL.GLU import * 

La notion de moteur de jeu vidéo

1. Définition

Un moteur de jeu vidéo est un ensemble logiciel qui permet de se faciliter la vie dans le cadre d’un développement de jeu vidéo. Prenons comme exemple le développement d’un jeu qui consiste à lancer un ballon sur le sol, ballon qui rebondit de façon réaliste, parfois sur le sol, parfois dans une flaque d’eau, auquel cas la simulation diffère un peu (projection d’eau, amortissement, etc.). Le moteur permet de se préoccuper de l’essentiel du jeu de simulation, c’est-à-dire le décor, la jouabilité, le scénario. Les aspects techniques sont dévolus entièrement au moteur de jeu. Entre autres :

  • la collision ballon/sol,

  • la simulation de la gravité,

  • la simulation des projections aqueuses,

  • la représentation 3D du ballon,

  • etc.

Autant d’aspects que nous pouvons regrouper dans une grande famille des aspects physiques, de représentations 2D/3D, de gestion des collisions, etc.

2. Créer son propre moteur de jeu ?

Pygame propose un certain nombre d’outils pour la gestion des collisions (fonctions de type spritecollide, etc.). Est-ce à dire que Pygame est un moteur de jeu ? Absolument pas. Certes, des aspects de Pygame pourraient le laisser penser (gestion des collisions, donc), mais cela ne va pas beaucoup plus loin. C’est tout simplement que ce n’est pas le rôle de Pygame, qui reste relativement bas niveau. Nous avons pu voir que faire de la 3D directement avec OpenGL n’est pas chose aisée, d’où l’intérêt d’avoir une surcouche qui encapsule des tâches récurrentes.

Dans le contexte Pygame, un moteur de jeu est donc plutôt une couche logicielle qui serait « au-dessus » de Pygame et qui encapsulerait un certain...