Object Following X Coords of Mouse Position Tkinter - python

I am trying to make pong, I want my paddle to follow the x position of the mouse. I just had the x position of the mouse assigned to a variable and it would add to itself every time I moved the mouse then just go off the screen. I now changed it a little bit but I just cannot get it to work
ERRORS:
Traceback (most recent call last):
File "Tkinter.py", line 1536, in __call__
return self.func(*args)
File "animationTest.py", line 51, in motion
self.diff = self.x - canvas.coords(self.paddle)
TypeError: unsupported operand type(s) for -: 'int' and 'list'
CODE:
from Tkinter import *
import time
HEIGHT = 500
WIDTH = 800
COLOR = 'blue'
SIZE = 50
root = Tk()
canvas = Canvas(root, width=WIDTH, height=HEIGHT, bg=COLOR)
canvas.pack()
class Ball:
def __init__(self, canvas):
self.ball = canvas.create_oval(0, 0, SIZE, SIZE, fill='black')
self.speedx = 6
self.speedy = 6
self.active = True
self.move_active()
def ball_update(self):
canvas.move(self.ball, self.speedx, self.speedy)
pos = canvas.coords(self.ball)
if pos[2] >= WIDTH or pos[0] <= 0:
self.speedx *= -1
if pos[3] >= HEIGHT or pos[1] <= 0:
self.speedy *= -1
def move_active(self):
if self.active:
self.ball_update()
root.after(1, self.move_active)
class Paddle:
def __init__(self, canvas):
self.paddle = canvas.create_rectangle(0,0,100,10, fill='red')
canvas.bind('<Motion>', self.motion)
self.active = True
self.move_active
def motion(self, event):
self.x = event.x
self.diff = self.x - canvas.coords(self.paddle)
print('the diff is:' ,self.diff)
print('the click is at: {}'.format(self.x))
def move_active(self):
if self.active:
self.motion()
root.after(1, self.move_active)
run = Ball(canvas)
run2 = Paddle(canvas)
root.mainloop()

There's no reason to read the current coordinates. You can use the event.x to calculate the new coordinates without knowing what the current coordinates are.
def motion(self, event):
'''update paddle coordinates using current mouse position'''
canvas.coords(self.paddle, event.x-50, 0, event.x+50, 10)
This simply overrides the coordinates 0,0,100,10 that you set in the __init__ method with new ones based on the mouse position.

Related

Pong Game From Python for absolute beginners

