I'm making a basic game. I want the ball to bounce back up ONLY when it hits the platform. So far, I've written code that will make the ball bounce off the top and bottom screen, but I'm having trouble with getting the ball to bounce off the platform.
from tkinter import *
import time
import tkinter
tk = Tk()
canvas = Canvas(tk, bg="white",width=(900),height=(500))
canvas.pack()
platform = canvas.create_rectangle(400,400,500,410)
def ball():
xspeed = 2
yspeed = 2
ball = canvas.create_oval(430,10,470,50)
while True:
canvas.move(ball, xspeed, yspeed)
pos = canvas.coords(ball)
if pos[2] >=900 or pos[0] <0:
xspeed = -xspeed
tk.update()
time.sleep(0.01)
def board():
board_right()
board_left()
def board_right(event):
xspeed = 5
yspeed = 0
canvas.move(platform,xspeed,yspeed)
tk.update
time.sleep(0.01)
def board_left(event):
xspeed = 5
yspeed = 0
canvas.move(platform,-xspeed,yspeed)
tk.update()
time.sleep(0.01)
canvas.bind_all("<Right>",board_right)
canvas.bind_all("<Left>",board_left)
ball()
tk.mainloop()
Do not use time.sleep() as it will block the tkinter mainloop, use after() instead.
To check whether the ball hits the platform, you need to get the center x of the ball and the lower y of the ball. If center x is within the left and right of platform and the ball lower y is the platform upper y, then reverse the ball y speed. Otherwise game over!
Below is sample code based on yours:
import tkinter as tk
root = tk.Tk()
width = 900
height = 500
canvas = tk.Canvas(root, bg='white', width=width, height=height)
canvas.pack()
ball = canvas.create_oval(430, 10, 470, 50, fill='green')
platform_y = height - 20
platform = canvas.create_rectangle(width//2-50, platform_y, width//2+50, platform_y+10, fill='black')
# ball moving speed
xspeed = yspeed = 2
def move_ball():
global xspeed, yspeed
x1, y1, x2, y2 = canvas.coords(ball)
if x1 <= 0 or x2 >= width:
# hit wall, reverse x speed
xspeed = -xspeed
if y1 <= 0:
# hit top wall
yspeed = 2
elif y2 >= platform_y:
# calculate center x of the ball
cx = (x1 + x2) // 2
# check whether platform is hit
px1, _, px2, _ = canvas.coords(platform)
if px1 <= cx <= px2:
yspeed = -2
else:
canvas.create_text(width//2, height//2, text='Game Over', font=('Arial Bold', 32), fill='red')
return
canvas.move(ball, xspeed, yspeed)
canvas.after(20, move_ball)
def board_right(event):
x1, y1, x2, y2 = canvas.coords(platform)
# make sure the platform is not moved beyond right wall
if x2 < width:
dx = min(width-x2, 10)
canvas.move(platform, dx, 0)
def board_left(event):
x1, y1, x2, y2 = canvas.coords(platform)
# make sure the platform is not moved beyond left wall
if x1 > 0:
dx = min(x1, 10)
canvas.move(platform, -dx, 0)
canvas.bind_all('<Right>', board_right)
canvas.bind_all('<Left>', board_left)
move_ball()
root.mainloop()
Related
I am writing a simple program in which I want the (ball image png) to bounce off the walls. So far, I have writen this code:
import tkinter as tk
root = tk.Tk()
WIDTH = 500
HEIGHT = 500
canvas = tk.Canvas(root,bg="white",width=WIDTH,height=HEIGHT)
canvas.pack()
img = tk.PhotoImage(file="images/ball1.png")
ball = canvas.create_image(0,0,anchor="nw",image=img)
yspeed = 2
xspeed = 2
def move_ball():
global xspeed,yspeed,ball
canvas.move(ball,xspeed,yspeed)
canvas.after(10,move_ball)
move_ball()
root.mainloop()
You can get the current position with the coords method, then code in a check for each of the 4 walls. Here's the first one:
def move_ball():
global xspeed,yspeed,ball
xpos, ypos = canvas.coords(ball)
if xpos + width_of_ball > WIDTH:
# ball hit the right edge, reverse x direction
xspeed *= -1
canvas.move(ball,xspeed,yspeed)
canvas.after(10,move_ball)
This answer is the same thing as #Novel answer (even though I wrote it before I saw theirs). The only difference is in the fact that the logic of update doesn't expect you to make any edits for it to work, it considers both horizontal and vertical directions, and resizing the main window is compensated for.
import tkinter as tk
root = tk.Tk()
root.title('Infinite Bounce Simulator')
root.geometry('400x300+300+300')
xspeed = 4
yspeed = 3
canvas = tk.Canvas(root, highlightthickness=0, bg='#111')
canvas.pack(expand=True, fill='both')
ball = canvas.create_oval((0, 0, 20, 20), fill='red')
def update():
global xspeed, yspeed, ball
canvas.move(ball, xspeed, yspeed)
#Left, Top, Right, Bottom coordinates
l, t, r, b = canvas.coords(ball)
#flip speeds when edges are reached
if r > canvas.winfo_width() or l < 0:
xspeed = -xspeed
if b > canvas.winfo_height() or t < 0:
yspeed = -yspeed
#do it all again in 10 milliseconds
root.after(10, update)
root.after_idle(update)
root.mainloop()
I am using the canvas widget from tkinter to create an ellipse and have it move around in the canvas.
However when the ellipse comes in contact with the border it gets stuck to wall instead of bouncing off.
I'm struggling with debugging the code, thanks in advance!
from tkinter import *
from time import *
import numpy as np
root = Tk()
root.wm_title("Bouncing Ball")
canvas = Canvas(root, width=400, height=400, bg="black")
canvas.grid()
size=10
x = 50
y = 50
myBall = canvas.create_oval(x-size, y-size, x+size, y+size, fill = "red")
while True:
root.update()
root.after(50)
dx = 5
dy = 0
#separating x and y cooridnates from tuple of canvas.coords
x = canvas.coords(myBall)[0]+10
y = canvas.coords(myBall)[1]+10
coordinates = np.array([x, y], dtype = int)
#Checking boundaries
if coordinates[0]-size <= 0:
dx = -1*dx
if coordinates[0]+size >= 400:
dx = -1*dx
if coordinates[1]-size <= 0:
dy = -1*dy
if coordinates[1]+size >= 400:
dy = -1*dy
print(coordinates) #Used to see what coordinates are doing
canvas.move(myBall, dx, dy) #Move ball by dx and dy
Here is a simple way to organize your bouncing ball program, and get you started with GUI programming:
While loops don't work well with a GUI mainloop; it is also not necessary to call update, the mainloop handles that.
Repeated actions are best handles with root.after.
I extracted the bounce logic inside a function bounce that calls itself using root.after. You will see that I simplified the logic.
I also parametrized the canvas size.
The initial speed components dx and dy are randomly chosen from a list of possible values so the game is not too boring.
Here is how it looks:
import tkinter as tk # <-- avoid star imports
import numpy as np
import random
WIDTH = 400
HEIGHT = 400
initial_speeds = [-6, -5, -4, 4, 5, 6]
dx, dy = 0, 0
while dx == dy:
dx, dy = random.choice(initial_speeds), random.choice(initial_speeds)
def bounce():
global dx, dy
x0, y0, x1, y1 = canvas.coords(my_ball)
if x0 <= 0 or x1 >= WIDTH: # compare to left of ball bounding box on the left wall, and to the right on the right wall
dx = -dx
if y0 <= 0 or y1 >= HEIGHT: # same for top and bottom walls
dy = -dy
canvas.move(my_ball, dx, dy)
root.after(50, bounce)
if __name__ == '__main__':
root = tk.Tk()
root.wm_title("Bouncing Ball")
canvas = tk.Canvas(root, width=400, height=400, bg="black")
canvas.pack(expand=True, fill=tk.BOTH)
size=10
x = 50
y = 50
my_ball = canvas.create_oval(x-size, y-size, x+size, y+size, fill="red")
bounce()
root.mainloop()
It's just basic math. The ball moves left when you subtract some amount from the x coordinate. If it hits the left wall and you want it to bounce to the right, you need to stop subtracting from x and start adding to x. The same is true for the y coordinate.
i am new to programming and am currently trying to get a chemistry stimulation with beaker which produces bubbles. after a lot of research I have managed to get a circle moving and also created a rectangle representing a beaker. however I would like the circles to be formed at the top of the beaker and move upwards in random and destroy itself when it meets the top which I just cant figure out. I would be very grateful if anyone can help me. thank you in advance.
my code is:
from tkinter import *
x = 10
y = 10
a = 50
b = 50
x_vel = 5
y_vel = 5
def move():
global x
global y
global x_vel
global y_vel
if x < 0:
x_vel = 5
if x > 350:
x_vel = -5
if y < 0:
y_vel = 5
if y > 150:
y_vel = -5
canvas1.move(circle, x_vel, y_vel)
coordinates = canvas1.coords(circle)
x = coordinates[0]
y = coordinates[1]
window.after(33, move)
window = Tk()
window.geometry("1000x1000")
canvas1=Canvas(window, height = 1000, width= 1000)
canvas1.grid (row=0, column=0, sticky=W)
coord = [x, y, a, b ]
circle = canvas1.create_oval(coord, outline="red", fill="red")
coord = [230, 270, 270, 310]
rect2 = canvas1.create_rectangle(coord, outline="Blue", fill="Blue")
move()
window.mainloop ()
Using random you can move it randomly.
If y < -height then object left screen and you can move it to start position.
import tkinter as tk
import random
def move():
global x
global y
global x_vel
global y_vel
# get random move
x_vel = random.randint(-5, 5)
y_vel = -5
canvas1.move(circle, x_vel, y_vel)
coordinates = canvas1.coords(circle)
x = coordinates[0]
y = coordinates[1]
# if outside screen move to start position
if y < -height:
x = start_x
y = start_y
canvas1.coords(circle, x, y, x+width, y+height)
window.after(33, move)
# --- main ---
start_x = 230
start_y = 270
x = start_x
y = start_y
width = 50
height = 50
x_vel = 5
y_vel = 5
window = tk.Tk()
window.geometry("1000x1000")
canvas1 = tk.Canvas(window, height=1000, width=1000)
canvas1.grid(row=0, column=0, sticky='w')
coord = [x, y, x+width, y+height]
circle = canvas1.create_oval(coord, outline="red", fill="red")
coord = [x, y, x+40, y+40]
rect2 = canvas1.create_rectangle(coord, outline="Blue", fill="Blue")
move()
window.mainloop ()
EDIT: using class you can easily have many items
import tkinter as tk
import random
class Bubble():
def __init__(self, canvas, x, y, size, color='red'):
self.canvas = canvas
self.x = x
self.y = y
self.start_x = x
self.start_y = y
self.size = size
self.color = color
self.circle = canvas.create_oval([x, y, x+size, y+size], outline=color, fill=color)
def move(self):
x_vel = random.randint(-5, 5)
y_vel = -5
self.canvas.move(self.circle, x_vel, y_vel)
coordinates = self.canvas.coords(self.circle)
self.x = coordinates[0]
self.y = coordinates[1]
# if outside screen move to start position
if self.y < -self.size:
self.x = self.start_x
self.y = self.start_y
self.canvas.coords(self.circle, self.x, self.y, self.x + self.size, self.y + self.size)
def move():
for item in bubbles:
item.move()
window.after(33, move)
# --- main ---
start_x = 230
start_y = 270
window = tk.Tk()
window.geometry("1000x1000")
canvas = tk.Canvas(window, height=1000, width=1000)
canvas.grid(row=0, column=0, sticky='w')
bubbles = []
for i in range(5):
offset = random.randint(10, 20)
b = Bubble(canvas, start_x+10, start_y-offset, 20, 'red')
bubbles.append(b)
for i in range(5):
offset = random.randint(0, 10)
b = Bubble(canvas, start_x+10, start_y-offset, 20, 'green')
bubbles.append(b)
coord = [start_x, start_y, start_x+40, start_y+40]
rect = canvas.create_rectangle(coord, outline="Blue", fill="Blue")
move()
window.mainloop ()
I have this program that I tried to make in python tkinter. A ball would appear on screen and every time I click I want the ball to glide to the point at which I clicked. The x and y locations of the ball changed but the ball only redraws after the ball is finished "moving." Can someone tell me what I am doing wrong.
from tkinter import *
import time
width = 1280
height = 700
ballRadius = 10
iterations = 100
mouseLocation = [width/2, height/2]
ballLocation = [width/2, height/2]
root = Tk()
def drawBall(x, y):
canvas.delete(ALL)
canvas.create_oval(x - ballRadius, y - ballRadius, x + ballRadius, y + ballRadius, fill="blue")
print(x, y)
def getBallLocation(event):
mouseLocation[0] = event.x
mouseLocation[1] = event.y
dx = (ballLocation[0] - mouseLocation[0]) / iterations
dy = (ballLocation[1] - mouseLocation[1]) / iterations
for i in range(iterations):
ballLocation[0] -= dx
ballLocation[1] -= dy
drawBall(round(ballLocation[0]), round(ballLocation[1]))
time.sleep(0.02)
ballLocation[0] = event.x
ballLocation[1] = event.y
canvas = Canvas(root, width=width, height=height, bg="black")
canvas.pack()
canvas.create_oval(width/2-ballRadius, height/2-ballRadius, width/2+ballRadius, height/2+ballRadius, fill="blue")
canvas.bind("<Button-1>", getBallLocation)
root.mainloop()
In your code time.sleep pauses the entire GUI, that's why you don't see the intermediate locations of the ball. Instead you can construct a function with widget.after method. Try the following:
print(x, y)
dx = 0
dy = 0
def getBallLocation(event):
canvas.unbind("<Button-1>")
global dx, dy
mouseLocation[0] = event.x
mouseLocation[1] = event.y
dx = (ballLocation[0] - mouseLocation[0]) / iterations
dy = (ballLocation[1] - mouseLocation[1]) / iterations
draw()
i = 0
def draw():
global i
ballLocation[0] -= dx
ballLocation[1] -= dy
drawBall(round(ballLocation[0]), round(ballLocation[1]))
if i < iterations-1:
canvas.after(20, draw)
i += 1
else:
canvas.bind("<Button-1>", getBallLocation)
i = 0
canvas = Canvas(root, width=width, height=height, bg="black")
I am creating a game of Pong using tkinter as a small project. I have written the game and it works fully. However I have encountered a weird bug where upon the game ending, when one player reaches a score of 3, when the game is restarted the velocity of the ball appears to double, and this happens every time the game is reset. the startgame function should set the variable dx (x movement of the ball) to 2 when it is called, so I am not sure why it appears to get faster by 2 everytime, as there are no additions present.
I have posted the whole code block below and am completely lost as to why this happens, any help would be much appreciated!
from tkinter import *
root = Tk()
#size of window
w = 600
h = 400
sw = root.winfo_screenwidth()
sh = root.winfo_screenheight()
x = (sw - w)/2
y = (sh - h)/2
root.geometry('%dx%d+%d+%d' % (w, h, x, y))
#playing frame, with Canvas inside
canvframe = Frame(relief=SUNKEN, width=600, height = 350, background="black")
canvframe.pack()
canv = Canvas(canvframe, background="black", width=600, height = 350)
canv.pack(fill=NONE)
# Objects in canvas
ball = canv.create_oval(300,160,310,170, outline="white", fill="white", width=2, tags=('ball'))
paddle1 = canv.create_rectangle(0,200,10,120, outline="white", fill="white", width=2, tags=('paddle1'))
paddle2 = canv.create_rectangle(590,200,600,120, outline="white", fill="white", width=2)
#Paddle movement
def moveUp1(event):
canv.move(paddle1, 0, -10)
pass
def moveUp2(event):
canv.move(paddle2, 0, -10)
pass
def moveDown1(event):
canv.move(paddle1, 0, 10)
pass
def moveDown2(event):
canv.move(paddle2, 0, 10)
pass
#InitialVelocity
dx = 0
dy = 0
#initial score
Player1score = 0
Player2score = 0
#start game - what happens when you push start button (reset velocity and scores)
def startgame():
global dy, dx, Player1score, Player2score
canv.coords(paddle1, 0,200,10,120)
canv.coords(paddle2, 590,200,600,120)
dx = 2
dy = 0
Player1score = 0
Player2score = 0
Player1scoreLabel.configure(text="Score: "+ str(Player1score))
Player2scoreLabel.configure(text="Score: "+ str(Player2score))
moveBall()
#Ball Movement
def moveBall():
global dy, dx, Player1score, Player2score
# to make ball bounce off paddle 1
if canv.coords(ball)[0]<=canv.coords(paddle1)[2] and canv.coords(paddle1)[1]<= canv.coords(ball)[1] <= canv.coords(paddle1)[3]:
dx = -dx
if canv.coords(paddle1)[1] <= canv.coords(ball)[1] <= (int((canv.coords(paddle1)[1] + canv.coords(paddle1)[3])) / 2 ):
dy -=1
canv.move(ball, dx, dy)
elif (int(canv.coords(paddle1)[1] + canv.coords(paddle1)[3]) / 2 ) <= canv.coords(ball)[3] <= canv.coords(paddle1)[3]:
dy += 1
canv.move(ball, dx, dy)
else:
canv.move(ball, dx, dy)
# to make ball bounce off paddle 2
elif canv.coords(ball)[2]>=canv.coords(paddle2)[0] and canv.coords(paddle2)[1]<= canv.coords(ball)[3] <= canv.coords(paddle2)[3]:
dx = -dx
if canv.coords(paddle2)[1] <= canv.coords(ball)[1] <= (int((canv.coords(paddle2)[1] + canv.coords(paddle2)[3])) / 2 ):
dy -= 1
canv.move(ball, dx, dy)
elif (int(canv.coords(paddle2)[1] + canv.coords(paddle2)[3])/ 2 ) <= canv.coords(ball)[3] <= canv.coords(paddle2)[3]:
dy += 1
canv.move(ball, dx, dy)
else:
canv.move(ball, dx, dy)
# to make ball bounce off roof
elif canv.coords(ball)[1]<=0:
dy = -dy
canv.move(ball, dx, dy)
# to mkae ball bounce of floor
elif canv.coords(ball)[1]>=325:
dy = -dy
canv.move(ball, dx, dy)
# if player 2 scores
elif canv.coords(ball)[2]<=0:
Player2score += 1
Player2scoreLabel.configure(text="Score: "+ str(Player2score))
canv.coords(ball, 300,160,310,170)
canv.coords(paddle1, 0,200,10,120)
canv.coords(paddle2, 590,200,600,120)
dx=2
dy=0
# if player1 scores
elif canv.coords(ball)[0]>=600:
Player1score += 1
Player1scoreLabel.configure(text="Score: "+ str(Player1score))
canv.coords(ball, 300,160,310,170)
canv.coords(paddle1, 0,200,10,120)
canv.coords(paddle2, 590,200,600,120)
dx=-2
dy=0
# end game if player 1 wins
elif Player1score==3:
dx=0
dy=0
# end game if player 2 wins
elif Player2score==3:
dx=0
dy=0
# move ball if nothign happens
else:
canv.move(ball, dx, dy)
canv.after(10, moveBall)
#buttons
Butframe = Frame(relief=SUNKEN, width=200, height = 150, background="white")
Butframe.pack()
startButton = Button(Butframe, text="Start", command = startgame)
startButton.pack(side=LEFT)
quitButton = Button(Butframe, text="Quit", command=root.destroy)
quitButton.pack(side=LEFT)
#scores
Score1frame = Frame(relief=SUNKEN, width=200, height = 150, background="white")
Score1frame.pack(side=LEFT)
Player1scoreLabel = Label(Score1frame, text="Score: "+ str(Player1score), background="green")
Player1scoreLabel.pack()
Score2frame = Frame(relief=SUNKEN, width=200, height = 150, background="white")
Score2frame.pack(side=RIGHT)
Player2scoreLabel = Label(Score2frame, text="Score: "+ str(Player2score), background="green")
Player2scoreLabel.pack()
#binding of movement keys
root.bind("<Up>", moveUp2)
root.bind("<w>", moveUp1)
root.bind("<Down>", moveDown2)
root.bind("<s>", moveDown1)
root.mainloop()
Your apparent speed increase is due to overlapping calls to the .after() method.
When one of the players wins, you should stop the loop in moveBall(), for example using a global variable called gameover like below:
# Initialize gameover variable
gameover = False
# (...) (Reset gameover to False in startgame() as well)
def moveBall():
global dy, dx, Player1score, Player2score, gameover
# (...)
# end game if player 1 or 2 wins
elif Player1score==3 or Player2score==3:
dx=0
dy=0
gameover = True
# move ball if nothing happens
else:
canv.move(ball, dx, dy)
# Repeat until game is over
if not gameover:
canv.after(10, moveBall)