Hide keyboard shortcuts

Hot-keys on this page

r m x p   toggle line displays

j k   next/prev highlighted chunk

0   (zero) top of page

1   (one) first highlighted chunk

1# Roll 'n' Jump 

2# Written in 2020, 2021 by Samuel Arsac, Hugo Buscemi, 

3# Matteo Chencerel, Rida Lali 

4# To the extent possible under law, the author(s) have dedicated all 

5# copyright and related and neighboring rights to this software to the 

6# public domain worldwide. This software is distributed without any warranty. 

7# You should have received a copy of the CC0 Public Domain Dedication along 

8# with this software. If not, see 

9# <http://creativecommons.org/publicdomain/zero/1.0/>. 

10 

11"""Gère l'abstraction de pygame.""" 

12 

13import re 

14from math import ceil 

15 

16import sys 

17import pygame 

18import rollnjump.conf as cf 

19 

20pygame.init() 

21 

22# Classes 

23Vec = pygame.math.Vector2 

24"""Classe des vecteurs de dimension 2.""" 

25Sprite = pygame.sprite.Sprite 

26"""Classe des sprites.""" 

27 

28# Les touches 

29KEYDOWN = pygame.KEYDOWN 

30"""Événement "touche enfoncée".""" 

31K_ESCAPE = pygame.K_ESCAPE 

32"""Touche échap.""" 

33K_SPACE = pygame.K_SPACE 

34"""Touche espace.""" 

35K_RETURN = pygame.K_RETURN 

36"""Touche entrée.""" 

37K_s = pygame.K_s 

38"""Touche S.""" 

39K_u = pygame.K_u 

40"""Touche U.""" 

41K_BACKSPACE = pygame.K_BACKSPACE 

42"""Touche retour.""" 

43K_ESCAPE = pygame.K_ESCAPE 

44"""Touche échap""" 

45MOUSEBUTTONDOWN = pygame.MOUSEBUTTONDOWN 

46"""Événement "clic de la souris".""" 

47VIDEORESIZE = pygame.VIDEORESIZE 

48"""Événement "redimensionnement de la fenêtre".""" 

49QUIT = pygame.QUIT 

50"""Événement "quitter le jeu".""" 

51 

52 

53def keyname(key): # pragma: no cover 

54 """ 

55 Renvoie le nom de la touche pressée. 

56 

57 Parameters 

58 ---------- 

59 key : Key 

60 Touche pressée 

61 

62 Returns 

63 ------- 

64 str 

65 Nom de la touche 

66 """ 

67 return pygame.key.name(key) 

68 

69 

70def keyidentifier(control): # pragma: no cover 

71 """ 

72 Renvoie la touche à partir d'une chaîne de caractères. 

73 

74 Parameters 

75 ---------- 

76 control : str 

77 Texte caractérisant la touche 

78 

79 Returns 

80 ------- 

81 Key 

82 Variable de la touche 

83 """ 

84 return pygame.key.key_code(control) 

85 

86 

87def load_image(path): # pragma: no cover 

88 """ 

89 Charge une image à partir d'un chemin. 

90 

91 Parameters 

92 ---------- 

93 path : str 

94 Chemin du fichier 

95 

96 Returns 

97 ------- 

98 Surface 

99 Image du fichier 

100 """ 

101 return pygame.image.load(path) 

102 

103 

104def load_music(path): # pragma: no cover 

105 """ 

106 Charge une musique à partir d'un chemin. 

107 

108 Parameters 

109 ---------- 

110 path : str 

111 Chemin du fichier 

112 """ 

113 pygame.mixer.music.load(path) 

114 

115 

116def play_music(): # pragma: no cover 

117 """ 

118 Lance la musique chargée avec load_music. 

119 

120 Boucle automatiquement à la fin du fichier. 

121 """ 

122 pygame.mixer.music.play(-1) 

123 

124 

125def pause_music(): # pragma: no cover 

126 """Met sur pause la musique chargée avec load_music.""" 

127 pygame.mixer.music.pause() 

128 

129 

130def unpause_music(): # pragma: no cover 

131 """Relance la musique chargée avec load_music.""" 

132 pygame.mixer.music.unpause() 

133 

134 

135def initialize_window(icon, title, width, 

136 height, graphical): # pragma: no cover 

