IdentifiantMot de passe
Loading...
Mot de passe oublié ?Je m'inscris ! (gratuit)

Game Over.

En continuant de Accrochons des briques en l'air, on rajoute une gestion du score et une limitation des tirs. Plus un bouton pour redémarrer le jeu. Ce qui donne toutefois une gestion de signaux assez conséquente mais on arrive à un jeu presque fonctionnel.
Cet exemple est disponible dans les versions PyQt5, PyQt6 et PySide6.

La suite sera Face au mur.
Avatar de josmiley
Membre expérimenté https://www.developpez.com
Le 22/08/2021 à 14:45
Citation Envoyé par Sve@r Voir le message
Je comprends. C'est vrai que Qt n'a pas pour objectif de faire des jeux à la base (et encore heureusement qu'il sait trouver l'intersection entre deux dessins sinon il fallait se la farcir mathématiquement) mais des IHM. Peut-être que le tuto de base n'aurait pas dû proposer cet exemple.

J'ai une idée. Je ne connais pas PyGame. Pourquoi ne referais-tu pas la même chose avec PyGame? Tu pourrais intituler ça comme "suite au tuto Qt, je vous propose le même exemple avec PyGame pour vous montrer comment faire la même chose avec un outil dédié". A mon avis ça devrait plaire...

olalala ... c'est beaucoup demander. Comme j'ai dit plus haut, c'est réinventer la roue à chaque fois.
mais je me suis quand même amusé à faire des class pour le slide et la jauge, voici un exemple si tu veux voir.

Code : Sélectionner tout
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
from pygame import *

font.init()

