2017-11-03 1 views
1

파이 게임 모듈을 사용하여 뱀 게임을 썼다.파이 게임 뱀 게임, 방향을 빠르게 바꿀 때 뱀이 움직이지 않는다

테스트 후, 나는 뱀의 방향을 빠르게 바꿀 때 그것을 발견했다. 예 : 뱀의 몸을 다음 줄로 움직이거나 반대 방향으로 변경하기 위해 두 개의 화살표 키를 아주 빨리 누르면 뱀이 정확하게 반응하지 않습니다. 대부분의 경우 작동하지만 뱀이 움직이지 않는 경우는 거의 없습니다. 나는 이것이 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

우선, 하나의 메인 루프를 사용해야합니다.

시작 또는 끝 화면을 렌더링하는 동안 이벤트 루프가 실행되지 않으므로 창과 상호 작용할 수 없습니다. 그 시간 동안 창을 움직이거나 닫을 수 없다는 것은 매우 성가신 일입니다. 어쩌면 이것을 처리하는 방법에 대한 예제는 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) 

이 뱀의 속도를 제어하는 ​​지금 얼마나 쉽게 참조 :

여기에 현재 코드에 대한 간단한 구현의 지금 모든이 300ms로 이동합니다.

+0

당신의 소중한 제안에 진심으로 감사드립니다. 그것은 문제를 해결할 수 있습니다. 나에게 보여준 다른 게시물 [link] (https://stackoverflow.com/a/14727074/142637)을보고 나서, 내 프로그램을 수정하기 위해 state-machine 방식을 사용하려고했습니다. 하지만 새로운 질문을 발견했습니다. – cyz1996

+0

정말 감사드립니다. 그것은 문제를 해결할 수 있습니다. 나에게 보여준 다른 게시물 [link] (https://stackoverflow.com/a/14727074/142637)을보고 나서, 내 프로그램을 수정하기 위해 state-machine 방식을 사용하려고했습니다. handle_event() 메서드에서 실제로 상태를 초기화하므로 새로운 질문을 발견했습니다. 내 문제는 GamePlay를 계속하려면 Pause에서 설정이 초기화된다는 것입니다. Pause 상태가 게임의 현재 상태를 "기억"하게하는 것이 있습니까? – cyz1996