如何阻止Pygame贪吃蛇游戏中蛇自撞(Python实现)
解决贪吃蛇反向转向自撞问题
这是我用Pygame制作的第一款游戏,本质是贪吃蛇,请忽略我粗糙的注释。
William's Snake Game
import pygame import time import random pygame.init() ##Global variables # Colours white = (255, 255, 255) black = (0, 0, 0) red = (255, 0, 0) green = (0, 155, 0) bground = (204, 204, 0) # Display vars. display_width = 800 display_height = 600 # Variables gameDisplay = pygame.display.set_mode((display_width, display_height)) ##resolution, note that this is a tuple. pygame.display.set_caption("Snake VS Apple") # Title at the top of the screen icon = pygame.image.load('logoapple32x32.png') # Loads the icon for the top left pygame.display.set_icon(icon) # Sets the icon for the top left snakeHeadImg = pygame.image.load("snakeHead.png") # Loads the image for the snake head appleImg = pygame.image.load("apple20x20.png") # Loads the image for the apple appleThickness = 20 # Defines how thick apple will be. Note to self: This is changable clock = pygame.time.Clock() # Starts clocking the game, used later for FPS blockSize = 20 # Defines how big the snake will be. Changing this will mess up collision detection. FPS = 15 # Frames per second. Called at the bottom of script smallfont = pygame.font.SysFont("arial", 25) ## format: ("font", fontsize) medfont = pygame.font.SysFont("arial", 40) ## largefont = pygame.font.SysFont("arial", 80) ## direction = "right" # Starting direction of snake, used in main gameLoop ## def pauseGame(): gameisPaused = True while gameisPaused: for event in pygame.event.get(): if event.type == pygame.QUIT: pygame.quit() quit() if event.type == pygame.KEYDOWN: if event.key == pygame.K_ESCAPE: gameisPaused = False elif event.key == pygame.K_q: pygame.quit() quit() gameDisplay.fill(white) message_to_screen("Paused", black, -100, size="large") message_to_screen("Press ESC to continue or Q to quit.", black, 25, size="small") pygame.display.update() clock.tick(5) def text_objects(text, color, size): # Function to render text if size == "small": textSurface = smallfont.render(text, True, color) elif size == "medium": textSurface = medfont.render(text, True, color) elif size == "large": textSurface = largefont.render(text, True, color) return textSurface, textSurface.get_rect() def message_to_screen(msg, color, y_displace=0, size="medium"): # Function to blit (draw) text to surface textSurf, textRect = text_objects(msg, color, size) textRect.center = (display_width / 2), (display_height / 2) + y_displace gameDisplay.blit(textSurf, textRect) ## def score(score): text = smallfont.render("Score: " + str(score), True, black) gameDisplay.blit(text, [0, 0]) def randAppleGen(): # Function to generate random apples randAppleX = round( random.randrange(0, display_width - appleThickness)) # /10.0)*10.0 ##Create another rand X value for new apple randAppleY = round( random.randrange(0, display_height - appleThickness)) # /10.0)*10.0 ##Create another rand Y value for new apple return randAppleX, randAppleY def gameIntro(): # Function for game menu. intro = True while intro: # Event handling during menu for eachEvent in pygame.event.get(): if eachEvent.type == pygame.QUIT: pygame.quit() quit() if eachEvent.type == pygame.KEYDOWN: if eachEvent.key == pygame.K_c: gameLoop() if eachEvent.key == pygame.K_q: pygame.quit() quit() # Text displayed in menu gameDisplay.fill(white) message_to_screen("Welcome to Slither", green, -90, "large") message_to_screen("The more apples you eat, the longer you are", black, 100) message_to_screen("The objective of the game is to eat red apples", black, 0, "small") message_to_screen("If you run into yourself, or the edges, you die!", black, 30, "small") message_to_screen("Press C to play or Q to quit.", black, 180) pygame.display.update() clock.tick(500) def snake(blockSize, snakeList): # Function to draw snake if direction == "right": head = pygame.transform.rotate(snakeHeadImg, 270) # In gameLoop, right,left,up,down are used to change direction of snakeHead elif direction == "left": head = pygame.transform.rotate(snakeHeadImg, 90) elif direction == "up": head = snakeHeadImg elif direction == "down": head = pygame.transform.rotate(snakeHeadImg, 180) gameDisplay.blit(head, (snakeList[-1][0], snakeList[-1][1])) ##??? for XandY in snakeList[:-1]: pygame.draw.rect(gameDisplay, green, [XandY[0], XandY[1], blockSize, blockSize]) ##width height, width height, drawing ##Main Game Loop def gameLoop(): global direction ## Make direction a global var. Important # Local variables gameExit = False gameOver = False lead_x = display_width / 2 lead_y = display_height / 2 lead_x_change = 10 lead_y_change = 0 snakeList = [] snakeLength = 3 randAppleX, randAppleY = randAppleGen() # Generate apples. Calls randAppleGen() # Main Game Loop ##eventHandler while not gameExit: snakeHead = [] # Creates list snakeHead snakeHead.append(lead_x) # Appends snakeHead x value to list snakeHead.append(lead_y) # Appends snakeHead y value to list snakeList.append(snakeHead) # Appends coordinates of snakeHead x,y to list while gameOver == True: # Handles game over gameDisplay.fill(white) message_to_screen("Game over", red, -50, size="large") message_to_screen("Press C to play again or Q to quit", black, 50, size="medium") pygame.display.update() for event in pygame.event.get(): # eventHandler for loss screen if event.type == pygame.QUIT: pygame.quit() quit() if event.type == pygame.KEYDOWN: if event.key == pygame.K_q: gameExit = True gameOver = False if event.key == pygame.K_c: direction = "right" gameLoop() for event in pygame.event.get(): # eventHandler for keyboard events during game if event.type == pygame.QUIT: gameExit = True if event.type == pygame.KEYDOWN: if event.key == pygame.K_LEFT or event.key == pygame.K_a: # Each of these handles either arrow keys or WASD key events. lead_x_change = -blockSize lead_y_change = 0 direction = "left" elif event.key == pygame.K_RIGHT or event.key == pygame.K_d: lead_x_change = blockSize lead_y_change = 0 direction = "right" elif event.key == pygame.K_UP or event.key == pygame.K_w: lead_y_change = -blockSize lead_x_change = 0 direction = "up" elif event.key == pygame.K_DOWN or event.key == pygame.K_s: lead_y_change = blockSize lead_x_change = 0 direction = "down" elif event.key == pygame.K_ESCAPE: pauseGame() # Checks if user has hit screen boundaries. if lead_x >= display_width or lead_x < 0 or lead_y >= display_height or lead_y < 0: # If user hits display boundaries gameOver = True # They lose lead_x += lead_x_change # Ensures continous movement of the snake lead_y += lead_y_change # Drawing gameDisplay.fill(bground) # Fills the display background with predefined bground colour (defined at the top) gameDisplay.blit(appleImg, (randAppleX, randAppleY)) ##Draws the apple using the appleImg, at random coordinates. if len( snakeList) > snakeLength: # If the length of the list of snake body coordinates is greater than the length del snakeList[0] # Delete the oldest value in the list (as the snake is constantly moving) for eachSegment in snakeList[:-1]: # For each coordinate in snakeList if eachSegment == snakeHead: # If the segment touches the snakeHead ## gameDisplay.fill(bground) ## snake(blockSize, snakeList) ## ## pygame.display.update time.sleep(0.3) gameOver = True # Game over snake(blockSize, snakeList) ##Creates snake using function snake score(snakeLength - 3) # Displays score (it minuses 3 because the snake starts at 3) pygame.display.update() ##Updates to screen ## COLLISION DETECTION if lead_x + blockSize > randAppleX and lead_x < randAppleX + appleThickness: if lead_y + blockSize > randAppleY and lead_y < randAppleY + appleThickness: randAppleX, randAppleY = randAppleGen() snakeLength += 1 clock.tick(FPS) pygame.quit() quit() ##update screen ## gameIntro() gameLoop()
问题描述
当蛇向右移动时,若向左转向,会撞到自身导致游戏结束。我希望设计成蛇在某一方向移动时,无法直接反向转向,只能转向垂直方向或保持原方向,避免自撞。我尝试过多种方法但都无效,请求技术帮助!
解决方案
这个问题的核心是在处理方向输入时,添加反向方向的判断拦截,不让蛇直接转向相反方向。我们只需要修改gameLoop里的键盘事件处理逻辑即可:
修改后的键盘事件处理代码
for event in pygame.event.get(): # eventHandler for keyboard events during game if event.type == pygame.QUIT: gameExit = True if event.type == pygame.KEYDOWN: if (event.key == pygame.K_LEFT or event.key == pygame.K_a) and direction != "right": # 蛇当前向右时,禁止向左转 lead_x_change = -blockSize lead_y_change = 0 direction = "left" elif (event.key == pygame.K_RIGHT or event.key == pygame.K_d) and direction != "left": # 蛇当前向左时,禁止向右转 lead_x_change = blockSize lead_y_change = 0 direction = "right" elif (event.key == pygame.K_UP or event.key == pygame.K_w) and direction != "down": # 蛇当前向下时,禁止向上转 lead_y_change = -blockSize lead_x_change = 0 direction = "up" elif (event.key == pygame.K_DOWN or event.key == pygame.K_s) and direction != "up": # 蛇当前向上时,禁止向下转 lead_y_change = blockSize lead_x_change = 0 direction = "down" elif event.key == pygame.K_ESCAPE: pauseGame()
逻辑说明
- 每次处理方向按键时,先判断当前蛇的运动方向:比如当蛇正在向右移动时,用户按下左方向键/A键,我们直接忽略这个输入,不改变方向和移动参数。
- 这样蛇只能转向与当前方向垂直的方向(比如向右时只能转上或下),或者保持原方向,从根源上避免了直接反向导致的自撞问题。
额外优化提示
你代码里的lead_x_change初始值是10,但blockSize是20,这里有点不一致,建议把初始的lead_x_change改成blockSize,这样移动步长和蛇块大小统一,避免潜在的碰撞检测问题:
lead_x_change = blockSize # 原来的10改成blockSize lead_y_change = 0
内容的提问来源于stack exchange,提问作者willlplaya
相关产品推荐
相关产品推荐