137 """ 

138 Initialise l'environnement graphique et la fenêtre. 

139 

140 Parameters 

141 ---------- 

142 icon : Surface 

143 Icone de la fenêtre 

144 title : str 

145 Nom de la fenêtre 

146 width : int 

147 Largeur de la fenêtre 

148 height : int 

149 Hauteur de la fenêtre 

150 graphical : bool 

151 Indique si la fenêtre doit être affichée 

152 

153 Returns 

154 ------- 

155 Surface * Surface 

156 Un couple (surface de jeu, surface à afficher) 

157 """ 

158 game = pygame.Surface((width, height)) 

159 if graphical: 

160 pygame.display.set_icon(load_image(icon)) 

161 pygame.display.set_caption(title) 

162 return (game, 

163 pygame.display.set_mode((width, height), 

164 flags=pygame.RESIZABLE)) 

165 return (game, None) 

166 

167 

168def resize_window(screen_size): # pragma: no cover 

169 """ 

170 Rétablit le ratio après un redimensionnement de fenêtre. 

171 

172 Parameters 

173 ---------- 

174 screen_size : int * int 

175 Taille de la fenêtre, au ratio quelconque 

176 """ 

177 ratio = min(screen_size[0] / cf.SCREEN_WIDTH, 

178 screen_size[1] / cf.SCREEN_HEIGHT) 

179 new_screen_size = (ceil(ratio * cf.SCREEN_WIDTH), 

180 ceil(ratio * cf.SCREEN_HEIGHT)) 

181 cf.WINDOWSURF = pygame.display.set_mode(new_screen_size, 

182 flags=pygame.RESIZABLE) 

183 

184 

185def initialize_clock(): # pragma: no cover 

186 """ 

187 Initialise le temps. 

188 

189 Returns 

190 ------- 

191 Clock 

192 Une horloge 

193 """ 

194 return pygame.time.Clock() 

195 

196 

197def make_event(event_type, attr=None): # pragma: no cover 

198 """ 

199 Renvoie un événement du type passé en entrée. 

200 

201 Parameters 

202 ---------- 

203 event_type : int 

204 Le type de l'événement 

205 attr : dict, optionnel 

206 Le dictionnaire des attributs 

207 

208 Returns 

209 ------- 

210 Event 

211 L'événement correspondant 

212 """ 

213 if attr is None: 

214 attr = {} 

215 return pygame.event.Event(event_type, attr) 

216 

217 

218def get_events(): # pragma: no cover 

219 """ 

220 Renvoie la liste des évènements. 

221 

222 Returns 

223 ------- 

224 Event list 

225 Liste des évènements 

226 """ 

227 return pygame.event.get() 

228 

229 

230def group_sprite_define(): # pragma: no cover 

231 """ 

232 Création d'un nouveau groupe de sprites. 

233 

234 Returns 

235 ------- 

236 Group 

237 le groupe de sprites 

238 """ 

239 return pygame.sprite.Group() 

240 

241 

242def add_to_group(sprite, group): # pragma: no cover 

243 """ 

244 Ajoute un sprite à un groupe de sprites. 

245 

246 Parameters 

247 ---------- 

248 sprite : Sprite 

249 Le sprite à ajouter 

250 group : Group 

251 Le groupe de sprites 

252 """ 

253 group.add(sprite) 

254 

255 

256def resize(surface, dimensions, destination=None): # pragma: no cover 

257 """ 

258 Change l'échelle de la surface en entrée. 

259 

260 Parameters 

261 ---------- 

262 surface : Surface 

263 La surface à modifier 

264 dimensions : int * int 

265 Les nouvelles dimensions 

266 destination : Surface, optionnel 

267 Nouvel objet à créer pour le redimensionnement 

268 

269 Returns 

270 ------- 

271 Surface 

272 Surface redimensionnée 

273 """ 

274 if destination is None: 

275 return pygame.transform.scale(surface, dimensions) 

276 return pygame.transform.scale(surface, dimensions, destination) 

277 

278 

279def resize_list(L, size): # pragma: no cover 

280 """ 

281 Redimensionne les images d'une liste. 

282 

283 Parameters 

284 ---------- 

285 L : Surface list 

286 Liste d'images 

287 size : int * int 

288 La résolution attendue 

289 

290 Returns 

291 ------- 

292 Surface list 

293 La liste des images redimensionnées 

294 """ 

295 for i, img in enumerate(L): 

296 L[i] = pygame.transform.scale(img, size) 

297 

298 

299def contact(sprite1, sprite2): # pragma: no cover 

