I just wrote a snake game using pygame module.
After testing, I found that when I rapidly change the snake direction. E.g. pressing two arrow keys very fast to move snake body to next line or change to opposite direction, the snake doesn't respond accurately. Most of the time it will work, but there are few times snake doesn't move. I believe this is because of the low FPS, but if I increase it, the snake will move so fast.
Here is the code:
# snake game
import pygame, sys, random, time
# game initialization
check_errors = pygame.init()
if check_errors[1] > 0:
print('(!) Got {0} errors during initializing pygame \
exiting...'.format(check_errors[1]))
sys.exit(-1)
else:
print('(+) pygame successfully initialized.')
# game screen
screen_width = 750
screen_height = 495
game_screen = pygame.display.set_mode((screen_width, screen_height))
pygame.display.set_caption('Snake game')
# colors
red = pygame.Color(255, 0, 0) # game over
green = pygame.Color(0, 255, 0) # snake body
black = pygame.Color(0, 0, 0) # player score
white = pygame.Color(255, 255, 255) # game background
brown = pygame.Color(165, 42, 42) # food
# FPS controller
fps_controller = pygame.time.Clock()
# game variables
start_x = 300
start_y = 150
step = 15 # block width is 10
initial_body_length = 3
snake_head = [start_x, start_y] # snake start position [x, y]
# initialize snake body, index 0 contains the snake head
snake_body = [[start_x - i * step, start_y] for i in range(initial_body_length)]
score = 0
level = 1
food_pos = [random.randrange(2, screen_width / step - 1) * step, \
random.randrange(2, screen_height / step - 1) * step] # don't put food at the border of the screen
food_spawn = True
direction = 'RIGHT'
next_direction = direction # new direction after user hits keyboard
def draw_game_menu():
count = 3
my_font = pygame.font.SysFont('monaco', 60)
while True:
game_screen.fill(white)
start_surface = my_font.render('Start in {0} seconds.'.format(count), True, black)
start_rect = start_surface.get_rect()
start_rect.midtop = (screen_width / 2, 80)
game_screen.blit(start_surface, start_rect)
esc_surface = my_font.render('''Press Esc to exit during game.''', True, black)
esc_rect = esc_surface.get_rect()
esc_rect.midtop = (screen_width / 2, 150)
game_screen.blit(esc_surface, esc_rect)
pause_surface = my_font.render('''Press Space to pause the game.''', True, black)
pause_rect = pause_surface.get_rect()
pause_rect.midtop = (screen_width / 2, 220)
game_screen.blit(pause_surface, pause_rect)
pygame.display.flip() # update the game screen
time.sleep(1)
fps_controller.tick()
count -= 1
if count == 0: break
def draw_game_pause():
my_font = pygame.font.SysFont('monaco', 40)
while True:
pause_surface = my_font.render('Press Space to continue.', True, black)
pause_rect = pause_surface.get_rect()
pause_rect.midtop = (screen_width / 2, 150)
game_screen.blit(pause_surface, pause_rect)
pygame.display.flip()
fps_controller.tick()
for event in pygame.event.get():
if event.type == pygame.KEYDOWN:
if event.key == pygame.K_SPACE: return
def show_score(game_over=False):
my_font = pygame.font.SysFont('monaco', 40)
score_surface = my_font.render('Score: {0}'.format(score), True, black)
score_rect = score_surface.get_rect()
if game_over == False:
score_rect.midtop = (75, 10)
else:
score_rect.midtop = (screen_width / 2, 130)
game_screen.blit(score_surface, score_rect)
# game over function
def draw_game_over():
my_font = pygame.font.SysFont('monaco', 60)
GO_surface = my_font.render('Game Over !', True, red)
GO_rect = GO_surface.get_rect()
GO_rect.midtop = (screen_width/2, 60)
game_screen.blit(GO_surface, GO_rect)
show_score(game_over=True)
pygame.display.flip() # update the game screen
time.sleep(4)
pygame.quit() # quit the game
sys.exit() # exit the console
def get_food(food_pos, snake_body):
for block in snake_body:
if block[0] == food_pos[0] and block[1] == food_pos[1]:
return True
return False
# game start menu
draw_game_menu()
# main logic of the game
while True:
for event in pygame.event.get():
if event.type == pygame.KEYDOWN: # if user press any button
if event.key == pygame.K_RIGHT or event.key == ord('d'):
next_direction = 'RIGHT'
elif event.key == pygame.K_LEFT or event.key == ord('a'):
next_direction = 'LEFT'
elif event.key == pygame.K_UP or event.key == ord('w'):
next_direction = 'UP'
elif event.key == pygame.K_DOWN or event.key == ord('s'):
next_direction = 'DOWN'
elif event.key == pygame.K_ESCAPE: # if user choose to quit the game
pygame.event.post(pygame.event.Event(pygame.QUIT))
elif event.key == pygame.K_SPACE:
draw_game_pause()
if event.type == pygame.QUIT:
pygame.quit()
sys.exit()
# validation of direction
if next_direction == 'RIGHT' and direction != 'LEFT':
direction = 'RIGHT'
elif next_direction == 'LEFT' and direction != 'RIGHT':
direction = 'LEFT'
elif next_direction == 'DOWN' and direction != 'UP':
direction = 'DOWN'
elif next_direction == 'UP' and direction != 'DOWN':
direction = 'UP'
# move snake head
if direction == 'RIGHT':
snake_head[0] += step
elif direction == 'LEFT':
snake_head[0] -= step
elif direction == 'DOWN':
snake_head[1] += step
elif direction == 'UP':
snake_head[1] -= step
# move snake body mechanism
# 1. insert a new block at the beginning of the body
# 2. check if snake has reached a food
# 2.1 if yes, then keep the modified body
# 2.2 if not, then delete the end of the body
snake_body.insert(0, list(snake_head))
if snake_head[0] == food_pos[0] and snake_head[1] == food_pos[1]:
food_spawn = False
score += 1
else:
snake_body.pop()
while food_spawn == False:
food_pos = [random.randrange(2, screen_width / step - 1) * step,
random.randrange(2, screen_height / step - 1) * step]
if get_food(food_pos, snake_body) == True:
food_spawn = False
else:
food_spawn = True
# fill game background
game_screen.fill(white)
# draw snake body
for pos in snake_body:
pygame.draw.rect(game_screen, green, pygame.Rect(pos[0], pos[1], step, step))
# draw food
pygame.draw.rect(game_screen, brown, pygame.Rect(food_pos[0], food_pos[1], step, step))
# check if snake hits the border
if (snake_head[0] > screen_width - step) or (snake_head[0] < 0) or \
(snake_head[1] > screen_height - step) or (snake_head[1] < 0):
draw_game_over()
# check if snake hits itself
for block in snake_body[1:]:
if snake_head[0] == block[0] and snake_head[1] == block[1]:
draw_game_over()
level = score//5 + 1
if level > 3:
level = 3
show_score(game_over=False)
pygame.display.flip()
if level == 1:
fps_controller.tick(8)
elif level == 2:
fps_controller.tick(10)
elif level == 3:
fps_controller.tick(12)
Please help to see if there is a way to improve, thanks.
First of all, you should try to use a single main loop.
While you're rendering the start or end screen, you can't interact with the window because no event loop runs. It's very annoying that you can't move or close the window during that time. Maybe take a look here for an example of how to handle this.
Second, really use a higher framerate, and don't tie the speed of your game objects to the frame rate.
There are several ways to handle this, an example is to use an event that signals when the snake should move. Here's an example I wrote for another question.
Here's a simple implementation for your current code:
MOVE_SNAKE = pygame.USEREVENT
pygame.time.set_timer(MOVE_SNAKE, 300)
# main logic of the game
while True:
for event in pygame.event.get():
if event.type == pygame.KEYDOWN: # if user press any button
if event.key == pygame.K_RIGHT or event.key == ord('d'):
next_direction = 'RIGHT'
elif event.key == pygame.K_LEFT or event.key == ord('a'):
next_direction = 'LEFT'
elif event.key == pygame.K_UP or event.key == ord('w'):
next_direction = 'UP'
elif event.key == pygame.K_DOWN or event.key == ord('s'):
next_direction = 'DOWN'
elif event.key == pygame.K_ESCAPE: # if user choose to quit the game
pygame.event.post(pygame.event.Event(pygame.QUIT))
elif event.key == pygame.K_SPACE:
draw_game_pause()
elif event.type == pygame.QUIT:
pygame.quit()
sys.exit()
elif event.type == MOVE_SNAKE:
# move snake head
if direction == 'RIGHT':
snake_head[0] += step
elif direction == 'LEFT':
snake_head[0] -= step
elif direction == 'DOWN':
snake_head[1] += step
elif direction == 'UP':
snake_head[1] -= step
# move snake body mechanism
# 1. insert a new block at the beginning of the body
# 2. check if snake has reached a food
# 2.1 if yes, then keep the modified body
# 2.2 if not, then delete the end of the body
snake_body.insert(0, list(snake_head))
if snake_head[0] == food_pos[0] and snake_head[1] == food_pos[1]:
food_spawn = False
score += 1
else:
snake_body.pop()
# validation of direction
if next_direction == 'RIGHT' and direction != 'LEFT':
direction = 'RIGHT'
elif next_direction == 'LEFT' and direction != 'RIGHT':
direction = 'LEFT'
elif next_direction == 'DOWN' and direction != 'UP':
direction = 'DOWN'
elif next_direction == 'UP' and direction != 'DOWN':
direction = 'UP'
while food_spawn == False:
food_pos = [random.randrange(2, screen_width / step - 1) * step,
random.randrange(2, screen_height / step - 1) * step]
if get_food(food_pos, snake_body) == True:
food_spawn = False
else:
food_spawn = True
# fill game background
game_screen.fill(white)
# draw snake body
for pos in snake_body:
pygame.draw.rect(game_screen, green, pygame.Rect(pos[0], pos[1], step, step))
# draw food
pygame.draw.rect(game_screen, brown, pygame.Rect(food_pos[0], food_pos[1], step, step))
# check if snake hits the border
if (snake_head[0] > screen_width - step) or (snake_head[0] < 0) or \
(snake_head[1] > screen_height - step) or (snake_head[1] < 0):
draw_game_over()
# check if snake hits itself
for block in snake_body[1:]:
if snake_head[0] == block[0] and snake_head[1] == block[1]:
draw_game_over()
new_level = score//5 + 1
if new_level != level:
pygame.time.set_timer(MOVE_SNAKE, 300 / new_level)
if new_level <= 3:
level = new_level
show_score(game_over=False)
pygame.display.flip()
fps_controller.tick(60)
See how easy it is now to control the speed of the snake: it moves now every 300ms.
Related
in the game that I have found on github there is a game over screen that shows your score, now I want to make the game restart if you press space so that you don't need to close the program and open it again to play it again. The problem isn't how to make the game restart when you press space but to actually make it restart. Here is the code:
import pygame
import random
import sys
import time
# Difficulty settings
# Easy -> 10
# Medium -> 25
# Hard -> 40
# Harder -> 60
# Impossible-> 120
difficulty = 25
# Window size
frame_size_x = 720
frame_size_y = 480
# Checks for errors encountered
check_errors = pygame.init()
# pygame.init() example output -> (6, 0)
# second number in tuple gives number of errors
if check_errors[1] > 0:
print(f'[!] Had {check_errors[1]} errors when initialising game, exiting...')
sys.exit(-1)
else:
print('[+] Game successfully initialised')
# Initialise game window
pygame.display.set_caption('Snake Eater')
game_window = pygame.display.set_mode((frame_size_x, frame_size_y))
# Colors (R, G, B)
black = pygame.Color(0, 0, 0)
white = pygame.Color(255, 255, 255)
red = pygame.Color(255, 0, 0)
green = pygame.Color(0, 255, 0)
blue = pygame.Color(0, 0, 255)
# FPS (frames per second) controller
fps_controller = pygame.time.Clock()
# Game variables
snake_pos = [100, 50]
snake_body = [[100, 50], [100 - 10, 50], [100 - (2 * 10), 50]]
food_pos = [random.randrange(1, (frame_size_x // 10)) * 10, random.randrange(1, (frame_size_y // 10)) * 10]
food_spawn = True
direction = 'RIGHT'
change_to = direction
score = 0
# Game Over
def game_over():
my_font = pygame.font.SysFont('times new roman', 90)
game_over_surface = my_font.render('YOU DIED', True, red)
game_over_rect = game_over_surface.get_rect()
game_over_rect.midtop = (frame_size_x / 2, frame_size_y / 4)
game_window.fill(black)
game_window.blit(game_over_surface, game_over_rect)
show_score(0, red, 'times', 20)
pygame.display.update()
time.sleep(3)
pygame.quit()
sys.exit()
# Score
def show_score(choice, color, font, size):
score_font = pygame.font.SysFont(font, size)
score_surface = score_font.render('Score : ' + str(score), True, color)
score_rect = score_surface.get_rect()
if choice == 1:
score_rect.midtop = (frame_size_x - 100, 15)
else:
score_rect.midtop = (frame_size_x / 2, frame_size_y / 1.25)
game_window.blit(score_surface, score_rect)
# pygame.display.flip()
# Main logic
while True:
for event in pygame.event.get():
if event.type == pygame.QUIT:
pygame.quit()
sys.exit()
# Whenever a key is pressed down
elif event.type == pygame.KEYDOWN:
# W -> Up; S -> Down; A -> Left; D -> Right
if event.key == pygame.K_UP or event.key == ord('w'):
change_to = 'UP'
if event.key == pygame.K_DOWN or event.key == ord('s'):
change_to = 'DOWN'
if event.key == pygame.K_LEFT or event.key == ord('a'):
change_to = 'LEFT'
if event.key == pygame.K_RIGHT or event.key == ord('d'):
change_to = 'RIGHT'
# Esc -> Create event to quit the game
if event.key == pygame.K_ESCAPE:
pygame.event.post(pygame.event.Event(pygame.QUIT))
# Making sure the snake cannot move in the opposite direction instantaneously
if change_to == 'UP' and direction != 'DOWN':
direction = 'UP'
if change_to == 'DOWN' and direction != 'UP':
direction = 'DOWN'
if change_to == 'LEFT' and direction != 'RIGHT':
direction = 'LEFT'
if change_to == 'RIGHT' and direction != 'LEFT':
direction = 'RIGHT'
# Moving the snake
if direction == 'UP':
snake_pos[1] -= 10
if direction == 'DOWN':
snake_pos[1] += 10
if direction == 'LEFT':
snake_pos[0] -= 10
if direction == 'RIGHT':
snake_pos[0] += 10
# Snake body growing mechanism
snake_body.insert(0, list(snake_pos))
if snake_pos[0] == food_pos[0] and snake_pos[1] == food_pos[1]:
score += 1
food_spawn = False
else:
snake_body.pop()
# Spawning food on the screen
if not food_spawn:
food_pos = [random.randrange(1, (frame_size_x // 10)) * 10, random.randrange(1, (frame_size_y // 10)) * 10]
food_spawn = True
# GFX
game_window.fill(black)
for pos in snake_body:
# Snake body
# .draw.rect(play_surface, color, xy-coordinate)
# xy-coordinate -> .Rect(x, y, size_x, size_y)
pygame.draw.rect(game_window, green, pygame.Rect(pos[0], pos[1], 10, 10))
# Snake food
pygame.draw.rect(game_window, white, pygame.Rect(food_pos[0], food_pos[1], 10, 10))
# Game Over conditions
# Getting out of bounds
if snake_pos[0] < 0 or snake_pos[0] > frame_size_x - 10:
game_over()
if snake_pos[1] < 0 or snake_pos[1] > frame_size_y - 10:
game_over()
# Touching the snake body
for block in snake_body[1:]:
if snake_pos[0] == block[0] and snake_pos[1] == block[1]:
game_over()
show_score(1, white, 'consolas', 20)
# Refresh game screen
pygame.display.update()
# Refresh rate
fps_controller.tick(difficulty)
Implement a function that initializes all the global game states:
def init_game():
global snake_pos, snake_body
global food_pos, food_spawn
global direction, change_to, score
# Game variables
snake_pos = [100, 50]
snake_body = [[100, 50], [100 - 10, 50], [100 - (2 * 10), 50]]
food_pos = [random.randrange(1, (frame_size_x // 10)) * 10, random.randrange(1, (frame_size_y // 10)) * 10]
food_spawn = True
direction = 'RIGHT'
change_to = direction
score = 0
Invoke the function when SPACE is pressed:
while True:
for event in pygame.event.get():
if event.type == pygame.QUIT:
pygame.quit()
sys.exit()
# Whenever a key is pressed down
elif event.type == pygame.KEYDOWN:
# [...]
if event.key == pygame.K_SPACE:
init_game()
This question already has answers here:
Not letting the character move out of the window
(2 answers)
How to make ball bounce off wall with PyGame?
(1 answer)
Closed 2 years ago.
i am new to Pygames and have been trying to figure out how to stop my Player1 (rect) from going off the edge of the screen, have tried so much and can't seem to understand what to do, any help is greatly appreciated
Here is my code so far...
import pygame # accesses pygame files
import sys # to communicate with windows
# game setup ################ only runs once
pygame.init() # starts the game engine
clock = pygame.time.Clock() # creates clock to limit frames per second
FPS = 60 # sets max speed of main loop
SCREENSIZE = SCREENWIDTH, SCREENHEIGHT = 500, 500 # sets size of screen/window
screen = pygame.display.set_mode(SCREENSIZE) # creates window and game screen
# set variables for colors RGB (0-255)
white = (255, 255, 255)
black = (0, 0, 0)
red = (255, 0, 0)
yellow = (255, 255, 0)
green = (0, 255, 0)
gameState = "running" # controls which state the games is in
player1XPos = 200
player1YPos = 200
player1Direction = ""
player1Speed = 5
# game loop #################### runs 60 times a second!
while gameState != "exit": # game loop - note: everything in the mainloop is indented one tab
for event in pygame.event.get(): # get user interaction events
if event.type == pygame.QUIT: # tests if window's X (close) has been clicked
gameState = "exit" # causes exit of game loop
#EDGE SCREEN #EDGE SCREEN #EDGE SCREEN #EDGE SCREEN #EDGE SCREEN #EDGE SCREEN #EDGE SCREEN #EDGE SCREEN
#EDGE SCREEN #EDGE SCREEN #EDGE SCREEN #EDGE SCREEN #EDGE SCREEN #EDGE SCREEN #EDGE SCREEN #EDGE SCREEN
if event.type == pygame.KEYDOWN:
if event.key == pygame.K_LEFT:
#print ("left")
player1XPos -= 0
player1Direction = "left"
if event.key == pygame.K_RIGHT:
#print ("right")
player1XPos += 0
player1Direction = "right"
if event.key == pygame.K_UP:
#print ("up")
player1YPos -= 0
player1Direction = "up"
if event.key == pygame.K_DOWN:
#print ("down")
player1YPos += 0
player1Direction = "down"
#increase and decrease player1 speed below
if event.type == pygame.KEYDOWN:
if event.key == ord('w'):
player1Speed = 2.5
if event.type == pygame.KEYDOWN:
if event.key == ord('q'):
player1Speed = 5
# Player 1 Event handler code now...
if player1Direction == "left":
player1XPos -= player1Speed
elif player1Direction == "right":
player1XPos += player1Speed
if player1Direction == "up":
player1YPos -= player1Speed
if player1Direction == "down":
player1YPos += player1Speed
screen.fill(black)
player1 = pygame.draw.rect(screen, red, (player1XPos, player1YPos, 100, 100))
#player1 invisible and visible after spacebar
if event.type == pygame.KEYDOWN:
if event.key == pygame.K_SPACE:
player1 = pygame.draw.rect(screen, black, (player1XPos, player1YPos, 100, 100))
# your code starts here ##############################
# your code ends here ###############################
pygame.display.flip() # transfers build screen to human visable screen
clock.tick(FPS) # limits game to frame per second, FPS value
pygame.display.update()
# out of game loop ###############
print("The game has closed") # notifies user the game has ended
pygame.quit() # stops the game engine
sys.exit() # close operating system window
Restrict the top left position of the player (player1XPos, player1YPos) to the bounds of the screen. If the player goes out off bounds, then reset the moving direction (player1Direction):
while gameState != "exit":
# [...]
# Player 1 Event handler code now...
if player1Direction == "left":
player1XPos -= player1Speed
elif player1Direction == "right":
player1XPos += player1Speed
if player1Direction == "up":
player1YPos -= player1Speed
if player1Direction == "down":
player1YPos += player1Speed
# limit the player to the bounds
if player1XPos < 0:
player1XPos = 0
player1Direction = ""
if player1XPos > 400:
player1XPos = 400
player1Direction = ""
if player1YPos < 0:
player1YPos = 0
player1Direction = ""
if player1YPos > 400:
player1YPos = 400
player1Direction = ""
screen.fill(black)
player1 = pygame.draw.rect(screen, red, (player1XPos, player1YPos, 100, 100))
this is my first game I have created using pygame, it's basically snake.
Please do not mind my hideous comments.
## William's Snake Game
import pygame
import time
import random
pygame.init()
##Global variables
# Colours
white = (255, 255, 255)
black = (0, 0, 0)
red = (255, 0, 0)
green = (0, 155, 0)
bground = (204, 204, 0)
# Display vars.
display_width = 800
display_height = 600
# Variables
gameDisplay = pygame.display.set_mode((display_width, display_height)) ##resolution, note that this is a tuple.
pygame.display.set_caption("Snake VS Apple") # Title at the top of the screen
icon = pygame.image.load('logoapple32x32.png') # Loads the icon for the top left
pygame.display.set_icon(icon) # Sets the icon for the top left
snakeHeadImg = pygame.image.load("snakeHead.png") # Loads the image for the snake head
appleImg = pygame.image.load("apple20x20.png") # Loads the image for the apple
appleThickness = 20 # Defines how thick apple will be. Note to self: This is changable
clock = pygame.time.Clock() # Starts clocking the game, used later for FPS
blockSize = 20 # Defines how big the snake will be. Changing this will mess up collision detection.
FPS = 15 # Frames per second. Called at the bottom of script
smallfont = pygame.font.SysFont("arial", 25) ## format: ("font", fontsize)
medfont = pygame.font.SysFont("arial", 40) ##
largefont = pygame.font.SysFont("arial", 80) ##
direction = "right" # Starting direction of snake, used in main gameLoop
##
def pauseGame():
gameisPaused = True
while gameisPaused:
for event in pygame.event.get():
if event.type == pygame.QUIT:
pygame.quit()
quit()
if event.type == pygame.KEYDOWN:
if event.key == pygame.K_ESCAPE:
gameisPaused = False
elif event.key == pygame.K_q:
pygame.quit()
quit()
gameDisplay.fill(white)
message_to_screen("Paused",
black,
-100,
size="large")
message_to_screen("Press ESC to continue or Q to quit.",
black,
25,
size="small")
pygame.display.update()
clock.tick(5)
def text_objects(text, color, size): # Function to render text
if size == "small":
textSurface = smallfont.render(text, True, color)
elif size == "medium":
textSurface = medfont.render(text, True, color)
elif size == "large":
textSurface = largefont.render(text, True, color)
return textSurface, textSurface.get_rect()
def message_to_screen(msg, color, y_displace=0, size="medium"): # Function to blit (draw) text to surface
textSurf, textRect = text_objects(msg, color, size)
textRect.center = (display_width / 2), (display_height / 2) + y_displace
gameDisplay.blit(textSurf, textRect)
##
def score(score):
text = smallfont.render("Score: " + str(score), True, black)
gameDisplay.blit(text, [0, 0])
def randAppleGen(): # Function to generate random apples
randAppleX = round(
random.randrange(0, display_width - appleThickness)) # /10.0)*10.0 ##Create another rand X value for new apple
randAppleY = round(
random.randrange(0, display_height - appleThickness)) # /10.0)*10.0 ##Create another rand Y value for new apple
return randAppleX, randAppleY
def gameIntro(): # Function for game menu.
intro = True
while intro: # Event handling during menu
for eachEvent in pygame.event.get():
if eachEvent.type == pygame.QUIT:
pygame.quit()
quit()
if eachEvent.type == pygame.KEYDOWN:
if eachEvent.key == pygame.K_c:
gameLoop()
if eachEvent.key == pygame.K_q:
pygame.quit()
quit()
# Text displayed in menu
gameDisplay.fill(white)
message_to_screen("Welcome to Slither",
green,
-90,
"large")
message_to_screen("The more apples you eat, the longer you are",
black,
100)
message_to_screen("The objective of the game is to eat red apples",
black,
0,
"small")
message_to_screen("If you run into yourself, or the edges, you die!",
black,
30,
"small")
message_to_screen("Press C to play or Q to quit.",
black,
180)
pygame.display.update()
clock.tick(500)
def snake(blockSize, snakeList): # Function to draw snake
if direction == "right":
head = pygame.transform.rotate(snakeHeadImg,
270) # In gameLoop, right,left,up,down are used to change direction of snakeHead
elif direction == "left":
head = pygame.transform.rotate(snakeHeadImg, 90)
elif direction == "up":
head = snakeHeadImg
elif direction == "down":
head = pygame.transform.rotate(snakeHeadImg, 180)
gameDisplay.blit(head, (snakeList[-1][0], snakeList[-1][1])) ##???
for XandY in snakeList[:-1]:
pygame.draw.rect(gameDisplay, green,
[XandY[0], XandY[1], blockSize, blockSize]) ##width height, width height, drawing
##Main Game Loop
def gameLoop():
global direction ## Make direction a global var. Important
# Local variables
gameExit = False
gameOver = False
lead_x = display_width / 2
lead_y = display_height / 2
lead_x_change = 10
lead_y_change = 0
snakeList = []
snakeLength = 3
randAppleX, randAppleY = randAppleGen() # Generate apples. Calls randAppleGen()
# Main Game Loop ##eventHandler
while not gameExit:
snakeHead = [] # Creates list snakeHead
snakeHead.append(lead_x) # Appends snakeHead x value to list
snakeHead.append(lead_y) # Appends snakeHead y value to list
snakeList.append(snakeHead) # Appends coordinates of snakeHead x,y to list
while gameOver == True: # Handles game over
gameDisplay.fill(white)
message_to_screen("Game over",
red,
-50,
size="large")
message_to_screen("Press C to play again or Q to quit",
black,
50,
size="medium")
pygame.display.update()
for event in pygame.event.get(): # eventHandler for loss screen
if event.type == pygame.QUIT:
pygame.quit()
quit()
if event.type == pygame.KEYDOWN:
if event.key == pygame.K_q:
gameExit = True
gameOver = False
if event.key == pygame.K_c:
direction = "right"
gameLoop()
for event in pygame.event.get(): # eventHandler for keyboard events during game
if event.type == pygame.QUIT:
gameExit = True
if event.type == pygame.KEYDOWN:
if event.key == pygame.K_LEFT or event.key == pygame.K_a: # Each of these handles either arrow keys or WASD key events.
lead_x_change = -blockSize
lead_y_change = 0
direction = "left"
elif event.key == pygame.K_RIGHT or event.key == pygame.K_d:
lead_x_change = blockSize
lead_y_change = 0
direction = "right"
elif event.key == pygame.K_UP or event.key == pygame.K_w:
lead_y_change = -blockSize
lead_x_change = 0
direction = "up"
elif event.key == pygame.K_DOWN or event.key == pygame.K_s:
lead_y_change = blockSize
lead_x_change = 0
direction = "down"
elif event.key == pygame.K_ESCAPE:
pauseGame()
# Checks if user has hit screen boundaries.
if lead_x >= display_width or lead_x < 0 or lead_y >= display_height or lead_y < 0: # If user hits display boundaries
gameOver = True # They lose
lead_x += lead_x_change # Ensures continous movement of the snake
lead_y += lead_y_change
# Drawing
gameDisplay.fill(bground) # Fills the display background with predefined bground colour (defined at the top)
gameDisplay.blit(appleImg,
(randAppleX, randAppleY)) ##Draws the apple using the appleImg, at random coordinates.
if len(
snakeList) > snakeLength: # If the length of the list of snake body coordinates is greater than the length
del snakeList[0] # Delete the oldest value in the list (as the snake is constantly moving)
for eachSegment in snakeList[:-1]: # For each coordinate in snakeList
if eachSegment == snakeHead: # If the segment touches the snakeHead
## gameDisplay.fill(bground)
## snake(blockSize, snakeList)
##
## pygame.display.update
time.sleep(0.3)
gameOver = True # Game over
snake(blockSize, snakeList) ##Creates snake using function snake
score(snakeLength - 3) # Displays score (it minuses 3 because the snake starts at 3)
pygame.display.update() ##Updates to screen
## COLLISION DETECTION
if lead_x + blockSize > randAppleX and lead_x < randAppleX + appleThickness:
if lead_y + blockSize > randAppleY and lead_y < randAppleY + appleThickness:
randAppleX, randAppleY = randAppleGen()
snakeLength += 1
clock.tick(FPS)
pygame.quit()
quit()
##update screen
##
gameIntro()
gameLoop()
##Code goes above.
I have a problem where if my snake is going right, and then it turns left, my snake will crash onto itself and the game will end.
I want to design this so when my snake is going right, it cannot simply turn left, crash into itself and end the game. So when it turns right, it can only turn up, down, or keep going right. So simply, I want to make it so it cannot run backwards into itself.
I have tried coding this in myself, and I have tried many methods but nothing has worked.
Please help!!
Without altering your code too much (and without testing), an easy way to get what you want is to add another condition to your key checks. If you alter the relevant code section as follows, all should be fine:
for event in pygame.event.get(): # eventHandler for keyboard events during game
if event.type == pygame.QUIT:
gameExit = True
if event.type == pygame.KEYDOWN:
if (event.key == pygame.K_LEFT or event.key == pygame.K_a) and direction != "right": # Each of these handles either arrow keys or WASD key events.
lead_x_change = -blockSize
lead_y_change = 0
direction = "left"
elif (event.key == pygame.K_RIGHT or event.key == pygame.K_d) and direction != "left":
lead_x_change = blockSize
lead_y_change = 0
direction = "right"
elif (event.key == pygame.K_UP or event.key == pygame.K_w) and direction != "down":
lead_y_change = -blockSize
lead_x_change = 0
direction = "up"
elif (event.key == pygame.K_DOWN or event.key == pygame.K_s) and direction != "up":
lead_y_change = blockSize
lead_x_change = 0
direction = "down"
elif event.key == pygame.K_ESCAPE:
pauseGame()
Mind the parentheses around the or'd key checks!
So I'm just learning how to work with classes and getting them to work between each different class. I'm trying to design a game where the user moves around and picks up food and each time the user picks up a piece of food the size of the character increases. I've done something similar before but now that there are classes involved I seem to have a hard time finding which class this should be part of. I added within the sprite update method that if it collides with a cherry then the size of the player should increase by 5 pixels each time. using the code :
self.Player.surface = pygame.transform.scale(self.Player.surface, (pwidth+5, pheight+5))
self.rect = self.Player.surface.get_rect()
Each time the game runs the player size doesn't change and for some reason the game no longer ends after the player has eaten a certain amount of cherries so I was just wondering if I was using a wrong method of changing the size of the player perhaps there may be an easier way to do so? Heres the rest of the code incase it helps at all.
import pygame, glob, random, time
from pygame.locals import *
from LabelClass import *
# CONSTANTS
WIDTH = 400 # Window width
HEIGHT = 400 # Window height
BLACK = (0,0,0) # Colors
WHITE = (255,255,255)
BACKGR = BLACK # Background Color
FOREGR = WHITE # Foreground Color
FPS = 40 # Frames per second
pwidth = 40
pheight = 40
class Food:
def __init__(self,screen,centerx,centery):
self.screen = screen
self.surface = pygame.image.load('cherry.png')
self.rect = self.surface.get_rect()
self.rect.centerx = centerx
self.rect.centery = centery
def draw(self):
self.screen.blit(self.surface,self.rect)
#pygame.display.update([self.rect])
class Player:
def __init__(self, screen, centerx,
centery, speed, backcolor):
self.surface = pygame.image.load('player.png')
self.rect = self.surface.get_rect()
self.rect.centerx = centerx
self.rect.centery = centery
self.speed = speed
self.screen = screen
self.backcolor = backcolor
self.dir = ''
def draw(self):
self.screen.blit(self.surface,self.rect)
#pygame.display.update([self.rect])
def move(self):
if self.dir != '':
if self.dir == 'd' and self.rect.bottom < HEIGHT:
self.rect.top += self.speed
if self.dir == 'u' and self.rect.top > 0:
self.rect.top -= self.speed
if self.dir == 'l' and self.rect.left > 0:
self.rect.left -= self.speed
if self.dir == 'r' and self.rect.right < WIDTH:
self.rect.right += self.speed
def jump(self,top,left):
self.rect.top = top
self.rect.left = left
class SpritesGame:
def __init__(self,screen):
self.screen = screen
screen.fill(BLACK)
pygame.display.update()
music_file = getRandomMusic()
pygame.mixer.music.load(music_file)
pygame.mixer.music.play(-1,0.0)
self.music = True
self.Foods = [ ]
self.Eaten = 0
for i in range(20):
self.Foods.append(
Food(self.screen,
WIDTH*random.randint(1,9)//10,
HEIGHT*random.randint(1,9)//10))
for f in self.Foods:
f.draw()
self.Player = Player(screen,WIDTH//2,HEIGHT//2,6,BLACK)
self.PickUpSound = pygame.mixer.Sound('pickup.wav')
self.PlaySound = True
self.startTime = time.clock()
self.endTime = -1
self.Won = False
def update(self):
self.screen.fill(BLACK)
pickedUp = False
for f in self.Foods[:]:
if self.Player.rect.colliderect(f.rect):
self.Foods.remove(f)
self.Foods.append(Food(self.screen,WIDTH*random.randint(1,9)//10,HEIGHT*random.randint(1,9)//10))
pickedUp = True
self.Eaten += 1
self.Player.surface = pygame.transform.scale(self.Player.surface, (pwidth+5, pheight+5))
self.rect = self.Player.surface.get_rect()
#self.rect.center = center
print self.Eaten
if pickedUp and self.PlaySound:
self.PickUpSound.play()
for f in self.Foods:
f.draw()
if self.Eaten == 40:
self.Won = True
self.endTime = time.clock()
self.Player.move()
self.Player.draw()
pygame.display.update()
def toggleMusic(self):
self.music = not self.music
if self.music:
pygame.mixer.music.play(-1,0.0)
else:
pygame.mixer.music.stop()
def run(self):
stop = False
while not stop:
for event in pygame.event.get():
if event.type == QUIT:
stop = True
if event.type == KEYDOWN: # Keeps moving as long as key down
if event.key == K_LEFT or event.key == ord('a'):
self.Player.dir = 'l'
if event.key == K_RIGHT or event.key == ord('d'):
self.Player.dir = 'r'
if event.key == K_UP or event.key == ord('w'):
self.Player.dir = 'u'
if event.key == K_DOWN or event.key == ord('s'):
self.Player.dir = 'd'
if event.type == KEYUP:
if event.key == ord('q'):
stop = True
if event.key == K_ESCAPE:
stop = True
if event.key == K_LEFT or event.key == ord('a'): # End repetition.
self.Player.dir = ''
if event.key == K_RIGHT or event.key == ord('d'):
self.Player.dir = ''
if event.key == K_UP or event.key == ord('w'):
self.Player.dir = ''
if event.key == K_DOWN or event.key == ord('s'):
self.Player.dir = ''
if event.key == ord('x'):
top = random.randint(0,
HEIGHT - self.Player.rect.height)
left = random.randint(0,
WIDTH - self.Player.rect.width)
self.Player.jump(top,left)
if event.key == ord('m'):
self.toggleMusic()
if event.key == ord('p'):
self.PlaySound = not self.PlaySound
mainClock.tick(FPS)
self.update()
if self.Won:
stop = True # END OF WHILE
if self.Won:
self.screen.fill(BLACK)
pygame.display.update()
msg = (str((int(self.endTime)
-int(self.startTime)))
+" seconds to finish. Hit Q.")
L2 = Label(display,WIDTH//2,HEIGHT*7//8,26,msg,WHITE,BLACK)
L2.draw()
stop = False
while not stop:
for event in pygame.event.get():
if event.type == KEYUP:
if event.key == ord('q'):
stop = True
pygame.event.get()
pygame.mixer.music.stop()
def getRandomMusic():
mfiles = glob.glob("*.wav")
mfiles.append(glob.glob("*.mid"))
r = random.randint(0,len(mfiles)-1)
return mfiles[r]
def OpeningScreen(screen):
screen.fill(BLACK)
pygame.display.update()
L1 = Label(display,WIDTH//2,HEIGHT*7//8,26,"Hit Q to Quit, P to Play.",WHITE, BLACK)
L1.draw()
# Properly initiate pygame
pygame.init()
# pygame.key.set_repeat(INT,INT)
# Set the clock up
mainClock = pygame.time.Clock()
# Initialize Display
display = pygame.display.set_mode((WIDTH,HEIGHT))
pygame.display.set_caption('Sprites and Sounds V06')
OpeningScreen(display)
stop = False
while not stop:
for event in pygame.event.get():
if event.type == QUIT:
stop = True
if event.type == KEYUP:
if event.key == ord('p'):
game = SpritesGame(display)
game.run()
OpeningScreen(display)
if event.key == ord('q'):
stop = True
pygame.quit()
Surface.get_rect() will always return a rect starting at (0,0), and you also are modifying SpritesGame.rect. I think you should change
self.rect = self.Player.surface.get_rect()
to
self.Player.rect.inflate_ip(5, 5)
My goal for this question is to be able to achieve an answer which explains how to make it so that when my character moves around to pick up the 'baddie(s)', the baddies will then automatically re-spawn onto the screen. Keeping total amount of sprites on the screen at any one time for the player to pick up at 40.
I am having problems being able to control the way the 'pikachu.jpg' sprites spawn. I am either able to spawn the sprites solely in a group fashion by entering an integer into my program manually or i write my attempt to test it wrong and the program upholds an infinite loop of drawing baddie sprites. I had set up a system which i believed should have coped with keeping the baddies at 40 sprites solid, though it did not work. I am not very good with python yet and i would really appreciate help with this issue.
import necessary pygame modules
import sys
import random
import time
import pygame
from pygame.locals import *
# initialise pygame
pygame.init()
# define window width and height variables for ease of access
WindowWidth = 600
WindowHeight = 500
global PLAY
PLAY = False
x = 600 / 3
y = 460 / 2 - 25
# load and define image sizes
enemyImage = pygame.image.load('pikachu.jpg')
enemyStretchedImage = pygame.transform.scale(enemyImage, (40,40))
screen = pygame.display.set_mode((WindowWidth, WindowHeight))
def play(PLAY):
playerImage = pygame.image.load('player.jpg')
playerStretchedImage = pygame.transform.scale(playerImage, (40, 40))
movex,movey = 0,0
charx, chary=300, 200
enemyy, enemyx = 10, 10
moveright = False
moveleft = False
spawn = True
direction = 'down'
baddie = []
for i in range(20):
baddie.append(pygame.Rect(random.randint(0, WindowWidth - 40), random.randint(0, 0), 40, 40))
while True:
for event in pygame.event.get():
if event.type == KEYDOWN:
if event.key == ord('m'):
pygame.mixer.music.stop()
music = False
if event.key == ord('n'):
pygame.mixer.music.play()
music = True
if event.key == ord('a'):
moveleft = True
if event.key == ord('d'):
moveright = True
if event.key == ord('w'):
movey = -0.5
if event.key == ord('s'):
movey = 0.5
if event.key == ord('p'):
time.sleep(5)
if event.type ==KEYUP:
if event.key == ord('a'):
moveleft = False
if moveleft == False:
movex = 0
if event.key == ord('d'):
moveright = False
if moveright == False:
movex = 0
if event.key == ord('w'):
movey = 0
if event.key == ord('s'):
movey = 0
elif event.type == QUIT:
pygame.quit()
sys.exit()
elif event.type == MOUSEBUTTONDOWN:
pygame.quit()
sys.exit()
screen.fill(pygame.Color("red"))
player = pygame.Rect(charx, chary, 40, 40)
if direction == 'down':
enemyy += 0.1
if moveright ==True:
movex = 0.5
if moveleft ==True:
movex = -0.5
if player.bottom > WindowHeight:
chary = WindowHeight - 40
movey = 0
if player.top < 0:
chary = 1
movey = 0
if player.left < 0:
charx = 1
movex = 0
if player.right > WindowWidth:
charx = WindowWidth - 40
movex = 0
for bad in baddie[:]: #Here is where my attempt of testing was.
if bad < 40:
spawn = True
if bad >= 40:
spawn = False
if spawn == True:
baddie.append(pygame.Rect(random.randint(0, WindowWidth - 40), random.randint(0,0), 40, 40))
screen.blit(playerStretchedImage, player)
charx+=movex
chary+=movey
for bad in baddie[:]:
if player.colliderect(bad):
baddie.remove(bad)
for bad in baddie:
screen.blit(enemyStretchedImage, bad)
pygame.display.update()
def presskey():
myfont = pygame.font.SysFont("monospace", 30)
label = myfont.render("press space to play!", 1, (255,125,60))
while True:
for event in pygame.event.get():
if event.type == QUIT:
pygame.quit()
sys.exit()
elif event.type == KEYDOWN:
if event.key == K_SPACE:
PLAY = True
if PLAY == True:
play(PLAY)
return
screen.fill(pygame.Color("cyan"))
screen.blit(label, (x,y))
pygame.display.update()
presskey()
Here you're trying to fill up count of baddies up to 40, but the bad is a rectangle and you're using it as a number of buddies:
for bad in baddie[:]: #Here is where my attempt of testing was.
if bad < 40:
spawn = True
if bad >= 40:
spawn = False
if spawn == True:
baddie.append(pygame.Rect(random.randint(0, WindowWidth - 40), random.randint(0,0), 40, 40))
I suggest you to replace those lines with:
if len(baddie)<40:
baddie.append(pygame.Rect(random.randint(0, WindowWidth - 40),
You can get the number of items in your [] list using len(baddie). You should get a new baddie each game loop cycle.