class Slider(Rect):
    
    def __init__(self,parent,rect):
        Rect.__init__(self,rect)
        self.surface          = parent
        self.button           = self.copy()
        self.button.width     = self.height
        self.focus            = False
        self.keyfocus         = False
        self.button_color     = Color('grey')
        self.line_left_color  = Color('CornflowerBlue')
        self.line_right_color = Color('black')
    
    def update(self,event):
        if event.type == MOUSEBUTTONDOWN and event.button == 1:
            if self.button.move(self.surface.get_abs_offset()).collidepoint(event.pos):
                self.focus = True
        
        elif event.type == MOUSEBUTTONUP and event.button == 1:
            self.focus = False
            
        elif event.type == MOUSEMOTION and self.focus:
            self.button.move_ip(event.rel)
            self.button.clamp_ip(self)
            return True
        
        return
    
    def draw(self):
        draw.line(self.surface,self.line_left_color, self.midleft, self.button.midleft, 4)
        draw.line(self.surface,self.line_right_color, self.midright, self.button.midright, 4)
        self.surface.fill(self.button_color,self.button)
        draw.rect(self.surface,self.line_left_color//Color(2,2,2),self.button,1)
    
    @property
    def value(self):
        return (self.button.x-self.x)/(self.w-self.h)
    
    @value.setter
    def value(self,value):
        self.button.x = value*(self.w-self.h)+self.x
        self.button.clamp_ip(self)


class Displayer(Rect):
    
    def __init__(self,parent,rect):
        Rect.__init__(self,rect)
        self.surface = parent
    
    def draw(self,text,font,color):
        text = font.render(text,1,color)
        rect = text.get_rect(center=self.center)
        self.surface.set_clip(self)
        self.surface.blit(text,rect)
        self.surface.set_clip()
        
        
        
        
class SliderWithGauge:
    
    font = font.Font(font.get_default_font(),30)
        
    def update(self,event):
        self.slider.update(event)
    
    def draw(self):
        self.title.draw(self.title.text,self.font,self.slider.button_color)
        self.slider.draw()
        rect = self.gauge.copy()
        rect.w *= self.slider.value
        self.gauge.surface.fill(self.slider.line_left_color,rect)
        self.gauge.draw(self.gauge.form(),self.font,self.slider.button_color)
        draw.rect(self.gauge.surface,self.slider.line_right_color,self.gauge,2)


class Canon:
    
    positionX = 0
    positionY = 980
    
    @staticmethod
    def draw():
        surface = Surface((300,300),SRCALPHA)
        draw.ellipse(surface,Color('black'),(100,100,100,100))
        draw.line(surface,Color('black'),(150,150),(240,150),26)
        draw.line(surface,Color('black'),(150,150),(250+50*force.slider.value,150),20)
        surface = transform.rotate(surface,angle.angle)        
        game_panel.blit(surface,surface.get_rect(center=(Canon.positionX,Canon.positionY)))


scr = display.set_mode((1000,1000))
ctrl_panel = scr.subsurface(500,10,490,980)
game_panel = scr.subsurface(10,10,490,980)

force = SliderWithGauge()
force.title = Displayer(ctrl_panel,(45,50,390,40))
force.title.text = "FORCE DU CANON"
force.slider = Slider(ctrl_panel,(45,100,390,40))
force.slider.button_color = Color('white')
force.gauge = Displayer(ctrl_panel,(45,150,390,40))
force.gauge.form = lambda:f'{int(force.slider.value*100)} %'


Angle = type('',(SliderWithGauge,),{})
angle = Angle()
angle.min = 5
angle.max = 70
angle.title = Displayer(ctrl_panel,(45,250,390,40))
angle.title.text = "ANGLE DU CANON"
angle.slider = Slider(ctrl_panel,(45,300,390,40))
angle.slider.button_color = Color('white')
angle.gauge = Displayer(ctrl_panel,(45,350,390,40))
angle.gauge.form = lambda:f'{int(angle.angle)}°'
Angle.angle = property(lambda self:int(self.slider.value*(self.max-self.min)+self.min))



timer = time.Clock()
laps  = 40
while True:
    laps += timer.tick()
    if laps >= 40:
        scr.fill(0)
        ctrl_panel.fill((100,100,100))
        game_panel.fill(-1)
        force.draw()
        angle.draw()
        Canon.draw()
        display.flip()
        laps = 0
    for ev in event.get():
        if ev.type == QUIT: break
        force.update(ev)
        angle.update(ev)
    else:
        continue
    break
Avatar de Sve@r
Expert éminent sénior https://www.developpez.com
Le 20/08/2021 à 20:17
Bonjour,

Je vous propose un nouvel élément à utiliser : Game Over.

En continuant de Accrochons des briques en l'air, on rajoute une gestion du score et une limitation des tirs. Plus un bouton pour redémarrer le jeu et on arrive à un jeu presque fonctionnel.

Il a été écrit en Python3/Qt5.

Qu'en pensez-vous ?
Avatar de josmiley
Membre expérimenté https://www.developpez.com
Le 21/08/2021 à 20:54
Je ne connais pas pyQt mais ça me paraît beaucoup 600 lignes de code.
Avatar de Sve@r
Expert éminent sénior https://www.developpez.com
Le 21/08/2021 à 22:16
Citation Envoyé par josmiley  Voir le message
Je ne connais pas pyQt mais ça me paraît beaucoup 600 lignes de code.

Salut, merci de t'intéresser. Mais si tu ne connais pas PyQt tu devrais commencer par le premier exemple Hello World qui n'en fait que 17

Sinon difficile de répondre à cet avis très (trop?) subjectif. Est-ce que 600 lignes pour ce programme qui génère une cible, gère un angle et une force, associe tout ça à un dessin et programme une trajectoire parabolique tout en gérant aussi l'intersection avec la cible et un compteur de coups c'est beaucoup? Peut-on arriver à faire la même chose en moins de lignes avec une autre librairie? Et "moins de lignes" sera-ce "beaucoup moins" ou seulement "un peu moins"??? Parce que ce sont vers ces questions que ta remarque nous entraine.
Bon déjà il faut savoir que dans le tuto officiel d'où j'ai sorti ces exemples (exemples écrits à l'origine en C++/Qt), ils sont découpés en plusieurs modules. Un module pour gérer le champ de tir, un module pour gérer les paramètres, etc. Et quand on découpe, c'est plus facile à appréhender. Mais pour ici, pour que les utilisateurs puissent télécharger facilement, j'ai mis le tout dans un gros et unique source qui fatalement devient énorme (et je suis en train de finir la 6° et dernière partie qui en fait presque 900).

Ensuite Qt c'est une librairie assez puissante mais (corollaire) assez lourde à programmer (et perso je trouve qu'elle en fait trop en voulant toucher à pleins de trucs qui ne sont pas de l'IHM tel sql, les threads, j'ai même vu que l'exemple originel passait par un QTime et qRand pour générer ses nombres aléatoires !!!) donc qui dit "lourde à programmer" dit "beaucoup de lignes". Et encore Qt5 a pas mal raccourcis certains trucs. Par exemple pour créer un bouton et le connecter à un slot, en Qt4 ça s'écrivait ainsi
Code python : Sélectionner tout
1
2
btn=QPushButton(...) 
QObject.connect(btn, SIGNAL("clicked(bool)"), autreWidget, SLOT("truc()")

Cette syntaxe déjà s'est vachement allégée sous Qt5 car elle s'écrit maintenant...
Code python : Sélectionner tout
1
2
btn=QPushButton(...) 
btn.clicked[bool].connect(autreWidget.truc)
... et peut même (quand on n'a qu'un signal à gérer et qu'on n'a pas besoin de récupérer le booléen "bouton pressé/bouton relaché") devenir alors btn=QPushButton(..., clicked=autreWidget.truc). Mais malgré ces optimisations, il reste toutefois que quand on veut faire pas mal de trucs il faut alors pas mal de lignes. Par exemple je viens de rajouter une mise à jour pour centrer le message "Missed/Gotcha" qui apparait quand le tir rate ou réussit. Ce message est un texte (donc un QLabel dans Qt). J'aurais bien aimé pouvoir directement écrire truc=QLabel(..., aligment=Qt.AlignHCenter) malheureusement l'option "alignment" n'est pas disponible à la construction du QLabel donc me faut 2 lignes, la première qui crée le QLabel et la seconde qui positionne l'alignement et ça on peut pas passer outre.

Mais bon, te plonger directement sur cet exemple sans connaitre Qt je comprends que ça puisse effrayer. C'est pour ça que j'en ai écrit 4 autres (en plus du tuto mentionné plus haut) qui commencent avec des éléments plus simples.
Avatar de josmiley
Membre expérimenté https://www.developpez.com
Le 21/08/2021 à 23:27
Ouais, trop d'options ... C'est pour ça que je préfère pygame. Certes il faut réinventer la roue à chaque fois, mais ça fait moins "bricolage" je trouve. Je veux dire, j'ai pas l'impression de planter un clou avec un marteau piqueur.
Avatar de Sve@r
Expert éminent sénior https://www.developpez.com
Le 22/08/2021 à 8:57
Je comprends. C'est vrai que Qt n'a pas pour objectif de faire des jeux à la base (et encore heureusement qu'il sait trouver l'intersection entre deux dessins sinon il fallait se la farcir mathématiquement) mais des IHM. Peut-être que le tuto de base n'aurait pas dû proposer cet exemple.

J'ai une idée. Je ne connais pas PyGame. Pourquoi ne referais-tu pas la même chose avec PyGame? Tu pourrais intituler ça comme "suite au tuto Qt, je vous propose le même exemple avec PyGame pour vous montrer comment faire la même chose avec un outil dédié". A mon avis ça devrait plaire...
Avatar de Sve@r
Expert éminent sénior https://www.developpez.com
Le 22/08/2021 à 19:18
Citation Envoyé par josmiley Voir le message
mais je me suis quand même amusé à faire des class pour le slide et la jauge, voici un exemple si tu veux voir.
Ben... chez-moi avec Python3.8 (sous Linux) ça ne fonctionne pas.
C'est la ligne draw.rect(self.gauge.surface,self.slider.line_right_color,self.gauge,width=2) qui est en erreur. Il me dit que draw() n'est pas présumé recevoir d'argument. Et si je la commente, j'ai la même chose sur draw.line(surface,Color('black'),(150,150),(240,150),width=26) et celle du dessous.

Mais si je commente ces 3 lignes, j'ai un résultat. J'ai deux sliders "force" et "angle" avec une barre de progression, tout comme mon truc (mais malheureusement pas de canon vu qu'il doit être dans les lignes commentées). Mais c'est amusant
Avatar de josmiley
Membre expérimenté https://www.developpez.com
Le 22/08/2021 à 19:41
je vois, certains keywords ne sont pas supportés avec pygame 1.9(je suppose que c'est le problème, j'utilise pygame 2.0). Il suffit de retirer width= et ne laisser que la valeur dans les lignes concernées.
je mets mon code à jour.
Avatar de Sve@r
Expert éminent sénior https://www.developpez.com
Le 22/08/2021 à 20:04
Citation Envoyé par josmiley Voir le message
je vois, certains keywords ne sont pas supportés avec pygame 1.9(je suppose que c'est le problème, j'utilise pygame 2.0).
Effectivement j'étais avec pygame 1.9 (pourtant installé depuis le net via pip3 !!!)
Citation Envoyé par josmiley Voir le message
Il suffit de retirer width= et ne laisser que la valeur dans les lignes concernées.
Mouais. Assez bizarre ce comportement vu que les appels de fonctions avec arguments nommés étaient déjà acceptés dans Python2. Mais effectivement en supprimant ça fonctionne. Merci de t'être investi dans ce minime exemple. Je pense que dourouc05 serait heureux si tu te lançais dans un tuto Pygame
Developpez.com décline toute responsabilité quant à l'utilisation des différents éléments téléchargés.