300 """ 

301 Indique si deux sprites sont en contact. 

302 

303 Parameters 

304 ---------- 

305 sprite1 : Sprite 

306 Le premier sprite 

307 sprite2 : Sprite 

308 Le second sprite 

309 

310 Returns 

311 ------- 

312 bool 

313 True si les deux sprites sont en contact 

314 """ 

315 return pygame.sprite.collide_rect(sprite1, sprite2) 

316 

317 

318def collide_group(sprite, group): # pragma: no cover 

319 """ 

320 Indique s'il y a une collision entre un sprite et un groupe de sprites. 

321 

322 Parameters 

323 ---------- 

324 sprite : Sprite 

325 Le sprite examiné 

326 group : Sprite group 

327 Le groupe de sprites examiné 

328 

329 Returns 

330 ------- 

331 bool 

332 True s'il y a une collision 

333 """ 

334 return pygame.sprite.spritecollideany(sprite, group) 

335 

336 

337def update_screen(): # pragma: no cover 

338 """Met à jour l'écran.""" 

339 pygame.display.flip() 

340 

341 

342def get_screen_size(): # pragma: no cover 

343 """ 

344 Renvoie la taille de la fenêtre. 

345 

346 Returns 

347 ------- 

348 int * int 

349 Dimensions de la fenêtre 

350 """ 

351 return pygame.display.get_surface().get_size() 

352 

353 

354def create_rect(array): # pragma: no cover 

355 """ 

356 Crée un objet rectangle. 

357 

358 Parameters 

359 ---------- 

360 array : int list 

361 liste contenant l'abscisse de la gauche du rectangle, 

362 l'ordonnée du haut du rectangle, 

363 sa largeur et sa hauteur 

364 

365 Returns 

366 ------- 

367 Rect 

368 Le rectangle correspondant 

369 """ 

370 return pygame.Rect(array) 

371 

372 

373def draw_rect(surface, color, rect): # pragma: no cover 

374 """ 

375 Dessine l'objet rectangle sur une surface. 

376 

377 Parameters 

378 ---------- 

379 surface : Surface 

380 La surface sur laquelle afficher le rectangle 

381 color : int * int * int 

382 La couleur (en RGB) 

383 rect : Rect 

384 Le rectangle à afficher 

385 """ 

386 pygame.draw.rect(surface, color, rect) 

387 

388 

389def mouse_pos(): # pragma: no cover 

390 """ 

391 Renvoie la position de la souris. 

392 

393 Returns 

394 ------- 

395 int * int 

396 La position du pointeur 

397 """ 

398 return pygame.mouse.get_pos() 

399 

400 

401def quit_game(): # pragma: no cover 

402 """Quitte le jeu.""" 

403 pygame.quit() 

404 sys.exit() 

405 

406 

407def font(font_name, size): # pragma: no cover 

408 """ 

409 Renvoie une fonte de la taille demandée. 

410 

411 Parameters 

412 ---------- 

413 font_name : str 

414 La police de caractères 

415 size : int 

416 La taille de la fonte 

417 

418 Returns 

419 ------- 

420 Font 

421 La fonte 

422 """ 

423 return pygame.font.Font(font_name, size) 

424 

425 

426def collide(obj, pos_next, rect): 

427 """ 

428 Gestion des collisions. 

429 

430 Parameters 

431 ---------- 

432 obj : Player / Item 

433 objet (joueur ou objet) dont on examine la collision 

434 pos_next : Vector2 

435 position suivante de l'objet 

436 rect : Rect 

437 ce qui est potentiellement en collision avec l'objet 

438 

439 Returns 

440 ------- 

441 bool * bool * Vector2 

442 un triplet (collision verticale, collision horizontale, 

443 modification de position necessaire) 

444 """ 

445 # On ne tient pas compte du cas dans lequel l'objet traverserait 

446 # une plateforme dans sa longueur entre deux positions, il ne serait 

447 # de toutes façons pas possible de jouer dans ce cas. 

448 pos_prev = obj.pos 

449 

450 if pos_next.x + obj.width > rect.left\ 

451 and pos_next.x < rect.right: 

452 # Dans la plateforme horizontalement 

453 

454 if pos_prev.y + obj.height <= rect.top: 

455 # Position initale au-dessus de la plateforme 

456 if pos_next.y + obj.height > rect.top: 

457 # Nouvelle position dans ou sous la plateforme 

