Is there a way to make a object follow another object? - python

So, I have been working on a project for a very basic Space Invaders. But, I can't seem to get the Bullet (see code) class to follow the Jet class(see code):
from tkinter import *
import time as t
class Ufo:
def __init__(self,canvas,color):
self.canvas = canvas
self.id = canvas.create_polygon(0,0,0,10,20,10,20,0,fill=color)
self.canvas.move(self.id, 245,50)
self.x = 0.5
self.y = 0
self.canvas_width = self.canvas.winfo_width()
def draw(self):
self.canvas.move(self.id,self.x,self.y)
pos = self.canvas.coords(self.id)
if pos[0] <= 0:
self.x = 0.5
if pos[6] >= self.canvas_width:
self.x = -0.5
class Jet:
def __init__(self,canvas,color):
self.canvas = canvas
self.id = canvas.create_polygon(0,0,10,-20,20,0,fill=color)
self.canvas.move(self.id,245,250)
self.x = 0
self.canvas_width = self.canvas.winfo_width()
canvas.bind_all('<KeyPress-Left>',self.turn_left)
canvas.bind_all('<KeyPress-Right>',self.turn_right)
def draw(self):
self.canvas.move(self.id,self.x,0)
pos = self.canvas.coords(self.id)
if pos[0] <= 0:
self.x = 0
if pos[4] >= self.canvas_width:
self.x = 0
def turn_left(self,evt):
self.x = -1
t.sleep(0.01)
def turn_right(self,evt):
self.x = 1
t.sleep(0.01)
class Bullet:
def __init__(self,canvas,color):
self.canvas = canvas
self.id = canvas.create_oval(10,10,25,25,fill=color)
self.canvas.move(self.id,245,250)
def draw(self):
self.canvas.move(self.id,1,0)
tk = Tk()
tk.title('Space Invaders')
tk.resizable(0,0)
tk.wm_attributes('-topmost',1)
canvas = Canvas(tk,width=500,height=400,bd=0,highlightthickness=0)
canvas.pack()
tk.update()
ufo = Ufo(canvas,'gray')
jet = Jet(canvas,'blue')
bullet = Bullet(canvas,'yellow')
while 1:
ufo.draw()
jet.draw()
tk.update_idletasks()
tk.update()
t.sleep(0.01)
I have already searched some websites (and yours, too) and couldn't find anything. Do I have any way to make the classes follow each other?

You could do it by changing Bullet.draw() to accept an x argument, and pass it the jet object's x in the loop.
Note I also modified your code so it follows the PEP 8 - Style Guide for Python Code to make it more readable. Strongly suggest you read and start following it.
from tkinter import *
import time as t
class Ufo:
def __init__(self, canvas, color):
self.canvas = canvas
self.id = canvas.create_polygon(0, 0, 0, 10, 20, 10, 20, 0, fill=color)
self.canvas.move(self.id, 245, 50)
self.x = 0
self.y = 0 # ADDED
self.canvas_width = self.canvas.winfo_width()
def draw(self):
self.canvas.move(self.id, self.x, 0)
pos = self.canvas.coords(self.id)
if pos[0] <= 0:
self.x = 0
if pos[4] >= self.canvas_width:
self.x = 0
class Jet:
def __init__(self, canvas, color):
self.canvas = canvas
self.id = canvas.create_polygon(0,0, 10,-20, 20,0, fill=color)
self.canvas.move(self.id, 245, 250)
self.x = 0
self.canvas_width = self.canvas.winfo_width()
canvas.bind_all('<KeyPress-Left>', self.turn_left)
canvas.bind_all('<KeyPress-Right>', self.turn_right)
def draw(self):
self.canvas.move(self.id, self.x, 0)
pos = self.canvas.coords(self.id)
if pos[0] <= 0:
self.x = 0
if pos[4] >= self.canvas_width:
self.x = 0
def turn_left(self, evt):
self.x = -1
t.sleep(0.01)
def turn_right(self, evt):
self.x = 1
t.sleep(0.01)
class Bullet:
def __init__(self, canvas, color):
self.canvas = canvas
self.id = canvas.create_oval(10, 10, 25, 25, fill=color)
self.canvas.move(self.id, 245, 250)
def draw(self, x):
self.canvas.move(self.id, x, 0)
tk = Tk()
tk.title('Space Invaders')
tk.resizable(0, 0)
tk.wm_attributes('-topmost', 1)
canvas = Canvas(tk, width=500, height=400, bd=0, highlightthickness=0)
canvas.pack()
tk.update()
ufo = Ufo(canvas, 'gray')
jet = Jet(canvas, 'blue')
bullet = Bullet(canvas, 'yellow')
while 1:
ufo.draw()
jet.draw()
bullet.draw(jet.x)
tk.update_idletasks()
tk.update()
t.sleep(0.01)

