Reputation: 876
I made a snake game with pygame and it works fine. However, it's sometimes not very responsive to user inputs. I'm using a clock and delay to set how fast the snake should move.
Movement code:
pygame.time.delay(1)
clock.tick(7)
player.move()
render_screen()
Render Screen:
screen.fill((0, 0, 0))
draw_grid()
player.draw()
food.draw()
screen.fill((0, 0, 0), (0, 513, 512, 50))
start.draw()
screen.blit(comic_font.render('Score: ' + str(score), False, (255, 100, 0)), (10, size + 5))
pygame.display.update()
Full Code:
import pygame
import random
pygame.init()
class Snake:
def __init__(self, x, y):
self.x = x
self.y = y
self.speed_x = 1
self.speed_y = 0
segments = []
for a in range(1, 4):
segments.append(Segment(self.x - a, self.y))
self.segments = segments
def move(self):
global score, game_mode
# handle key inputs
keys = pygame.key.get_pressed()
for key in keys:
if self.speed_x == 0:
if keys[pygame.K_a]:
self.speed_x = -1
self.speed_y = 0
if keys[pygame.K_d]:
self.speed_x = 1
self.speed_y = 0
elif self.speed_y == 0:
if keys[pygame.K_w]:
self.speed_x = 0
self.speed_y = -1
if keys[pygame.K_s]:
self.speed_x = 0
self.speed_y = 1
# check for wall collisions
if self.x + self.speed_x < 0 or self.x + self.speed_x > rows - 1 or self.y + self.speed_y > rows - 1 or \
self.y + self.speed_y < 0:
game_mode = "end"
start.text = "RETRY"
return None
# moves snake
self.segments.insert(0, Segment(self.x, self.y))
self.x += self.speed_x
self.y += self.speed_y
# checks for self collisions
for segment in self.segments:
if self.x == segment.x and self.y == segment.y and self.segments.index(segment)!=len(self.segments)-1:
game_mode = "end"
start.text = "RETRY"
break
# checks for eating food
if self.x == food.x and self.y == food.y:
food.x = random.randrange(0, 16)
food.y = random.randrange(0, 16)
score += 1
else:
self.segments.pop()
def draw(self):
for segment in self.segments:
segment.draw()
pygame.draw.rect(screen, (255, 0, 0), (self.x * size / rows, self.y * size / rows, size / rows, size / rows))
class Segment:
def __init__(self, x, y):
self.x = x
self.y = y
def draw(self):
pygame.draw.rect(screen, (0, 255 - player.segments.index(self), 0),
(self.x * size / rows, self.y * size / rows, size / rows, size / rows))
class Food:
def __init__(self, x, y):
self.x = x
self.y = y
def draw(self):
pygame.draw.rect(screen, (255, 255, 0), (self.x * size / rows, self.y * size / rows, size / rows, size / rows))
class button:
def __init__(self, x, y, r, g, b, text, font, width, height):
self.x = x
self.y = y
self.r = r
self.b = b
self.g = g
self.text = text
self.font = font
self.width = width
self.height = height
def draw(self):
pygame.draw.rect(screen, (self.r, self.g, self.b), (self.x, self.y, self.width, self.height))
screen.blit(self.font.render(self.text, False, (0, 100, 0)), (self.x + 10, self.y))
def mouse_over(self, pos):
if self.x < pos[0] < self.x + self.width and self.y < pos[1] < self.y + self.height:
return True
def draw_grid():
margin = size / rows
x = 0
y = 0
for line in range(rows):
x = x + margin
y = y + margin
pygame.draw.line(screen, (255, 255, 255), (x, 0), (x, size))
pygame.draw.line(screen, (255, 255, 255), (0, y), (size, y))
def render_screen():
screen.fill((0, 0, 0))
draw_grid()
player.draw()
food.draw()
screen.fill((0, 0, 0), (0, 513, 512, 50))
start.draw()
screen.blit(comic_font.render('Score: ' + str(score), False, (255, 100, 0)), (10, size + 5))
pygame.display.update()
size = 512
rows = 16
screen = pygame.display.set_mode((size, size + 50))
pygame.display.set_caption("Snake")
comic_font = pygame.font.SysFont("Comic Sans MS", 30)
clock = pygame.time.Clock()
running = True
game_mode = "wait"
player = Snake(8, 8)
food = Food(random.randrange(0, 16), random.randrange(0, 16))
start = button(size - 155, size + 5, 0, 255, 0, "PLAY", comic_font, 150, 40)
score = 0
render_screen()
while running:
for event in pygame.event.get():
pos = pygame.mouse.get_pos()
if event.type == pygame.QUIT:
running = False
elif event.type == pygame.MOUSEBUTTONDOWN:
if start.mouse_over(pos):
if game_mode == "wait":
game_mode = "play"
start.text = "PAUSE"
elif game_mode == "play":
game_mode = "wait"
start.text = "RESUME"
elif game_mode == "end":
game_mode = "play"
start.text = "PAUSE"
score = 0
running = True
game_mode = "wait"
player = Snake(8, 8)
food = Food(random.randrange(0, 16), random.randrange(0, 16))
render_screen()
if game_mode == "play":
pygame.time.delay(1)
clock.tick(7)
player.move()
render_screen()
The full code is above to reproduce the problem.
After experimenting with this a bit, it seems that when the pygame.time.delay(1) is smaller, there will be less delay.
I think that the user gets the input read only between the tics so they have to input exactly on the spot.
I think maybe this is why the input is less behind when the delay is smaller as the delay will affect how long between each input.
Does anyone know why the game doesn't receive inputs well at certain times?
Upvotes: 0
Views: 176
Reputation: 210880
pygame.key.get_pressed()
returns a list, with the states of all keyboard buttons. It makes no sense to iterate through this list, because you just want to evaluate the current state of the keys wasd.
Set the speed dependent on the pressed keys:
class Snake:
# [...]
def move(self):
# [...]
# handle key inputs
keys = pygame.key.get_pressed()
if self.speed_x == 0:
if keys[pygame.K_a]:
self.speed_x, self.speed_y = -1, 0
elif keys[pygame.K_d]:
self.speed_x, self.speed_y = 1, 0
elif self.speed_y == 0:
if keys[pygame.K_w]:
self.speed_x, self.speed_y = 0, -1
elif keys[pygame.K_s]:
self.speed_x, self.speed_y = 0, 1
# [...]
pygame.time.Clock.tick
delays to keep the game running slower than the given ticks per second. The states are returned by pygame.key.get_pressed()
are evaluated, when the events are handled (pygame.event.get()
).
Delay the game, after the events have been handled, the snake has been moved and the display was updated:
while running:
for event in pygame.event.get():
# [...]
if game_mode == "play":
player.move()
render_screen()
clock.tick(7) # <---
Upvotes: 2