Bonjour à tous ! Je vous présente un petit projet que j'ai fait en python + pygame.
J'ai découvert Linux grâce à des liveCD, et à l'époque dessus il y avait un jeu qui s'appelait "planets" dont le but était… de faire tourner des planètes. Cependant je ne retrouve plus ce jeu :/ . De plus y'a un petit moment sur ZdS on avait parlait (ici) de gravitation. J'ai donc réutilisé ce qu'on avait dit et j'ai recodé une partie de ce jeu.
Le jeu
Il faut faire orbiter les planètes. C'est très con comme jeu. Mais j'aime bien
En cliquant avec la sourie on fait apparaître une planète avec une vitesse initiale. Et puis alea jacta est. Avec la roulette on zoom et avec le clic droit on se déplace dans l'image. Les planètes on une masse dépendante de notre niveau de zoom, ce qui permet de faire des grosses/petites planètes.
Lorsque 2 planètes entrent en collision, elles fusionnent (c'est pas un jeu réaliste on a le droit :D).
Code
C'est du python3 avec pygame+numpy. Je précise que c'est la première fois que j'utilise python, et encore plus pygame du coup. C'est d'ailleurs pour ca que je post ici : si vous avez des critiques sur le code je suis preneur !
Dépendances
pour python 2
- installer python2-pygame
- installer python2-numpy
pour python 3
- installer python-pygame-hg (sous arch, dans AUR, sinon je sais pas pour les autres distrib')
- installer python-numpy
Le plus simple reste donc de le faire tourner en python2 puisque ca marche correctement en python2 et que pygame s'y trouve facilement.
Le nom des paquets marche sous Arch, je garantie pas sous d'autre distrib'
Le programme à lancer est : planetes.py
, qui appelle ObjCelest
et newOC
contenu dans objCelest.py
, qui sont les 2 classes du jeu.
Mettre planetes.py
et objCelest.py
dans le même répertoire et
python planetes.py
lance le jeu (pas oublier de le mettre en exécutable avant).
La configuration d'écran de base est 650x650, mais c'est facilement modifiable (winw et winh au début de planetes.py
).
Je n'ai pas de github ou autre, du coup je vous met les 2 fichiers comme ca.
planetes.py
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 | # coding: utf8 from objCelest import ObjCelest, newOC import numpy as np from math import * import pygame from pygame.locals import * from random import * pygame.init() pygame.display.set_caption("L'art de tourner autour du problème...", "Spine Runtine") myfont = pygame.font.SysFont("monospace", 15) fontPause = pygame.font.SysFont("monospace", 30) winw = 700 # windows width winh = 700 # windows height bg = (0,0,0) # background myclock = pygame.time.Clock() fpsLim = 50 userDone = False UnitPxRange = range(10,1000,1) UnitPx=float(UnitPxRange[190]) bouge=False # ======= fonctions def afficheAide(): aide=True ligne = [] ligne.append("========== Guide des boutons ===========") ligne.append("a : efface toutes les planetes") ligne.append("p : met le jeu en pause") ligne.append("h : affiche cet aide") ligne.append("Echap : quitte le jeu") ligne.append("") ligne.append("~~~~~~ Gestion de la sourie ~~~~~~") ligne.append("Bouton gauche : créer une planète.") ligne.append(" Maintenez enfoncé pour une vitesse initiale.") ligne.append(" Relachez ou cliquez sur un autre bouton") ligne.append(" de la sourie pour l'envoyer") ligne.append("Bouton droit : déplace l'écran.") ligne.append(" Maintenez enfoncé, déplacez et relachez le bouton.") ligne.append("Roulette : zoom") ligne.append("") ligne.append("Appuyez sur 'p' ou 'echap' pour revenir au jeu.") pygame.draw.rect(caneva, (0,0,0), (int(winw/5), 100, 500, len(ligne)*15)) for i,text in enumerate(ligne): text = myfont.render( text, 1 , (0,0,255)) caneva.blit(text, (int(winw/5), int(100+i*15))) pauseFonction(True) def coord2px(pos, UnitPx, winw, winh): """ tranforme les coordonees en px """ xPx = int(pos[0]*UnitPx + int(winw/2)) yPx = int(-pos[1]*UnitPx + int(winh/2)) return np.array([xPx, yPx]) def px2coord(Px, UnitPx, winw, winh): """ transforme les px en coordonees """ posx = float(Px[0] - winw/2)/UnitPx posy = -float(Px[1] - winh/2)/UnitPx return np.array([posx, posy]) def affichage(listOC, UnitPx, winw, winh, caneva): """ affiche les planetes et du texte""" for oc in listOC: [xPx, yPx] = coord2px(oc.pos, UnitPx, winw, winh) color = oc.color r = int(oc.r*UnitPx) pygame.draw.circle(caneva, color , [xPx, yPx], r, 0) # render text label = myfont.render("Il y a %d planete(s)" % len(listOC), 1, (0,0,255)) label2 = myfont.render("Echelle : 1 = %d px" % UnitPx, 1, (0,0,255)) caneva.blit(label, (10, 10)) caneva.blit(label2, (10,30)) def drag(listOC, move): for oc in listOC: oc.pos += move oc.posOld += move def pauseFonction(pause): pausetext = fontPause.render("Pause", 1, (255,255,0)) caneva.blit(pausetext, (winw/2-50, winh/2)) pygame.display.flip() userDone = False while pause: for event in pygame.event.get(): if (event.type == pygame.KEYDOWN and event.key == K_p): pause = False elif (event.type == pygame.QUIT or (event.type == pygame.KEYDOWN and event.key == K_ESCAPE)): pause = False userDone = True pygame.time.wait(10) return userDone # ===== Initialise la fenetre ======================================= caneva = pygame.display.set_mode((winw,winh)) caneva.fill(bg) # ===== Initialise les planetes ===================================== listOC = [] # Contient la liste des objets celestes # listOC(masse, vitesse, position) listOC.append(ObjCelest(1.0, [0.,0.], [0.,0])) listOC.append(ObjCelest(0.01, [0,0.8], [-1.5,0])) listOC.append(ObjCelest(0.001, [0,1.175], [-1.6,0])) new = newOC(0, np.array([0.,0.]), np.array([0.,0.]), 0) # contient la planète en formation lors d'un clic # ===== affiche l'aide ============================================== afficheAide() # ===== Et c'est partie !!! ========================================= # pas de temps dt = 1e-2 # Premier pas de temps for oc in listOC: oc.vitesse4pos(oc.v,dt) while not userDone: myclock.tick(fpsLim) # Quel bouton de la sourie est appuye if (pygame.mouse.get_pressed()[0]): # bouton gauche # Bouton gauche, ajout d'une planete # On stock temporairement sa vitesse (utilisateur), jusqu'a ce que le bouton soit relache [posxtmp, posytmp] = px2coord(pygame.mouse.get_pos(), UnitPx, winw, winh) vx = (posxtmp - posxNew) * 1 vy = (posytmp - posyNew) * 1 mass = pi * (10/UnitPx)**2 new = newOC(mass, [vx, vy], [posxNew, posyNew], 1) elif pygame.mouse.get_pressed()[2]: # bouton droit # On stock temporairement jusqu'ou on veut se deplacer, jusqu'a ce que le bouton soit relache [posxBougetmp, posyBougetmp] = px2coord(pygame.mouse.get_pos(), UnitPx, winw, winh) bouge = 1 # booleen pour le deplacement # ===== gestion des evenements for event in pygame.event.get(): if (event.type == pygame.QUIT): userDone = True elif (event.type == pygame.KEYDOWN): if (event.key == K_ESCAPE): userDone = True elif (event.key == K_h): afficheAide() elif (event.key == K_a): # supprime les objets celestes del listOC listOC=[] elif (event.key == K_p): # ===== mise en pause pause = True userDone = pauseFonction(pause) if (event.type == pygame.MOUSEBUTTONDOWN): if event.button == 1: # Ajout d'une planete, on note les coordonees du clic [posxNew, posyNew] = px2coord(event.pos, UnitPx, winw, winh) elif event.button == 3: # On bouge l'ecran, on note les coordonees du clic [posxBouge, posyBouge] = px2coord(event.pos, UnitPx, winw, winh) elif event.button == 4 and UnitPx < len(UnitPxRange): # zoom in UnitPx = float(UnitPxRange[int(UnitPx)]) elif event.button == 5 and UnitPx > 10: # zoom out UnitPx = float(UnitPxRange[int(UnitPx)-20]) elif (event.type == pygame.MOUSEBUTTONUP and new.ok): # on relache un bouton donc on creer une planete # ajout de la planete listOC.append(ObjCelest(new.m, new.v, [posxNew, posyNew])) # calcul de sa vitesse et de son premier deplacement oc = listOC[-1] oc.vitesse4pos(oc.v, dt) new = newOC(0, np.array([0.,0.]), np.array([0.,0.]), 0) # remise a zero elif (event.type == pygame.MOUSEBUTTONUP and bouge): # on bouge (deplace l'ecran) movex = posxBougetmp - posxBouge movey = posyBougetmp - posyBouge drag(listOC, np.array([movex, movey])) bouge = False # ===== Gestion des collisions ================================== # Ne gere qu'une collision par loop # sinon risque de supprimer une planetes qui est dans 2 collisions # et error not found... for oc in listOC: collision = oc.collision(listOC) if collision != []: msum = collision[0].mass + collision[1].mass #V0 = (collision[0].pos-collision[0].posOld)/dt #V1 = (collision[1].pos-collision[1].posOld)/dt #Vf = (collision[0].mass*V0 + collision[1].mass*V1)/(msum) #print(Vf) if collision[0].mass>collision[1].mass: collision[0].mass=msum collision[0].r=sqrt(msum/pi) listOC.remove(collision[1]) else: collision[1].mass=msum collision[1].r=sqrt(msum/pi) listOC.remove(collision[0]) break # on ne gère qu'une collision par loop, donc on s'arrete la. # ===== Deplacement ============================================= for oc in listOC: oc.acceleration(listOC) for oc in listOC: # x(n+1)=2x(n)-x(n-1)-a(n)*dt^2 oc.posNplusUn = 2*oc.pos - oc.posOld + oc.a * dt**2 oc.posOld = oc.pos oc.pos = oc.posNplusUn # ===== Affichage =============================================== caneva.fill(bg) affichage(listOC, UnitPx, winw, winh, caneva ) if new.ok: # tracer de la planete en cours d'initialisation [posxtmpPx, posytmpPx] = coord2px([posxtmp, posytmp], UnitPx, winw, winh) [posxNewPx, posyNewPx] = coord2px([posxNew, posyNew], UnitPx, winw, winh) pygame.draw.line(caneva, (255,0,255) , [posxNewPx, posyNewPx], [posxtmpPx, posytmpPx], 1) pygame.draw.circle(caneva, (255,0,255), [posxNewPx, posyNewPx], int(10), 1) pygame.display.flip() |
C'était le plus gros, le suivant il est petit
objCelest.py
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 | # coding: utf8 import numpy as np from math import * from random import * class ObjCelest: """Definit la class pour tous les objets celestes""" nbObjCelest=0 listofcolor=[] for i in range(50,250,10): for j in range(40,250,10): for k in range(50,250,10): listofcolor.append((i,j,k)) def __init__(self, m, v, pos): """Initialise les caracteristiques de l'objet celeste ID, couleur, masse, rayon, vitesse, acceleration et position. Le rayon est déterminer par masse = pi * r**2 L'acceleration est mise à jour ensuite posOld est la position l'instant d'avant""" self.r = sqrt(m/pi) self.mass = m self.v = np.array(v) self.a = np.array([0.,0.]) self.pos = np.array(pos) self.posOld = np.array(pos) self.ID = ObjCelest.nbObjCelest self.color = choice(ObjCelest.listofcolor) ObjCelest.nbObjCelest += 1 def distance(self, obj): """ Retourne la distance entre lui est un autre objet""" D = sqrt( (self.pos[0]-obj.pos[0])**2 + (self.pos[1]-obj.pos[1])**2) return D def vitesse4pos(self, v, dt): """Mise à jour de la position de l'objet grace à sa vitesse """ self.pos += v*dt def acceleration(self, listOC): """Renvoi l'acceleration de l'objet "self" due aux autres objet celestes" """ self.a = np.array([0.,0.]) for obj in listOC: if obj.ID != self.ID: D = self.distance(obj) ax = obj.mass * (obj.pos[0]-self.pos[0]) / D**3 ay = obj.mass * (obj.pos[1]-self.pos[1]) / D**3 self.a += np.array([ax, ay]) def collision(self, listOC): """Détermine si l'objet rentre en collision avec un autre """ collision=[] for oc in listOC: if oc.ID != self.ID: D = self.distance(oc) if (D < self.r + oc.r): collision.append(self) collision.append(oc) return collision return collision class newOC(): """ nouvel objet celeste en formation """ def __init__(self, m, v, pos, okornot): """ on lui donne une masse, vitesse et position. okornot (booleen) permet de le placer ou non sur l'ecran et donc de le prendre en consideration """ self.m = m self.v = v self.pos = pos self.ok = okornot |
Capture d'écran
Lors du démarrage du jeu il y a 3 planètes qui orbitent tranquillement. Tout se passe bien sauf qu'au bout d'un moment la "lune" se sépare de sa "planète" et orbite autour du "soleil", pour finalement se crasher sur sa planète . Appuyez sur a
pour supprimer les planètes.
Je suis preneur de toutes remarques, tant sur la forme que sur le fond. Sachant que c'est volontairement un jeu pas très évolué et simple.