Related

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)

tkinter: object with different movement patterns

I'm working on a simulation in which some cubes of the same class are moving randomly. My aim is to give them another moving pattern, when they fulfill some characteristics (for example their object number).
My Problem:
If they fulfill the characteristics, how can I "switch off" the first moving pattern and activate the next?
Here a strongly simplified example of the simulation, and how it doesn't work:
from tkinter import *
from random import *
class Cubes:
def __init__(self, master, canvas, number, x1, y1, color):
self.master = master
self.canvas = canvas
self.number = number
self.x1 = x1
self.y1 = y1
self.x2 = x1 + 15
self.y2 = y1 + 15
self.color = color
self.rectangle = canvas.create_rectangle(x1, y1, self.x2, self.y2, fill=color)
def movement(self):
self.x = randint(-10, 10)
self.y = randint(-10, 10)
canvas.move(self.rectangle, self.x, self.y)
if self.number == 2:
def movementII(self):
canvas.move(self.rectangle, 0, 0)
self.canvas.after(100, self.movementII)
self.canvas.after(100, self.movement)
if __name__ == "__main__":
master = Tk()
canvas = Canvas(master, width=900, height=600)
canvas.pack()
master.title("Simulation")
cube = Cubes(master, canvas, 2, randint(50, 800), randint(25, 500), "black")
cube.movement()
mainloop()
how can I "switch off" the first moving pattern and activate the next?
When you call after, it returns a unique identifier. You can save that identifier and then later pass it to after_cancel to cancel the job if it hasn't already run.
I'm not entirely clear what you want, but if you want to turn off the old movement when you switch to the new, it would look something like this:
class Cubes:
def __init__(self, master, canvas, number, x1, y1, color):
...
self.after_id = None
...
def cancel(self):
if self.after_id is not None:
self.after_cancel(self.after_id)
self.after_id = None
def movement(self):
self.x = randint(-10, 10)
self.y = randint(-10, 10)
canvas.move(self.rectangle, self.x, self.y)
if self.number == 2:
def movementII(self):
canvas.move(self.rectangle, 0, 0)
self.cancel()
self.after_id = self.canvas.after(100, self.movementII)
self.after_id = self.canvas.after(100, self.movement)
A better way might be to have a single method that you call with after, and it simply calls the appropriate method.
For example, something like this:
def move(self):
if self.number == 1:
self.movement_1()
elif self.number == 2:
self.movement_2()
self.canvas.after(100, self.move)
def movement_1(self):
self.x = randint(-10, 10)
self.y = randint(-10, 10)
canvas.move(self.rectangle, self.x, self.y)
def movement_2(self):
canvas.move(self.rectangle, 0, 0)
Then, to switch the movement method, just change self.number and it will automatically be called at the appropriate time.

Tkinter canvas doesn't show my "player" symbol

