Adding obstacle generation and detection to pong game - python

This project is being done without pygame or other libraries that aren't built into Python 3. I have already made a pong game with a paddle and a ball moving around the screen. When the ball hits the paddle it bounces off like it normally does in any game like this. I want to generate a grid of rectangle shapes in the top middle part of the frame with the paddle and ball, and make it so when the ball hits a rectangle, the rectangle disappears. What is an effective way to do this and what would it look like, roughly speaking? Here is what I am working with currently:
from tkinter import *
import tkinter.font
import time
class Display(Frame):
def __init__(self):
Frame.__init__(self)
self.master.title("Animation")
self.grid()
horizontal_direction = "east"
vertical_direction = "south"
self.canvas_width = 800
self.canvas_height = 400
self.paddle_x = 20
self.paddle_y = 80
self.left_rect_side = 360 #forposition
self.canvas = Canvas(self, width=self.canvas_width, height=self.canvas_height, bg = "white")
self.canvas.grid(row = 1, column = 0)
self.master.bind('<Left>', lambda event: self.leftKey(self))
self.master.bind('<Right>', lambda event: self.rightKey(self))
self.x = 5
self.y = 5
diameter = 20
self.canvas.create_oval(self.x, self.y, self.x + diameter, self.y + diameter, outline="#000000"
, fill="red", tags="circle")
self.canvas.create_rectangle(self.canvas_width/2 - self.paddle_y/2, self.canvas_height - self.paddle_x,
self.canvas_width/2 + self.paddle_y/2, self.canvas_height,
fill="black", tags="paddle")
fontx = tkinter.font.Font(family = "Verdana", size = 20)
self.lives = 5
self.lifedisplay = Label(self, width = -800, height = -20,
font = fontx, text = "Lives left: " + str(self.lives))
self.lifedisplay.grid(row = 0, column = 0)
mvt = 2
while True:
if self.y + diameter > self.canvas_height:
self.lives -= 1
self.lifedisplay.configure(text = "Lives left: " + str(self.lives))
if self.lives <= 0:
self.canvas.move("circle", -self.x, -self.y)
break
if self.y + diameter >= self.canvas_height - self.paddle_x:
if self.x + diameter > self.left_rect_side and self.x < self.left_rect_side + self.paddle_y:
vertical_direction = "north"
if horizontal_direction == "east":
if self.x + diameter >= self.canvas_width:
horizontal_direction = "west"
else:
self.canvas.move("circle", mvt, 0)
self.x += mvt
else:
if self.x + diameter <= diameter:
horizontal_direction = "east"
else:
self.canvas.move("circle", -mvt, 0)
self.x -= mvt
if vertical_direction == "south":
if self.y + diameter >= self.canvas_height:
vertical_direction = "north"
self.canvas.move("circle", 0, -mvt)
self.y -= mvt
else:
self.canvas.move("circle", 0, mvt)
self.y += mvt
else:
if self.y + diameter <= diameter:
vertical_direction = "south"
else:
self.canvas.move("circle", 0, -mvt)
self.y -= mvt
self.canvas.update()
self.canvas.after(15)
#staticmethod
def leftKey(self):
if self.left_rect_side >= 10:
self.canvas.move("paddle", -5, 0)
self.left_rect_side -= 5
self.canvas.update()
#staticmethod
def rightKey(self):
if self.left_rect_side <= self.canvas_width - self.paddle_y - 5:
self.canvas.move("paddle", 5, 0)
self.left_rect_side += 5
self.canvas.update()
def main():
Display().mainloop()
main()

To do this, you should make a list of a bunch of rectangle objects that have 4 attributes:
x coordinate
y coordinate
width
height
Additionally, you will have to figure out the x/y coordinate for the center of your circle. Then, each frame, have a method that checks for a collision between the circle and each rectangle. Remove the rectangle from this list of rectangles if it is hit by the circle, then only draw the rectangles from the list onto the screen.
Checking for the collision is the hardest part. I would recommend checking out this StackOverflow answer:
Circle-Rectangle collision detection (intersection)
This next article might be a little harder to understand, but check this out too:
https://yal.cc/rectangle-circle-intersection-test/
I'm not going to write the code for you (that's not what SO is for), but I hope this helped. Let me know if you need help understanding.

