pygame蛇游戏,当快速改变方向时,蛇不会移动

时间:2017-11-03 15:19:47

标签: python pygame

我刚刚使用pygame模块写了一个蛇游戏。

经过测试,我发现当我迅速改变蛇的方向时。例如。快速按下两个箭头键将蛇体移动到下一行或向相反方向移动,蛇没有准确响应。大部分时间它都会起作用,但蛇几次都不会移动。我相信这是因为FPS很低,但是如果我增加它,蛇会移动得那么快。

以下是代码:

# snake game

import pygame, sys, random, time

# game initialization
check_errors = pygame.init()

if check_errors[1] > 0:
    print('(!) Got {0} errors during initializing pygame \
        exiting...'.format(check_errors[1]))
    sys.exit(-1)
else:
    print('(+) pygame successfully initialized.')

# game screen
screen_width = 750
screen_height = 495
game_screen = pygame.display.set_mode((screen_width, screen_height))
pygame.display.set_caption('Snake game')

# colors
red = pygame.Color(255, 0, 0)  # game over
green = pygame.Color(0, 255, 0)  # snake body
black = pygame.Color(0, 0, 0)  # player score
white = pygame.Color(255, 255, 255)  # game background
brown = pygame.Color(165, 42, 42)  # food

# FPS controller
fps_controller = pygame.time.Clock()

# game variables
start_x = 300
start_y = 150
step = 15  # block width is 10
initial_body_length = 3
snake_head = [start_x, start_y]  # snake start position [x, y]
# initialize snake body, index 0 contains the snake head
snake_body = [[start_x - i * step, start_y] for i in range(initial_body_length)]
score = 0
level = 1

food_pos = [random.randrange(2, screen_width / step - 1) * step, \
            random.randrange(2, screen_height / step - 1) * step]  # don't put food at the border of the screen
food_spawn = True

direction = 'RIGHT'
next_direction = direction  # new direction after user hits keyboard


def draw_game_menu():
    count = 3
    my_font = pygame.font.SysFont('monaco', 60)
    while True:
        game_screen.fill(white)

        start_surface = my_font.render('Start in {0} seconds.'.format(count), True, black)
        start_rect = start_surface.get_rect()
        start_rect.midtop = (screen_width / 2, 80)
        game_screen.blit(start_surface, start_rect)

        esc_surface = my_font.render('''Press Esc to exit during game.''', True, black)
        esc_rect = esc_surface.get_rect()
        esc_rect.midtop = (screen_width / 2, 150)
        game_screen.blit(esc_surface, esc_rect)

        pause_surface = my_font.render('''Press Space to pause the game.''', True, black)
        pause_rect = pause_surface.get_rect()
        pause_rect.midtop = (screen_width / 2, 220)
        game_screen.blit(pause_surface, pause_rect)

        pygame.display.flip()  # update the game screen
        time.sleep(1)
        fps_controller.tick()
        count -= 1
        if count == 0: break

def draw_game_pause():
    my_font = pygame.font.SysFont('monaco', 40)
    while True:
        pause_surface = my_font.render('Press Space to continue.', True, black)
        pause_rect = pause_surface.get_rect()
        pause_rect.midtop = (screen_width / 2, 150)
        game_screen.blit(pause_surface, pause_rect)
        pygame.display.flip()
        fps_controller.tick()
        for event in pygame.event.get():
            if event.type == pygame.KEYDOWN:
                if event.key == pygame.K_SPACE: return

def show_score(game_over=False):
    my_font = pygame.font.SysFont('monaco', 40)
    score_surface = my_font.render('Score: {0}'.format(score), True, black)
    score_rect = score_surface.get_rect()
    if game_over == False:
        score_rect.midtop = (75, 10)
    else:
        score_rect.midtop = (screen_width / 2, 130)
    game_screen.blit(score_surface, score_rect)