i am running into two error going two routes the ball is find works great but the score text wont work, every time i implement it it throws the two errors.
from superwires import games, color
from random import *
"""Main application file"""
#the screen width and height
SCREEN_WIDTH = 640
SCREEN_HEIGHT = 480
points = 0
score = 0
"""Paddle object that is controlled by the player. Moves up and down. Collides with the ball. Points are added when ball is collided with"""
class Paddle(games.Sprite):
HEIGHT = 480/20
paddle_image = games.load_image('paddle.png', transparent=True)
def __init__(self):
super(Paddle, self).__init__(image = Paddle.paddle_image,
x = 40,
y = 50,
is_collideable=True)
self.score = games.Text(value = 0,
size = 30,
color = color.green,
x = SCREEN_WIDTH - 25,
y = SCREEN_HEIGHT - 431)
games.screen.add(self.score)
games.screen.mainloop()
""" updates Position based on mouse Position"""
def update(self):
self.y = games.mouse.y
self.Check_Collision()
def Check_Collision(self):
for PongBall in self.overlapping_sprites:
PongBall.handle_collide()
"""actual ball in play """
class PongBall(games.Sprite):
#velocity variables
vel_x = 5
vel_y = 0
#pos of boundry to ball pos
lower_bounds_check = 0
upper_bounds_check = 0
#checks if paddle gets collided with
def handle_collide(self):
if((SCREEN_WIDTH/2) < self.x):
PongBall.vel_x = randint(-10,-5)
PongBall.vel_y = randint(-10,-5)
elif((SCREEN_WIDTH/2) > self.x):
PongBall.vel_x = randint(5,10)
PongBall.vel_y = randint(5,10)
#checks if it hit the bottom; bool function
def check_boundry_lower(self):
PongBall.lower_bounds_check = (SCREEN_HEIGHT/2)
return ((self.y - PongBall.upper_bounds_check) > PongBall.lower_bounds_check)
#checks if it hits the top; bool function
def check_boundry_upper(self):
PongBall.upper_bounds_check = (SCREEN_HEIGHT/2)
return ((self.y + PongBall.upper_bounds_check) < PongBall.upper_bounds_check)
#Resets the velocity based on boundry collision
def boundry_update(self):
if(self.check_boundry_upper()):
PongBall.vel_y = randint(5,10)
return
elif(self.check_boundry_lower()):
PongBall.vel_y = randint(-10,-5)
return
def update(self):
self.boundry_update()
self.x += PongBall.vel_x
self.y += PongBall.vel_y
"""
Entry point Function
"""
# Create background, paddle, and ball. Then call the game mainloop.
# crate the window
games.init(SCREEN_WIDTH, SCREEN_HEIGHT, 50, virtual = False)
#sets/loads background
background = games.load_image(f"background.png", transparent=True)
games.screen.background = background
# paddle image
paddle_image = games.load_image(f"paddle.png", transparent = True)
#PingPong ball
PingPongBall = games.load_image(f"Ball.png", transparent = True)
#paddle Right and left
paddle_1 = Paddle()
paddle_2 = Paddle()
#pingball ball instance
ball = PongBall(image = PingPongBall, x = 150, y = 250, is_collideable=True)
#adding all three sprites to screen
games.screen.add(ball)
games.screen.add(paddle_1)
games.screen.add(paddle_2)
#each instance update function
ball.update()
paddle_1.update()
paddle_2.update()
#adding text
#main loop
games.screen.mainloop()
i get this error;
Traceback (most recent call last):
File "C:\Users\gamer\Desktop\PingPong\main.py", line 15, in <module>
class Paddle(games.Sprite):
File "C:\Users\gamer\Desktop\PingPong\main.py", line 17, in Paddle
paddle_image = games.load_image('paddle.png', transparent=True)
File "C:\Users\gamer\AppData\Local\Programs\Python\Python310\lib\site-packages\superwires\games.py", line 836, in load_image
if not screen.virtual:
AttributeError: 'NoneType' object has no attribute 'virtual'
Please help i cant seem to escape the error even if i dont pass through the constructor i get text object doesnt not have handle_collide() attribute
-Thanks guys
new error:
Traceback (most recent call last):
File "C:\Users\gamer\Desktop\PingPong\main.py", line 153, in <module>
games.screen.mainloop()
File "C:\Users\gamer\AppData\Local\Programs\Python\Python310\lib\site-packages\superwires\games.py", line 783, in mainloop
sprite._process_sprite()
File "C:\Users\gamer\AppData\Local\Programs\Python\Python310\lib\site-packages\superwires\games.py", line 440, in _process_sprite
self.update()
File "C:\Users\gamer\Desktop\PingPong\main.py", line 40, in update
self.Check_Collision()
File "C:\Users\gamer\Desktop\PingPong\main.py", line 36, in Check_Collision
PongBall.handle_collide()
AttributeError: 'Text' object has no attribute 'handle_collide'
You need to have the games.load in the __init__ function so as not to call it before the environment is set up.
Replace this:
class Paddle(games.Sprite):
HEIGHT = 480/20
paddle_image = games.load_image('paddle.png', transparent=True)
def __init__(self):
super(Paddle, self).__init__(image = Paddle.paddle_image,
x = 40,
y = 50,
is_collideable=True)
with this:
class Paddle(games.Sprite):
HEIGHT = 480/20
def __init__(self):
self.paddle_image = games.load_image('paddle.png', transparent=True)
super(Paddle, self).__init__(image = self.paddle_image,
x = 40,
y = 50,
is_collideable=True)

How do I make the square move when a button is pressed on the keyboard? Tkinter, Python