Related

python kivy collision detection isn't working

Im trying to create a game with Python's Kivy but my collision detection system isnt working i've tried many different methods on youtube but still no success it either does detect anything or just gives me error messages
def collides(self, player, ball2):
r1x = player.pos[0]
r1y = player.pos[1]
r2x = ball2.pos[0]
r2y = ball2.pos[1]
r1w = player.size[0]
r1h = player.size[1]
r2w = ball2.size[0]
r2h = ball2.size[1]
if r1x < r2x + r2w and r1x + r1w > r2x and r1y < r2y + r2h and r1y + r1h > r2y:
print("True")
return True
else:
return False
print('False')
Your code in collides seems OK but rest of code (in repo) doesn't look good.
I took code from repo and first I made many changes to make it cleaner - I made class Sprite similar to pygame.Sprite
And next I tried use collisions and they work for me.
I keep all balls on list so I can use for-loop to work with all ball. And I can add more balls and it will still works the same. And I can remove ball from list when it is "killed".
I also run all with one schedule_interval. When I click button then I only change speed vx without running another schedule_interval. And this way in update() I can first I make calculation, next I can check collisions and at the end I can move rect on canvas - and this way rect doesn't blik when I have to move it back to previous position (ie. when I detect collision with border).
from kivy.app import App
from kivy.graphics import Ellipse, Rectangle, Color
from kivy.metrics import dp
from kivy.properties import Clock, ObjectProperty, NumericProperty
from kivy.uix.boxlayout import BoxLayout
from kivy.uix.widget import Widget
# How to play the game: Click left and right to move along the 'x' axis to stop click button to move in the opposite
# direction once, to go faster just repeatedly click the direction you want to go BUT there's a catch the faster
# you are going the harder it is to stop sp be carefull. you can teleport to the other side of the screen but only from
# the right side to the left side
# Objective: Dodge all incoming enemies until you reach the next level
# Level layout: Lvl 1: Space invaders type mode Lvl 2: Platform runner type mode Lvl 3: undecided...
# Goal: Make this game playable both on mobile and pc
class Sprite():
def __init__(self, x, y, size, color, vx, vy):
'''Set all values.'''
self.start_x = x
self.start_y = y
self.x = x
self.y = y
self.size = size
self.color = color
self.vx = vx
self.vy = vy
self.rect = None
#self.alive = True
def create_rect(self):
'''Execute it in `with canvas:` in `on_size()`.'''
Color(*self.color)
self.rect = Rectangle(pos=(self.x, self.y), size=(self.size, self.size))
def set_start_pos(self, center_x, center_y):
'''Move to start position.'''
self.x = center_x + self.start_x
self.y = center_y + self.start_y
def move(self):
'''Calculate new position without moving object on `canvas`.'''
self.x += self.vx
self.y += self.vy
def draw(self):
'''Move object on canvas.'''
self.rect.pos = (self.x, self.y)
def check_collision_circle(self, other):
distance = (((self.x-other.x)**2) + ((self.y-other.y)**2)) ** 0.5
#if distance < (self.size + other.size)/2:
# print(True)
# return True
#else:
# return False
return distance < (self.size + other.size)/2:
def check_collision_rect(self, other):
# code `... and ...` gives `True` or `False`
# and it doesn't need `if ...: return True else: return False`
return (
(other.x <= self.x + self.size) and
(self.x <= other.x + other.size) and
(other.y <= self.y + self.size) and
(self.y <= other.y + other.size)
)
class MainCanvas(Widget):
rec_x = NumericProperty(0)
inc = dp(3)
ball_size = dp(35)
my_player = ObjectProperty(Rectangle)
def __init__(self, **kwargs):
super().__init__(**kwargs)
self.player = Sprite(x=-self.ball_size/2, y=145, size=dp(15), vx=dp(0), vy=dp(0), color=(1, .3, .5))
self.balls = [
Sprite(x=0, y=-2000, size=dp(15), vx=dp(0), vy=dp(8), color=(1, 0, 0)),
Sprite(x=100, y=-1000, size=dp(15), vx=dp(0), vy=dp(5), color=(1, 1, 0)),
Sprite(x=-200, y=-1000, size=dp(30), vx=dp(0), vy=dp(5), color=(1, 1, 1)),
Sprite(x=300, y=-600, size=dp(15), vx=dp(0), vy=dp(5), color=(1, 1, 1)),
]
with self.canvas:
for ball in self.balls:
ball.create_rect()
self.player.create_rect()
Clock.schedule_interval(self.update, 1/60)
def on_size(self, *args):
print(f'on_size : {self.width}x{self.height}')
for ball in self.balls:
ball.set_start_pos(self.center_x, self.center_y)
self.player.set_start_pos(self.center_x, self.center_y)
self.rec_x = self.player.x
def update(self, dt):
# all in one function to control if it check collision after move, and draw only after all calculations
# --- moves (without draws) ---
self.player_move(dt)
# move green rectangle below player
self.rec_x = self.player.x
self.ball_move(dt)
# --- collisions (without draws) ---
live_balls = []
for ball in self.balls:
if self.player.check_collision_rect(ball):
#if self.player.check_collision_circle(ball):
print('killed')
# hide
#ball.set_start_pos(self.center_x, self.center_y)
#ball.draw()
# or remove from canvas
self.canvas.remove(ball.rect)
else:
live_balls.append(ball)
self.balls = live_balls
# --- draws ---
self.player.draw()
for ball in self.balls:
ball.draw()
def on_left_click(self):
print('Left Clicked')
self.player.vx -= self.inc
def on_right_click(self):
print('Right Clicked')
self.player.vx += self.inc
def ball_move(self, dt):
for ball in self.balls:
ball.move()
if ball.y + ball.size > self.height:
ball.set_start_pos(self.center_x, self.center_y)
def player_move(self, dt):
self.player.move()
# moving left and stop on screen border
if self.player.vx < 0 and self.player.x < 0:
self.player.x = 0
self.player.vx = 0
# moving right and jump to left side when leave screen
if self.player.vx > 0 and self.width < self.player.x:
self.player.x = 0
class TheFalling(App):
pass
app = TheFalling()
app.run()
#app.stop()
app.root_window.close()

