A la demande générale, voilà le tetris ...
Je ne suis vraiment pas doué pour expliquer les choses, je vais faire de mon mieux, je suis ouvert à toutes suggestions.
Je pars du principe que tout le monde connais le jeu.
Il y a 36 façons de coder un tetris. Je vais essayer de vous expliquer ma façon de faire; je ne sais pas si c'est la meilleure ou la pire, mais elle fonctionne.
Comme ça fait assez longtemps que j'en ai pas coder un, je vais tout reprendre depuis la page blanche.
c'est partie:
1- observation du jeu:
soit un objet qui chute dans un tableau, il est mobile et répond au clavier, il interagit avec le décor car il ne peut occuper une place déjà prise.
sa vitesse de chute est constante, et répond donc à une temporisation.
il y a donc 3 facteurs qui agissent de l'objet:
- l'évènement clavier (gauche, droite, bas et rotation)
- l'évènement temps (fait chuter à intervals réguliers)
- le décor (empêche le déplacement)
l'objet n'occupe que des espaces vides.
si on considère l'objet comme un élément coloré, il ne peut occuper qu'un espace 'noir'.
si on considère l'objet comme une densité égale à 1, il ne peut occuper qu'un espace de 'densité 0'.
il y a donc une notion binaire ...
on peut donc définir un tableau et un objet comme ceci:
les '1' ne pouvant occuper le place d'autres '1', il suffit de limiter le tableau avec des '1' pour empêcher l'objet d'en sortir.
et là ça semble évident, chaque ligne du tableau et des objets peut être codée sur un entier.
2- opérateurs logiques et numériques: -comment savoir si un objet n'empiète pas sur un espace déjà occupé?
on sait que:
un '0' peut occuper la place d'un '0'
un '0' peut occuper la place d'un '1'
un '1' peut occuper la place d'un '0'
mais
un '1' ne peut occuper la place d'un '1'
ce schéma correspond à un 'ET NON' logique.
-comment l'objet se déplace à gauche ou à droite?
c'est simple, il suffit de déplacer les '1'.
si la ligne 2 d'un objet est codée '0000011000' soit 24 en décimal
24 multiplié par 2 donne 48, soit '0000110000' en binaire
et 24 divisé par 2 donne 12, soit '0000001100' en binaire
les '1' semblent s'être déplacés ...
en appliquant ceci aux 4 lignes de l'objet, tout l'objet se déplace.
c'est pour cela que l'objet à la même largeur que le tableau, car 'supperposé' à celui-ci, opérateurs logiques et numériques s'appliqueront plus facilement.
-ok, et la rotation alors?
de même que l'on code l'objet en forme de 'L' sur 4 int (ou des bin ou hex, c'est vous qui voyez),
on code ses 4 phases de rotation ainsi:
(les chiffres sont bidons)
objet_en_forme_de_L = (45,26,96,12) , (57,84,32,79) , (84,23,15,47) , (63,35,29,21)
un index pointe l'objet en cours; il suffit de le décrémenter pour créer la rotation.
3 -problèmes et solutions ... pb:
admettons que le jeu sur déroule avec l'objet_en_forme_de_L[2]
si je déplace celui-ci complètement à droite, au prochaine cycle où il sera utilisé, il apparaitra à droite et non plus au milieu du tableau... sl:
on utilise une copie de l'objet_en_forme_de_L[2] que l'on pourra manipuler.
pb:
l'objet_en_forme_de_L[2] est complètement à droite du tableau
si j'applique une rotation, l'objet_en_forme_de_L[1] sera, lui, au milieu du tableau ... sl:
on utilise une copie de l'objet_en_forme_de_L pour palier au 1er problème, et, au lieu de déplacer que l'objet indexé, on déplace tout le groupe.
4- les évènements:
4.1- clavier
les 4 touches 'arrow' sont utilisées.
l'objet se déplace à gauche, droite et bas tant que les touches 'left','right' et 'down' sont maintenues enfoncées,
mais ne 'tourne' qu'à l'enfoncement de la touche 'up'.
pygame met à disposition plusieurs outils pour gérer le clavier ... Exercice:
écrivez un 'mainloop' qui réagit de la façon expliquée ci-dessus; vérifiez le fonctionnement en 'printant' la touche enfoncée ('left','right','up' et 'down')
from pygame import *
screen = display.set_mode((200,200))
key.set_repeat(50,50)
k_up_is_up = False
while True:
ev = event.wait()
if ev.type == QUIT: exit()
elif ev.type == KEYDOWN:
if ev.key == K_DOWN: print 'down'
elif ev.key == K_LEFT: print 'left'
elif ev.key == K_RIGHT: print 'right'
elif ev.key == K_UP and not k_up_is_up:
print 'up'
k_up_is_up = True
elif ev.type == KEYUP and ev.key == K_UP: k_up_is_up = False
4.2- temps
l'objet chute de lui-même à vitesse constante(par cycle),ce qui signifie qu'il répond à un timing; non bloquant évidement...
la encore pygame dispose de plusieurs solutions pour gérer le temps.
Exercice:
modifiez le 'mainloop' de façon à ce que soit 'printé' 'down' toutes les 500ms.
from pygame import *
screen = display.set_mode((200,200))
key.set_repeat(50,50)
time.set_timer(KEYDOWN,500)
#ici l'astuce est de simuler l'appuie d'une touche
#time.set_timer() poste un evenement a interval regulier
#mais il n'est pas possible de specifier des attributs
#ainsi l'evenement KEYDOWN aura un attribut key egal a 0
#0 ne correspondant a aucune touche du clavier il n'y a pas de conflict
k_up_is_up = False
while True:
ev = event.wait()
if ev.type == QUIT: exit()
elif ev.type == KEYDOWN:
if not ev.key or ev.key== K_DOWN: print 'down'
elif ev.key == K_LEFT: print 'left'
elif ev.key == K_RIGHT: print 'right'
elif ev.key == K_UP and not k_up_is_up:
print 'up'
k_up_is_up = True
elif ev.type == KEYUP and ev.key == K_UP: k_up_is_up = False
5- interaction objet/décor:
algo du jeu (un parmis d'autre :p):
il est simple ...
soit un tableau vide.
tant que le tableau ne déborde pas d'objets:
un nouvel objet est généré et fait sa vie d'objet. Il vit jusqu'à ce qu'il ne puisse plus chuter.
si l'objet fraîchement généré ne peut chuter au moins 1 fois, c'est que le tableau déborde.
cycle de jeu:
un cycle est le temps de vie d'un objet.
au début d'un cycle, l'objet apparait hors champs, au dessus du tableau visible. Comme il ne peut pas survivre dans le vide sidérale du out of range,
il est nécessaire que le tableau s'étende de façon à contenir l'objet.
un tableau visible fait 10 x 20 cases:
puis il chute, se pose et meurt.
en mourrant il se pétrifie et fait partie du décor.
la encore la magie des opérateurs logiques opère...
si 100000000001 est un élément du décor
et 000111000000 est un élément de l'objet 100000000001OU(logigue) 000111000000 donne 100111000001 comme nouvelle valeur à l'élément du décor.
(étant donné que deux '1' ne peuvent se chevaucher, ça marhe aussi avec le 'OU EXCLUSIF')
un nouveau cycle est généré ...
interactions:
Exercice:
1-écrivez une fonction qui teste si un objet peut descendre d'une ligne où pas.
il faut donc au préalable définir un tableau et au moins un objet dans une phase de rotation.
démarrez de la ligne 0 du tableau et laisser, via le 'mainloop', chuter l'objet qui ne devra pas dépasse le bas du tableau.
pour vérifiez le fonctionnement, ecriver une fonction qui affiche en console un truc de ce genre ...
2-pour empêcher l'objet de sortir du tableau on y a collé des bordures; ça vous l'aviez compris.
modifiez la fonction test() et 'mainloop' pour tenir compte du déplacement lattéral de l'oblet.
3-modifiez la fonction test() et 'mainloop' pour tenir compte de la rotation de l'objet.
4-modifiez 'mainloop' pour générer des objets tant que le tableau ne déborde pas ...
5-et pour finir, vous l'avez devinez ... (j'abrège car j'en ai marre )
modifiez 'mainloop' pour supprimer les 'lignes pleines' et insérer des lignes vides.
from pygame import *
from random import choice
empty_ligne = 2049
all_set = 4095
tableau = [empty_ligne]*24+[all_set]
objet = (
((0, 0, 8, 14), (0, 12, 8, 8), (0, 0, 14, 2), (0, 4, 4, 12)),
((0, 0, 4, 14), (0, 4, 6, 4), (0, 0, 14, 4), (0, 2, 6, 2)),
((0, 0, 6, 12), (0, 4, 6, 2), (0, 0, 6, 12), (0, 4, 6, 2)),
((0, 0, 12, 6), (0, 2, 6, 4), (0, 0, 12, 6), (0, 2, 6, 4)),
((0, 0, 2, 14), (0, 4, 4, 6), (0, 0, 14, 2), (0, 6, 4, 4)),
((0, 0, 6, 6), (0, 0, 6, 6), (0, 0, 6, 6), (0, 0, 6, 6)),
((0, 0, 0, 15), (4, 4, 4, 4), (0, 0, 0, 15), (2, 2, 2, 2))
)
screen = display.set_mode((200,400))
key.set_repeat(50,50)
time.set_timer(KEYDOWN,500)
k_up_is_up = False
test = lambda shift,down,rotation: not any([int(i*shift)&tableau[e] for e,i in enumerate(obj[(one+rotation)%4],ligne+down)])
def affiche():
t = tableau[:]
for i in range(4): t[ligne+i] = t[ligne+i]|obj[one][i]
for i in t[4:]:
for j in bin(i)[2:]: print 'X' if int(j) else ' ',
print
ligne = 1
while ligne:
ligne = 0
obj = choice(objet)[:]
one = choice((0,1,2,3))
while True:
ev = event.wait()
if ev.type == KEYDOWN:
if not ev.key or ev.key== K_DOWN:
if test(1,1,0): ligne+=1
else:
for e,i in enumerate(obj[one],ligne):
tableau[e] = tableau[e]|i
if tableau[e] == all_set:
del(tableau[e])
tableau.insert(0,empty_ligne)
break
elif ev.key == K_LEFT:
if test(2,0,0): obj = [[j<<1 for j in i]for i in obj]
elif ev.key == K_RIGHT:
if test(0.5,0,0): obj = [[j>>1 for j in i]for i in obj]
elif ev.key == K_UP and not k_up_is_up:
k_up_is_up = True
if test(1,0,1): one = (one+1)%4
elif ev.type == KEYUP and ev.key == K_UP: k_up_is_up = False
elif ev.type == QUIT: exit()
affiche()
Je trouve ton explication fort intéressante et certains points assez astucieux.
J'ai commencé ce projet (peut-être pas par là où il aurait fallu), j'y travaille.
A l'issue de ce développement, je penserai avoir tout ce qu'il me faut pour lancer mon projet.
J'avais écrit un Tetris, il y a un bout de temps, pour m'entrainer avec Pygame, avec quelques features sympa (hard-fall, ombre de la pièce, passage au niveau supérieur passé un certain score, tout ça) mais le code n'est vraiment pas très joli à voir.
S'il faut, j'essayerai de le reprendre et le programmer de façon plus élégante pour le publier ici.
voilà, le noyau est fait.
on va donc attaquer le rendu et les autres options du jeu, comme le comptage des points, le design, etc ...
la 1ere chose que je vous propose c'est modifier le code pour pouvoir initialiser des tableaux de tailles variables.
pas que ce soit essentiel, mais on ne sais pas encore à quoi ça ressemblera à la fin, et s'il faut modifier des valeurs, autant modifier quelques variables et ne pas se taper toutes les constantes du code.
Citation : NoHaR
J'avais écrit un Tetris, il y a un bout de temps, pour m'entrainer avec Pygame, avec quelques features sympa (hard-fall, ombre de la pièce, passage au niveau supérieur passé un certain score, tout ça)
oui, faites des propositions comme ça, et postez aussi de zoulis dessins parce que niveau design je suis super nul
Je ne suis vraiment pas doué pour expliquer les choses
Bah, c'est plutôt bien!
Perso il y a un truc qui me gène...
J'ai déjà codé un tétris, et ta méthode me séduit(j'ai jamais pensé à ça...).
Mais, comment gères tu les différentes couleurs, justes avec les bits?o_O
Ton explication me plait, mais là, je bloque...
je connais 3, 4 méthodes pour représenter les figures dans un tétris.
Ce que tu proposes est original, mais je pense qu'il y a blocage, ou alors je suis à coté de la plaque(c'est fort possible! )
Salut!
Perso il y a un truc qui me gène...
J'ai déjà codé un tétris, et ta méthode me séduit(j'ai jamais pensé à ça...).
Mais, comment gères tu les différentes couleurs, justes avec les bits?o_O
c'est vrai que cette méthode ne permet pas de gérer les combos de couleurs, du moins proprement; si c'est ce dont tu veux parler.
il est toujours possible de lire la/les couleur/s d'une ligne en lisant la couleur des pixels.
En principe, dans Tetris, on ne fait pas de combos avec les couleurs, chaque pièce a une couleur donnée, point.
Ceci dit, c'est vrai que ça parait problématique. Mais ce genre de chose peut se régler une fois que l'on décide de gérer l'affichage avec des groupes de sprites et de dirtysprites.
Comprendre par là : une fois qu'une pièce est posée, l'objet qui la modélise n'a plus besoin des mêmes propriétés, et il devient plus simple d'enregistrer chaque bloc séparément, avec sa couleur dans la matrice qui représente le plateau.
De même, entre deux affichages, souvent, on n'a pas besoin de raffraichir tout l'écran, mais juste la zone couverte par la pièce (et éventuellement son ombre) à l'instant t et celle couverte par la pièce (et éventuellement son nombre) à l'instant t+1 (sauf, évidemment, quand on fait une ligne, et qu'il faut la faire disparaître et tout faire tomber au-dessus)…
C'est pour cette raison que je voudrais recoder mon Tetris avant de le soumettre ici. En l'état, il ne gère pas les couleurs des blocs, et le raffraichissement de l'écran est complet à chaque fois.
Ceci dit, c'est vrai que ça parait problématique. Mais ce genre de chose peut se régler une fois que l'on décide de gérer l'affichage avec des groupes de sprites et de dirtysprites.
Comprendre par là : une fois qu'une pièce est posée, l'objet qui la modélise n'a plus besoin des mêmes propriétés, et il devient plus simple d'enregistrer chaque bloc séparément, avec sa couleur dans la matrice qui représente le plateau.
ha, j'avais pas compris ça comme ça ...
nul besoin de connaitre la couleur d'une pièce posée, puisqu'elle n'existe plus; elle fait partie du décor. Voyez-vous ...
c'est vrai que je vous ai donné un code où le plateau est entièrement 'retracé', mais c'est une contrainte dû au mode console.
import pygame
import random
BLOCK_SIZE = 16
GRID_WIDTH, GRID_HEIGHT = 10, 22
RESOLUTION = (BLOCK_SIZE * (GRID_WIDTH + 20) , BLOCK_SIZE * GRID_HEIGHT)
COLORS = (128, 128, 128), \
(255, 0, 0), \
( 0, 0, 255), \
(255, 255, 0), \
(139, 69, 19), \
(255, 0, 255), \
(255, 255, 255), \
(0 , 255, 0), \
(0 , 255, 255)
# -----------------------------------------------------------------------------
class Block:
def __init__(self, x, y, id):
self.x, self.y = x, y
self.color = COLORS[id]
def display(self, ox, oy):
screen = pygame.display.get_surface()
x, y = ox + self.x, oy + self.y
screen.fill(self.color, (x * BLOCK_SIZE, y * BLOCK_SIZE, \
BLOCK_SIZE, BLOCK_SIZE))
# -----------------------------------------------------------------------------
class Figure:
tetriminos = (( 0, 0), ( 0, 0), ( 0, 0), ( 0, 0)), \
((-2, 0), (-1, 0), ( 0, 0), ( 1, 0)), \
((-1, -1), ( 0, -1), (-1, 0), ( 0, 0)), \
((-1, 0), ( 0, 0), ( 1, 0), ( 0, 1)), \
((-1, 0), ( 0, 0), ( 1, 0), ( 1, 1)), \
((-1, 0), ( 0, 0), ( 1, 0), (-1, 1)), \
((-1, 0), ( 0, 0), ( 0, 1), ( 1, 1)), \
(( 0, 0), ( 1, 0), (-1, 1), ( 0, 1))
def __init__(self, id):
self.x, self.y = GRID_WIDTH / 2, 0
self.id = id
self.blocks = [Block(b[0], b[1], id) for b in Figure.tetriminos[id]]
def display(self):
for b in self.blocks:
b.display(self.x, self.y)
def checkPos(self, x, y, grid):
for b in self.blocks:
nxtX, nxtY = x + b.x, y + b.y
if nxtX < 0 or nxtX >= GRID_WIDTH or nxtY >= GRID_HEIGHT or grid[nxtY][nxtX]:
return False
return True
def setPos(self, x, y):
self.x, self.y = x, y
def checkRotate(self, grid):
for b in self.blocks:
nxtX, nxtY = self.x + b.y, self.y - b.x
if nxtX < 0 or nxtX >= GRID_WIDTH or nxtY >= GRID_HEIGHT \
or grid[nxtY][nxtX]:
return False
return True
def rotate(self):
for b in self.blocks:
b.x, b.y = b.y, -b.x
# ----------------------------------------------------------------------------
class Playfield:
def __init__(self):
self.grid = [[0 for i in xrange(GRID_WIDTH)] \
for j in xrange(GRID_HEIGHT)]
self.nxtFigure = Figure(random.randrange(1, 8))
self.crtFigure = Figure(random.randrange(1, 8))
self.nxtTime = pygame.time.get_ticks() + 700
def update(self, dx, dy, rotate):
if rotate:
self.updateRotate()
if dx or dy:
self.updateMove(dx, dy)
crtTime = pygame.time.get_ticks()
if crtTime > self.nxtTime:
x, y = self.crtFigure.x, self.crtFigure.y + 1
if self.crtFigure.checkPos(x, y, self.grid):
self.crtFigure.setPos(x, y)
else :
self.storeFigure()
if self.checkGameOver():
return True
self.crtFigure = self.nxtFigure
self.nxtFigure = Figure(random.randrange(1, 8))
self.nxtTime = crtTime + 700
return False
def updateRotate(self):
if self.crtFigure.checkRotate(self.grid):
self.crtFigure.rotate()
def updateMove(self, dx, dy):
x, y = self.crtFigure.x + dx, self.crtFigure.y + dy
if self.crtFigure.checkPos(x, y, self.grid):
self.crtFigure.setPos(x, y)
def storeFigure(self):
for b in self.crtFigure.blocks:
x, y = self.crtFigure.x + b.x, self.crtFigure.y + b.y
self.grid[y][x] = self.crtFigure.id
self.checkLines()
def checkLines(self):
for i, row in enumerate(self.grid):
full = True
for j, case in enumerate(row):
if not case:
full = False
break
if full:
self.deleteLine(i)
def deleteLine(self, row):
for i in range(row, 1, -1):
self.grid[i] = self.grid[i - 1]
def checkGameOver(self):
for case in self.grid[0]:
if case:
return True
return False
def display(self):
screen = pygame.display.get_surface()
screen.fill(0)
for i, row in enumerate(self.grid):
for j, case in enumerate(row):
screen.fill(COLORS[self.grid[i][j]], ( \
j * BLOCK_SIZE, \
i * BLOCK_SIZE, \
BLOCK_SIZE, \
BLOCK_SIZE \
))
self.crtFigure.display()
self.nxtFigure.setPos(GRID_WIDTH + 3, 1)
self.nxtFigure.display()
self.nxtFigure.setPos(GRID_WIDTH / 2, 0)
# -----------------------------------------------------------------------------
class Game:
def __init__(self):
pygame.init()
pygame.display.set_mode(RESOLUTION)
self.playfield = Playfield()
def run(self):
done = False
while not done:
dx, dy = 0, 0
rotate = False
for event in pygame.event.get():
if event.type == pygame.QUIT:
done = True
if not done:
keys = pygame.key.get_pressed()
if keys[pygame.K_LEFT]:
dx = -1
elif keys[pygame.K_RIGHT]:
dx = 1
elif keys[pygame.K_DOWN]:
dy = 1
elif keys[pygame.K_UP]:
rotate = True
done = self.playfield.update(dx, dy, rotate)
self.playfield.display()
pygame.time.delay(100)
pygame.display.flip()
game = Game()
game.run()
Hum, je fais nettement plus long que Josmiley et avec moins de fonctionnalités(pas de score et pas de niveau encore.
On va essayer de rendre ça plus joli.
@GurneyH:
pygame.key.get_pressed() n'est pas une bonne solution pour tester les touches car un appuie bref n'est pas forcément détecté s'il se produit entre 2 pygame.key.get_pressed()
@GurneyH:
pygame.key.get_pressed() n'est pas une bonne solution pour tester les touches car un appuie bref n'est pas forcément détecté s'il se produit entre 2 pygame.key.get_pressed()
Sauf erreur de ma part, on va récupèrer le dernier event KEY_DOWN ou KEY_UP de la file.
Alors en effet, on loupe certaines actions, mais en pratique pour des jeux de ce style, je demande à voir la perte réelle.
Sinon, si on utilise le polling il faut une variable supplémentaire, pour éviter au joueur de marteler la touche pour déplacer la figure.
En pratique je n'ai jamais constater de perte(notable pour le joueur), avec le mapping...
Après, pygame.key.get_pressed(), n'est peut-être pas ce que je crois, (l'équivalent de SDl_GetKeyState)?
Mais pour un jeu de ce style, ce ne sera pas ma première priorité.
J'ai d'ailleurs un débordement de tableau plus grave que je vais devoir corriger.
Y a quand même un manque flagrant de réactivité et c'est un jeu de rapidité ... non ?
C'est vrai.
Mais je pense, que c'est à cause de la tempo de 100ms que j'ai collé avant le flip dans la boucle principale, justement pour que ne soit pas trop réactif.
C'est un peu du bricolage, mais ce n'est pas la faute du mapping.
Par contre j'ai un bug sévère qui se produit de manière alétoire, il me semble, et qui rempli la figure courante jusqu'en haut du plateau de jeu. Je n'arrive pas à trouver l'erreur.
Mais je pense, que c'est à cause de la tempo de 100ms que j'ai collé avant le flip dans la boucle principale, justement pour que ne soit pas trop réactif.
C'est un peu du bricolage, mais ce n'est pas la faute du mapping.
ben oui, ça oblige à maintenir la touche au moins 100ms, mais bon, si c'est fait exprès
ben oui, ça oblige à maintenir la touche au moins 100ms, mais bon, si c'est fait exprès
Si je ne met pas de tempo c'est injouable en fait. :p(les rotations vont bien trop vite).
Je vais rejouer à un "vrai tetris", il ne me semble pas qu'on était obligé d'appuyer plusieurs fois pour se déplacer de 2 cases par exemple.
edit: Je viens de vérifier, sur la snes, on se déplace en maintenant appuyer, pour les rotations, il faut appuyer plusieurs fois.
Je vais pouvoir virer ma tempo!
Bon, désolé, je n'ai pas eu le temps de corriger mon design (dont je ne suis pas fier), mais mon tetris ("loltriz") se trouve ici. Il s'agit d'un de mes premiers projets en python.
pour le lancer, il suffit de lancer python 2.x sur le fichier "loltriz.py"
Bon, désolé, je n'ai pas eu le temps de corriger mon design (dont je ne suis pas fier), mais mon tetris ("loltriz") se trouve ici. Il s'agit d'un de mes premiers projets en python.
Joli projet !
En tant que joueur, voici mon avis : jeu fluide et qui se joue très bien. Je trouve très bonne l'idée de mettre une image de la cible (j'aurais juste mis une couleur plus claire peut-être). Je trouve par contre assez gênant le contour non bien rectiligne ainsi que la police peu lisible ainsi aussi que l'absence de couleurs (ça reste des détails car le jeu est très bon).
Du point de vue du code, je ne suis pas assez connaisseur pour pouvoir juger de façon compétente. J'ai l'impression que les concepts OO de ton code sont assez simples, tu crées des classes ok mais tu utilises à peine l'héritage (class OptionToggler). Sinon, il y a quand même quelque chose qui me gêne : c'est que par exemple ton moteur de jeu appelle Pygame. Ce que je trouve dommage -- mais ce n'était sans doute pas dans ton projet initial, -- c'est que ton code implémente un Tétris mais ne me semble pas du tout réutilisable si on veut coder un Tétris avec Tkinter ou PyQt. Or, un Tétris reste un Tétris, le principe du jeu ne change pas quelle que soit le GUI utilisé et je trouve qu'il est dommage d'avoir à coder plusieurs fois la même chose. Maintenant, peut-être que ce dont je parle là est difficile à implémenter, je serais curieux d'avoir ton avis.
Bon, désolé, je n'ai pas eu le temps de corriger mon design (dont je ne suis pas fier), mais mon tetris ("loltriz") se trouve ici. Il s'agit d'un de mes premiers projets en python.
pour le lancer, il suffit de lancer python 2.x sur le fichier "loltriz.py"
Voici un screenshot :
Je trouve le design excellent.
j'vais essayer d'intégrer le 'shadow' à mon code, tiens ...
Citation : candide
<citation rid="5595737">
Du point de vue du code, je ne suis pas assez connaisseur pour pouvoir juger de façon compétente. J'ai l'impression que les concepts OO de ton code sont assez simples, tu crées des classes ok mais tu utilises à peine l'héritage (class OptionToggler). Sinon, il y a quand même quelque chose qui me gêne : c'est que par exemple ton moteur de jeu appelle Pygame. Ce que je trouve dommage -- mais ce n'était sans doute pas dans ton projet initial, -- c'est que ton code implémente un Tétris mais ne me semble pas du tout réutilisable si on veut coder un Tétris avec Tkinter ou PyQt. Or, un Tétris reste un Tétris, le principe du jeu ne change pas quelle que soit le GUI utilisé et je trouve qu'il est dommage d'avoir à coder plusieurs fois la même chose. Maintenant, peut-être que ce dont je parle là est difficile à implémenter, je serais curieux d'avoir ton avis.
je suis assez d'accord avec candide au sujet du OO; j'essaie souvent de créer des class dont les objets peuvent évoluer indépendament de l'environnement. Mais en graphique, ces objets devraient se tenir informé de l'environnement dans lequel ils évoluent, ne serait-ce que pour afficher un truc au bon endroit. Et là, la 'généralisation' de la class compliquerait plus le code qu'autre chose, amha.
Par contre, en séparant le 'calcul' du 'rendu' ...
Bon étant donné que c'est moi qui ai commis réalisé ce code, je vais me permettre d'être très critique dessus.
L'intention
Comme je l'ai dit, il s'agit de l'un des tous premiers projets que j'ai réalisés avec Python pour me faire la main dessus.
Le but était d'obtenir un jeu avec un design visuel rigolo style "dessiné au marqueur sur un tableau blanc", d'où les lignes non-rectilignes (mais un peu quand même) et la police difficile à lire. Je suis d'accord sur le manque de couleur, et c'est un problème très difficile à régler en l'état à cause de certains problèmes de conception. J'ai mis pas mal l'accent sur la jouabilité, cependant, parce que j'avais pour ce jeu un client direct et très exigeant : ma copine.
La conception et ses lourdeurs
Pour résumer, je qualifierais cette conception d'intermédiaire. L'accent n'a pas du tout été mis dessus, mais quelques éléments ont été faits avec un certain soucis de réutilisabilité (comme le menu, que je voulais utilisable "un peu comme dans n'importe quelle lib graphique", et pour lequel j'ai essayé de jouer avec le "tout-est-objet" de Python — le OptionToggler en est un bon exemple, quoi que très perfectible avec le recul —).
Pour ce qui est des concepts OO que je n'utilise pas beaucoup (ou qui ne sont pas très respectés), je dirais que le problème ne vient pas nécessairement du manque d'héritage, mais qu'il est plus "bas" encore conceptuellement : mes classes ne sont pas toutes à responsabilité unique (beaucoup sont des do-it-all classes, très difficiles à re-factoriser et maintenir en l'état), et je n'ai pas assez eu le soucis de définir une interface publique (ou classe "virtuelle") pour chaque composant, implémentée par la suite par des classes filles. En conséquence, on se retrouve avec un moteur peu hiérarchisé, qui dépend effectivement de Pygame : le couplage entre l'abstraction et l'implémentation est beaucoup trop fort (d'autant plus que les mécanismes que j'utilise pour l'affichage ne sont vraiment, vraiment pas performants, bien que cela ne se sente pas du tout à l'utilisation).
Si j'avais à recoder ce jeu avec l'intention que son code soit "visible", je pense que j'aurais une conception plus claire :
* Les abstractions (le "moteur de jeu" actuel) seraient bien séparées du reste (plus qu'actuellement).
* Le système d'affichage serait plus optimisé, notamment en dérivant les classes pièces et en utilisant des "dirty sprites", afin d'éviter de redessiner tout l'écran à chaque fois.
Pour ce qui est du moteur, par contre, dans un jeu comme celui-ci, il est nécessairement beaucoup plus simple de le faire dépendre de la bibliothèque : si le but était de créer un moteur générique pour un Tetris, il faudrait le spécifier à la base, et ça demande de créer beaucoup plus d'abstractions : dans un cas simple comme ici (le système du jeu n'étant vraiment pas complexe), on peut raisonnablement dépendre de Pygame, c'est un choix qui me semble acceptable pour trouver l'équilibre entre la modularité du design (qui demande de créer plein de petites classes hiérarchisées à responsabilité unique, typiquement) et la simplicité du code.
× Après avoir cliqué sur "Répondre" vous serez invité à vous connecter pour que votre message soit publié.
× Attention, ce sujet est très ancien. Le déterrer n'est pas forcément approprié. Nous te conseillons de créer un nouveau sujet pour poser ta question.
Python c'est bon, mangez-en.
Python c'est bon, mangez-en.
Python c'est bon, mangez-en.
Python c'est bon, mangez-en.
Python c'est bon, mangez-en.
Python c'est bon, mangez-en.
Python c'est bon, mangez-en.
Python c'est bon, mangez-en.
Python c'est bon, mangez-en.
Python c'est bon, mangez-en.
Python c'est bon, mangez-en.
Python c'est bon, mangez-en.