Home > OS >  Why does this collision not work properly?
Why does this collision not work properly?

Time:06-11

I am coding a basic ping pong game in python.

I have this code:

from tkinter import *   
from tkinter.ttk import *
from math import sqrt  
import time, random

# creates tkinter window or root window   
Window = Tk()  
HEIGHT = 700  
WIDTH = 700  
c = Canvas(Window, width = WIDTH, height = HEIGHT)  
c.pack()   
c.configure(bg='black')  


#Define mid x and y  

MID_X = WIDTH / 2  

MID_Y = HEIGHT / 2  

#Create paddles and ball   
paddle_1 = c.create_rectangle(0, 0, 100, 20, fill='grey', outline = 'white')    
paddle_2 = c.create_rectangle(0, 0, 100, 20, fill='grey', outline = 'white')   
ball = c.create_oval(0, 0, 30, 30, fill='white', outline = 'grey')
c.move(paddle_1, 300, 0)
c.move(paddle_2, 300, 680)  
c.move(ball, 330, 340)  
id = [paddle_1, paddle_2, ball]
ball_move_x = 0
ball_move_y = 10
cooldown = 0

#get co-ordinates of object  
def get_coords(i):  
    pos = c.coords(i) 
    x = int(pos[0]   pos[2]/2)  
    y = int(pos[1]   pos[3]/2)
    return x, y

#Bounce (makes goofy physics)
def bounce(x, y):
    x  = random.randint(1,20)/10
    x *= -1
    y  = 1
    y *= -1
    return x, y

#Collision code (uses co-ords and checks if within a certain range)  
def collision(paddle_x, paddle_y, ball_x, ball_y, x = ball_move_x, y = ball_move_y,):  
    if(ball_x in range(paddle_x-50, paddle_x 50, 1) and ball_y in range(paddle_y-30, paddle_y 30, 1)):
        x, y = bounce(ball_move_x, ball_move_y)
        print("collision")
        return x, y
    else:
        return x, y

# Optimised movement functions without cycling through with an if operator  
def move_paddle_1_left(e):
    c.move(paddle_1, -100, 0)  
def move_paddle_1_right(e):  
    c.move(paddle_1,  100, 0)  
def move_paddle_2_left(e):  
    c.move(paddle_2, -100, 0)  
def move_paddle_2_right(e):  
    c.move(paddle_2,  100, 0) 
 
# bind functions to key   
c.bind_all('<KeyPress-a>', move_paddle_1_left)  
c.bind_all('<KeyPress-d>', move_paddle_1_right)   
c.bind_all('<Left>', move_paddle_2_left)   
c.bind_all('<Right>', move_paddle_2_right)   
c.pack()   
#MAIN GAME LOOP  
while True:  
    Window.update()
    paddle_1_x, paddle_1_y = get_coords(id[0])  
    paddle_2_x, paddle_2_y = get_coords(id[1])  
    ball_x, ball_y = get_coords(id[2]) 
    ball_move_x, ball_move_y = collision(paddle_1_x, paddle_1_y, ball_x, ball_y)
    ball_move_x, ball_move_y = collision(paddle_2_x, paddle_2_y, ball_x, ball_y)
    c.move(ball,  ball_move_x,  ball_move_y)
    time.sleep(0.0333333)  
    Window.update()

The problem is that the collision (so far, only bouncing off paddles is implemented) is buggy: it will essentially collide and collide infinitely.

My approach to collision detection and response is that I get the balls middle co-ords and the bat's middle co-ords; if the ball's co-ords are within a range of the bats co-ords I count it as a collision.

Since then I have been having an issue where essentially it will collide infinitely and just spam up and down. Why does this occur, and how can I fix it?

CodePudding user response:

When you detect collision then you use x *= -1 to move in different direction (up) but in next moves you don't use -1 to keep the same direction and it move again down.

You should keep this value as global variable

direction_x = 1
direction_y = 1

and alwasy use

return x*direction_x, y*direction_y

And when you detect collision then change direction

direction_y = -direction_y
direction_x = 1
direction_y = 1


def collision(paddle_x, paddle_y, ball_x, ball_y, x = ball_move_x, y = ball_move_y,):  
    global direction_x
    global direction_y
    
    if (paddle_x-50 <= ball_x <= paddle_x 50) and (paddle_y-30 <= ball_y <= paddle_y 30):
        direction_y = - direction_y
        print("collision")
        
    return x*direction_x, y*direction_y

Full working code with other changes

EDIT:

You forgot () when you calculate center - you have to first add and later divide but without () it first divided and later added.

    x = int((pos[0]   pos[2])/2)  
    y = int((pos[1]   pos[3])/2)

import tkinter as tk  # PEP8: `import *` is not preferred
from math import sqrt  
import time
import random

# --- constants ---

HEIGHT = 700  
WIDTH  = 700  

MID_X = WIDTH / 2  
MID_Y = HEIGHT / 2  

BALL_MOVE_X = 0
BALL_MOVE_Y = 10

# --- functions ---

def get_coords(canvas, item):  
    pos = canvas.coords(item) 
    x = int( (pos[0]   pos[2]) / 2 )  
    y = int( (pos[1]   pos[3]) / 2 )
    return x, y

def collision(paddle_x, paddle_y, ball_x, ball_y, x=BALL_MOVE_X, y=BALL_MOVE_Y):  
    global direction_x
    global direction_y
    
    if (paddle_x-50 <= ball_x <= paddle_x 50) and (paddle_y-30 <= ball_y <= paddle_y 30):
        direction_y = - direction_y
        print("paddle collision")
        
    return x*direction_x, y*direction_y

def move_paddle_1_left(e):
    canvas.move(paddle_1, -100, 0)
    
