PyGame Snake уменьшает скорость при увеличении размера окна

#python #pygame

#python #pygame

Вопрос:

Я боролся с этой проблемой уже пару дней. Я хочу в конечном итоге преобразовать свой скрипт игры snake в exe, чтобы мои друзья могли играть. Я хочу, чтобы змея двигалась с одинаковой скоростью, независимо от размера окна.

Например: мой размер окна прямо сейчас равен (400, 400). Если я увеличу размер до (800, 800), змея будет двигаться медленнее. Однако скорость змеи постоянна на уровне 20 пикселей. Кажется, что мой основной игровой цикл выполняется медленнее по мере увеличения размера окна.

Я знаю, что у змеи больше пикселей для перемещения при большем размере окна, но как это вообще влияет на скорость змеи? Я думаю, что решение заключается где-то в скорости, с которой я рисую змею на экране, но не могу быть уверен.

 import pygame
import sys
import random
import math
import time

pygame.display.set_caption('Snake')

pygame.font.init()

game_running = True

width = 400
height = 400

size = (width, height)

window = pygame.display.set_mode(size) # our surface type

pygame.display.set_caption("Snake Game by Nick Rinaldi")

class Food:
    def __init__(self, block_size, surface, x_loc, y_loc): # pass in color and random_x/random_y. block size is a constant 
        self.block_size = block_size
        self.surface = surface # green
        self.x_loc = x_loc
        self.y_loc = y_loc
        self.mask = pygame.mask.from_surface(self.surface)

    def draw(self, window):
        window.blit(self.surface, (self.x_loc, self.y_loc))

class Snake:

    def __init__(self, block_size, surface, x_loc, y_loc):
        self.block_size = block_size
        self.surface = surface # red
        self.x_loc = x_loc
        self.y_loc = y_loc
        self.body = []
        self.direction = None
        self.velocity = 20
        self.mask = pygame.mask.from_surface(self.surface)
 
    def draw(self, color, window, block_size):
        self.seg = []
        self.head = pygame.Rect(self.x_loc, self.y_loc, block_size, block_size)
        pygame.draw.rect(window, color, self.head)
        if len(self.body) > 0:
            for unit in self.body:
                segment = pygame.Rect(unit[0], unit[1], block_size, block_size)
                pygame.draw.rect(window, color, segment)
                self.seg.append(segment)

    def add_unit(self):
        if len(self.body) != 0:
            index = len(self.body) - 1
            x = self.body[index][0]
            y = self.body[index][1]
            self.body.append([x, y])
        else:
            self.body.append([1000, 1000])

    def move(self, step):
        for index in range(len(self.body) -1, 0, -1):
            x = self.body[index-1][0]
            y = self.body[index-1][1]
            self.body[index] = [x, y]
        if len(self.body) > 0:
            self.body[0] = [self.x_loc, self.y_loc]
        if self.direction == "right": # if specific constant, keep moving in direction
            self.x_loc  = self.velocity * step
        if self.direction == "left":
            self.x_loc -= self.velocity * step
        if self.direction == "down":
            self.y_loc  = self.velocity * step
        if self.direction == "up":
            self.y_loc -= self.velocity * step

    def collision(self, obj):
        return collide(food)

def gameOver(snake):

    white = pygame.Color(255, 255, 255)

    display = True
    while display:

        window.fill(white)
        score_font = pygame.font.SysFont("Courier New", 16)
        score_label = score_font.render("Your score was: "   str(len(snake.body)   1), 1, (0, 0, 0))
        replay_label = score_font.render("To replay, click the mouse button", 1, (0, 0, 0))
        window.blit(score_label, (50, 100))
        window.blit(replay_label, (50, 130))
        pygame.display.update()

        for event in pygame.event.get(): # if we hit "x" to close out the game, close out the game.
                if event.type == pygame.QUIT:
                    pygame.quit()
                    exit()
                if event.type == pygame.MOUSEBUTTONDOWN:
                    main()


    pygame.quit()
    sys.exit()


clock = pygame.time.Clock()

