Problems developing a bricks-breaking game in tkinter (collisions with objects)

2

I'm a few days studying and trying to get the ball by hitting the rectangles from above they add 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()
    
asked by anonymous 19.06.2018 / 14:29

1 answer

1

Okay - I adjusted your code to get through the difficulty you encountered - but I did not hold back and wrote a bit 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 were not saving every rectangle created on the Python side - yes, tkinter kept an internal reference for each rectangle and that allowed he would find the collisions and even allow the rectangles where there was a collision to be erased - but without a Python side reference, you would not have to count which rectangles were destroyed, and count points, or detect the end of the phase.

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

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

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

Also, I've refaced a few things - in particular absolute values should not be within the code - especially when you've taken the trouble to have a separate module with only those constants. (which you 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 set before the code.

And finally, another more noticeable change is that I've added code to get the clicks of the mouse and update the player's position - with that, plus the detection of collision 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 did not modify your comments - and I kept the update mechanics from the ball to the players - If you are going to continue the game from there, you must now modify the "finished" function to display warnings with tkinter, not with "print" and "input", and set newGame until it works when called a second time.

A point system would also be cool! : -)

    
19.06.2018 / 22:16