Python Snake Game - Why does the food only gets eaten occasionally? - python

I'm currently taking a python course where we had to built a snake game. I did not follow the tutors instuctions and I tried to do it on my own. I could make it work for the most part, but I can't figure out why the purple food elements sometimes are not eaten by the snake, even though the snake directly strikes over the food. I would appreciate it, if someone with more experience than me could look over my code to find the bug, I certainly don't know where the mistake is. Here is the code:
from turtle import Turtle, Screen
import random
import time
class Snake():
def __init__(self):
screen.onkeypress(self.look_left, 'a')
screen.onkeypress(self.look_right, 'd')
screen.listen()
self.snake_squares = {
1: Turtle(),
2: Turtle(),
3: Turtle(),
}
for square in self.snake_squares:
self.design_square(self.snake_squares[square])
self.snake_head = self.snake_squares[1]
self.look_left()
def design_square(self, current_square):
current_square.penup()
current_square.shape('square')
current_square.color('white')
current_square.shapesize(1.00)
current_square.pensize(1)
def add_square(self, new_score):
square_amount = new_score + 3
self.snake_squares[square_amount] = Turtle()
self.design_square(self.snake_squares[square_amount])
self.snake_squares[square_amount].setposition(self.old_position)
def look_left(self):
self.snake_head.left(90)
def look_right(self):
self.snake_head.right(90)
def move(self):
self.old_position = self.snake_head.position()
self.snake_head.forward(10.00)
all_square_positions = []
for square in self.snake_squares:
if square != 1:
next_square = self.snake_squares[square]
new_position = self.old_position
self.old_position = next_square.position()
next_square.setposition(new_position)
all_square_positions.append(new_position)
return self.snake_head.position(), all_square_positions
def check_self_hit(self, head_position, tail_positions):
if head_position in tail_positions:
return False
else:
return True
class Board():
def __init__(self, height):
self.score_board = Turtle()
self.score_board.hideturtle()
self.score_board.setposition(0, height + 10)
self.score_board.color('white')
self.score = 0
def add_to_score(self, old_score):
self.score_board.color('black')
self.score_board.write(arg=f"Score: {self.score}", font=('Arial', 25, "normal"), align='center')
self.score = old_score + 1
self.score_board.color('white')
self.score_board.write(arg=f"Score: {self.score}", font=('Arial', 25, "normal"), align='center')
return self.score
class Border():
def __init__(self, width, height):
self.width = width
self.height = height
self.positive_borders = [self.width, self.height]
self.negative_borders = [- self.width, - self.height]
def check_borders(self, snake_position):
no_hit = True
for border in self.positive_borders:
if snake_position[0] >= border:
no_hit = False
elif snake_position[1] >= border:
no_hit = False
else:
pass
for border in self.negative_borders:
if snake_position[0] <= border:
no_hit = False
elif snake_position[1] <= border:
no_hit = False
else:
pass
return no_hit
def print_border(self, width, height):
line = Turtle()
line.hideturtle()
line.setposition(-float(width), -float(height))
line.shape("circle")
line.speed('fastest')
line.color('white')
for wall in range(4):
line.forward(800)
line.left(90)
class Food():
def __init__(self, width, height):
self.width = width
self.height = height
self.food = Turtle()
self.food.hideturtle()
self.food.speed('fastest')
self.food.penup()
self.food.pensize(3)
self.food.shape('circle')
self.food.color('purple')
def create_food(self):
self.food.showturtle()
self.food.clear()
return self.random_position()
def random_position(self):
x_min = -self.width + 10 #-390
x_max = self.width - 10 #390
y_min = -self.height + 10 #-390
y_max = self.height - 10 #390
x = 11
y = 11
while x % 10.00 != 0.00:
x = float(random.randint(x_min, x_max))
while y % 10.00 != 0.00:
y = float(random.randint(y_min, y_max))
self.food.setposition((x, y))
x += 10.00
y += 10.00
all_possible_hits = []
for hits in range(3):
all_possible_hits.append((x, y -10.00))
x -= 10.00
for hits in range(3):
all_possible_hits.append((x + 10.00, y))
y -= 10.00
return all_possible_hits
def erase_food(self):
self.food.hideturtle()
screen = Screen()
screen.tracer(0)
canvas_width = 400
canvas_height = 400
screen.bgcolor("black")
border = Border(canvas_width, canvas_height)
border.print_border(canvas_width, canvas_height)
snake = Snake()
food = Food(canvas_width, canvas_height)
board = Board(canvas_height)
current_score = board.add_to_score(-1)
current_food_positions = food.create_food()
snake_alive = True
screen.update()
snake_path = []
while snake_alive:
time.sleep(0.03)
screen.update()
snake_current_position, all_positions = snake.move()
snake_path.append(snake_current_position)
if snake.check_self_hit(snake_current_position, all_positions):
if border.check_borders(snake_current_position):
if snake_current_position in current_food_positions:
current_score = board.add_to_score(current_score)
snake.add_square(current_score)
food.erase_food()
current_food_positions = food.create_food()
screen.update()
else:
snake_alive = False
else:
snake_alive = False
screen.exitonclick()
I don't really know why that happens, especially because it only happens sometimes and at different score amounts.

