Pygame 当 fps 超过 15 时蛇速度太高
Pygame snake velocity too high when the fps above 15
在我使用 pygame 制作的贪吃蛇游戏中,我很难理解速度的物理原理。
问题是一旦我将 fps 设置为 15 以上,蛇的速度也会增加。
我知道这与我发现有效的毫秒等有关,高 fps 速度慢。但是在那个时候,我无法让 X 和 Y 正确,这样我就可以吃苹果了。
在这一点上我几乎迷路了。
这是我的整个贪吃蛇游戏。我不介意分享它,因为我想一旦完成就开源它。
import pygame
import random
from pygame.locals import (
K_UP,
K_DOWN,
K_LEFT,
K_RIGHT,
K_ESCAPE,
KEYDOWN,
K_n,
K_o,
K_w,
K_a,
K_s,
K_d,
K_RETURN,
QUIT,
)
pygame.init()
pygame.display.set_caption("Le jeu snake !")
class Game:
def __init__(self):
self.white = (255, 255, 255)
self.black = (0, 0, 0)
self.red = (255, 0, 0)
self.blue = (0, 0, 255)
self.green = (0, 255, 0)
self.background = pygame.image.load("snake_bg.png")
self.SCREEN_WIDTH = 600
self.SCREEN_HEIGHT = 400
self.screen = pygame.display.set_mode([self.SCREEN_WIDTH, self. SCREEN_HEIGHT])
self.running = True
self.paused = False
self.agreed = False
self.snake_block = 25
self.apple_block = 25
self.snake_x = 0
self.snake_y = 0
self.apple_x = 0
self.apple_y = 0
self.center = (self.SCREEN_WIDTH // 2, self.SCREEN_HEIGHT // 2)
self.width_grid = 0
self.height_grid = 0
self.snake_x_change = 0
self.snake_y_change = 0
self.snake_list = list()
self.snake_head = list()
self.snake_length = 1
self.score = 0
self.clock = pygame.time.Clock()
self.velocity = 25
def draw_grid(self):
self.width_grid = [x * 25 for x in range(0, self.SCREEN_WIDTH)]
self.height_grid = [y * 25 for y in range(0, self.SCREEN_WIDTH)]
"""for grid_x in self.width_grid:
pygame.draw.line(self.screen, self.white, [0, grid_x], [self.SCREEN_WIDTH, grid_x], 2)
if grid_x >= 600:
break
for grid_y in self.height_grid:
pygame.draw.line(self.screen, self.white, [grid_y, 0], [grid_y, self.SCREEN_WIDTH], 2)
if grid_y >= 600:
break"""
def set_position(self, thing):
if thing == "snake":
self.snake_x = self.SCREEN_WIDTH / 2
self.snake_y = self.SCREEN_HEIGHT / 2
if thing == "apple":
self.apple_x = random.choice(self.width_grid[0:24])
self.apple_y = random.choice(self.height_grid[0:16])
def draw(self, obj):
if obj == "snake":
for XnY in self.snake_list:
pygame.draw.rect(self.screen, self.green, (XnY[0], XnY[1], self.snake_block, self.snake_block), 2)
elif obj == "apple":
pygame.draw.rect(self.screen, self.red, (self.apple_x, self.apple_y, self.apple_block, self.apple_block))
def set_keys_direction(self):
for event in pygame.event.get():
if event.type == pygame.QUIT:
self.running = False
if event.type == KEYDOWN:
if event.key == K_DOWN:
self.snake_y_change = self.velocity
self.snake_x_change = 0
if event.key == K_RIGHT:
self.snake_x_change = self.velocity
self.snake_y_change = 0
if event.key == K_UP:
self.snake_y_change = -self.velocity
self.snake_x_change = 0
if event.key == K_LEFT:
self.snake_x_change = -self.velocity
self.snake_y_change = 0
if event.key == K_s:
self.snake_y_change = self.velocity
self.snake_x_change = 0
if event.key == K_w:
self.snake_y_change = -self.velocity
self.snake_x_change = 0
if event.key == K_d:
self.snake_x_change = self.velocity
self.snake_y_change = 0
if event.key == K_a:
print("Q works")
self.snake_x_change = -self.velocity
self.snake_y_change = 0
if not self.agreed:
if event.key == K_ESCAPE:
pygame.quit()
if event.key == K_RETURN:
self.agreed = True
if self.agreed:
if event.key == K_n:
pygame.quit()
if event.key == K_o:
self.paused = False
self.score = 0
if event.key == K_ESCAPE:
self.snake_length += 1
self.elapsed = self.clock.tick(30)
def build_snake(self):
snake_head = list()
snake_head.append(self.snake_x)
snake_head.append(self.snake_y)
self.snake_list.append(snake_head)
if len(self.snake_list) > self.snake_length:
del self.snake_list[0]
for snake in self.snake_list[:-1]:
if snake == snake_head:
self.snake_reset()
self.draw("snake")
def check_apple_eaten(self):
if self.snake_x == self.apple_x and self.snake_y == self.apple_y:
self.set_position("apple")
self.snake_length += 1
self.score += 1
def snake_borders_check(self):
if self.snake_x < 0 or self.snake_x > self.SCREEN_WIDTH - 25:
self.snake_reset()
if self.snake_y < 0 or self.snake_y > self.SCREEN_HEIGHT - 25:
self.snake_reset()
def snake_reset(self):
self.paused = True
self.set_position("snake")
self.set_position("apple")
del self.snake_list[1:]
self.snake_length = 1
def snake_bit_check(self):
pass
"""if len(self.snake_list) >= 6:
for snake in self.snake_list[2:]:
if self.snake_list[0][0] == snake[0] and self.snake_list[0][1] == snake[1]:
print("SnakeList[0][0]: {0} || SnakeList[0][1]: {0}".format(self.snake_list[0][0],
self.snake_list[0][1]))
print("snake: {0}".format(snake))
self.snake_reset()"""
def show_text(self, message, position, font_name="fonts/arial_narrow_7.ttf", font_size=32):
font = pygame.font.Font(font_name, font_size)
text = font.render(message, True, self.white, self.black)
text_rect = text.get_rect(center=position)
self.screen.blit(text, text_rect)
def game_over(self):
self.screen.blit(self.background, (0, 0))
self.show_text("Ton score: {0}".format(self.score), (100, 20))
self.show_text("Tu as perdu ! Veux-tu recommencer ?",
(self.SCREEN_WIDTH // 2, self.SCREEN_HEIGHT // 2 - 100))
self.show_text("Appuie sur O (oui) ou sur N (non)",
(self.SCREEN_WIDTH // 2, self.SCREEN_HEIGHT // 2))
self.show_text("Sinon, Echap pour quitter",
(self.SCREEN_WIDTH // 2, self.SCREEN_HEIGHT // 2 + 100))
def check_agreement(self):
self.show_text("Ceci est une réplique du jeu snake faite",
(self.SCREEN_WIDTH // 2, self.SCREEN_HEIGHT // 2 - 100))
self.show_text("par Kevin pour un simple projet personnel.",
(self.SCREEN_WIDTH // 2 + 1, self.SCREEN_HEIGHT // 2 - 70))
self.show_text("Si vous voulez jouer au jeu, appuyez sur la",
(self.SCREEN_WIDTH // 2 + 5, self.SCREEN_HEIGHT // 2 - 20))
self.show_text("touche Entrée, sinon Echap pour quitter !",
(self.SCREEN_WIDTH // 2 - 11, self.SCREEN_HEIGHT // 2 + 10))
self.show_text("- Utilise les touches flèches pour changer de direction",
(self.SCREEN_WIDTH // 2 - 10, self.SCREEN_HEIGHT // 2 + 55), font_size=24)
self.show_text("- Ne te mord pas toi-même",
(self.SCREEN_WIDTH // 2 - 144, self.SCREEN_HEIGHT // 2 + 75), font_size=24)
self.show_text("- Ne rentre pas dans les bords",
(self.SCREEN_WIDTH // 2 - 128, self.SCREEN_HEIGHT // 2 + 95), font_size=24)
def game(self):
self.draw_grid()
self.set_position("snake")
self.set_position("apple")
while self.running:
self.screen.blit(self.background, (0, 0))
self.set_keys_direction()
if self.agreed:
self.draw_grid()
self.draw("apple")
self.build_snake()
self.check_apple_eaten()
self.snake_bit_check()
self.snake_borders_check()
else:
self.check_agreement()
if not self.paused:
self.snake_x += self.snake_x_change
self.snake_y += self.snake_y_change
else:
self.game_over()
self.clock.tick(30)
pygame.display.flip()
game = Game()
game.game()
pygame.quit()
self.clock.tick()
的 return 值是自上次调用以来经过的时间。
使用 return 值来控制速度。定义蛇每秒移动的距离(例如 self.velocity = 400
表示每秒 400 像素)。获取帧之间的时间 (delta_t
) 并根据经过的时间 (delta_t / 1000
) 缩放蛇的移动:
class Game:
def __init__(self):
# [...]
# distance per second
self.velocity = 400
# [...]
def game(self):
# [...]
while self.running:
delta_t = self.clock.tick(30)
# [...]
if not self.paused:
step = delta_t / 1000 # / 1000 because unit of velocity is seconds
self.snake_x += self.snake_x_change * step
self.snake_y += self.snake_y_change * step
else:
self.game_over()
pygame.display.flip()
使用此设置可以轻松控制蛇的速度。例如,当蛇长大时,可以提高速度(例如self.velocity += 50
)。
当然画蛇的时候要把蛇的位置(self.snake_x
,self.snake_y
)四舍五入到网格大小的倍数(25的倍数)碰撞测试。使用 round
这样做:
x, y = round(self.snake_x / 25) * 25, round(self.snake_y / 25) * 25
确保存储在snake_list
中的位置是25的倍数。如果蛇的头已经到达新的字段,只需将新的头添加到列表中:
if len(self.snake_list) <= 0 or snake_head != self.snake_list[-1]:
self.snake_list.append(snake_head)
将其应用于方法 build_snake
draw
和 check_apple_eaten
:
class Game:
# [...]
def build_snake(self):
snake_head = list()
x, y = round(self.snake_x / 25) * 25, round(self.snake_y / 25) * 25
snake_head.append(x)
snake_head.append(y)
if len(self.snake_list) <= 0 or snake_head != self.snake_list[-1]:
self.snake_list.append(snake_head)
if len(self.snake_list) > self.snake_length:
del self.snake_list[0]
for snake in self.snake_list[:-1]:
if snake == snake_head:
self.snake_reset()
self.draw("snake")
def check_apple_eaten(self):
x, y = round(self.snake_x / 25) * 25, round(self.snake_y / 25) * 25
if x == self.apple_x and y == self.apple_y:
self.set_position("apple")
self.snake_length += 1
self.score += 1
def snake_borders_check(self):
x, y = round(self.snake_x / 25) * 25, round(self.snake_y / 25) * 25
if x < 0 or x > self.SCREEN_WIDTH - 25:
self.snake_reset()
if y < 0 or y > self.SCREEN_HEIGHT - 25:
self.snake_reset()
在我使用 pygame 制作的贪吃蛇游戏中,我很难理解速度的物理原理。 问题是一旦我将 fps 设置为 15 以上,蛇的速度也会增加。 我知道这与我发现有效的毫秒等有关,高 fps 速度慢。但是在那个时候,我无法让 X 和 Y 正确,这样我就可以吃苹果了。 在这一点上我几乎迷路了。 这是我的整个贪吃蛇游戏。我不介意分享它,因为我想一旦完成就开源它。
import pygame
import random
from pygame.locals import (
K_UP,
K_DOWN,
K_LEFT,
K_RIGHT,
K_ESCAPE,
KEYDOWN,
K_n,
K_o,
K_w,
K_a,
K_s,
K_d,
K_RETURN,
QUIT,
)
pygame.init()
pygame.display.set_caption("Le jeu snake !")
class Game:
def __init__(self):
self.white = (255, 255, 255)
self.black = (0, 0, 0)
self.red = (255, 0, 0)
self.blue = (0, 0, 255)
self.green = (0, 255, 0)
self.background = pygame.image.load("snake_bg.png")
self.SCREEN_WIDTH = 600
self.SCREEN_HEIGHT = 400
self.screen = pygame.display.set_mode([self.SCREEN_WIDTH, self. SCREEN_HEIGHT])
self.running = True
self.paused = False
self.agreed = False
self.snake_block = 25
self.apple_block = 25
self.snake_x = 0
self.snake_y = 0
self.apple_x = 0
self.apple_y = 0
self.center = (self.SCREEN_WIDTH // 2, self.SCREEN_HEIGHT // 2)
self.width_grid = 0
self.height_grid = 0
self.snake_x_change = 0
self.snake_y_change = 0
self.snake_list = list()
self.snake_head = list()
self.snake_length = 1
self.score = 0
self.clock = pygame.time.Clock()
self.velocity = 25
def draw_grid(self):
self.width_grid = [x * 25 for x in range(0, self.SCREEN_WIDTH)]
self.height_grid = [y * 25 for y in range(0, self.SCREEN_WIDTH)]
"""for grid_x in self.width_grid:
pygame.draw.line(self.screen, self.white, [0, grid_x], [self.SCREEN_WIDTH, grid_x], 2)
if grid_x >= 600:
break
for grid_y in self.height_grid:
pygame.draw.line(self.screen, self.white, [grid_y, 0], [grid_y, self.SCREEN_WIDTH], 2)
if grid_y >= 600:
break"""
def set_position(self, thing):
if thing == "snake":
self.snake_x = self.SCREEN_WIDTH / 2
self.snake_y = self.SCREEN_HEIGHT / 2
if thing == "apple":
self.apple_x = random.choice(self.width_grid[0:24])
self.apple_y = random.choice(self.height_grid[0:16])
def draw(self, obj):
if obj == "snake":
for XnY in self.snake_list:
pygame.draw.rect(self.screen, self.green, (XnY[0], XnY[1], self.snake_block, self.snake_block), 2)
elif obj == "apple":
pygame.draw.rect(self.screen, self.red, (self.apple_x, self.apple_y, self.apple_block, self.apple_block))
def set_keys_direction(self):
for event in pygame.event.get():
if event.type == pygame.QUIT:
self.running = False
if event.type == KEYDOWN:
if event.key == K_DOWN:
self.snake_y_change = self.velocity
self.snake_x_change = 0
if event.key == K_RIGHT:
self.snake_x_change = self.velocity
self.snake_y_change = 0
if event.key == K_UP:
self.snake_y_change = -self.velocity
self.snake_x_change = 0
if event.key == K_LEFT:
self.snake_x_change = -self.velocity
self.snake_y_change = 0
if event.key == K_s:
self.snake_y_change = self.velocity
self.snake_x_change = 0
if event.key == K_w:
self.snake_y_change = -self.velocity
self.snake_x_change = 0
if event.key == K_d:
self.snake_x_change = self.velocity
self.snake_y_change = 0
if event.key == K_a:
print("Q works")
self.snake_x_change = -self.velocity
self.snake_y_change = 0
if not self.agreed:
if event.key == K_ESCAPE:
pygame.quit()
if event.key == K_RETURN:
self.agreed = True
if self.agreed:
if event.key == K_n:
pygame.quit()
if event.key == K_o:
self.paused = False
self.score = 0
if event.key == K_ESCAPE:
self.snake_length += 1
self.elapsed = self.clock.tick(30)
def build_snake(self):
snake_head = list()
snake_head.append(self.snake_x)
snake_head.append(self.snake_y)
self.snake_list.append(snake_head)
if len(self.snake_list) > self.snake_length:
del self.snake_list[0]
for snake in self.snake_list[:-1]:
if snake == snake_head:
self.snake_reset()
self.draw("snake")
def check_apple_eaten(self):
if self.snake_x == self.apple_x and self.snake_y == self.apple_y:
self.set_position("apple")
self.snake_length += 1
self.score += 1
def snake_borders_check(self):
if self.snake_x < 0 or self.snake_x > self.SCREEN_WIDTH - 25:
self.snake_reset()
if self.snake_y < 0 or self.snake_y > self.SCREEN_HEIGHT - 25:
self.snake_reset()
def snake_reset(self):
self.paused = True
self.set_position("snake")
self.set_position("apple")
del self.snake_list[1:]
self.snake_length = 1
def snake_bit_check(self):
pass
"""if len(self.snake_list) >= 6:
for snake in self.snake_list[2:]:
if self.snake_list[0][0] == snake[0] and self.snake_list[0][1] == snake[1]:
print("SnakeList[0][0]: {0} || SnakeList[0][1]: {0}".format(self.snake_list[0][0],
self.snake_list[0][1]))
print("snake: {0}".format(snake))
self.snake_reset()"""
def show_text(self, message, position, font_name="fonts/arial_narrow_7.ttf", font_size=32):
font = pygame.font.Font(font_name, font_size)
text = font.render(message, True, self.white, self.black)
text_rect = text.get_rect(center=position)
self.screen.blit(text, text_rect)
def game_over(self):
self.screen.blit(self.background, (0, 0))
self.show_text("Ton score: {0}".format(self.score), (100, 20))
self.show_text("Tu as perdu ! Veux-tu recommencer ?",
(self.SCREEN_WIDTH // 2, self.SCREEN_HEIGHT // 2 - 100))
self.show_text("Appuie sur O (oui) ou sur N (non)",
(self.SCREEN_WIDTH // 2, self.SCREEN_HEIGHT // 2))
self.show_text("Sinon, Echap pour quitter",
(self.SCREEN_WIDTH // 2, self.SCREEN_HEIGHT // 2 + 100))
def check_agreement(self):
self.show_text("Ceci est une réplique du jeu snake faite",
(self.SCREEN_WIDTH // 2, self.SCREEN_HEIGHT // 2 - 100))
self.show_text("par Kevin pour un simple projet personnel.",
(self.SCREEN_WIDTH // 2 + 1, self.SCREEN_HEIGHT // 2 - 70))
self.show_text("Si vous voulez jouer au jeu, appuyez sur la",
(self.SCREEN_WIDTH // 2 + 5, self.SCREEN_HEIGHT // 2 - 20))
self.show_text("touche Entrée, sinon Echap pour quitter !",
(self.SCREEN_WIDTH // 2 - 11, self.SCREEN_HEIGHT // 2 + 10))
self.show_text("- Utilise les touches flèches pour changer de direction",
(self.SCREEN_WIDTH // 2 - 10, self.SCREEN_HEIGHT // 2 + 55), font_size=24)
self.show_text("- Ne te mord pas toi-même",
(self.SCREEN_WIDTH // 2 - 144, self.SCREEN_HEIGHT // 2 + 75), font_size=24)
self.show_text("- Ne rentre pas dans les bords",
(self.SCREEN_WIDTH // 2 - 128, self.SCREEN_HEIGHT // 2 + 95), font_size=24)
def game(self):
self.draw_grid()
self.set_position("snake")
self.set_position("apple")
while self.running:
self.screen.blit(self.background, (0, 0))
self.set_keys_direction()
if self.agreed:
self.draw_grid()
self.draw("apple")
self.build_snake()
self.check_apple_eaten()
self.snake_bit_check()
self.snake_borders_check()
else:
self.check_agreement()
if not self.paused:
self.snake_x += self.snake_x_change
self.snake_y += self.snake_y_change
else:
self.game_over()
self.clock.tick(30)
pygame.display.flip()
game = Game()
game.game()
pygame.quit()
self.clock.tick()
的 return 值是自上次调用以来经过的时间。
使用 return 值来控制速度。定义蛇每秒移动的距离(例如 self.velocity = 400
表示每秒 400 像素)。获取帧之间的时间 (delta_t
) 并根据经过的时间 (delta_t / 1000
) 缩放蛇的移动:
class Game:
def __init__(self):
# [...]
# distance per second
self.velocity = 400
# [...]
def game(self):
# [...]
while self.running:
delta_t = self.clock.tick(30)
# [...]
if not self.paused:
step = delta_t / 1000 # / 1000 because unit of velocity is seconds
self.snake_x += self.snake_x_change * step
self.snake_y += self.snake_y_change * step
else:
self.game_over()
pygame.display.flip()
使用此设置可以轻松控制蛇的速度。例如,当蛇长大时,可以提高速度(例如self.velocity += 50
)。
当然画蛇的时候要把蛇的位置(self.snake_x
,self.snake_y
)四舍五入到网格大小的倍数(25的倍数)碰撞测试。使用 round
这样做:
x, y = round(self.snake_x / 25) * 25, round(self.snake_y / 25) * 25
确保存储在snake_list
中的位置是25的倍数。如果蛇的头已经到达新的字段,只需将新的头添加到列表中:
if len(self.snake_list) <= 0 or snake_head != self.snake_list[-1]:
self.snake_list.append(snake_head)
将其应用于方法 build_snake
draw
和 check_apple_eaten
:
class Game:
# [...]
def build_snake(self):
snake_head = list()
x, y = round(self.snake_x / 25) * 25, round(self.snake_y / 25) * 25
snake_head.append(x)
snake_head.append(y)
if len(self.snake_list) <= 0 or snake_head != self.snake_list[-1]:
self.snake_list.append(snake_head)
if len(self.snake_list) > self.snake_length:
del self.snake_list[0]
for snake in self.snake_list[:-1]:
if snake == snake_head:
self.snake_reset()
self.draw("snake")
def check_apple_eaten(self):
x, y = round(self.snake_x / 25) * 25, round(self.snake_y / 25) * 25
if x == self.apple_x and y == self.apple_y:
self.set_position("apple")
self.snake_length += 1
self.score += 1
def snake_borders_check(self):
x, y = round(self.snake_x / 25) * 25, round(self.snake_y / 25) * 25
if x < 0 or x > self.SCREEN_WIDTH - 25:
self.snake_reset()
if y < 0 or y > self.SCREEN_HEIGHT - 25:
self.snake_reset()