Problems to develop a Bricks Breaking-style game on Tkinter (collisions with objects)

Asked

Viewed 175 times

2

I’m a few days studying and trying to get the ball to hit the rectangles from above they fade and invert the side

 from tkinter import *
 from constantes import *
 import random 
 class Jogo():
     def __init__(self):

          #Criar tela principal

          self.root = Tk()
          self.root.geometry('%ix%i' %(LARGURA, ALTURA))
          self.root.title('Arcade')
          self.root.resizable(False,False)

          #Criar frame para conter o canvas

          self.frame = Frame(bg='blue')
          self.frame.pack()

          #Criar canvas

          self.canvas = Canvas(self.frame, bg='blue', width=CANVAS_L, height=CANVAS_A, cursor='target')
          self.canvas.pack()

          #Criando objetos dentro do canvas

          #self.canvas.create_line(10,10,390,390,            fill='white')

          self.comecar = Button(self.root, text='INICIAR', command=self.comecar )
          self.comecar.pack()

          self.novoJogo()


     def novoJogo(self):
          #Criar objetos do jogo
          self.player = self.canvas.create_rectangle(195,360,280,375, fill='white')
               #Criar a bolinha
          raio = 29
          p = (100,200)
          self.ovo = self.canvas.create_oval(p[0],p[1],p[0]+raio,p[1]+raio, fill='grey',)
          #Velocidade da bola
          self.b_vx = 7
          self.b_vy = 7
          #Posição da bola
          self.b_x, self.b_y = p

          self.r = []
          for i in range(3):
               c = random.choice(['green', 'yellow','red'])
          #retangulos superiores horizontais
          r = self.canvas.create_rectangle(6,10,80,30,fill=c)
          r = self.canvas.create_rectangle(82,10,160,30,fill=c)
          r = self.canvas.create_rectangle(162,10,240,30,fill=c)
          r = self.canvas.create_rectangle(242,10,320,30,fill=c)
          r = self.canvas.create_rectangle(322,10,398,30,fill=c)
          #retangulos superiores verticais 2ª FILEIRA
          r = self.canvas.create_rectangle(6,32,80,52,fill=c)
          r = self.canvas.create_rectangle(82,32,160,52,fill=c)
          r = self.canvas.create_rectangle(162,32,240,52,fill=c)
          r = self.canvas.create_rectangle(242,32,320,52,fill=c)
          r = self.canvas.create_rectangle(322,32,398,52,fill=c)
          #3ª FILERIRA
          r = self.canvas.create_rectangle(6,54,80,74,fill=c)
          r = self.canvas.create_rectangle(82,54,160,74,fill=c)
          r = self.canvas.create_rectangle(162,54,240,74,fill=c)
          r = self.canvas.create_rectangle(242,54,320,74,fill=c)
          r = self.canvas.create_rectangle(322,54,398,74,fill=c)
          #4ª FILEIRA
          r = self.canvas.create_rectangle(6,76,80,96,fill=c)
          r = self.canvas.create_rectangle(82,76,160,96,fill=c)
          r = self.canvas.create_rectangle(162,76,240,96,fill=c)
          r = self.canvas.create_rectangle(242,76,320,96,fill=c)
          r = self.canvas.create_rectangle(322,76,398,96,fill=c)
          self.r.append(r)

          self.jogando = True

     def comecar(self):
          self.jogar()

     def jogar(self):
          if self.jogando:
               self.update()
               self.root.after(5, self.jogar)
          else:
               self.acabou(self.msg)

     def update(self):
          self.canvas.move(self.ovo, self.b_vx , self.b_vy)
          #atualizar o movimento da bola e sua posição
          self.b_x += self.b_vx
          self.b_y += self.b_vy
          #verificar se a bola esta batendo dos lados
          if self.b_x > CANVAS_L - 29 or self.b_x < 0:
               self.b_vx *= -1
          if self.b_y > CANVAS_A - 29 or self.b_y < 0:
               self.b_vy *= -1
          #verificar se existe colisões com os objetos
          self.verificar_colisao()

     def verificar_colisao(self):
               #Criar uma boulding box para capturar a posição da bola
          coord = self.canvas.bbox(self.ovo)
          colisoes = self.canvas.find_overlapping(*coord)
          print(*colisoes)
         # print(coord)
          #se o numero de colisões é diferente de 0
          if len(colisoes) != 1 :
          #verificar se o id do objeto colidido é diferente do id do objeto player
               if len(colisoes) != self.player:







if __name__ == '__main__':
     Jogo()
  • 1

    All your rectangles are r? You do not keep a reference for each rectangle or this is not necessary?

  • Honestly I’m a little lost but they must be referenced for the program to recognize them

1 answer

1

Ok - I tweaked your code to get past the difficulty you encountered - but I didn’t hold back and wrote a little more code until it became playable.

The main change made, and that has to do with the point where you put your doubt is - you weren’t holding every rectangle created on the Python side - yes, Tkinter kept an internal reference for each rectangle and this allowed it to find the collisions and even allow the rectangles where there was a collision to be erased - but without a reference on the Python side, you wouldn’t have to count which rectangles were destroyed, and count points, or detect the end of the phase.

That being said: it makes no sense for you to write a program to control a computer that does ~10 billion operations per second, and have to place the coordinates of your rectangles manually, it is not??

So - based on the coordinates you were drawing, I modified the code to draw all the rectangles into two loops - the computer does the math - we just put the parameters as "rectangle width".

With this and an append to each rectangle in "self. r", a call to self.canvas.delete and self.r.remove Within the collision check this solved the point you asked.

