Python - ウィンドウサイズが大きくなると PyGame Snake の速度が低下する

okwaves2024-01-25  6

私はここ数日間、この問題に悩んでいます。最終的には、友達がプレイできるように、スネーク ゲーム スクリプトを 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)```

はい、問題は解決しました

– ニック・リナルディ

2020 年 9 月 10 日 1:58

1

完了。お知らせいただきありがとうございます。

– ニック・リナルディ

2020 年 9 月 10 日 22:17



------------------------

ゲームのボトルネックは関数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本の線を描きたいとします。したがって、for ループは 1 つで十分です。

範囲のステップ引数を使用して、垂直線と水平線の位置のリストを定義します

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)



------------------------

si の場合ze は 400 * 400 ピクセル、合計ピクセル 160,000 なので、20 ピクセル レートで移動します。800 * 800 ボード (320,000 ピクセル) よりもピクセルが少ないため、ピクセルが少ないため、より速く進んでいるように見えます。異なるサイズのボードで正しい速度を計算する方程式を見つけてください。

敬具 ザック

1

わかりました。感謝するのは理にかなっています。しかし、正しい速度を計算する方程式を作成するという点では、どこから始めればよいのかさえわかりません。

– ニック・リナルディ

2020 年 9 月 3 日 21:45

総合生活情報サイト - OKWAVES
総合生活情報サイト - OKWAVES
生活総合情報サイトokwaves(オールアバウト)。その道のプロ(専門家)が、日常生活をより豊かに快適にするノウハウから業界の最新動向、読み物コラムまで、多彩なコンテンツを発信。