2017-11-03 2 views
1

私はちょうどpygameモジュールを使ってスネークゲームを書いた。pygame snake game、急速に方向を変えるとスネークが動かない

テストの後、私は急速にスネークの方向を変えたときにそれを見つけました。例えば。 2つの矢印キーを非常に速く押すと、ヘビ本体が次の行に移動したり、反対方向に移動したりすると、ヘビは正確に反応しません。たいていの場合、動作しますが、ヘビが動かないことはほとんどありません。私はこれが低い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) 

、感謝を改善する方法があるかどうかを確認するために助けてください:ここでは

はコードがあります。

答えて

2

まず、メインループを1つ使用するようにしてください。

開始画面または終了画面をレンダリングしている間は、イベントループが実行されないため、ウィンドウと対話できません。その間にウィンドウを移動したり閉じることができないのは非常に面倒です。おそらくこれを処理する方法の例についてはhereを見てください。

第2に、実際にはより高いフレームレートを使用し、ゲームオブジェクトの速度をフレームレートに結びつけません。

これを処理するにはいくつかの方法がありますが、スネークを移動するタイミングを示す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ミリ秒を移動します。

+0

貴重なご意見ありがとうございました。それは問題を解決することができます。あなたが私に示したあなたの別の投稿[link](https://stackoverflow.com/a/14727074/142637)を見て、私は自分のプログラムを変更するためにステートマシンアプローチを使用しようとしました。しかし、私は新しい質問を見つけました。 – cyz1996

+0

貴重な提案をありがとうございました。それは問題を解決することができます。あなたが私に示したあなたの別の投稿[link](https://stackoverflow.com/a/14727074/142637)を見て、私は自分のプログラムを変更するためにステートマシンアプローチを使用しようとしました。しかし、handle_event()メソッドでは実際に状態を初期化するので、新しい質問が見つかりました。私の問題は、GamePlayを続行するための一時停止から、設定が初期化されることでした。一時停止状態でゲームの現在の状態を「覚えている」ようにすることは非常にありますか? – cyz1996

関連する問題