# game over function
def draw_game_over():
    my_font = pygame.font.SysFont('monaco', 60)
    GO_surface = my_font.render('Game Over !', True, red)
    GO_rect = GO_surface.get_rect()
    GO_rect.midtop = (screen_width/2, 60)
    game_screen.blit(GO_surface, GO_rect)

    show_score(game_over=True)
    pygame.display.flip()  # update the game screen

    time.sleep(4)
    pygame.quit()  # quit the game
    sys.exit()  # exit the console

def get_food(food_pos, snake_body):
    for block in snake_body:
        if block[0] == food_pos[0] and block[1] == food_pos[1]:
            return True
    return False


# game start menu
draw_game_menu()

# main logic of the game
while True:
    for event in pygame.event.get():
        if event.type == pygame.KEYDOWN:  # if user press any button
            if event.key == pygame.K_RIGHT or event.key == ord('d'):
                next_direction = 'RIGHT'
            elif event.key == pygame.K_LEFT or event.key == ord('a'):
                next_direction = 'LEFT'
            elif event.key == pygame.K_UP or event.key == ord('w'):
                next_direction = 'UP'
            elif event.key == pygame.K_DOWN or event.key == ord('s'):
                next_direction = 'DOWN'
            elif event.key == pygame.K_ESCAPE:  # if user choose to quit the game
                pygame.event.post(pygame.event.Event(pygame.QUIT))
            elif event.key == pygame.K_SPACE:
                draw_game_pause()
        if event.type == pygame.QUIT:
            pygame.quit()
            sys.exit()

    # validation of direction
    if next_direction == 'RIGHT' and direction != 'LEFT':
        direction = 'RIGHT'
    elif next_direction == 'LEFT' and direction != 'RIGHT':
        direction = 'LEFT'
    elif next_direction == 'DOWN' and direction != 'UP':
        direction = 'DOWN'
    elif next_direction == 'UP' and direction != 'DOWN':
        direction = 'UP'

    # move snake head
    if direction == 'RIGHT':
        snake_head[0] += step
    elif direction == 'LEFT':
        snake_head[0] -= step
    elif direction == 'DOWN':
        snake_head[1] += step
    elif direction == 'UP':
        snake_head[1] -= step

    # move snake body mechanism
    #   1. insert a new block at the beginning of the body
    #   2. check if snake has reached a food
    #       2.1 if yes, then keep the modified body
    #       2.2 if not, then delete the end of the body
    snake_body.insert(0, list(snake_head))
    if snake_head[0] == food_pos[0] and snake_head[1] == food_pos[1]:
        food_spawn = False
        score += 1
    else:
        snake_body.pop()

    while food_spawn == False:
        food_pos = [random.randrange(2, screen_width / step - 1) * step,
                    random.randrange(2, screen_height / step - 1) * step]
        if get_food(food_pos, snake_body) == True:
            food_spawn = False
        else:
            food_spawn = True

    # fill game background
    game_screen.fill(white)

    # draw snake body
    for pos in snake_body:
        pygame.draw.rect(game_screen, green, pygame.Rect(pos[0], pos[1], step, step))

    # draw food
    pygame.draw.rect(game_screen, brown, pygame.Rect(food_pos[0], food_pos[1], step, step))

    # check if snake hits the border
    if (snake_head[0] > screen_width - step) or (snake_head[0] < 0) or \
            (snake_head[1] > screen_height - step) or (snake_head[1] < 0):
        draw_game_over()

    # check if snake hits itself
    for block in snake_body[1:]:
        if snake_head[0] == block[0] and snake_head[1] == block[1]:
            draw_game_over()

    level = score//5 + 1
    if level > 3:
        level = 3

    show_score(game_over=False)
    pygame.display.flip()

    if level == 1:
        fps_controller.tick(8)
    elif level == 2:
        fps_controller.tick(10)
    elif level == 3:
        fps_controller.tick(12)

请帮助看看是否有改善的方法,谢谢。

1 个答案:

答案 0 :(得分:2)

首先,您应该尝试使用单个主循环。