A few things here:
As the first comment pointed out, generally, you would not come to stackoverflow for debugging advice like this, at least without trying to debug on your own and posting the results with your question
Something you will need to keep in mind always is that directly comparing floating point values for equality is a big no-no in any programming (that is only one of the hundreds of articles explaining why, for a much more in depth and possibly over your head look, check out this). I believe that is directly where your problem lies in this case as well. By using snake_current_position in current_food_positions, you are likely asking for that comparison to be made between a bunch of floats as your have it written, and therefor failing the equality check when it looks like it should be true.
To find this out and debug your next program on your own, think about how debugging could be done. There are many tools but the simplest is sometimes just to print things. When taking a look myself, the first thing I did was add
print("snake:")
print(snake_pos_int)
print("food")
print(current_food_positions)
as well as turned the game speed down (sleep time up) so I could watch the output. This allowed me to get an idea if the values were sane, and also notice that the values were printing as floating point numbers with different amounts of rounding (10.0 vs 10.00) which is another indicator you definitely should not be directly comparing them.
This brings me to the "solution"
from turtle import Turtle, Screen
import random
import time
class Snake():
def __init__(self):
screen.onkeypress(self.look_left, 'a')
screen.onkeypress(self.look_right, 'd')
screen.listen()
self.snake_squares = {
1: Turtle(),
2: Turtle(),
3: Turtle(),
}
for square in self.snake_squares:
self.design_square(self.snake_squares[square])
self.snake_head = self.snake_squares[1]
self.look_left()
def design_square(self, current_square):
current_square.penup()
current_square.shape('square')
current_square.color('white')
current_square.shapesize(1)
current_square.pensize(1)
def add_square(self, new_score):
square_amount = new_score + 3
self.snake_squares[square_amount] = Turtle()
self.design_square(self.snake_squares[square_amount])
self.snake_squares[square_amount].setposition(self.old_position)
def look_left(self):
self.snake_head.left(90)
def look_right(self):
self.snake_head.right(90)
def move(self):
self.old_position = self.snake_head.position()
self.snake_head.forward(10)
all_square_positions = []
for square in self.snake_squares:
if square != 1:
next_square = self.snake_squares[square]
new_position = self.old_position
self.old_position = next_square.position()
next_square.setposition(new_position)
all_square_positions.append(new_position)
return self.snake_head.position(), all_square_positions
def check_self_hit(self, head_position, tail_positions):
if head_position in tail_positions:
return False
else:
return True
class Board():
def __init__(self, height):
self.score_board = Turtle()
self.score_board.hideturtle()
self.score_board.setposition(0, height + 10)
self.score_board.color('white')
self.score = 0
def add_to_score(self, old_score):
self.score_board.color('black')
self.score_board.write(arg=f"Score: {self.score}", font=('Arial', 25, "normal"), align='center')
self.score = old_score + 1
self.score_board.color('white')
self.score_board.write(arg=f"Score: {self.score}", font=('Arial', 25, "normal"), align='center')
return self.score
class Border():
def __init__(self, width, height):
self.width = width
self.height = height
self.positive_borders = [self.width, self.height]
self.negative_borders = [- self.width, - self.height]
def check_borders(self, snake_position):
no_hit = True
for border in self.positive_borders:
if snake_position[0] >= border:
no_hit = False
elif snake_position[1] >= border:
no_hit = False
else:
pass
for border in self.negative_borders:
if snake_position[0] <= border:
no_hit = False
elif snake_position[1] <= border:
no_hit = False
else:
pass
return no_hit
def print_border(self, width, height):
line = Turtle()
line.hideturtle()
line.setposition(-float(width), -float(height))
line.shape("circle")
line.speed('fastest')
line.color('white')
for wall in range(4):
line.forward(800)
line.left(90)
class Food():
def __init__(self, width, height):
self.width = width
self.height = height
self.food = Turtle()
self.food.hideturtle()
self.food.speed('fastest')
self.food.penup()
self.food.pensize(3)
self.food.shape('circle')
self.food.color('purple')
def create_food(self):
self.food.showturtle()
self.food.clear()
return self.random_position()
def random_position(self):
x_min = -self.width + 10 #-390
x_max = self.width - 10 #390
y_min = -self.height + 10 #-390
y_max = self.height - 10 #390
x = 11
y = 11
while x % 10 != 0:
x = random.randint(x_min, x_max)
while y % 10 != 0:
y = random.randint(y_min, y_max)
self.food.setposition((x, y))
x += 10
y += 10
all_possible_hits = []
for hits in range(3):
all_possible_hits.append((x, y -10))
x -= 10
for hits in range(3):
all_possible_hits.append((x+10, y))
y -= 10
return all_possible_hits
def erase_food(self):
self.food.hideturtle()
screen = Screen()
screen.tracer(0)
canvas_width = 400
canvas_height = 400
screen.bgcolor("black")
border = Border(canvas_width, canvas_height)
border.print_border(canvas_width, canvas_height)
snake = Snake()
food = Food(canvas_width, canvas_height)
board = Board(canvas_height)
current_score = board.add_to_score(-1)
current_food_positions = food.create_food()
snake_alive = True
screen.update()
snake_path = []
while snake_alive:
time.sleep(0.1)
screen.update()
snake_current_position, all_positions = snake.move()
snake_path.append(snake_current_position)
if snake.check_self_hit(snake_current_position, all_positions):
if border.check_borders(snake_current_position):
snake_pos_int = (round(int(snake_current_position[0])/10)*10, round(int(snake_current_position[1])/10)*10)
if snake_pos_int in current_food_positions:
current_score = board.add_to_score(current_score)
snake.add_square(current_score)
food.erase_food()
current_food_positions = food.create_food()
screen.update()
else:
snake_alive = False
else:
snake_alive = False
screen.exitonclick()
You can note the main change: The previously floating point values are integers (I notice that you even cast the random ints for food to floats, do not do this if you do not need to, in your case this only hurt) and the snake head values are somewhat hacked to be integers (simply casting them to integers was not enough, as in another demonstration of why not to use floats here, at one point one got rounded down to 9, breaking the game, so I apply a rounding to the nearest 10 as well). THIS IS NOT AN IDEAL SOLUTION. It is meant to demonstrate that the problem is the use of floats and that you should be using integers for this all the way through.
With this hacky solution, I was able to run multiple games out to and past 25 score, so I believe that mostly fixes your main problem. I will leave it to you to fix the secondary but related issue that if your snake grows long enough to run into itself, the collision detection may not work consistently because of the same issue with comparing floats for equality as you do with if head_position in tail_positions:.

