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()