I am beginner in Python and want just to make a red square that will move by pressed keys on keyboard. But after I had run this, only green canvas showed up, no red square. Here is the code:
Imports:
try:
import Tkinter
except:
import tkinter as Tkinter
import time
Here just some constants
__author__ = "Brano"
print(__author__)
GAME_WIDTH = 800
GAME_HEIGHT = 600
GAME_BG = 'green'
MOVE_SPEED = 10
Main class with initialization:
class Game(Tkinter.Tk):
def __init__(self, *args, **kwargs):
Tkinter.Tk.__init__(self, *args, **kwargs)
# Trigger Of Other Functions
self.x = GAME_WIDTH/2
self.y = GAME_HEIGHT/2
self.create_board()
self.create_men()
self.bind('<Any-KeyPress>',self.move)
Creating board:
def create_board(self):
self.board = Tkinter.Canvas(width=GAME_WIDTH, height=GAME_HEIGHT,
bg=GAME_BG)
self.board.pack(padx=10, pady=10)
return
Creating red square:
def create_men(self):
self.men = self.board.create_rectangle(300, 300, 310, 310, fill='red')
return
Move square after pressing keys:
def move(self, event=None):
key = event.keysym
if key=='Left':
self.x = MOVE_SPEED
elif key=='Right':
self.x = -MOVE_SPEED
elif key=='Up':
self.y = MOVE_SPEED
elif key=='Down':
self.y = -MOVE_SPEED
else:
pass
return
Just tkinter updates:
def TkUpdate(self):
self.update()
self.update_idletasks()
return
My main move update:
def GameUpdate(self):
self.board.move(self.men, self.x, self.y)
return
Checking if it is imported:
if __name__ == '__main__':
root=Game(className=" Snake Game ")
while True:
root.TkUpdate()
root.GameUpdate()
time.sleep(0.09)
else :
print("U cannot import me !")
Here is the canvas I have
Here is the whole class:
class Game(Tkinter.Tk):
def __init__(self, *args, **kwargs):
Tkinter.Tk.__init__(self, *args, **kwargs)
# Trigger Of Other Functions
self.x = GAME_WIDTH/2
self.y = GAME_HEIGHT/2
self.create_board()
self.create_men()
self.bind('<Any-KeyPress>',self.move)
def create_board(self):
self.board = Tkinter.Canvas(width=GAME_WIDTH, height=GAME_HEIGHT, bg=GAME_BG)
self.board.pack(padx=10, pady=10)
return
def create_men(self):
self.men = self.board.create_rectangle(300, 300, 310, 310, fill='red')
return
def move(self, event=None):
key = event.keysym
if key=='Left':
self.x = MOVE_SPEED
elif key=='Right':
self.x = -MOVE_SPEED
elif key=='Up':
self.y = MOVE_SPEED
elif key=='Down':
self.y = -MOVE_SPEED
else:
pass
return
def TkUpdate(self):
self.update()
self.update_idletasks()
return
def GameUpdate(self):
self.board.move(self.men, self.x, self.y)
return
Avoid calling update and sleep. Instead use after and mainloop. Your code should schedule your gameloop using after and enter the mainloop after setting up everything. Then keep scheduling the gameloop with after calls.
Your actual problem is you have confused using self.x and self.y as a position in the setup but then use these as a relative move in the game loop. So when you first call GameUpdate you move the rectangle by half the canvas size and it immediately moves out of view. Below is a version that uses these as the 'man' position and asserts the position using coords each time around the game loop.
import tkinter as tk
GAME_WIDTH = 800
GAME_HEIGHT = 600
GAME_BG = 'green'
MOVE_SPEED = 10
MAN_SIZE = 20
class Game(tk.Tk):
def __init__(self, *args, **kwargs):
tk.Tk.__init__(self, *args, **kwargs)
# Trigger Of Other Functions
self.x = GAME_WIDTH/2
self.y = GAME_HEIGHT/2
self.create_board()
self.create_men()
self.bind('<Any-KeyPress>',self.move)
def create_board(self):
self.board = tk.Canvas(width=GAME_WIDTH, height=GAME_HEIGHT, bg=GAME_BG)
self.board.pack(padx=10, pady=10)
def create_men(self):
self.men = self.board.create_rectangle((self.x, self.y, self.x + MAN_SIZE, self.y + MAN_SIZE), fill='red', outline='black')
def move(self, event=None):
key = event.keysym
print(key)
if key=='Left':
self.x = self.x - MOVE_SPEED
elif key=='Right':
self.x = self.x + MOVE_SPEED
elif key=='Up':
self.y = self.y - MOVE_SPEED
elif key=='Down':
self.y = self.y + MOVE_SPEED
else:
pass
return
def game_loop(self):
self.board.coords(self.men, (self.x, self.y, self.x + MAN_SIZE, self.y + MAN_SIZE))
self.after(10, self.game_loop)
def main():
game = Game(className="Snake")
game.after(10, game.game_loop)
game.mainloop()
if __name__ == '__main__':
main()

How to detect if a key is being held down in Tkinter?