How do I make the square move when pressing the "d" button (for example) on the keyboard?
from tkinter import *
root = Tk()
root.title('Snake')
root["width"] = 400
root["height"] = 400
field = Canvas(root)
x = 0
y = 0
def snake(x, y):
field.create_rectangle(10, 20, 30, 40)
field.grid(row=x, column=y)
x += 1
y += 1
return(x, y)
root.bind("<KeyPress>", snake(x=x, y=y))
root.mainloop()
An easy way is to use event.char. It returns the character of the button pressed. Then check which button it was and move it if it is w,a,s,d -
from tkinter import *
root = Tk()
root.title('Snake')
root["width"] = 400
root["height"] = 400
field = Canvas(root)
rect = field.create_rectangle(10, 20, 30, 40)
field.grid(row=0, column=0)
def snake(event):
x = 0 # Default
y = 0
if event.char == 'w':
y = -10
if event.char == 'a':
x = -10
if event.char == 's':
y = 10
if event.char == 'd':
x = 10
field.move(rect,x,y)
root.bind("<Key>", snake)
root.mainloop()
This is one way you can do it:
import tkinter as tk
from tkinter import Canvas
root = tk.Tk()
root.title('Snake')
root.geometry("450x450")
w = 400
h = 400
x = w//2
y = h//2
field = Canvas(root, width=w, heigh=h, bg="white")
field.pack(pady=5)
my_rectangle = field.create_rectangle(10, 20, 30, 40)
def left(event):
x = -10
y = 0
field.move(my_rectangle, x, y)
def right(event):
x = 10
y = 0
field.move(my_rectangle, x, y)
def up(event):
x = 0
y = -10
field.move(my_rectangle, x, y)
def down(event):
x = 0
y = 10
field.move(my_rectangle, x, y)
root.bind("<Left>", left)
root.bind("<Right>", right)
root.bind("<Up>", up)
root.bind("<Down>", down)
root.mainloop()
First create root, and give geometry to it, in this case it is 450x450.
After that create variables that will store height and width, and coordinates x and y. Then, create canvas (field) and in canvas specify where canvas will be located and geometry of canvas (and background color). When canvas is created, we create rectangle. Functions left, right, up and down will cover movement of rectangle on canvas. field.move(my_rectangle, x, y) - this line of code will move my_rectangle on canvas by x and y, or by left, right up or down, depends what is passed. root.bind("<Left>", left) - bind left arrow key to left function. Thats why event is parameter of fucntion.
An alternative method
from tkinter import *
root = Tk()
root.title('Snake')
root["width"] = 400
root["height"] = 400
field = Canvas(root)
SIZE = 50
class Snake:
def __init__(self,x,y,canvas):
self.x = x
self.y = y
self.canvas = canvas
self.direction = (SIZE,0)
def keypress(self,event):
if event.keysym == 'Right':
self.direction = (SIZE,0)
if event.keysym == 'Left':
self.direction = (-SIZE,0)
if event.keysym == 'Up':
self.direction = (0,-SIZE)
if event.keysym == 'Down':
self.direction = (0,SIZE)
def move(self):
self.x += self.direction[0]
self.y += self.direction[1]
def redraw(self):
self.canvas.delete('snake')
self.move()
self.canvas.create_rectangle(self.x,self.y,self.x+SIZE,self.y+SIZE,fill="green",tag="snake")
def update_screen():
snake.redraw()
root.after(1000,update_screen)
snake = Snake(0,0,field)
root.bind("<KeyPress>", lambda event: snake.keypress(event))
root.after_idle(update_screen)
field.grid()
root.mainloop()
This uses an object for the snake with methods to react to key presses and redraw the snake. This deletes the existing rectangles and draws new ones rather than moving (might prove more useful for showing the different snake segments).
Advantage of this method, is that the snake can be made to move regardless of whether the user has pressed a button or not. If no button has been pressed the snake will continue in the same direction.
Once you have a more complex game, I'd expect your Snake class to contain a list of the different snake segments that get drawn each time. When the snake moves over a fruit, a new segment is added. See below for a moving snake with multiple segments
from tkinter import *
root = Tk()
root.title('Snake')
root["width"] = 400
root["height"] = 400
field = Canvas(root)
SIZE = 10
class Snake:
def __init__(self,x,y,canvas):
self.x = x
self.y = y
self.canvas = canvas
self.direction = (SIZE,0)
self.segments = [(self.x,self.y)]
self.length = 5
def keypress(self,event):
if event.keysym == 'Right':
self.direction = (SIZE,0)
if event.keysym == 'Left':
self.direction = (-SIZE,0)
if event.keysym == 'Up':
self.direction = (0,-SIZE)
if event.keysym == 'Down':
self.direction = (0,SIZE)
def move(self):
self.x += self.direction[0]
self.y += self.direction[1]
new_segment = (self.segments[-1][0]+self.direction[0],self.segments[-1][1]+self.direction[1])
self.segments.append(new_segment)
if len(self.segments) > self.length:
self.segments.pop(0)
def redraw(self):
self.canvas.delete('snake')
self.move()
for seg in self.segments:
self.canvas.create_rectangle(seg[0],seg[1],seg[0]+SIZE,seg[1]+SIZE,fill="green",tag="snake")
#self.canvas.create_rectangle(self.x,self.y,self.x+SIZE,self.y+SIZE,fill="green",tag="snake")
def update_screen():
snake.redraw()
root.after(1000,update_screen)
snake = Snake(0,0,field)
root.bind("<KeyPress>", lambda event: snake.keypress(event))
root.after_idle(update_screen)
field.grid()
root.mainloop()

tkinter.TclError: invalid command name ".!canvas"