当您渲染开始或结束屏幕时,您无法与窗口交互,因为没有事件循环运行。在此期间你不能移动或关闭窗户是非常烦人的。也许看看here,了解如何处理这个问题。

其次,确实使用更高的帧速率,并且不要将游戏对象的速度与帧速率联系起来。

有几种方法可以解决这个问题,例如使用event来表示蛇应该移动的时候。 Here's我为另一个问题写的一个例子。

以下是您当前代码的简单实现:

MOVE_SNAKE = pygame.USEREVENT
pygame.time.set_timer(MOVE_SNAKE, 300)

# main logic of the game
while True:
    for event in pygame.event.get():
        if event.type == pygame.KEYDOWN:  # if user press any button
            if event.key == pygame.K_RIGHT or event.key == ord('d'):
                next_direction = 'RIGHT'
            elif event.key == pygame.K_LEFT or event.key == ord('a'):
                next_direction = 'LEFT'
            elif event.key == pygame.K_UP or event.key == ord('w'):
                next_direction = 'UP'
            elif event.key == pygame.K_DOWN or event.key == ord('s'):
                next_direction = 'DOWN'
            elif event.key == pygame.K_ESCAPE:  # if user choose to quit the game
                pygame.event.post(pygame.event.Event(pygame.QUIT))
            elif event.key == pygame.K_SPACE:
                draw_game_pause()
        elif event.type == pygame.QUIT:
            pygame.quit()
            sys.exit()
        elif event.type == MOVE_SNAKE:
            # move snake head
            if direction == 'RIGHT':
                snake_head[0] += step
            elif direction == 'LEFT':
                snake_head[0] -= step
            elif direction == 'DOWN':
                snake_head[1] += step
            elif direction == 'UP':
                snake_head[1] -= step

            # move snake body mechanism
            #   1. insert a new block at the beginning of the body
            #   2. check if snake has reached a food
            #       2.1 if yes, then keep the modified body
            #       2.2 if not, then delete the end of the body
            snake_body.insert(0, list(snake_head))
            if snake_head[0] == food_pos[0] and snake_head[1] == food_pos[1]:
                food_spawn = False
                score += 1
            else:
                snake_body.pop()

    # validation of direction
    if next_direction == 'RIGHT' and direction != 'LEFT':
        direction = 'RIGHT'
    elif next_direction == 'LEFT' and direction != 'RIGHT':
        direction = 'LEFT'
    elif next_direction == 'DOWN' and direction != 'UP':
        direction = 'DOWN'
    elif next_direction == 'UP' and direction != 'DOWN':
        direction = 'UP'



    while food_spawn == False:
        food_pos = [random.randrange(2, screen_width / step - 1) * step,
                    random.randrange(2, screen_height / step - 1) * step]
        if get_food(food_pos, snake_body) == True:
            food_spawn = False
        else:
            food_spawn = True

    # fill game background
    game_screen.fill(white)

    # draw snake body
    for pos in snake_body:
        pygame.draw.rect(game_screen, green, pygame.Rect(pos[0], pos[1], step, step))

    # draw food
    pygame.draw.rect(game_screen, brown, pygame.Rect(food_pos[0], food_pos[1], step, step))

    # check if snake hits the border
    if (snake_head[0] > screen_width - step) or (snake_head[0] < 0) or \
            (snake_head[1] > screen_height - step) or (snake_head[1] < 0):
        draw_game_over()

    # check if snake hits itself
    for block in snake_body[1:]:
        if snake_head[0] == block[0] and snake_head[1] == block[1]:
            draw_game_over()

    new_level = score//5 + 1
    if new_level != level:
        pygame.time.set_timer(MOVE_SNAKE, 300 / new_level)
        if new_level <= 3:
            level = new_level

    show_score(game_over=False)
    pygame.display.flip()

    fps_controller.tick(60)

看看现在控制蛇的速度是多么容易:它现在每300毫秒移动一次。