Related

Hi, I need to build a platform game using only tkinter, cant use pygame, and Im having trouble moving two objects at the same time

cant move the character at the same time the obstacle is moving, pirincipal is the name of the image that works as the player
def move():
def left(event):
x = -5
y = 0
canvasGame.update()
edgeReached()
canvasGame.move(principal,x, y)
collision()
def right(event):
x = 5
y = 0
canvasGame.update()
edgeReached()
canvasGame.move(principal,x, y)
collision()
canvasGame.bind_all("<Left>", left)
canvasGame.bind_all("<Right>", right
move()
class Barrel:
def __init__(self, canvas):
self.canvas = canvas
self.obs = canvasGame.create_image(125, 500, image=nuevoObs, anchor=tk.NW)
self.x = 16
self.y = 0
def movement(self):
while True:
coords = canvasGame.coords(self.obs)
if (coords[0] >= 1000):
self.x = -10
self.y = 0
elif (coords[0] < 0):
self.x = 10
self.y = 0
self.canvas.move(self.obs, self.x,self.y)
canvasGame.update()
time.sleep(0.05)
def createBarrel():
barrel = Barrel(canvasGame)
circle_thread = Thread(target=barrel.movement())
circle_thread.daemon = True
circle_thread.start()
createBarrel()
def plzmove():
moveplz = Thread(target=move())
moveplz.daemon = True
moveplz.start()
plzmove()
I tried creating threads but the problem continues, if there is a barrel moving, the player cant move (if there is nothing else moving, the player can move with freedom), also, the player movement is just an image being moved, any tip is appreciated, much love.

reset object position in pygame

Trying to code the classic arcade game 'Pong', I've gotten stuck trying to reset the 'ball' into it's original position after the computer scores.
class Pong:
def __init__(self, width, height, x,y, color, screenw, screenh):
self.width = width
self.height = height
self.x = x
self.y = y
self.point = (self.x,self.y)
self.color = color
self.speed = random.randint(3,5)
self.screenw = screenw
self.screenh = screenh
self.dx = random.choice([-2,-1,1,2])
self.dy = random.choice([1,-1])
self.compscore = 0
self.playerscore = 0
self.score = False
def game_logic(self):
x,y = self.point
x += self.speed*self.dx
y += self.speed*self.dy
if x + self.width >= self.screenw:
self.dx = -1
self.color = GREEN
self.playerpoint()
print(str(self.playerscore)+" : "+str(self.compscore))
if x <= 100:
self.dx = 1
self.color = WHITE
self.comppoint()
print(str(self.playerscore)+" : "+str(self.compscore))
if y + self.height >= self.screenh:
self.dy = -1
self.color = ORANGE
if y <= 0:
self.dy = 1
self.color = SALMON
self.point = (x,y)
return
def resetpong(self):
self.point = (200,200)
self.dx = random.choice([-2,-1,1,2])
self.dy = random.choice([1,-1])
return self.point
def comppoint(self):
self.compscore += 1
print("The computer has scored a point.")
self.resetpong()
return self.compscore
def playerpoint(self):
self.playerscore += 1
print("Nice! You've scored a point.")
self.resetpong()
return self.playerscore
I've created the reset method and no matter where I've put it, whether in an if statement in the game_logic method in my pygame starter, or inside the game_logic in the Pong class. It does work though if I set it to a keybinding?
Am I an idiot?
The function resetpong changes the value of self.point. This function gets called by playerpoint or comppoint. The call to playerpoint or comppoint occurs in the function game_logic. This line at the end of game_logic:
self.point = (x,y)
therefore clobbers the new value of self.point. A similar problem affects the variable self.dx which gets set in game_logic and then clobbered by a call to playerpoint or comppoint.
Change the function game_logic as follows to fix both of these:
def game_logic(self):
x,y = self.point
x += self.speed*self.dx
y += self.speed*self.dy
self.point = x, y # parenthesis not needed here
if x + self.width >= self.screenw:
self.color = GREEN
self.playerpoint()
print(str(self.playerscore)+" : "+str(self.compscore))
elif x <= 100: # elif here: only test this if previous if is false
self.color = WHITE
self.comppoint()
print(str(self.playerscore)+" : "+str(self.compscore))
if y + self.height >= self.screenh:
self.dy = -1
self.color = ORANGE
elif y <= 0: # elif here: only test this if previous if is false
self.dy = 1
self.color = SALMON
# return not needed here
I also recommend removing the variables self.x and self.yfrom the Pong constructor since they are never used. The variable self.point contains those numbers and it's a violation of a basic principle to keep the same information in two different places.

Sprites not appearing correctly onscreen - pygame

I'm in a game programming class and I'm supposed to make a game where there are sprites falling down from the top of the screen that you catch or avoid. Right now I'm working on making the objects fall and they were working at first but then I screwed something up because now they appear patchily/randomly disappear while falling down the screen. I've tried changing various things in the code to fix the problem but I've been stuck for a while.
I'm pretty certain that I messed something up either blitting the coal/candy to the screen or adding new ones to fall once others disappear, but I included a large portion of my code just in case something there is messing it up. I'll highlight the likely messed up sections below.
Oh, I also always really appreciate comments on how to make code more concise/efficient, but the teacher expects the code to be similar to how he taught us, so if you do have a specific fix, if it could keep the code similar to how it is right now I'd really appreciate it!
Thank you so much!
#Setting up time/timer
time = 6000
TICKTOCK = 0
pygame.time.set_timer (TICKTOCK+1, 10)
#Class for candy
class Candy:
def __init__(self):
self.x = random.randint(0, SCREEN_WIDTH - 150)
self.y = random.randint(-1000, 0)
self.image_Candy = pygame.image.load('konpeito.png')
self.height = self.image_Candy.get_height()
self.width = self.image_Candy.get_width()
def collide (self, sprite):
selfRect = pygame.Rect(self.x, self.y, self.width, self.height)
spriteRect = pygame.Rect(sprite.x, sprite.y, sprite.width, sprite.height)
if selfRect.colliderect(spriteRect):
return True
else:
return False
#class for coal
class Coal:
def __init__(self):
self.x = random.randint(0, SCREEN_WIDTH - 150)
self.y = random.randint(-1000, SCREEN_HEIGHT)
self.image_Coal = pygame.image.load('coal.png')
self.width = self.image_Coal.get_width()
self.height = self.image_Coal.get_height()
def collide (self, sprite):
selfRect = pygame.Rect(self.x, self.y, self.width, self.height)
spriteRect = pygame.Rect(sprite.x, sprite.y, sprite.width, sprite.height)
if selfRect.colliderect(spriteRect):
return True
else:
return False
#class for sootsprite (collects candy and coal)
class Sootsprite:
def __init__(self):
self.x = random.randint(0, SCREEN_WIDTH)
self.y = random.randint(0, SCREEN_HEIGHT)
self.image_bowl = pygame.image.load('sootsprite.png')
self.height = self.image_bowl.get_height()
self.width = self.image_bowl.get_width()
clock = pygame.time.Clock()
fps = 10
#Creating candies and rocks
bowl = []
for i in range (15):
candyInstance = Candy()
bowl.append(candyInstance)
rocks = []
for i in range (8):
coalInstance = Coal()
rocks.append(coalInstance)
catch = Sootsprite()
playground.fill(cyan)
Game_Over = False
while not Game_Over:
font = pygame.font.SysFont(None, 30)
endfont = pygame.font.SysFont(None, 100)
text = font.render('Points: ' + str(total) + '/15', True, black)
playground.blit(text, (0,0))
timer = font.render('Time remaining: ' + str(time), True, black)
playground.blit(timer, (0, 40))
end = endfont.render("GAME OVER." + str(total) + " POINTS EARNED", True, black)
This is where I blitted things onscreen and potentially screwed up:
playground.blit(catch.image_bowl, (catch.x, catch.y))
playground.blit(candyInstance.image_Candy, (candyInstance.x, candyInstance.y))
playground.blit(coalInstance.image_Coal, (coalInstance.x, coalInstance.y))
for event in pygame.event.get():
if event.type == pygame.QUIT:
Game_Over = True
if event.type == TICKTOCK+1:
time -=1
#ends game when time is over
if time < 0:
playground.blit(end, (300, 400))
#moving sootsprite with mouse
if event.type == MOUSEMOTION:
(catch.x, catch.y) = pygame.mouse.get_pos()
#making candy fall
for candyInstance in bowl:
if candyInstance.x <= 0:
candyInstance.x += 0
elif candyInstance.x >= 0 :
candyInstance.x -=0
if candyInstance.y <= (SCREEN_HEIGHT+150):
candyInstance.y += 20
elif candyInstance.y > (SCREEN_HEIGHT + 150) :
candyInstance.y = -100
this is where I'm removing things and adding new ones and might've messed up:
#removing candy when collected
for candyInstance in bowl:
if candyInstance.collide(catch):
bowl.remove(candyInstance)
total += 1
candyInstance = Candy()
bowl.append(candyInstance)
#making coal fall
for coalInstance in rocks:
if coalInstance.x <= 0:
coalInstance.x += 0
elif coalInstance.x >= 0 :
coalInstance.x -=0
if coalInstance.y <= (SCREEN_HEIGHT + 200):
coalInstance.y += 20
elif coalInstance.y > (SCREEN_HEIGHT + 200) :
coalInstance.y = -100
this is also a place where I removed objects and added new ones:
#removing coal when collected
for coalInstance in rocks:
if coalInstance.collide(catch):
rocks.remove(coalInstance)
total -= 1
coalInstance = Coal()
rocks.append(coalInstance)
pygame.display.flip()
playground.fill(cyan)
clock.tick (fps)
pygame.quit()
You blit only one candy. Use for loop to blit all candies.
for candyInstance in bowl:
playground.blit(candyInstance.image_Candy, (candyInstance.x, candyInstance.y))
You have the same problem with rocks
for coalInstance in rocks:
playground.blit(coalInstance.image_Coal, (coalInstance.x, coalInstance.y))

Why my ball Sprite doesnt always bounce on my Sprite bar?!

I have some problems and I cant figure out how to fix them...
It is really simple game. A bar moved by the mouse and one ball is (or some balls) bouncing.
The user just need to keep the ball bouncing.
The user can choose how many balls he wants (1,2,3), the size of the bar (small, medium,large) and the speed of the balls (slow, normal, fast).
Problems:
- sometimes everything works fine, and sometimes the ball (or balls) just goes through the bar. Like if the collision function does not work. Is there any other way I can do?
everytime there is a collision, the score should add 10 points for the total displayed on top of the screen, but this score is been overwriting all the time.
For this game be run, I just have to call the function (startGame) from other file (settings), where it also sends the value of number of balls, size of bar and speed of balls.
If anyone can help I appreciate.
Thanks
from livewires import games, color
from tkinter import*
import random
games.init(screen_width = 735, screen_height = 350, fps = 60)
class Bounce(games.Sprite):
global total_score
total_score = 0
def update(self):
global total_score
if self.bottom == 315 and self.overlapping_sprites:
self.dy = -self.dy
total_score += 10
the_score = games.Text(value = 0, size = 25,
color = color.gray,x = 700, y = 20)
games.screen.add(the_score)
if self.right > games.screen.width or self.left < 0:
self.dx = -self.dx
if self.top < 0:
self.dy = -self.dy
if self.bottom == 400:
lose_message = games.Message(value = " - GAME OVER -",
size = 50,
color = color.gray,
x = games.screen.width/2,
y = games.screen.height/2,
lifetime = 300,
after_death = games.screen.quit)
games.screen.add(lose_message)
class Bar_moving(games.Sprite):
def update(self):
self.x = games.mouse.x
self.y = 315
if self.left < 0:
self.left = 0
if self.right > games.screen.width:
self.right = games.screen.width
class have_settings():
def __init__(self):
global the_ball_speed
global the_ball_number
global the_bar_size
if "the_ball_speed" not in globals():
the_ball_speed = "normal"
if "the_bar_size" not in globals():
the_bar_size = "medium"
if "the_ball_number" not in globals():
the_ball_number = 1
def set_all(self, number, size, speed):
global the_ball_speed
global the_bar_size
global the_ball_number
if speed in ("slow","normal","fast"):
the_ball_speed = speed
if size in ("small","medium","large"):
the_bar_size = size
if number in (1,2,3):
the_ball_number = number
def startGame():
call = have_settings()
background = games.load_image("BG.jpg", transparent = False)
games.screen.background = background
#-------------------------------------SPEED
sp_is = 0
if the_ball_speed == "slow":
sp_is = 2
elif the_ball_speed == "normal":
sp_is = 3
elif the_ball_speed == "fast":
sp_is = 4
#-------------------------------------BALL NUMBER
if the_ball_number in (1,2,3):
for n in range(the_ball_number):
position_x_list =(50,150,250,350,400,450,500,550)
position_x = random.choice(position_x_list)
position_y_list =(50,100,150,200,225,250)
position_y = random.choice(position_y_list)
vert_speed_list = (-2,2)
vert_speed = random.choice(vert_speed_list)
ball_img = games.load_image("ball.bmp")
ball = Bounce(image = ball_img,
x = position_x,
y = position_y,
dx = vert_speed,
dy = - sp_is)
games.screen.add(ball)
#-------------------------------------BAR SIZE
if the_bar_size in ("small","medium","large"):
if the_bar_size == "small":
bar_pic = "bar_small.jpg"
elif the_bar_size == "medium":
bar_pic = "bar_medium.jpg"
elif the_bar_size == "large":
bar_pic = "bar_large.jpg"
bar = games.load_image(bar_pic, transparent = False)
the_bar = Bar_moving(image = bar, x = games.mouse.x)
games.screen.add(the_bar)
games.mouse.is_visible = False
games.screen.event_grab = True
games.screen.mainloop()
You should do a greater than check rather than an equals check as follows:
if self.bottom >= 315 and self.overlapping_sprites:
^^
instead of
if self.bottom == 315 and self.overlapping_sprites:
This is because rarely will the ball's y position ever perfectly line up with the bottom. In some cases it may go from y==314 to y==316. In such cases, your method above wouldn't work. Therefore, you should be a greater than test rather than an equality test.
You can apply similar changes everywhere else and it should work.

Need help timing the iterations for a for loop

any help you can give me would be great. I am working on a simple game in python. I would like to have a countdown timer running on the screen. Based on other questions answered on this site, I think I have the code mostly correct, the problem I am having is that I am running the timer code using a "for" loop. I am not really sure why it is not working for me at the moment, when I run the game, I do not get any errors, the countdown displays but it has only counted down one second and it sticks on this time (01:29). Any suggestions would be really appreciated. The code that I am struggling with is towards the end of the program in a section called """Timer Countdown""" in the main loop for the game (line 354). Many thanks.
# Ice Hockey Game
from livewires import games, color
from tkinter import*
import pygame
import math, random
import os
import sys
import time
pygame.init()
games.init(screen_width = 1016, screen_height = 511, fps = 50)
################################################################################
"""timer variables"""
################################################################################
clock = pygame.time.Clock()
font = pygame.font.Font(None, 25)
red = ( 255, 0, 0)
frame_count = 0
frame_rate = 50
start_time = 90
done= False
output_string = ""
################################################################################
"""Score display variables"""
################################################################################
right_score = games.Text(value = 0, size = 25, color = color.black,
top = 30, right = games.screen.width - 250,
is_collideable = False)
games.screen.add(right_score)
left_score = games.Text(value = 0, size = 25, color = color.black,
top = 30, right = 250,
is_collideable = False)
games.screen.add(left_score)
player1_message = games.Message(value = "Player 1 Score",
size = 35,
color = color.black,
x = 250,
y = 15,
lifetime = 100000000,
is_collideable = False)
games.screen.add(player1_message)
player2_message = games.Message(value = "Player 2 Score",
size = 35,
color = color.black,
x = 750,
y = 15,
lifetime = 100000000,
is_collideable = False)
games.screen.add(player2_message)
###############################################################################
"""Player 1 and Player 2 WIN messages"""
###############################################################################
p1_won_message = games.Message(value = "Player 1 Wins!!!",
size = 100,
color = color.blue,
x = games.screen.width/2,
y = games.screen.height/2,
lifetime = 500,
after_death = games.screen.quit,
is_collideable = False)
p2_won_message = games.Message(value = "Player 2 Wins!!!",
size = 100,
color = color.blue,
x = games.screen.width/2,
y = games.screen.height/2,
lifetime = 500,
after_death = games.screen.quit,
is_collideable = False)
##############################################################################
"""Goal animation images and functions"""
################################################################################
"""Animation images"""
goal_anim_files = ["Goal_Anim_001.bmp", "Goal_Anim_002.bmp", "Goal_Anim_003.bmp",
"Goal_Anim_004.bmp","Goal_Anim_005.bmp","Goal_Anim_006.bmp",
"Goal_Anim_007.bmp","Goal_Anim_008.bmp","Goal_Anim_009.bmp","Goal_Anim_010.bmp", "Goal_Anim_011.bmp", "Goal_Anim_012.bmp", "Goal_Anim_013.bmp",
"Goal_Anim_014.bmp","Goal_Anim_015.bmp","Goal_Anim_016.bmp",
"Goal_Anim_017.bmp","Goal_Anim_018.bmp","Goal_Anim_019.bmp","Goal_Anim_020.bmp", "Goal_Anim_021.bmp","Goal_Anim_022.bmp", "Goal_Anim_023.bmp",
"Goal_Anim_024.bmp","Goal_Anim_025.bmp"]
def goal_msg_left(self):
global goal_anim_left
goal_anim_left = games.Animation(images = goal_anim_files, x = 250,
y= games.screen.height/2, n_repeats = 1,
repeat_interval = 1, is_collideable = False)
#print("inside goal_msg_left")
def goal_msg_right(self):
global goal_anim_right
goal_anim_right = games.Animation(images = goal_anim_files, x = 750,
y= games.screen.height/2, n_repeats = 1,
repeat_interval = 1, is_collideable = False)
#print("inside goal_msg_right")
class Leftgoalanim(games.Animation):
"""goal animation"""
def update(self):
self.check_collide()
def check_collide(self):
for player1 in self.overlapping_sprites:
player1.handle_player_collide()
for player2 in self.overlapping_sprites:
player2.handle_player_collide()
class Leftgoal(games.Sprite):
def update(self):
self.check_collide()
self.check_goal()
def check_collide(self):
""" Check for collision with puck. """
for puck in self.overlapping_sprites:
puck.handle_collide()
def handle_collide(self):
""" Move to a random screen location. """
self.dx = -self.dx
self.dy = 0
self.x = games.screen.width/2
self.y = games.screen.height/2
def check_goal(self):
""" Check if left goal. """
global goal_anim_left
for puck in self.overlapping_sprites:
#puck.handle_collide()
goal_msg_left(self)
games.screen.add(goal_anim_left)
right_score.value += 1
if right_score.value >= 10:
games.screen.add(p2_won_message)
class Rightgoal(games.Sprite):
def update(self):
self.check_collide()
self.check_goal()
def check_collide(self):
""" Check for collision with puck. """
for puck in self.overlapping_sprites:
puck.handle_collide()
def handle_collide(self):
""" Move to a random screen location. """
self.dx = -self.dx
self.dy = 0
self.x = games.screen.width/2
self.y = games.screen.height/2
def check_goal(self):
""" Check if left goal. """
for puck in self.overlapping_sprites:
#puck.handle_collide()
goal_msg_right(self)
games.screen.add(goal_anim_right)
left_score.value += 1
if left_score.value >= 10:
games.screen.add(p1_won_message)
################################################################################
"""Classes for Players sprites"""
################################################################################
class Player1(games.Sprite):
"""move the player 1"""
VELOCITY_STEP = .03
VELOCITY_MAX = 3
def update(self):
if games.keyboard.is_pressed(games.K_w):
self.y -= 3
if games.keyboard.is_pressed(games.K_s):
self.y += 3
if games.keyboard.is_pressed(games.K_a):
self.x -= 3
if games.keyboard.is_pressed(games.K_d):
self.x += 3
if self.right > 940:
self.x = 913
if self.left < 85:
self.x = 108
if self.bottom > games.screen.height:
self.y = 475
if self.top < 0:
self.y = 50
self.check_collide()
def check_collide(self):
""" Check for collision with puck. """
for puck in self.overlapping_sprites:
puck.handle_collide()
def handle_player_collide(self):
self.dx = -self.dx
self.dy = 0
def handle_collide(self):
""" Move to a random screen location. """
self.dx = -self.dx
self.dy = 0
class Player2(games.Sprite):
"""move the player 2"""
VELOCITY_STEP = .03
VELOCITY_MAX = 3
def update(self):
if games.keyboard.is_pressed(games.K_UP):
self.y -= 3
if games.keyboard.is_pressed(games.K_DOWN):
self.y += 3
if games.keyboard.is_pressed(games.K_LEFT):
self.x -= 3
if games.keyboard.is_pressed(games.K_RIGHT):
self.x += 3
if self.right > 940:
self.x = 913
if self.left < 85:
self.x = 108
if self.bottom > games.screen.height:
self.y = 475
if self.top < 0:
self.y = 50
self.check_collide()
def check_collide(self):
""" Check for collision with puck. """
for puck in self.overlapping_sprites:
puck.handle_collide()
def handle_collide(self):
""" Move to a random screen location. """
self.dx = -self.dx
self.dy = 0
def handle_player_collide(self):
self.dx = -self.dx
self.dy = 0
################################################################################
"""Class for Puck"""
################################################################################
class Puck(games.Sprite):
""" A bouncing puck. """
def update(self):
""" Reverse a velocity component if edge of screen reached. """
if self.right > games.screen.width or self.left < 0:
self.dx = -self.dx
if self.bottom > games.screen.height or self.top < 0:
self.dy = -self.dy
def handle_collide(self):
""" reverses x direction when collides. """
self.dx = -self.dx
self.dy = self.dy
def handle_goal(self):
"""what happens to the puck when goal"""
self.dx = -self.dx
self.dy = self.dy
################################################################################
"""Main Loop For Game"""
################################################################################
def main():
wall_image = games.load_image("Table_002_1016H_511W.jpg", transparent = False)
games.screen.background = wall_image
#########image left and right goal images and add them to game##########
left_goal_image = games.load_image("Goal_left_cropped.bmp")
the_left_goal = Leftgoal(image = left_goal_image,
x = 40,
y = games.screen.height/2,
)
right_goal_image = games.load_image("Goal_right_cropped.bmp")
the_right_goal = Rightgoal(image = right_goal_image,
x = 976,
y = games.screen.height/2,
)
#########player 1 import image and add to game##########################
player1_image = games.load_image("Player_red_half_50_75_Fixed.bmp")
the_player1 = Player1(image = player1_image,
x = games.screen.width/4,
y = games.screen.height/2)
games.screen.add(the_player1)
#########player 2 import image and add to game##########################
player2_image = games.load_image("Player_green_half_50_75_flipped_fixed.bmp")
the_player2 = Player2(image = player2_image,
x = 750,
y = games.screen.height/2)
games.screen.add(the_player2)
################Import image for the Puck and Add to the game###########
puck_image = games.load_image("puck_small.png", transparent = True)
the_puck = Puck(image = puck_image,
x = games.screen.width/2,
y = games.screen.height/2,
dx = -3,
dy = 3)
counter = 0
###########################################################################
"""TIMER COUNTDOWN"""
###########################################################################
clock = pygame.time.Clock()
font = pygame.font.Font(None, 25)
red = ( 255, 0, 0)
frame_count = 0
frame_rate = 50
start_time = 90
done= False
output_string = ""
for n in reversed(range(0, start_time)):
total_seconds = frame_count // frame_rate
minutes = total_seconds // 60
seconds = total_seconds % 60
output_string = "Time: {0:02}:{1:02}".format(minutes, seconds)
# Blit to the screen
text = font.render(output_string, True, red)
#screen.blit(text, [250, 250])
# --- Timer going down ---
# --- Timer going up ---
# Calculate total seconds
total_seconds = start_time - (frame_count // frame_rate)
if total_seconds < 0:
total_seconds = 0
# Divide by 60 to get total minutes
minutes = total_seconds // 60
# Use modulus (remainder) to get seconds
seconds = total_seconds % 60
# Use python string formatting to format in leading zeros
output_string = "Time left: {0:02}:{1:02}".format(minutes, seconds)
# Blit to the screen
text = font.render(output_string, True, red)
#screen.blit(text, [250, 280])
# ALL CODE TO DRAW SHOULD GO ABOVE THIS COMMENT
frame_count += 1
# Limit to 20 frames per second
clock.tick_busy_loop(50)
# Go ahead and update the screen with what we've drawn.
pygame.display.flip()
###Timer Display###
timer_text = games.Message(value = "TIME",
size = 45,
color = color.red,
top = 30, right = games.screen.width/2,
lifetime = 100000000,
is_collideable = False)
timer_countdown = games.Text(value = output_string, size = 25, color = color.red,
top = 60, right = 600,
is_collideable = False)
games.screen.add(timer_countdown)
games.screen.add(timer_text)
games.screen.add(the_left_goal)
games.screen.add(the_right_goal)
games.screen.add(the_puck)
games.screen.event_grab = True
games.mouse.is_visible = False
games.screen.mainloop()
# start the game
main()
Using pygame.time.get_ticks() to store a start time,
calling this again every frame will give a time greater than your stored start time, simply subtract your start time from this to get the interval in milliseconds since the timer started
As #jonrsharpe said however, it should be a code "sample", not the whole sodding thing, As such I might not have actually answered your question, I can't tell because I'm not going to read and understand the whole code.

Categories

Resources