Is there any way I can make my code easier to understand and simpler? [closed]

Closed. This question is opinion-based. It is not currently accepting answers.
Want to improve this question? Update the question so it can be answered with facts and citations by editing this post.
Closed 3 years ago.
Improve this question
I am wondering if by any chance there is a way to optimise this portion of my code. Regarding the pathing system and the redraw section. I find it really difficult to follow through it. Even changing the whole thing into a more understanding format will suit me. Thank you very much.
class wolf(object): #creating the wolf class
wolf_right = ['pics/WR.png'] + ['pics/WR' + str(i) + '.png' for i in range(2, 18)] #identifies the right wolf pics
wolf_left = ['pics/WL.png'] + ['pics/WL' + str(i) + '.png' for i in range(2, 18)] #identifies the left wolf pics
run_right = [pg.transform.smoothscale(pg.image.load(img), (280,160)) for img in wolf_right] #loads the right wolf pics
run_left = [pg.transform.smoothscale(pg.image.load(img), (280,160)) for img in wolf_left] #load the left wolf pics
def __init__(self, x, y, width, height, finish): #initialising the object wolf
self.x = x #x coord of wolf
self.y = y #y coord of wolf
self.width = width #width of wolf
self.height = height #height of wolf
self.path = [x, finish] # This part here determines the movement limits of the wolf, back and forth
self.run_distance = 0 #wolf is initially did not move
self.velocity = 9 #speed of wolf moving
self.collision_box = (self.x + 60, self.y, 280, 160)#the arguments inside are the coordinates designating the sides of the box
#and then the width and the height of the box
def pathing(self): #pathing system
if self.velocity > 0: # If wolf moving to the right
if self.x < self.path[1] + self.velocity: #ensures that it keeps moving if wolf is not at finish
self.x += self.velocity #allows wolf to move
else: #if the finish is reached then go backwards
self.velocity = self.velocity * -1 #where velocity goes negative
#according to displacement, a particle with -ve velocity goes backwards
self.x += self.velocity #allows wolf to move
else: # If wold is going to the left
if self.x > self.path[0] - self.velocity: #ensures that it keeps moving if the wolf is not at finish
self.x += self.velocity #allows wolf to move
else: #if the finish is reached then go backwards
self.velocity = self.velocity * -1 #where velocity goes negative
#according to displacement, a particle with -ve velocity goes backwards
self.x += self.velocity #allows wolf to move
def got_hit(self): #function if the wolf takes damage from the ninja
print("Congrats, you have hit the wolf!")
def redraw(self, win):#just like for the ninja we do the same steps
self.pathing()
if self.run_distance + 1 >= 51: #This time I am running 17 sprites thus, 17 * 3 (where 3 sprites per second)
self.run_distance = 0
if self.velocity < 0: #if velocity is increasing meaning movement, links left images with left movement
win.blit(self.run_left[self.run_distance//3], (self.x,self.y))
self.run_distance += 1
else: #else linking right images with right movement
win.blit(self.run_right[self.run_distance//3], (self.x,self.y))
self.run_distance += 1
#pg.draw.rect(win, (0,200,0), self.collision_box,2) #this will draw a green box around the wolf of lines thickness 2
self.collision_box = (self.x + 50 , self.y, 200, 150) # ensures the box is drawn and is updated alongside motion
The code is not that complicated, but that's my opinion. I read it once through and it was easy to follow. Just one thing, self.x += self.velocity is done at the end of each of the 4 cases in pathing. It is sufficient to do it once at the end of pathing, instead of separately in each case. Something similar can be done for self.run_distance += 1 in redraw:
class wolf(object): #creating the wolf class
# [...]
def pathing(self): #pathing system
if self.velocity > 0 and self.x >= self.path[1] + self.velocity or \
self.velocity < 0 and self.x <= self.path[0] - self.velocity:
self.velocity = self.velocity * -1
self.x += self.velocity
# [...]
def redraw(self, win):#just like for the ninja we do the same steps
self.pathing()
if self.run_distance + 1 >= 51: #This time I am running 17 sprites thus, 17 * 3 (where 3 sprites per second)
self.run_distance = 0
run_surf = self.run_left if self.velocity < 0 else self.run_right
win.blit(run_surf[self.run_distance//3], (self.x,self.y))
self.run_distance += 1
#pg.draw.rect(win, (0,200,0), self.collision_box,2) #this will draw a green box around the wolf of lines thickness 2
self.collision_box = (self.x + 50 , self.y, 200, 150) # ensures the box is drawn and is updated alongside motion
Anyway in pygame it is intended to use pygame.Rect, pygame.sprite.Sprite and pygame.sprite.Group.
Each Sprite should have the attributes .rect and .image and the method update(). The Sprites should be contained in Groups. The Groups can be drawn (draw()) and updated (update()).
That makes the code easy to read, short, comprehensible and extendable. e.g.:
(Class Names should normally use the CapWords convention.)
class Wolf(pygame.sprite.Sprite):
wolf_right = ['pics/WR.png'] + ['pics/WR' + str(i) + '.png' for i in range(2, 18)] #identifies the right wolf pics
wolf_left = ['pics/WL.png'] + ['pics/WL' + str(i) + '.png' for i in range(2, 18)] #identifies the left wolf pics
run_right = [pg.transform.smoothscale(pg.image.load(img), (280,160)) for img in wolf_right] #loads the right wolf pics
run_left = [pg.transform.smoothscale(pg.image.load(img), (280,160)) for img in wolf_left] #load the left wolf pics
def __init__(self, x, y, finish):
super().__init__():
self.image = run_left[0]
self.rect = pygame.Rect(x, y, 280, 160)
self.path = [x, finish] # This part here determines the movement limits of the wolf, back and forth
self.run_distance = 0 #wolf is initially did not move
self.velocity = 9 #speed of wolf moving
self.collision_box = (self.rect.x + 60, self.rect.y, 280, 160) #the arguments inside are the coordinates designating the sides of the box
#and then the width and the height of the box
def update(self, angle):
if self.velocity > 0 and self.rect.x >= self.path[1] + self.velocity or \
self.velocity < 0 and self.rect.x <= self.path[0] - self.velocity:
self.velocity = self.velocity * -1
self.rect.x += self.velocity
if self.run_distance + 1 >= 51: #This time I am running 17 sprites thus, 17 * 3 (where 3 sprites per second)
self.run_distance = 0
run_surf = self.run_left if self.velocity < 0 else self.run_right
if self.run_distance//3 > len(run_surf):
self.run_distance = 0
self.image = run_surf[self.run_distance//3]
self.run_distance += 1
self.collision_box = (self.rect.x + 50 , self.rect.y, 200, 150) # ensures the box is drawn and is updated alongside motion
wolf = Wolf(........)
all_sprites = pygame.sprite.Group()
all_sprites.add(wolf)
while True:
# [...]
all_sprites.update(win)
# [...]
all_sprites.draw(win)
pygame.display.flip()

Clarification on Collision Detection and Updating Labels

I am trying to work on a paddle ball game that entails keeping a ball bouncing away from the bottom for as long as possible. In this game, there are five lives, which can get depleted when the ball hits the bottom of the screen, and resets itself once it has reached 0 lives. However, the ball does not bounce off of the paddle as it should, nor does the label for the score update as it ought to. The code for how I am trying to achieve the collision currently is tied to the vertical placement of the ball, the relative section of which is as follows:
if vertical_direction == "south":
top_y += dy
if top_y >= (self.canvas_height - ball_diameter) - 20:
if self.top_paddle <= top_y and self.top_paddle + 80 >= top_y:
top_y = self.canvas_height - ball_diameter
vertical_direction = "north"
elif top_y >= self.canvas_height - ball_diameter:
lives -= 1
if (lives >= 0):
top_x = 2
top_y = 2
self.canvas.delete("ball")
ball = self.canvas.create_oval(top_x, top_y, top_x + ball_diameter,
top_y + ball_diameter, fill = "blue", tags = "ball")
var.set("Lives: " +lives_Label)
else:
lives= 5
top_x = 2
top_y = 2
self.canvas.delete(ball)
ball = self.canvas.create_oval(top_x, top_y, top_x + ball_diameter,
top_y + ball_diameter, fill = "blue", tags = "ball")
I have looked at a bit of code which I found to be most similar to mine, and tried to implement its methods for detecting collision as displayed above. My only guess is there is some obvious logical inconsistency I failed to notice that is obvious to a more experienced coder, and that is the cause of both of my problems.
For reference, here is the complete code thus far:
from tkinter import *
import tkinter.font
class BallBounce (Frame):
def __init__(self):
Frame.__init__(self)
self.master.title("Bouncing Ball")
self.grid()
lives = 5
lives_Label = str(lives)
var = StringVar()
font = tkinter.font.Font(family = "Verdana", size = 20)
self._label = Label(self, font = font, textvariable = var)
var.set("Lives: "+lives_Label)
self._label.grid(row = 0, column = 0)
self.canvas_width = 800
self.canvas_height = 400
self.canvas = Canvas(self, width = self.canvas_width, height = self.canvas_height,
bg = "white")
self.canvas.grid(row = 1, column = 0)
frame = Frame(self)
frame.grid(row = 1, column = 0)
top_x = 0
top_y = 0
ball_diameter = 20
self.canvas.create_oval(top_x, top_y, ball_diameter, ball_diameter, fill = "blue", tags = "ball")
horizontal_direction = "east"
vertical_direction = "south"
self.canvas.create_rectangle (self.canvas_width / 2, self.canvas_height - 20, self.canvas_width / 2 + 80,
self.canvas_height, fill = "black", tags = "paddle")
self.top_paddle = self.canvas_width/2
self.canvas.bind("<Left>", lambda event: self.canvas.move("paddle", -5, 0))
self.canvas.bind("<Right>", lambda event: self.canvas.move("paddle", 5, 0))
self.canvas.focus_set()
dx = 2
dy = 2
while True:
if horizontal_direction == "east":
top_x += dx # dx is 2 because the ball moves 2 pixels horizontally every 15 milliseconds
if top_x >= self.canvas_width - ball_diameter: # ball has hit east wall
top_x = self.canvas_width - ball_diameter
horizontal_direction = "west" # change direction
self.canvas.move("ball", dx, 0) # move ball horizontally dx pixels to the right/east
else: # i.e., horizontal_direction is "west"
top_x -= dx
if top_x <= 0: # ball has hit west wall
top_x = 0 # you may need to adjust this a little
horizontal_direction = "east" # change direction
self.canvas.move("ball", -dx, 0) # move ball horizontally dx pixels to the left/west
if vertical_direction == "south":
top_y += dy
if top_y >= (self.canvas_height - ball_diameter) - 20:
if self.top_paddle <= top_y and self.top_paddle + 80 >= top_y:
top_y = self.canvas_height - ball_diameter
vertical_direction = "north"
elif top_y >= self.canvas_height - ball_diameter:
lives -= 1
if (lives >= 0):
top_x = 2
top_y = 2
self.canvas.delete("ball")
ball = self.canvas.create_oval(top_x, top_y, top_x + ball_diameter,
top_y + ball_diameter, fill = "blue", tags = "ball")
var.set("Lives: " +lives_Label)
else:
lives= 5
top_x = 2
top_y = 2
self.canvas.delete(ball)
ball = self.canvas.create_oval(top_x, top_y, top_x + ball_diameter,
top_y + ball_diameter, fill = "blue", tags = "ball")
self.canvas.move("ball", 0, dy)
else:
top_y -= dy
if top_y <= 0:
top_y = 0
vertical_direction = "south"
self.canvas.move("ball", 0, -dy)
self.canvas.after(15)
self.canvas.update()
def main():
BallBounce().mainloop()
main()

Tkinter GUI not opening when compiled

My python program I created doesn't seem to be opening when I click compile. The reason I am confused is because I have a .mainloop() attached to the class. Through my own testing I have deduced that it has something to do with the while loop in my code. Additionally, not sure if this helps, but when I abort the program the following appears in the console:
File "C:\Users\zach\Anaconda3\lib\tkinter\__init__.py", line 2585, in move
self.tk.call((self._w, 'move') + args)
KeyboardInterrupt
here is my completed code for:
from tkinter import *
class GUI(Frame):
def __init__(self):
Frame.__init__(self)
self.master.title("Lab 8")
self.grid()
canvas_width = 800
canvas_height = 400
self.canvas = Canvas(self, width = canvas_width, height =
canvas_height, bg = "white")
self.canvas.grid()
ball_diameter = 20
top_x = 2
top_y = 2
self.canvas.create_oval(top_x, top_y, top_x + ball_diameter,
top_y + ball_diameter, fill = "black", tags = "ball")
horizontal_direction = "east"
vertical_direction = "south"
dx = 2
dy = 2
while True:
if horizontal_direction == "east":
self.canvas.move("ball", dx, 0) # move ball horizontally dx pixels to the right/east
top_x += dx # dx is 2 because the ball moves 2 pixels horizontally every 15 milliseconds
if top_x >= canvas_width - ball_diameter: # ball has hit east wall
horizontal_direction = "west" # change direction
else: # i.e., horizontal_direction is "west"
self.canvas.move("ball", -dx, 0) # move ball horizontally dx pixels to the left/west
top_x -= dx
if top_x <= 0: # ball has hit west wall
horizontal_direction = "east" # change direction
if vertical_direction == "south":
self.canvas.move("ball", 0, dy)
top_y += dy
if top_y >= canvas_height - ball_diameter:
vertical_direction = "north"
else:
self.canvas.move("ball", 0, -dy)
top_y -= dy
if top_y <= 0 :
vertical_direction = "south"
def main():
GUI().mainloop()
main()
I figured it out it was because I forgot to add the update and after methods to wait for the ball and update its position.
Your issue is that you run an infinite loop in your __init__ function. It will never reach your main loop to run the GUI. You need to let __init__ end and to call your update code, not in a tight loop but at a set time interval.
Break out the code to move the shape into a separate function, minus the infinite loop and call that function at intervals using the after method of tk widgets.
from tkinter import *
class GUI(Frame):
def __init__(self):
Frame.__init__(self)
self.master.title("Lab 8")
self.grid()
self.canvas_width = 800
self.canvas_height = 400
self.canvas = Canvas(self,
width=self.canvas_width,
height=self.canvas_height,
bg="white")
self.canvas.grid()
self.ball_diameter = 20
self.top_x = 2
self.top_y = 2
self.canvas.create_oval(self.top_x,
self.top_y,
self.top_x + self.ball_diameter,
self.top_y + self.ball_diameter,
fill = "black", tags = "ball")
self.horizontal_direction = "east"
self.vertical_direction = "south"
self.dx = 2
self.dy = 2
self.after(10, self.move)
def move(self):
if self.horizontal_direction == "east":
self.canvas.move("ball", self.dx, 0) # move ball horizontally dx pixels to the right/east
self.top_x += self.dx # dx is 2 because the ball moves 2 pixels horizontally every 15 milliseconds
if self.top_x >= self.canvas_width - self.ball_diameter: # ball has hit east wall
self.horizontal_direction = "west" # change direction
else: # i.e., horizontal_direction is "west"
self.canvas.move("ball", -self.dx, 0) # move ball horizontally dx pixels to the left/west
self.top_x -= self.dx
if self.top_x <= 0: # ball has hit west wall
self.horizontal_direction = "east" # change direction
if self.vertical_direction == "south":
self.canvas.move("ball", 0, self.dy)
self.top_y += self.dy
if self.top_y >= self.canvas_height - self.ball_diameter:
self.vertical_direction = "north"
else:
self.canvas.move("ball", 0, -self.dy)
self.top_y -= self.dy
if self.top_y <= 0 :
self.vertical_direction = "south"
self.after(10, self.move)
def main():
GUI().mainloop()
main()
Note that the number of self variables has exploded and become pretty unmanageable IMHO, which might indicate you need to break out some of those values into other classes.
But fundamentally this runs. It ain't pretty, but you can work on that next.

Why does the collide happen many times?

I'm using openGL with Pyglet which is a python package. I have to use this language and this package, it is for an assignment. I have a basic Brickbreaker style game that is basically a keep it up game.
I create a ball and a paddle.
I separately create a bounding box class that will be used to create the hit boxes for each object.
class BoundBox:
def __init__ (self, width, height, pos):
self.width = width
self.height = height
self.pos = pos
Then I create the boxes themselves
paddleBox = BoundBox(200, 20, [0,0])
ballBox = BoundBox(40, 40, [236, 236])
In the update function which is running # pyglet.clock.schedule_interval(update,1/100.0) I call the checkcollide() function which checks if there was a collision:
def checkForCollide():
global collides
if overlap(ballBox, paddleBox):
vel = 1.05
ballVel[0] = ballVel[0]*vel #Ball speeds up when collide happens
ballVel[1] = ballVel[1]*vel
ballVel[1] = -ballVel[1] #Change direction on collision
ballPos[1] = -ballPos[1]
collides += 1 #counts how many collides happen
The overlap function is returning a boolean if there's an overlap in hit boxes:
def overlap(box1, box2):
return (box1.pos[0] <= box2.width + box2.pos[0]) and(box1.width + box1.pos[0] >= box2.pos[0]) and(box1.pos[1] <= box2.height + box2.pos[1]) and(box1.height + box1.pos[1] >= box2.pos[1])
pos[0] is the minimum x
width is the maximum x
pos[1] is the minimum y
height is the maximum y
When I run the game and the ball hits the paddle it flickers about 15 times and increments the collides counter every time it detects a hit. Collides then prints in the console. Why does this flicker happen? How do I stop it?
Here is the program's full code (you must have pyglet installed to run it):
import sys, time, math
from pyglet.gl import *
from euclid import *
from pyglet.window import key
from pyglet.clock import *
window = pyglet.window.Window(512,512)
quadric = gluNewQuadric()
ballPos = Vector2(256,256)
ballVel = Vector2(200,145)
x1 = 0
bar = pyglet.graphics.vertex_list(4, ('v2f', [0,0, 0,20, 200,0, 200,20]))
startTime = time.clock()
collides = 0
#pos is minx, miny
class BoundBox:
def __init__ (self, width, height, pos):
self.width = width
self.height = height
self.pos = pos
def overlap(box1, box2):
return (box1.pos[0] <= box2.width + box2.pos[0]) and(box1.width + box1.pos[0] >= box2.pos[0]) and(box1.pos[1] <= box2.height + box2.pos[1]) and(box1.height + box1.pos[1] >= box2.pos[1])
paddleBox = BoundBox(200, 20, [0,0])
ballBox = BoundBox(40, 40, [236, 236])
#window.event
def on_draw():
glClear(GL_COLOR_BUFFER_BIT)
glPushMatrix()
glPushMatrix()
glColor3f(1,1,1)
glTranslatef(x1, 0, 0)
bar.draw(GL_TRIANGLE_STRIP)
glPopMatrix()
glTranslatef(ballPos[0], ballPos[1], 0)
glColor3f(1,0,0)
gluDisk(quadric, 0, 20, 32, 1)
glPopMatrix()
#window.event
def on_key_press(symbol, modifiers):
global x1
dist = 30
if symbol == key.RIGHT:
#print "right"
x1 += dist
elif symbol == key.LEFT:
#print "left"
x1 -= dist
def checkForBounce():
if ballPos[0] > 512.0:
ballVel[0] = -ballVel[0]
ballPos[0] = 512.0 - (ballPos[0] - 512.0)
elif ballPos[0] < 0.0:
ballVel[0] = -ballVel[0]
ballPos[0] = -ballPos[0]
if ballPos[1] > 512.0:
ballVel[1] = -ballVel[1]
ballPos[1] = 512.0 - (ballPos[1] - 512.0)
elif ballPos[1] < -100.0:
gameOver()
def gameOver():
global collides
'''global startTime
elapsed = (time.time() - startTime)
score = elapsed * .000000001
finalscore = '%.1f' % round(score, 1)'''
gostr = "GAME OVER"
print gostr
str = "Your score = "
print str
print collides
pyglet.app.exit()
def checkForCollide():
global collides
if overlap(ballBox, paddleBox):
vel = 1.05
ballVel[0] = ballVel[0]*vel #Ball speeds up when collide happens
ballVel[1] = ballVel[1]*vel
ballVel[1] = -ballVel[1] #Change direction on collision
ballPos[1] = -ballPos[1]
collides += 1 #counts how many collides happen
print collides
#glscale(0.5, 1, 1)
def update(dt):
global ballPos, ballVel, ballBox, x1, paddleBox
ballBox = BoundBox(40, 40, [ballPos[0], ballPos[1]])
paddleBox = BoundBox(200, 20, [x1,0])
#print paddleBox.pos
#print ballBox.pos
ballPos += ballVel * dt
checkForBounce()
checkForCollide()
pyglet.clock.schedule_interval(update,1/100.0)
pyglet.app.run()
I don't think you wanted to invert the position here:
def checkForCollide():
global collides
if overlap(ballBox, paddleBox):
vel = 1.05
ballVel[0] = ballVel[0]*vel #Ball speeds up when collide happens
ballVel[1] = ballVel[1]*vel
ballVel[1] = -ballVel[1] #Change direction on collision
ballPos[1] = -ballPos[1]
collides += 1 #counts how many collides happen
What were you trying to do with this line?
ballPos[1] = -ballPos[1]
I suspect that is why you are flickering.

Categories