458 obj.FLAG_JUMP = True 

459 return (True, False, 

460 Vec(pos_next.x, rect.top - obj.height)) 

461 

462 elif pos_prev.y >= rect.bottom: 

463 # Position initiale en-dessous de la plateforme 

464 if pos_next.y < rect.bottom: 

465 # Nouvelle position dans ou au-dessus de la plateforme 

466 return (True, False, Vec(pos_next.x, rect.bottom)) 

467 

468 elif pos_next.y + obj.height > rect.top\ 

469 and pos_next.y < rect.bottom: 

470 # On ne considère que les collisions à gauche des plateformes 

471 return (False, True, Vec(rect.left - obj.width, pos_next.y)) 

472 

473 return(False, False, None) 

474 

475 

476def update_pos_vel(obj, ground): 

477 """ 

478 Met à jour la position et la vitesse de l'objet. 

479 

480 Parameters 

481 ---------- 

482 obj : Player / Item 

483 L'objet à mettre à jour 

484 ground : Sprite group 

485 Le groupe des plateformes 

486 """ 

487 obj.vel += obj.acc 

488 posnext = obj.pos + obj.vel + 0.5 * obj.acc 

489 

490 for plat in ground: # Gestion des collisions 

491 coll = collide(obj, posnext, plat.rect) 

492 if coll[0] or coll[1]: 

493 posnext = coll[2] 

494 if coll[0]: 

495 obj.vel.y = 0 

496 if coll[1]: 

497 obj.vel.x = 0 

498 

499 obj.pos = posnext 

500 obj.rect.topleft = obj.pos # Mise à jour de la position 

501 

502 

503def onlydigits(value): 

504 """ 

505 Filtre `value` pour ne garder que les chiffres. 

506 

507 On peut ainsi retirer toutes les sauts de lignes présents 

508 dans le fichier `score.txt`. 

509 

510 Parameters 

511 ---------- 

512 value : str 

513 La chaîne à filtrer 

514 

515 Returns 

516 ------- 

517 str 

518 La chaîne obtenue après filtrage 

519 """ 

520 final_chain = "" 

521 for i in value: 

522 if '0' <= i <= '9': 

523 final_chain += i 

524 return final_chain 

525 

526 

527def onlyalphanum(value): 

528 """ 

529 Filtre `value` pour ne garder que les caractères alphanumériques. 

530 

531 Parameters 

532 ---------- 

533 value : str 

534 La chaîne à filtrer 

535 

536 Returns 

537 ------- 

538 str 

539 La chaîne obtenue après filtrage 

540 """ 

541 return re.sub(r'[^A-Za-z0-9]+', '', value) 

542 

543 

544class GameObject(Sprite): 

545 """ 

546 Classe des objets du monde (hors joueur). 

547 

548 Attributes 

549 ---------- 

550 pos : int * int 

551 Position de l'objet 

552 scroll : float 

553 Vitesse de déplacement 

554 image : Sprite 

555 Image de l'objet 

556 rect : Rect 

557 Rectangle encadrant l'objet 

558 FLAG_creation : bool 

559 Drapeau pour gérer la création des objets 

560 """ 

561 

562 def __init__(self, position, scroll, image): 

563 """ 

564 Initialisation. 

565 

566 Parameters 

567 ---------- 

568 position : int * int 

569 Position de l'objet 

570 scroll : float 

571 Vitesse de déplacement 

572 image : Sprite 

573 image de l'objet 

574 """ 

575 super().__init__() 

576 self.pos = Vec(position) 

577 self.scroll = scroll 

578 # À 0 l'objet ne bouge pas, 

579 # À 1 l'objet se déplace à la vitesse du sol 

580 self.image = image 

581 self.rect = self.image.get_rect(topleft=self.pos) 

582 # Limite à partir de laquelle on génère un nouvel objet sur sa droite 

583 # FLAG_creation est un flag pour ne générer qu'un seul nouvel objet 

584 self.FLAG_creation = True 

585 

586 def update(self): 

587 """Met à jour le vecteur position.""" 

588 posnext = self.pos + self.scroll * Vec(-cf.SPEED, 0) 

589 self.pos = posnext 

590 self.rect.topleft = self.pos 

591 if self.rect.right < 0: # si l'objet sort de l'écran 

592 # print(self) 

593 self.kill() # on le supprime 

594 # On met à jour l'image 

595 cf.DISPLAYSURF.blit(self.image, self.rect)