def main():

    game_over = False

    x = 20 # x position
    y = 20 # y position

    block_snakes = []

    pygame.init()

    clock = pygame.time.Clock()

    red = pygame.Color(255, 0, 0)
    green = pygame.Color(0, 255, 0)
    white = pygame.Color(255, 255, 255)
    black = pygame.Color(0, 0, 0)

    block_size = 20

    randx_green = random.randrange(0, width, 20)
    randy_green = random.randrange(0, height, 20)
    randx_red = random.randrange(0, width, 20)
    randy_red = random.randrange(0, height, 20)


    red_square = pygame.Surface((block_size, block_size))
    red_square.fill(red)
    green_square = pygame.Surface((block_size, block_size))
    green_square.fill(green)

    snake = Snake(block_size, red_square, 20, 20) # create snake instance
    food = Food(block_size, green_square, randx_green, randy_green) # create food instance

    def redraw_window():

        draw_grid(window, height, width, white)

    while game_running:

        dt = clock.tick(30) # time passed between each call 

        step = dt/1000
        print(step)

        FPS = 60

        window.fill(black)

        food.draw(window)

        snake.draw(red, window, block_size)

        redraw_window()

        for event in pygame.event.get(): # if we hit "x" to close out the game, close out the game.
            if event.type == pygame.QUIT:
                pygame.quit()
                exit()

        keys = pygame.key.get_pressed()

        if keys[pygame.K_RIGHT]: # sets direction attribute as a constant
            snake.direction = "right"
        if keys[pygame.K_LEFT]:
            snake.direction = "left"            
        if keys[pygame.K_DOWN]:
            snake.direction = "down"
        if keys[pygame.K_UP]:
            snake.direction = "up"

        snake.move(step)

        collision = collide_food(snake.x_loc, snake.y_loc, food.x_loc, food.y_loc)

        if collision:
            ac_rand_x = random.randrange(0, width, 20) # after collision, random x
            ac_rand_y = random.randrange(0, height, 20) # after collision, random y
            # check snake.direction. 

            food = Food(block_size, green_square, ac_rand_x, ac_rand_y)
            food.draw(window)

            snake.add_unit()

        wall_collide = collide_wall(snake.x_loc, snake.y_loc)

        if wall_collide:
            gameOver(snake)
            # break

        for block in snake.body:
            if snake.x_loc == block[0] and snake.y_loc == block[1]:
                gameOver(snake)

        pygame.display.update()
        # clock.tick(FPS)
    


def collide_food(snake_x, snake_y, obj_x, obj_y):
    distance = math.sqrt((math.pow(snake_x - obj_x, 2))   (math.pow(snake_y - obj_y, 2)))
    if distance < 20:
        return True
    else:
        return False

def collide_wall(snake_x, snake_y):
    if snake_x > width:
        game_over = True
        return game_over
    if snake_y > height:
        game_over = True
        return game_over
    if snake_x < 0:
        game_over = True
        return game_over
    if snake_y < 0:
        game_over = True
        return game_over

def collide_self(snake_x, snake_y, body_x, body_y):
    if (snake_x and snake_y) == (body_x and body_y):
        return True
    else:
        return False
    

def draw_grid(window, height, width, color):

    x = 0
    y = 0

    grid_blocks = 20
    for i in range(height):
        x  = 20
        pygame.draw.line(window, color, (x, 0), (x, height), 1)
        for j in range(width):
            y  = 20
            pygame.draw.line(window, color, (0, y), (height, y), 1)

    # pygame.display.update()


def display_score():
    score_font = pygame.font.SysFont()

def main_menu(width, height):

    clock = pygame.time.Clock()
    FPS = 60

    width = width
    height = height

    run = True
    title_font = pygame.font.SysFont("Courier New", 16)
    title_font.set_bold(True)
    white = pygame.Color(255, 255, 255)

    while run:
        window.fill(white)
        title_label = title_font.render("Snake Game by Nick Rinaldi ", 1, (0, 0, 0))
        sponser_label = title_font.render("Sponsored by @goodproblemsnyc", 1, (0, 0, 0))
        window.blit(title_label, ((width/4, height/4)))
        window.blit(sponser_label, ((width/4, height/4   30)))
        pygame.display.update()
        for event in pygame.event.get(): # if we hit "x" to close out the game, close out the game.
            if event.type == pygame.QUIT:
                pygame.quit()
                exit()
            if event.type == pygame.MOUSEBUTTONDOWN:
                main()

    pygame.quit()

main_menu(width, height)```
  

Комментарии:

1. Да, проблема решена

2. Выполнено. Спасибо, что сообщили мне.

Ответ №1:

Узким местом в вашей игре является функция draw_grid , которая выводит слишком много строк из окна.

 def draw_grid(window, height, width, color):

   x = 0
   y = 0

   grid_blocks = 20
   for i in range(height):
       x  = 20
       pygame.draw.line(window, color, (x, 0), (x, height), 1)
       for j in range(width):
           y  = 20
           pygame.draw.line(window, color, (0, y), (height, y), 1)
  

Если вы рисуете линию за пределами окна, оператор ничего не рисует, тем не менее, вложенные циклы for все еще выполняются.
Кроме того, вам не нужны вложенные циклы. Вы не хотите рисовать 19 горизонтальных линий для каждой вертикальной линии. Вы хотите нарисовать 19 вертикальных и 19 горизонтальных линий. Следовательно, достаточно 1 for цикла.

Используйте аргумент step range для определения списка позиций для вертикальных и горизонтальных линий

 def draw_grid(window, height, width, color):

    tile_size = 20
    for p in range(tile_size, height, tile_size):
        pygame.draw.line(window, color, (p, 0), (p, height), 1)
        pygame.draw.line(window, color, (0, p), (height, p), 1)
  

Ответ №2:

если размер равен 400 * 400 пикселям, общее количество пикселей 160000, поэтому перемещение происходит со скоростью 20 пикселей, поскольку на плате 800 * 800 пикселей меньше (320000 пикселей), похоже, что вы движетесь быстрее, потому что пикселей меньше. Найдите уравнение для вычисления правильной скорости на платах разного размера.

С уважением, Зак

Комментарии:

1. Понял. Благодарность имеет смысл. Однако я даже не уверен, с чего начать с точки зрения создания уравнения для вычисления правильной скорости.