Also, I’ve refactored some things - in particular absolute values shouldn’t stay within the code - especially when you’ve gone to the trouble of having a separate module with just these constants. (which Oce did not put together with the code - I had to undo the import and find reasonable values for its constants). Anyway, I put some other values as constants to be defined before the code.

And finally, another more noticeable change is that I added code to receive the mouse clicks and update the player’s position - with this, and more the collision detection with rectangles and the ground, leaving the game functional.

from tkinter import *
import random 

LARGURA, ALTURA = 460, 410
CANVAS_L, CANVAS_A = 420, 380
PLAYER_L, PLAYER_A = 85, 15
RAIO = 29
PLAYER_VEL = 12

class Jogo():
    def __init__(self):

        #Criar tela principal

        self.root = Tk()
        self.root.geometry('%ix%i' %(LARGURA, ALTURA))
        self.root.title('Arcade')
        self.root.resizable(False,False)

        #Criar frame para conter o canvas

        self.frame = Frame(bg='blue')
        self.frame.pack()

        #Criar canvas

        self.canvas = Canvas(self.frame, bg='blue', width=CANVAS_L, height=CANVAS_A, cursor='target')
        self.canvas.pack()

        #Criando objetos dentro do canvas

        #self.canvas.create_line(10,10,390,390,           fill='white')

        self.comecar = Button(self.root, text='INICIAR', command=self.comecar )
        self.comecar.pack()

        self.novoJogo()


    def novoJogo(self):
        #Criar objetos do jogo
        self.v_basica = 7
        self.p_x = 195
        self.p_y = 360
        self.p_alvo_x = self.p_x
        p_x2 = self.p_x + PLAYER_L
        p_y2 = self.p_y + PLAYER_A
        self.player = self.canvas.create_rectangle(self.p_x, self.p_y, p_x2, p_y2, fill='white')
        #Criar a bolinha
        p = (100,200)
        self.ovo = self.canvas.create_oval(p[0],p[1],p[0]+RAIO,p[1]+RAIO, fill='grey',)
        #Velocidade da bola
        self.b_vx = self.v_basica
        self.b_vy = self.v_basica
        self.v_player_x = PLAYER_VEL
        #Posição da bola
        self.b_x, self.b_y = p

        self.r = []
        offset_x, offset_y = 4, 8
        rect_height = 20
        rect_width = 80
        rect_spacing = 2
        for line in range(4):
            for column in range(5):
               c = random.choice(['green', 'yellow','red'])
               x1 = offset_x + (rect_width + rect_spacing) * column 
               x2 = x1 + rect_width
               y1 = offset_y + (rect_height + rect_spacing) * line
               y2 = y1 + rect_height
               r = self.canvas.create_rectangle(x1, y1, x2, y2,fill=c)

               self.r.append(r)
        self.canvas.bind("<Button-1>", self.mover_player)
        self.jogando = True

    def comecar(self):
        self.jogar()

    def jogar(self):
        if self.jogando:
             self.update()
             self.root.after(30, self.jogar)
        else:
             self.acabou(self.msg)

    def mover_player(self, evento):
        self.p_alvo_x = max(0, evento.x - PLAYER_L // 2)
        print(self.p_alvo_x)

    def update(self):
        self.canvas.move(self.ovo, self.b_vx , self.b_vy)
        if self.p_x != self.p_alvo_x:
            sinal = -1 if self.p_x > self.p_alvo_x else 1
            mudanca_player = sinal * self.v_player_x
            if self.p_x + PLAYER_L + mudanca_player < CANVAS_L or self.p_x + mudanca_player > 0:
                self.p_x += mudanca_player
                sinal2 = -1 if self.p_x > self.p_alvo_x else 1
                if sinal != sinal2:
                    self.p_alvo_x = self.p_x
                self.canvas.move(self.player, mudanca_player, 0)

        #atualizar o movimento da bola e sua posição
        self.b_x += self.b_vx
        self.b_y += self.b_vy
        #verificar se a bola esta batendo dos lados
        if self.b_x > CANVAS_L - RAIO or self.b_x < 0:
             self.b_vx *= -1
        if self.b_y < 0:
             self.b_vy *= -1
        if self.b_y > CANVAS_A - RAIO:
            self.msg = "Bolinha atingiu o chão"
            self.jogando = False
        #verificar se existe colisões com os objetos
        self.verificar_colisao()
        if not self.r:
            self.msg = "Fase concluida"
            self.jogando = False

    def verificar_colisao(self):
        #Criar uma boulding box para capturar a posição da bola
        coord = self.canvas.bbox(self.ovo)
        colisoes = self.canvas.find_overlapping(*coord)
        print(*colisoes)
        # print(coord)
        #se o numero de colisões é diferente de 0
        if len(colisoes) != 1 :
        #verificar se o id do objeto colidido é diferente do id do objeto player
             self.b_vy *= -1
             for item in colisoes:
                if item not in self.r:  # colisão com a propria bola ou com o player
                    continue
                self.canvas.delete(item)
                self.r.remove(item)

    def acabou(self, msg):
        print("Fim de jogo")
        print(msg)
        input()
        quit()



if __name__ == '__main__':
    Jogo()
    mainloop()

I haven’t modified your comments - and I’ve kept the ball update mechanics for the players - If you’re going to continue the game from there, you now need to modify the "finished" function to display warnings with Tkinter, not with "print" and "input"and adjust new Mode until it works when it is called a second time.

A points system would also be cool! :-)

Browser other questions tagged

You are not signed in. Login or sign up in order to post.