You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何阻止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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.15 08:47:15