I'm pretty new to python and to programming in general. I'm trying to make the game Bounce. The game runs as expected but as soon as I close the window, it shows an error.
This is the code:
from tkinter import *
import random
import time
# Creating the window:
window = Tk()
window.title("Bounce")
window.geometry('600x600')
window.resizable(False, False)
# Creating the canvas containing the game:
canvas = Canvas(window, width = 450, height = 450, bg = "black")
canvas.pack(padx = 50, pady= 50)
score = canvas.create_text(10, 20, fill = "white")
window.update()
# Creating the ball:
class Ball:
def __init__(self, canvas1, paddle1, color):
self.canvas = canvas1
self.paddle = paddle1
self.id = canvas1.create_oval(10, 10, 25, 25, fill = color) # The starting point of the ball
self.canvas.move(self.id, 190, 160)
starting_direction = [-3, -2, -1, 0, 1, 2, 3]
random.shuffle(starting_direction)
self.x = starting_direction[0]
self.y = -3
self.canvas_height = self.canvas.winfo_height()
self.canvas_width = self.canvas.winfo_width()
# Detecting the collision between the ball and the paddle:
def hit_paddle(self, ballcoords):
paddle_pos = self.canvas.coords(self.paddle.id)
if ballcoords[0] <= paddle_pos[2] and ballcoords[2] >= paddle_pos[0]:
if paddle_pos[3] >= ballcoords[3] >= paddle_pos[1]:
return True
return False
# Detecting the collision between the the ball and the canvas sides:
def draw(self):
self.canvas.move(self.id, self.x, self.y)
ballcoords = self.canvas.coords(self.id)
if ballcoords[1] <= 0:
self.y = 3
if ballcoords[3] >= self.canvas_height:
self.y = 0
self.x = 0
self.canvas.create_text(225, 150, text = "Game Over!", font = ("Arial", 16), fill = "white")
if ballcoords[0] <= 0:
self.x = 3
if ballcoords[2] >= self.canvas_width:
self.x = -3
if self.hit_paddle(ballcoords):
self.y = -3
class Paddle:
def __init__(self, canvas1, color):
self.canvas1 = canvas
self.id = canvas.create_rectangle(0, 0, 100, 10, fill = color)
self.canvas1.move(self.id, 180, 350)
self.x = 0
self.y = 0
self.canvas1_width = canvas1.winfo_width()
self.canvas1.bind_all("<Left>", self.left)
self.canvas1.bind_all("<Right>", self.right)
def draw(self):
self.canvas1.move(self.id, self.x, 0)
paddlecoords = self.canvas1.coords(self.id)
if paddlecoords[0] <= 0:
self.x = 0
if paddlecoords[2] >= self.canvas1_width:
self.x = 0
def right(self, event):
self.x = 3
def left(self, event):
self.x = -3
paddle = Paddle(canvas, color = "white")
ball = Ball(canvas, paddle, color = "red")
while True:
ball.draw()
paddle.draw()
window.update_idletasks()
window.update()
time.sleep(0.001)
This is the error:
Traceback (most recent call last):
File "D:\CSCI201\Arcade Games Project\Bounce\Bounce_Game.py", line 111, in <module>
ball.draw()
File "D:\CSCI201\Arcade Games Project\Bounce\Bounce_Game.py", line 64, in draw
self.canvas.move(self.id, self.x, self.y)
File "C:\Users\M.Youssry\AppData\Local\Programs\Python\Python39\lib\tkinter\__init__.py", line 2916, in move
self.tk.call((self._w, 'move') + args)
_tkinter.TclError: invalid command name ".!canvas"
I've tried inserting .mainloop() as suggested to another user having the same problem but it hasn't worked for me.
It is caused by close button on top-right corner of window, the only way you have to stop script. After you click close button, window destried, so no widget, like canvas, exist.
You can set a flag to identify if while loop should stop and exit in handler of window close button event.
window.protocol("WM_DELETE_WINDOW", handler)
Here, you can exit script any time by click close button of window.
from tkinter import *
import random
import time
# Creating the window:
window = Tk()
window.title("Bounce")
window.geometry('600x600')
window.resizable(False, False)
# Creating the canvas containing the game:
canvas = Canvas(window, width = 450, height = 450, bg = "black")
canvas.pack(padx = 50, pady= 50)
score = canvas.create_text(10, 20, fill = "white")
window.update()
# Creating the ball:
class Ball:
def __init__(self, canvas1, paddle1, color):
self.canvas = canvas1
self.paddle = paddle1
self.id = canvas1.create_oval(10, 10, 25, 25, fill = color) # The starting point of the ball
self.canvas.move(self.id, 190, 160)
starting_direction = [-3, -2, -1, 0, 1, 2, 3]
random.shuffle(starting_direction)
self.x = starting_direction[0]
self.y = -3
self.canvas_height = self.canvas.winfo_height()
self.canvas_width = self.canvas.winfo_width()
# Detecting the collision between the ball and the paddle:
def hit_paddle(self, ballcoords):
paddle_pos = self.canvas.coords(self.paddle.id)
if ballcoords[0] <= paddle_pos[2] and ballcoords[2] >= paddle_pos[0]:
if paddle_pos[3] >= ballcoords[3] >= paddle_pos[1]:
return True
return False
# Detecting the collision between the the ball and the canvas sides:
def draw(self):
self.canvas.move(self.id, self.x, self.y)
ballcoords = self.canvas.coords(self.id)
if ballcoords[1] <= 0:
self.y = 3
if ballcoords[3] >= self.canvas_height:
self.y = 0
self.x = 0
self.canvas.create_text(225, 150, text = "Game Over!", font = ("Arial", 16), fill = "white")
if ballcoords[0] <= 0:
self.x = 3
if ballcoords[2] >= self.canvas_width:
self.x = -3
if self.hit_paddle(ballcoords):
self.y = -3
class Paddle:
def __init__(self, canvas1, color):
self.canvas1 = canvas
self.id = canvas.create_rectangle(0, 0, 100, 10, fill = color)
self.canvas1.move(self.id, 180, 350)
self.x = 0
self.y = 0
self.canvas1_width = canvas1.winfo_width()
self.canvas1.bind_all("<Left>", self.left)
self.canvas1.bind_all("<Right>", self.right)
def draw(self):
self.canvas1.move(self.id, self.x, 0)
paddlecoords = self.canvas1.coords(self.id)
if paddlecoords[0] <= 0:
self.x = 0
if paddlecoords[2] >= self.canvas1_width:
self.x = 0
def right(self, event):
self.x = 3
def left(self, event):
self.x = -3
paddle = Paddle(canvas, color = "white")
ball = Ball(canvas, paddle, color = "red")
# New code after here
def handler():
global run
run = False
window.protocol("WM_DELETE_WINDOW", handler)
run = True
while run:
# New code before here
ball.draw()
paddle.draw()
window.update_idletasks()
window.update()
time.sleep(0.01)
window.destroy() # should always destroy window before exit
I had this problem and solved it by restarting my iPython-console (Spyder)