def move_paddle_1_right(e):  
    canvas.move(paddle_1,  100, 0)
    
def move_paddle_2_left(e):  
    canvas.move(paddle_2, -100, 0)
    
def move_paddle_2_right(e):  
    canvas.move(paddle_2,  100, 0) 

def gameloop():
    # MAIN GAME LOOP  

    paddle_1_x, paddle_1_y = get_coords(canvas, paddle_1)  
    paddle_2_x, paddle_2_y = get_coords(canvas, paddle_2)
    
    ball_x, ball_y = get_coords(canvas, ball)
    
    ball_move_x, ball_move_y = collision(paddle_1_x, paddle_1_y, ball_x, ball_y)
    ball_move_x, ball_move_y = collision(paddle_2_x, paddle_2_y, ball_x, ball_y)
    canvas.move(ball, ball_move_x, ball_move_y)

    window.after(25, gameloop)  # repeat after 25ms
    
# --- main ---

window = tk.Tk()    # PEP8: `lower case name`

canvas = tk.Canvas(window, width=WIDTH, height=HEIGHT, bg='black')  # PEP8: `=` without spaces inside `( )`
canvas.pack()   

paddle_1 = canvas.create_rectangle(0, 0, 100, 20, fill='grey', outline='white')    
paddle_2 = canvas.create_rectangle(0, 0, 100, 20, fill='grey', outline='white')   
canvas.move(paddle_1, 300, 0)
canvas.move(paddle_2, 300, 680)  

ball = canvas.create_oval(0, 0, 30, 30, fill='white', outline='grey')
canvas.move(ball, 330, 340)  

ball_move_x = 0
ball_move_y = 10

direction_x = 1
direction_y = 1

cooldown = 0
 
canvas.bind_all('<KeyPress-a>', move_paddle_1_left)  
canvas.bind_all('<KeyPress-d>', move_paddle_1_right)   
canvas.bind_all('<Left>', move_paddle_2_left)   
canvas.bind_all('<Right>', move_paddle_2_right)   

window.after(25, gameloop)  # 25ms = 0.025s = 40 Frames Per Second (FPS)

window.mainloop()

PEP 8 -- Style Guide for Python Code


EDIT:

Version which change direction when touch window's border

import tkinter as tk  # PEP8: `import *` is not preferred
from math import sqrt  
import time
import random

# --- constants ---

HEIGHT = 700  
WIDTH  = 700  

MID_X = WIDTH / 2  
MID_Y = HEIGHT / 2  

BALL_MOVE_X = 5
BALL_MOVE_Y = 10

# --- functions ---

def get_coords(canvas, item):  
    pos = canvas.coords(item) 
    x = int((pos[0]   pos[2])/2)  
    y = int((pos[1]   pos[3])/2)
    return x, y

def collision(paddle_x, paddle_y, ball_x, ball_y, x=BALL_MOVE_X, y=BALL_MOVE_Y):  
    global direction_x
    global direction_y
    
    if (paddle_x-50 <= ball_x <= paddle_x 50) and (paddle_y-30 <= ball_y <= paddle_y 30):
        direction_y = - direction_y
        print("paddle collision")
        #return x, y
    
    return x, y

def move_paddle_1_left(e):
    canvas.move(paddle_1, -100, 0)
    
def move_paddle_1_right(e):  
    canvas.move(paddle_1,  100, 0)
    
def move_paddle_2_left(e):  
    canvas.move(paddle_2, -100, 0)
    
def move_paddle_2_right(e):  
    canvas.move(paddle_2,  100, 0) 

def gameloop():
    # MAIN GAME LOOP  
    global direction_x
    global direction_y
 
    paddle_1_x, paddle_1_y = get_coords(canvas, paddle_1)  
    paddle_2_x, paddle_2_y = get_coords(canvas, paddle_2)
    
    ball_x, ball_y = get_coords(canvas, ball)

    if ball_x <= 0 or ball_x >= WIDTH:
        direction_x = - direction_x

    if ball_y <= 0 or ball_y >= HEIGHT:
        print("Get point")        
        direction_y = - direction_y
        
    ball_move_x, ball_move_y = collision(paddle_1_x, paddle_1_y, ball_x, ball_y)
    ball_move_x, ball_move_y = collision(paddle_2_x, paddle_2_y, ball_x, ball_y)
        
    canvas.move(ball, ball_move_x*direction_x, ball_move_y*direction_y)

    window.after(25, gameloop)  # repeat after 25ms
    
# --- main ---


window = tk.Tk()    # PEP8: `lower case name`

canvas = tk.Canvas(window, width=WIDTH, height=HEIGHT, bg='black')  # PEP8: `=` without spaces inside `( )`
canvas.pack()   

paddle_1 = canvas.create_rectangle(0, 0, 100, 20, fill='grey', outline='white')    
paddle_2 = canvas.create_rectangle(0, 0, 100, 20, fill='grey', outline='white')   
canvas.move(paddle_1, 300, 0)
canvas.move(paddle_2, 300, 680)  

ball = canvas.create_oval(0, 0, 30, 30, fill='white', outline='grey')
canvas.move(ball, 330, 340)  

ball_move_x = 0
ball_move_y = 10

direction_x = 1
direction_y = 1

cooldown = 0
 
canvas.bind_all('<KeyPress-a>', move_paddle_1_left)  
canvas.bind_all('<KeyPress-d>', move_paddle_1_right)   
canvas.bind_all('<Left>', move_paddle_2_left)   
canvas.bind_all('<Right>', move_paddle_2_right)   

window.after(25, gameloop)  # 25ms = 0.025s = 40 Frames Per Second (FPS)

window.mainloop()
  • Related