As a novice when it comes to Python, I've tried programming my own game to start, with the advice of a guidebook. However, for this game, I'm trying to detect when a key is held down consistently instead of just pressed. The current code I'm using doesn't make the character move, and without the halt(self, evt) code being implemented, causes the ship to speed up uncontrollably after the button is held down for long enough.
from tkinter import *
import random
import time
class Game:
def __init__(self):
self.tk = Tk()
self.tk.title("Shooter")
self.tk.resizable(0, 0)
self.tk.wm_attributes("-topmost", 1)
self.canvas = Canvas(self.tk, width=500, height=1000, highlightthickness=0)
self.canvas.pack()
self.tk.update()
self.canvas_height = 1000
self.canvas_width = 500
self.bg = PhotoImage(file="background.gif")
w = self.bg.width()
h = self.bg.height()
for x in range(0, 5):
for y in range(0, 10):
self.canvas.create_image(x * w, y * h, \
image=self.bg, anchor='nw')
self.sprites = []
self.running = True
def mainloop(self):
while 1:
if self.running == True:
for sprite in self.sprites:
sprite.move()
self.tk.update_idletasks()
self.tk.update()
time.sleep(0.01)
class Coords:
def __init__(self, x1=0, y1=0, x2=0, y2=0):
self.x1 = x1
self.y1 = y1
self.x2 = x2
self.y2 = y2
class Sprite:
def __init__(self, game):
self.game = game
self.endgame = False
self.coordinates = None
def move(self):
pass
def coords(self):
return self.coordinates
class PlayerSprite(Sprite):
def __init__(self, game):
Sprite.__init__(self, game)
self.renderimage = [
PhotoImage(file="player_1.gif"),
PhotoImage(file="player_2.gif"),
PhotoImage(file="player_3.gif"),
PhotoImage(file="player_4.gif"),
]
self.image = game.canvas.create_image(225, 900, \
image=self.renderimage[0], anchor='nw')
self.x = 0
self.y = 0
self.velx = 0
self.current_image = 0
self.current_image_add = 1
self.shoot_timer = 0
self.last_time = time.time()
self.coordinates = Coords()
x_move = None
y_move = None
game.canvas.bind_all('<KeyPress-Left>', self.move_left)
game.canvas.bind_all('<KeyPress-Right>', self.move_right)
game.canvas.bind_all('<KeyPress-Up>', self.move_up)
game.canvas.bind_all('<KeyPress-Down>', self.move_down)
game.canvas.bind_all('<KeyPress-Left>', self.halt)
game.canvas.bind_all('<KeyPress-Right>', self.halt)
game.canvas.bind_all('<KeyPress-Up>', self.halt)
game.canvas.bind_all('<KeyPress-Down>', self.halt)
game.canvas.bind_all('<space>', self.shoot)
def move_left(self, evt):
x_move = self.x - 1.5
self.x = x_move
def move_right(self, evt):
x_move = self.x + 1.5
self.x = x_move
def move_up(self, evt):
y_move = self.y - 1.5
self.y = y_move
def move_down(self, evt):
y_move = self.y + 1.5
self.y = y_move
def halt(self, evt):
time.sleep(0.01)
if x_move < 0:
x_move = -1.5
elif x_move > 0:
x_move = 1.5
elif y_move < 0:
y_move = -1.5
elif y_move > 0:
y_move = 1.5
def shoot(self, evt):
print("Placeholder")
def move(self):
self.game.canvas.move(self.image, self.x, self.y)
def coords(self):
xy = self.game.canvas.coords(self.image)
self.coordinates.x1 = xy[0]
self.coordinates.y1 = xy[1]
self.coordinates.x2 = xy[0] + 24
self.coordinates.y2 = xy[1] + 32
return self.coordinates
g = Game()
sp = PlayerSprite(g)
g.sprites.append(sp)
g.mainloop()
My goal is to have my character move at a constant rate (as opposed to uncontrollably fast after a while) when the respective key is pressed.
The most straightforward solution to your question would be to avoid adding a value at every keypress, but rather set a constant value.
def move_left(self, evt):
x_move = -5
self.x = x_move
The movement would however lose its dynamic, but it will be constant. Otherwise, you could create a max value. Something like this:
def move_left(self, evt):
int max_val_left = -10
if( self.x < max_val_left):
x_move = self.x - 1.5
self.x = x_move
Thereby forcing self.x to remain capped and constant if it has reached the max_val.
Holding down a key is essentially the same as pressing that key repeatedly. What you're doing by adding to/subtracting from the self.x/self.y attributes in your move_* functions is you're increasing the amount that the canvas will move your player sprite in each function call (e.g. from 1.5 to 3 to 4.5 to 6, etc. as you hold down a direcitonal key).
Since the canvas will be moving your player by (self.x, self.y) units every time "move" is called under the "PlayerSprite" class, we want self.x and self.y to be either 0 or whatever speed you desire (1.5 in the following code). So instead of adding to self.x and self.y, we should assign it to a constant value:
def move_left(self, evt):
self.x = -1.5
def move_right(self, evt):
self.x = 1.5
def move_up(self, evt):
self.y = -1.5
def move_down(self, evt):
self.y = -1.5
Also, instead of using "halt", what you could do is include 'KeyRelease-*' bindings to stop your player once you've stopped holding down a directional key:
game.canvas.bind_all('KeyRelease-Left'>, self.stop_horz_move)
game.canvas.bind_all('KeyRelease-Right'>, self.stop_horz_move)
game.canvas.bind_all('KeyRelease-Up'>, self.stop_vert_move)
game.canvas.bind_all('KeyRelease-Down'>, self.stop_vert_move)
(I've generalized the left and right directions to horz as well as up and down to vert to save on the number of function definitions.)
Then you can create functions that assign your self.x value or self.y value to 0, so that your player doesn't move once "move" is called.
def stop_move_horz(self, evt):
self.x = 0
def stop_move_vert(self, evt):
self.y = 0

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