How to manage event-handler recursion in python turtle?

I've been working on a Pong game, using the turtle module in Python. Below is my code:
from turtle import Turtle, _Screen, TurtleScreen
from random import choice, randrange, randint
from tkinter import *
from tkinter import messagebox
class Field(_Screen):
def __init__(self, width = 1024, height = 600):
# Get __init__ from _Screen
super().__init__()
# Get __init__ from TurtleScreen (parent class of _Screen)
TurtleScreen.__init__(self, self._canvas)
if Turtle._screen is None:
Turtle._screen = self
self.width = width
self.height = height
self.setup(self.width+100,self.height+50)
self.screensize(self.width,self.height)
self.title("Pong")
self.bgcolor("black")
# Define size of score bar, above the play field
self.score_height = self.height/10
# Offset 0 axis line, due to score bar
self.yzero = -(self.height/2 - (self.height-self.score_height)/2)
class Ball(Turtle):
def __init__(self, velocity = 5, size = 1, color = "white"):
super().__init__(shape="circle",visible=False)
self.color(color)
self.speed(0)
self.penup()
self.shapesize(size,size)
self.setposition(0,field.yzero)
self.st()
self.velocity = velocity
self.dirrection = 0
def player_collision(self,player):
bx,by = self.position()
px,py = player.position()
x_off = ball.shapesize()[1]*10 + player.shapesize()[0]*10
y_off = ball.shapesize()[0]*10 + player.shapesize()[1]*10
if px > 0:
if (bx > px-x_off and by <= py+y_off and by >= py-y_off):
return True
elif px < 0:
if (bx < px+x_off and by <= py+y_off and by >= py-y_off):
return True
return False
def court_collision(self,court):
if (ball.ycor() >= ((field.height/2)-
court.score_height-self.shapesize()[0]*10)
or ball.ycor() <= -court.height/2+10): return True
return False
def out_left(self,court):
if self.xcor() <= -court.width/2: return True
return False
def out_right(self,court):
if self.xcor() >= court.width/2: return True
return False
class Player(Turtle):
def __init__(self, x=0, y=0, color="white", up=None, down=None):
super().__init__(shape="square",visible=False)
self.color(color)
self.speed(0)
self.penup()
# setup player paddle
self.shapesize(1,10)
# Rotate turtle, to allow the use of forward method
self.setheading(90)
self.setposition(x,y)
self.st()
self.score = 0
self.height = self.shapesize()[1]*10
self.velocity = 50
self.ondrag(self.drag)
self.upkey = up
self.downkey = down
def drag(self,x,y):
self.ondrag(None) # Disable event handler to avoid recursion
if y >= (field.height/2-field.score_height) - self.height:
y = (field.height/2-field.score_height) - self.height
if y <= -field.height/2+self.height:
y = -field.height/2+self.height
self.goto(self.xcor(),y)
self.ondrag(self.drag) # Reactivate event handler
def up(self):
#field.onkeypress(None, self.upkey)
if (self.ycor()+self.height <=
(field.height-field.score_height)/2+field.yzero):
self.forward(self.velocity)
#field.onkeypress(self.up, self.upkey)
def down(self):
#field.onkeypress(None, self.downkey)
if self.ycor()-self.height >= -field.height/2:
self.forward(-self.velocity)
#field.onkeypress(self.down, self.downkey)
class Score(Turtle):
def __init__(self):
super().__init__(visible=False)
self.speed(0)
self.color("white")
self.pensize(3)
# Draw lower border
self.penup()
self.goto(-field.width,-field.height/2)
self.pendown()
self.goto(field.width,-field.height/2)
# Draw upper border
self.penup()
self.goto(-field.width,field.height/2-field.score_height)
self.pendown()
self.goto(field.width,field.height/2-field.score_height)
self.penup()
# Draw score
self.goto(-100,field.height/2-field.score_height)
self.write(player2.score,font=("Monospace",50,"bold"))
self.goto(100,field.height/2-field.score_height)
self.write(player1.score,font=("Monospace",50,"bold"))
def update(self):
# Clear the previous score
for i in range(3):
self.undo()
# And write the new one
self.write(player2.score,font=("Monospace",50,"bold"))
self.goto(100,field.height/2-field.score_height)
self.write(player1.score,font=("Monospace",50,"bold"))
class Game:
def __init__(self,court,difficulty=0):
# Difficulty = increase in ball speed
self.difficulty = difficulty
# Setup event handlers
court.onkeypress(self.qt, "Escape")
court.onkeypress(player1.up, player1.upkey)
court.onkeypress(player1.down, player1.downkey)
court.onkeypress(player2.up, player2.upkey)
court.onkeypress(player2.down, player2.downkey)
court.onkey(self.pause, "p")
court.listen()
# Try to implement game pause. Not working, for the moment
#self.pause = False
#self.pause_count = 0
def reset(self):
ball.setposition(0,field.yzero)
player1.setposition(player1.xcor(),field.yzero)
player2.setposition(player2.xcor(),field.yzero)
ball.dirrection = choice([0,180]) # Left or right
ball.setheading(ball.dirrection+randrange(-80,80))
def restart(self):
self.reset()
self.player1_score = 0
self.player2_score = 0
self.difficulty = 0
def qt(self):
prompt = Tk()
prompt.eval('tk::PlaceWindow %s center' % prompt.winfo_toplevel())
prompt.withdraw()
answer = messagebox.askyesno("Quit", "Are you sure you want to quit?")
if answer == True:
field.bye()
return
# Not currently working
def pause(self):
if self.pause_count % 2 == 0:
self.pause == True
else:
self.pause = False
class Play(Turtle):
def __init__(self):
super().__init__(visible=False)
self.shape("square")
self.color("white")
self.speed(0)
self.penup()
self.shapesize(2,4)
self.goto(-field.width/2,field.height/2-field.score_height/2)
self.write("Play",font=("Monospace",20,"bold"))
field.onscreenclick(self.click)
def click(self,x,y):
print(x,y)
if (x <= -field.width/2+field.width/2/10 and
x >= -field.width/2 and
y >= field.height/2-field.score_height/2 and y <= field.height/2):
self.color("green")
self.clear()
self.write("Play",font=("Monospace",20,"bold"))
self.color("white")
self.clear()
self.write("Play",font=("Monospace",20,"bold"))
game.reset()
main()
def main():
ball.forward(ball.velocity+game.difficulty)
# Check for paddle collision
if ball.player_collision(player1) or ball.player_collision(player2):
ball.setheading(180 - ball.heading())
# Bounce from upper or lower border
if ball.court_collision(field):
ball.setheading(-ball.heading())
# Check for ball out of field and update player score
elif ball.out_right(field):
game.reset()
player2.score += 1
score.update()
game.difficulty += 0.5
elif ball.out_left(field):
game.reset()
player1.score += 1
score.update()
game.difficulty += 0.5
field.ontimer(main)
if __name__ == "__main__":
field = Field(1280,720)
ball = Ball()
player1 = Player(field.width/2,field.yzero,up = "Up", down = "Down")
player2 = Player(-field.width/2,field.yzero, up = "w", down = "s")
game = Game(field)
score = Score()
play_button = Play()
#field.mainloop()
It kind of works, but if you use the keys to play, it will eventually return an error:
RecursionError: maximum recursion depth exceeded while calling a
Python object
It would first seem that the problem is with the main() function, but the actual problem is with the event-handler for the key presses. If I only play using the mouse, the game will give no error, it will just feel jerky.
I've read the following subjects so far:
maximum recursion depth exceeded
Avoid RecursionError in turtle paint code
Turtle.onkeypress not working (Python)
And tried to implement the solutions found there. The only one that works for me is disabling the event-handler for the ondrag() function. If I try to use the same solution on the player (uncomment the lines in up() and down() methods of Player) it will only work when main() is not running. If I start the main() function it will just run once and deactivate.
So what I need help with is:
Avoiding the maximum recursion error. (it only happens when main() is active);
Making the ondrag function work without jerking the main() function;
the qt() method from the game class is not properly working if main() is running.
So do you guys think I can improve these aspects?
Edit: Below is the full traceback
Exception in Tkinter callback
Traceback (most recent call last):
File "C:\Users\Bogey\AppData\Local\Programs\Python\Python37-32\lib\tkinter\__init__.py", line 1702, in __call__
return self.func(*args)
File "C:\Users\Bogey\AppData\Local\Programs\Python\Python37-32\lib\tkinter\__init__.py", line 746, in callit
func(*args)
File "C:/Users/Bogey/Desktop/asd.py", line 209, in main
ball.forward(ball.velocity+game.difficulty)
File "C:\Users\Bogey\AppData\Local\Programs\Python\Python37-32\lib\turtle.py", line 1637, in forward
self._go(distance)
File "C:\Users\Bogey\AppData\Local\Programs\Python\Python37-32\lib\turtle.py", line 1604, in _go
ende = self._position + self._orient * distance
RecursionError: maximum recursion depth exceeded
The primary issue I see is you're doing too much needless calculation during game play. For example, consider the court_collision() method which gets called on every ball movement:
def court_collision(self,court):
if (ball.ycor() >= ((field.height/2)-
court.score_height-self.shapesize()[0]*10)
or ball.ycor() <= -court.height/2+10): return True
return False
Of all these values, only ball.ycor() is changing, the rest should have been computed before game play began and stashed so that the method looks more like:
def court_collision(self):
return not self.wall_top_offset > ball.ycor() > self.wall_bottom_offset
Ditto for player_collision(), drag(), etc.
The main() function should be really be the move() method of Ball.
I've other nits but they don't have anything to do with game performance.

