fps超过15时Pygame蛇的速度太高 [英] Pygame snake velocity too high when the fps above 15
问题描述
在使用pygame制作的这款蛇游戏中,我很难理解速度的物理原理. 问题是,一旦我将fps设置为15以上,蛇的速度也会增加. 我知道这与我发现可以工作的毫秒等有关,以慢速获得高fps.但是在那时,我不能正确地选择X和Y,这样我才能吃苹果. 在这一点上,我几乎迷失了. 这是我的整个蛇游戏.我不介意分享它,因为我想在完成后立即将其开源.
I am having a hard time figuring the physics of speed in this snake game I made using pygame. The issue is that as soon as I set the fps to be above 15, the snake's speed increases as well. I know that this has to do with milliseconds etc which I found to work, high fps with slow speed. However at that point, I could not get the X and Y to be correct so that I can eat the apple. I am pretty much lost at this point. Here's my entire snake game. I don't mind sharing it since I thought to open source it as soon as it's finished.
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()
的返回值是自上次调用以来经过的时间.
使用返回值控制速度.定义蛇每秒移动的距离(例如self.velocity = 400
表示每秒400像素).获取到帧之间的时间(delta_t
),并按照经过的时间(delta_t / 1000
)缩放蛇的运动:
The return value of self.clock.tick()
is the time which has passed since the last call.
Use the return value to control the speed. Define the distance, of movement of the snake per second (e.g. self.velocity = 400
means 400 pixel per second). Get the time between to frames (delta_t
) and scale the movement of the snake by the elapsed time (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
).
With this setup it is ease to control the speed of the snake. For instance, the speed can be increased (e.g. self.velocity += 50
), when the snake grows.
当然,在绘制蛇和进行碰撞测试时,必须将蛇的位置(self.snake_x
,self.snake_y
)四舍五入为网格大小的倍数(25的倍数).使用 round
这样做:
Of course you have to round the position of the snake (self.snake_x
, self.snake_y
) to a multiple of the grid size (multiple of 25) when you draw the snake and when you do the collision test. Use round
to do so:
x, y = round(self.snake_x / 25) * 25, round(self.snake_y / 25) * 25
确保存储在snake_list
中的位置是25的倍数.如果蛇的头部到达了新字段,只需将新头部附加到列表中即可.
Ensure that the positions which are stored in snake_list
are a multiple of 25. Just append a new head to the list, if the head of the snake has reached a new field:
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()
这篇关于fps超过15时Pygame蛇的速度太高的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!