I am currently studying computer science in university doing a foundation year, so I'm new to programming, we are doing a python unit and I'm doing my own project outside of the course content.
I've been trying to make a bouncing ball animation using Tkinter. However I have two balls, red and green, for some reason they don't seem to touch the top or left side of the canvas before the bouncing, and the distance from the edge seems to constantly increase.
from tkinter import *
import random
import time
root = Tk()
def balls():
#speeds
xspeed = 5
yspeed = 3
canvas = Canvas(root, width = 1000, height = 1000, bg="grey")
root.title("collision detection")
canvas.grid()
greenBall = canvas.create_oval(5, 5, 35, 35, fill="green")
redBall = canvas.create_oval(970, 970, 1000, 1000, fill="red")
while True:
#greenball
canvas.move(greenBall, xspeed, yspeed)
posgreen = canvas.coords(greenBall)
if posgreen[3] >= 1000 or posgreen[1] <= 0:
yspeed = -yspeed
if posgreen[2] >= 1000 or posgreen[0] <= 0:
xspeed = -xspeed
#redball
canvas.move(redBall, -xspeed, -yspeed)
posred = canvas.coords(redBall)
if posred[3] >= 1000 or posred[1] <= 0:
yspeed = -yspeed
if posred[2] >= 1000 or posred[0] <= 0:
xspeed = -xspeed
root.update()
time.sleep(0.01)
pass
balls()
root.mainloop()
I have seen youtube videos of people showing how to do this and they seem to code it the same way but dont have this issue.
I made some modifications in you code. Problem was in speed definition, in you code speed is changed globally 2 times instead of each time for each ball.
Here you go:
from tkinter import *
import time
root = Tk()
def balls():
# define speed for each ball
green_x_speed, green_y_speed = [5,3]
red_x_speed, red_y_speed = [5,3]
canvas = Canvas(root, width=800, height=800, bg="grey")
root.title("collision detection")
canvas.grid()
green_ball = canvas.create_oval(20, 20, 30, 10, fill="green")
red_ball = canvas.create_oval(780, 780, 790, 790, fill="red")
while True:
# green ball
canvas.move(green_ball, green_x_speed, green_y_speed)
green_coordinates = canvas.coords(green_ball)
if green_coordinates[3] >= 800 or green_coordinates[1] <= 0:
green_y_speed = -green_y_speed
if green_coordinates[2] >= 800 or green_coordinates[0] <= 0:
green_x_speed = -green_x_speed
# red ball
canvas.move(red_ball, red_x_speed, red_y_speed)
red_coordinates = canvas.coords(red_ball)
if red_coordinates[3] >= 800 or red_coordinates[1] <= 0:
red_y_speed = -red_y_speed
if red_coordinates[2] >= 800 or red_coordinates[0] <= 0:
red_x_speed = -red_x_speed
time.sleep(0.01)
root.update()
balls()
root.mainloop()
Related
I was just wondering how I would make this go horizontal or diagonal? right now, it is bouncing vertically would I need to change any of the variables or the pos?
I have tried to change the y to x as well as change the the horizontal width to height but it isn't working can anyone help, mind you I started coding a few weeks ago so i am a little new to everything
from tkinter import *
import time
import tkinter
from turtle import width
tk = Tk()
tk.title("bouncing ball")
tk.resizable(0,0)
canvas_width= 400
canvas_height =500
ball_size = 25
timer_duration = 20
y_move = 2
def draw():
global y_move
canvas.move(ball1, 0, y_move)
pos = canvas.coords(ball1)
top_y = pos[1]
bottom_y = pos[2]
if top_y <= 0:
y_move = -y_move
elif bottom_y >= canvas_height:
y_move = -y_move
def master_timer():
draw()
tk.update_idletasks()
tk.update()
tk.after(timer_duration, master_timer)
canvas = tkinter.Canvas(tk, width=canvas_width, height=canvas_height, bd=0, highlightthickness= 0)
ball1 = canvas.create_oval(0, 0, ball_size,ball_size,fill="red")
#move ball to center
canvas.move(ball1, canvas_width/2, canvas_width/2)
master_timer()
canvas.pack()
tk.mainloop()
This is my code:
from time import sleep
from tkinter import *
def moveWin(win, velx, vely):
x = win.winfo_x()
y = win.winfo_y()
win.geometry(f"+{str(x + velx)}+{str(y + vely)}")
downx, downy = x+width, y+height
global sWidth
global sHeight
if x <= 0 or downx >= sWidth:
velx = -velx
if y <= 0 or downy >= sHeight:
vely = -vely
return [x, y, downx, downy]
root = Tk()
width = 300
height = 300
velx = 1
vely = 1
sWidth = root.winfo_screenwidth() # gives 1366
sHeight = root.winfo_screenheight() # gives 1080
root.geometry("+250+250")
while True:
root.update()
root.geometry("300x300")
pos = moveWin(root, velx, vely)
print(pos)
sleep(0.01)
I want to bounce back my window when it touches the screen edge but its just going off the screen
whats wrong in my code?
pls help
If you need to modify global variables, then don't pass them as parameters. Instead, add
def movewin(win):
global velx
global vely
at the top of your function.
BIG FOLLOWUP
The more important issue in your app has to do with coordinates. root.winfo_x() and root.winfo_y() do NOT return the upper left corner of your window. Instead, they return the upper left corner of the drawable area, inside the borders and below the title bar. That screws up your drawing, and means you try to position off the bottom of the screen, which Tkinter fixes up.
The solution here is to track the x and y positions yourself, instead of fetching them from Tk.
Tkinter is mostly garbage. You would be better served by looking at pygame for simple games, or at a real GUI system like Qt or wxPython for applications.
from time import sleep
from tkinter import *
class Window(Tk):
def __init__(self):
Tk.__init__(self)
self.width = 300
self.height = 300
self.velx = 1
self.vely = 1
self.pos = (250,250)
self.geometry(f"{self.width}x{self.height}+{self.pos[0]}+{self.pos[1]}")
def moveWin(self):
x = self.pos[0] + self.velx
y = self.pos[1] + self.vely
downx, downy = x+self.width, y+self.height
sWidth = self.winfo_screenwidth() # gives 1366
sHeight = self.winfo_screenheight() # gives 1080
if x <= 0 or downx >= sWidth:
self.velx = -self.velx
if y <= 0 or downy >= sHeight:
self.vely = -self.vely
self.pos = (x,y)
self.geometry(f"+{x}+{y}")
return [x, y, downx, downy]
root = Window()
while True:
root.update()
pos = root.moveWin()
print(pos)
sleep(0.01)
I write a game in python in which the goal is to bounce the ball off the platform.
Everything works pretty well, but the platform's movement is not that smooth. Could you help me make the platform movement more smooth? If the code isn't too clear, I'm sorry, but I'm new in python
import tkinter as tk
import random
root = tk.Tk()
width = 900
height = 500
canvas = tk.Canvas(root, bg='white', width=width, height=height)
canvas.pack()
x = random.randrange(700)
ball = canvas.create_oval(x+10, 10, x+50, 50, fill='green')
platform_y = height - 20
platform = canvas.create_rectangle(width//2-50, platform_y, width//2+50, platform_y+10, fill='black')
xspeed = 2
yspeed = 2
skore = 0
body = 0
def move_ball():
global xspeed
global yspeed
x1, y1, x2, y2 = canvas.coords(ball)
if x1 <= 0 or x2 >= width:
xspeed = -xspeed
if y1 <= 0:
yspeed = 10
elif y2 == platform_y:
cx = (x1 + x2) // 2
px1, _, px2, _ = canvas.coords(platform)
if px1 <= cx <= px2:
yspeed = -10
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)
if x2 < width:
dx = min(width-x2, 10)
canvas.move(platform, dx, 0)
def board_left(event):
x1, y1, x2, y2 = canvas.coords(platform)
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()
The problem is that the speed of the platform is dependent on the auto-repeat speed of your keyboard.
Instead of moving once for each <Right> or <Left> event, use a key press to start the platform moving in the desired direction and a key release to stop the platform moving. Then, use after to repeatedly move the platform in the given direction.
Example:
after_id = None
def platform_move(direction):
"""
direction should be -1 to move left, +1 to move right,
or 0 to stop moving
"""
global after_id
speed = 10
if direction == 0:
canvas.after_cancel(after_id)
after_id = None
else:
canvas.move(platform, direction*speed, 0)
after_id = canvas.after(5, platform_move, direction)
canvas.bind_all("<KeyPress-Right>", lambda event: platform_move(1))
canvas.bind_all("<KeyRelease-Right>", lambda event: platform_move(0))
canvas.bind_all("<KeyPress-Left>", lambda event: platform_move(-1))
canvas.bind_all("<KeyRelease-Left>", lambda event: platform_move(0))
The above code doesn't handle the case where you might press both keys at the same time, but that can be handled with a little additional logic. The point is to show how you can use the keys to start and stop an animation.
My question is about displaying and updating text, in order to display the score on screen.
I would like to create a score like the real game that would appear on the screen. But after researching Google, I have not found anyone wishing to increase a score on the screen ...
Indeed, I would like the score to increase each time the bird passes between the pipes and therefore whenever the pipes have an X of 67 pixels. So does anyone know how to do this?
from tkinter import *
import random
from random import randint
def sauter(event):
canvas.move(image_oiseau, 0, -10*DY)
def deplacement():
global tuyx,tuyx2,h,H,oisx,oisy,solx,sol2x
x0, y0, x1, y1 = canvas.bbox(image_oiseau)
if y1 < 416:
canvas.move(image_oiseau, 0, DY)
canvas.coords(image_sol,solx,512)
if solx >= -144:
solx=solx-5
else:
solx=144
canvas.coords(image_sol2,sol2x,512)
if sol2x >= 144:
sol2x=sol2x-5
else:
sol2x=432
canvas.coords(image_tuyau_haut,tuyx,h)
canvas.coords(image_tuyau_bas,tuyx,h-241)
if tuyx>=-28:
tuyx=tuyx-5
else:
tuyx=316
h=randint(256,505)
canvas.coords(image_tuyau_haut2,tuyx2,H)
canvas.coords(image_tuyau_bas2,tuyx2,H-241)
if tuyx2>=-28:
tuyx2=tuyx2-5
else:
tuyx2=316
H=randint(256,505)
canvas.after(40,deplacement)
LARGEUR = 286
HAUTEUR = 510
DY = 5
tuyx=316
tuyx2=488
h=randint(256,505)
H=randint(256,505)
oisx=67
oisy=244
solx=144
sol2x=432
fenetre = Tk()
canvas = Canvas(fenetre, width=LARGEUR, height=HAUTEUR)
fond = PhotoImage(file="background-day.png")
fond2 = PhotoImage(file="background-night.png")
fond=[fond,fond2]
F= random.choice(fond)
canvas.create_image(144,256, anchor=CENTER,image=F)
tuyau_haut = PhotoImage(file="tuyau_vers_le_haut.png")
image_tuyau_haut = canvas.create_image(tuyx,h,anchor=CENTER,image=tuyau_haut)
image_tuyau_haut2 = canvas.create_image(tuyx2,H,anchor=CENTER,image=tuyau_haut)
tuyau_bas = PhotoImage(file="tuyau_vers_le_bas.png")
image_tuyau_bas = canvas.create_image(tuyx,h,anchor=CENTER,image=tuyau_bas)
image_tuyau_bas2 = canvas.create_image(tuyx2,H,anchor=CENTER,image=tuyau_bas)
sol = PhotoImage(file="sol-day.png")
image_sol = canvas.create_image(144,512, anchor=S,image=sol)
image_sol2 = canvas.create_image(432,512, anchor=S,image=sol)
oiseau = PhotoImage(file="yellowbird-midflap.png")
oiseau2 = PhotoImage(file="bluebird-midflap.png")
oiseau3 = PhotoImage(file="redbird-midflap.png")
oiseau=[oiseau,oiseau2,oiseau3]
O=random.choice(oiseau)
image_oiseau=canvas.create_image(oisx,oisy, anchor=W,image=O)
deplacement()
canvas.pack()
canvas.focus_set()
canvas.bind("<space>",sauter)
fenetre.mainloop()
Could someone explain the problem to me because I thought it would be easy :(
Here are the pictures of the game :)
Here are the pictures of the game
Here is one approach to display the scores: It uses a tk.Label, that is updated at the same time the score increases.
The trigger that increases the score is currently a random call to on_change; you can modify this to be a test if a pipe x coordinates becomes lower than the bird x coordinates (the bird successfully crossed the obstacle)
You can, if you want relocate the score label on the canvas.
import random
import tkinter as tk
WIDTH, HEIGHT = 500, 500
def create_pipes():
pipes = []
for x in range(0, WIDTH, 40):
y1 = random.randrange(50, HEIGHT - 50)
y0 = y1 + 50
pipes.append(canvas.create_line(x, 0, x, y1))
pipes.append(canvas.create_line(x, y0, x, HEIGHT))
return pipes
def move_pipes():
for pipe in pipes:
canvas.move(pipe, -2, 0)
x, y0, _, y1 = canvas.coords(pipe)
if x < 0:
canvas.coords(pipe, WIDTH+20, y0, WIDTH+20, y1)
if random.randrange(0, 20) == 10:
on_change()
root.after(40, move_pipes)
def on_change():
global score
score += 1
score_variable.set(f'score: {score}')
root = tk.Tk()
tk.Button(root, text='start', command=move_pipes).pack()
score = 0
score_variable = tk.StringVar(root, f'score: {score}')
score_lbl = tk.Label(root, textvariable=score_variable)
score_lbl.pack()
canvas = tk.Canvas(root, width=WIDTH, height=HEIGHT, bg="cyan")
canvas.pack()
pipes = create_pipes()
root.mainloop()
I am trying to move the green object called char relative to the mouse x and mouse y coordinates but I don't know how. Can anyone help me? In case you are wondering i am trying to make a version of single player agario.
from tkinter import *
import random
from random import uniform, randrange
import time
#left,top,right,bottom
tk = Tk()
canvas = Canvas(tk,width=600,height=600)
canvas.pack()
pointcount = 0
char = canvas.create_oval(400,400,440,440,fill="green")
pos1 = canvas.coords(char)
x = canvas.canvasx()
y = canvas.canvasy()
class Ball:#ball characteristics#
def __init__(self,color,size):
self.shape = canvas.create_oval(10,10,50,50,fill=color)
self.xspeed = randrange(-5,7)
self.yspeed = randrange(-5,7)
def move(self):#ball animation#
global pointcount
canvas.move(self.shape,self.xspeed,self.yspeed)
pos = canvas.coords(self.shape)
if pos[0] <= 0 or pos[2] >= 600:#if ball hits the wall#
self.xspeed = -self.xspeed
if pos[1] <= 0 or pos[3] >= 600:
self.yspeed = -self.yspeed
left_var = abs(pos[0] - pos1[0])
top_var = abs(pos[1] - pos1[1])
if left_var == 0 and top_var == 0:
pointcount = pointcount + 1
print(pointcount)
colors = ["red","blue","green","yellow","purple","orange"]
balls = []
for i in range(10):
balls.append(Ball(random.choice(colors),50))
while True:
for ball in balls:
ball.move()
tk.update()
time.sleep(0.01)
To move the green circle with the mouse, you need to bind the position of the circle to mouse motion event. Here is an example where the circle is always centered on the mouse when the mouse is in the canvas:
from tkinter import *
root = Tk()
canvas = Canvas(root)
canvas.pack(fill="both", expand=True)
char = canvas.create_oval(400,400,440,440,fill="green")
def follow_mouse(event):
""" the center of char follows the mouse """
x, y = event.x, event.y
canvas.coords(char, x - 20, y - 20, x + 20, y + 20)
# bind follow_mouse function to mouse motion events
canvas.bind('<Motion>', follow_mouse)
root.mainloop()