Trying to make a ball object bounce off a paddle

I'm new to python and am working on a final project for my intro to python class. I have the majority of a paddle ball game done, but can't figure out how to make the ball object bounce off of my paddle object.
I've looked on Stackoverflow for a while and have spent a few hours trying to figure it out on my own without any success. If anyone has any ideas I could really use the help.
If there's anything I need to explain better in order for you to get a better understanding please just comment.
GUI FILE:
Import tkinter, random, particle, and helpers
from tkinter import *
from ball import *
from paddle import *
from time import *
class PaddleBall:
def __init__(self, window):
''' Construct the paddle ball GUI '''
self.window = window
self.window.protocol('WM_DELETE_WINDOW', self.safe_exit)
self.width = 700
self.height = 900
self.canvas = Canvas(self.window, bg='black', width=self.width, height=self.height, highlightthickness=0)
self.canvas.bind_all("<KeyPress-Left>", self.move_left)
self.canvas.bind_all("<KeyPress-Right>", self.move_right)
self.canvas.pack()
# Create a label to indicate instructions
instructions = Label(window, text="Controls: Left & Right Arrow Keys")
instructions.pack(side=BOTTOM, expand=YES)
# Create a button to clear Ball
restart_button = Button(window, text="Play", command=self.reset)
restart_button.pack(side=BOTTOM, expand=YES)
self.ball = Ball(350, 350)
self.paddle = Paddle(300, 850, 400, 860, 0, 0)
self.terminated = False
self.render()
def ballobject(self):
self.ball = Ball(350, 350)
self.paddle = Paddle(300, 850, 400, 860, 0, 0)
self.render()
def reset(self):
self.terminated = True
def safe_exit(self):
''' Turn off the event loop before closing the GUI '''
self.terminated = True
self.window.destroy()
# Render everything
def render(self):
# While program is not terminated
if not self.terminated:
# Erase Canvas
self.canvas.delete(ALL)
# Move ball
self.ball.move(self.canvas, self.paddle)
# Render ball
self.ball.render(self.canvas)
# Render paddle
self.paddle.render(self.canvas)
# use distance() to detect collision between ball and paddle.
'''Ball.bounce(self)'''
# Animate the particles movement
self.canvas.after(10, self.render)
else:
# Erase Canvas
self.canvas.delete(ALL)
self.terminated = False
self.canvas.after(50, self.ballobject)
def move_left(self, event):
self.paddle.move_left(event)
def move_right(self, event):
self.paddle.move_right(event)
if __name__ == '__main__':
root = Tk()
root.option_add('*font', ('Verdana', 12, 'bold italic')) # Found at http://effbot.org/tkinterbook/tkinter-widget-styling.htm
root.resizable(0,0) # Found at https://mail.python.org/pipermail/tutor/2001-September/008504.html
root.title('Paddle Ball')
root.wm_attributes("-topmost", -1)
app = PaddleBall(root)
root.mainloop()
BALL CLASS FILE:
class Ball:
'''
Ball models a single ball that may be rendered to a canvas
'''
def __init__(self, x, y, radius = 15,):
'''
Constructor
'''
self._x = x
self._y = y
self._velX = randint(-10,10)
self._velY = randint(-10,-5)
self._radius = radius
self._color = 'white'
self._tx = 350
self._ty = 400
self._t = ""
self._tfill = "red"
self._tfont = ("Arial", 35, "bold italic")
# This method renders the ball
def render(self, canvas):
canvas.create_oval(self._x - self._radius, self._y - self._radius, self._x + self._radius, self._y + self._radius, fill = self._color)
canvas.create_text(self._tx, self._ty, text = self._t, fill = self._tfill, font = self._tfont)
# This method moves the ball
def move(self, canvas, Paddle):
# Update Position
self._x += self._velX
self._y += self._velY
# If the ball hits any of the wall negate the velocity
if (self._x + self._radius > canvas.winfo_reqwidth() and self._velX > 0) or (self._x - self._radius < 0 and self._velX < 0):
self._velX = -self._velX
if (self._y + self._radius < 0 and self._velY < 0):
self._velY = -self._velY
if (self._y + self._radius > canvas.winfo_reqheight() and self._velY > 0):
self._velY = 0
self._velX = 0
self._t = " GAME OVER! \n Click the play button to play again."
#*****THIS IS WHAT I'M HAVING TROUBLE WITH******
# Determine if the ball hits the paddle
if ((self._x + self._radius > Paddle._x(self) and self._velX > 0) or (self._x + self._radius < Paddle._x2(self))) and (self._y < Paddle._y(self)):
self._velX = -self._velX
PADDLE CLASS FILE:
# Import math and helpers
from tkinter import *
import math
from gui import *
class Paddle:
def __init__(self, x, y, x2, y2, velX, velY):
'''
Constructor
'''
self._x = x
self._y = y
self._x2 = x2
self._y2 = y2
self._velX = velX
self._velY = velY
self._color = 'white'
def getpadx(self):
return self._x
def getpady(self):
return self._y
def getpadx1(self):
return self._x2
def getpady2(self):
return self._y2
# This method renders the paddle
def render(self, canvas):
canvas.create_rectangle(self._x, self._y, self._x2, self._y2, fill = self._color)
# This method moves the paddle
def move(self, canvas):
# Update Position
# If the paddle hits any of the wall negate the velocity
if (self._x + self._radius > canvas.winfo_reqwidth() and self._velX > 0) or (self._x - self._radius < 0 and self._velX < 0):
self._velX = -self._velX
def move_left(self, event):
self._x -= 35
self._x2 -= 35
def move_right(self, event):
self._x += 35
self._x2 += 35
I figured it out with the help of a friend. All I had to do was change this code:
From this:
if ((self._x + self._radius > Paddle._x(self) and self._velX > 0) or (self._x + self._radius < Paddle._x2(self))) and (self._y < Paddle._y(self)):
self._velX = -self._velX
To this:
`if (self._x > Paddle._x) and (self._x < Paddle._x2):
if (self._y + self._radius > Paddle._y):
self._velY = -self._velY
self._velX = self._velX + randint(-2,2)`

Categories