Search code examples
pythonlisttkintercollision-detectiontkinter-canvas

How can I add collisions to a list of objects (circles) in tkinter?


import time
from tkinter import *
import random

class SpaceField:
    def __init__(self):

        self.window = Tk()
        self.window.title("Asteriods")
        self.canvas = self.canvas_display() #creates canvas
        self.asteriods = self.asteriod_creation_seperation() #creates asteroids 
        self.active = True
        self.move_active()  #Moves asteroids 
        self.canvas.update()

    def asteriod_creation_seperation(self): #creation of multple asteriods
        asteriod_spacingx = random.randint(1,800)
        asteriod_spacingy = random.randint(1,800)
        asteriod_list = list() # could list([])
        for i in range(15):
            asteriod = self.canvas.create_oval( 30, 50 , 80 , 100 , tags="asteriod", width=2, outline="white")
            asteriod_list.append("asteriod")
            self.canvas.move(asteriod, asteriod_spacingx, asteriod_spacingy)
            asteriod_spacingx = random.randint(1,500)
            asteriod_spacingy = random.randint(1,500)
            print(asteriod_spacingy)
        return asteriod_list

Asteroid Creation. Creates asteroids and gives them random positions.

    def asteriod_update(self):  #asteriods movement method #MAin problem
            x12 = 1
            self.canvas.move("asteriod", 3, x12)
            pos = self.canvas.coords("asteriod")
            print(pos)
            if (pos)[2] > 500:
                x12 *= 5

I think this is where I need to add the collision detection. I just have no idea how to combine the lists of the circles and the collisions.


    def move_active(self): #Basically a true loop
        if self.active:
            self.asteriod_update()
            self.window.after(40, self.move_active)

def canvas_display(self):  #canvas
    canvas = Canvas(self.window, width=500, height=400, background='black')
    canvas.pack(expand=True, fill="both")
    canvas.update()
    return canvas

Canvas display nothing special


    def run(self):
        self.window.mainloop()

if __name__ == '__main__':
    SpaceF = SpaceField()
    SpaceF.run()


Solution

  • Asteroids is a classic game but there were a number of problems in your code. The main one was calling move_active during initialization. This prevented the code from completing its mainloop initialization.

    The other problem was the asteroid_update method that basically didn't do anything, also using tags to control all asteroids didn't work either.

    Everything else was OK, although you might consider using polygons.

    Here is one way to produce a bouncing objects program. I've inserted remarks that describe the methods used.

    Objects change the speed and direction when they hit the boundary so their trajectories are randomized.

    from tkinter import *
    from random import randint as rnd
    
    class SpaceField:
        def __init__(self):
    
            self.window = Tk()
            self.window.title("Asteriods")
            # Define canvas size and active flag
            self.wide, self.high, self.active = 500, 400, True
            self.canvas_display()
            self.asteriod_creation_seperation()
    
        def asteriod_creation_seperation(self):
            self.asteroids, self.speed = [], []
            size, radius = 50, 25
            for i in range(15):
                spacex = rnd(size, self.wide - size)
                spacey = rnd(size, self.high - size)
                self.asteroids.append( # Store oval item id
                    self.canvas.create_oval(
                        spacex, spacey, spacex+size, spacey+size,
                        width=2, tags = "asteriod", outline = "white"))
                self.speed.append((rnd(1,4),rnd(1,4))) # Store random speed x, y
    
        def asteriod_update(self): # MAIN DRIVER: Work on ALL asteroids
            for i, a in enumerate(self.asteroids):
                xx, yy = self.speed[i] # get speed data
                x, y, w, h = self.canvas.coords(a)
                # check for boundary hit then change direction and speed
                if x < 0 or w > self.wide:
                    xx = -xx * rnd(1, 4)
                if y < 0 or h > self.high:
                    yy = -yy * rnd(1, 4)
                # Limit max and min speed then store it
                self.speed[i] = (max( -4, min( xx, 4)), max( -4, min( yy, 4 )))
                self.canvas.move(a, xx, yy) # update asteroid position
    
        def move_active(self):
            if self.active:
                self.asteriod_update()
                self.window.after(40, self.move_active)
    
        def canvas_display(self):
            self.canvas = Canvas(
                self.window, width = self.wide,
                height = self.high, background = "black")
            self.canvas.pack(expand = True, fill = "both")
    
        def run(self): # Begin asteroids here so that mainloop is executed
            self.window.after(200, self.move_active)
            self.window.mainloop()
    
    if __name__ == "__main__":
        SpaceF = SpaceField()
        SpaceF.run()