Скорость пигмеев-змей слишком высока, когда fps выше 15 - PullRequest
1 голос
/ 04 апреля 2020

Мне трудно понять физику скорости в этой игре со змеями, которую я сделал с помощью pygame. Проблема в том, что как только я установил частоту кадров выше 15, скорость змеи также возрастет. Я знаю, что это связано с миллисекундами и т. Д. c, которые, как я обнаружил, работают: высокая частота кадров с низкой скоростью. Однако в тот момент я не мог заставить 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()

1 Ответ

1 голос
/ 05 апреля 2020

Возвращаемое значение self.clock.tick() - это время, прошедшее с момента последнего вызова. Используйте возвращаемое значение для контроля скорости. Определите расстояние перемещения змеи в секунду (например, 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()
...