【Python游戏开发】贪吃蛇游戏demo拓展

时间:2024-10-05 16:48:33

拓展上一项目【Python游戏开发】贪吃蛇

实现穿墙效果

# 检测游戏是否结束
def check_gameover():
    global finished
    # 移除蛇头位置超过窗口判断
    for n in range(len(body) - 1):
        if(body[n].x == snake_head.x and body[n].y == snake_head.y):
            finished = True     
# 状态检测
def check_status():
    # 如果蛇头的位置超出窗口,则修改其对应坐标为另一边
    if snake_head.x - SIZE < 0 :
        snake_head.x = WIDTH - SIZE
    elif snake_head.x + SIZE > WIDTH:
        snake_head.x = SIZE
    elif snake_head.y - SIZE < 0:
        snake_head.y = HEIGHT
    elif snake_head.y + SIZE > HEIGHT:
        snake_head.y = SIZE

运行游戏,当蛇头经过墙壁时,会从另一端出现

在这里插入图片描述

游戏胜利判定

failed = False                                      # 游戏胜利标识
def draw():
    screen.fill((255,255,255))                      
    snake_head.draw()                              
    food.draw()                                     
    for b in body:
        b.draw()
    if finished:
        screen.draw.text("Game Over!",center=(WIDTH // 2,HEIGHT // 2),fontsize = 50,color = "red")
    # 绘制胜利提示图像
    if failed:
        screen.draw.text("Failed!",center=(WIDTH // 2,HEIGHT // 2),fontsize = 50,color = "red")
# 检测游戏是否结束
def check_gameover():
    global finished
    global failed
    # 如果蛇身长度大于等于10,则游戏胜利
    if len(body) >= 10:
        failed = True
    for n in range(len(body) - 1):
        if(body[n].x == snake_head.x and body[n].y == snake_head.y):
            finished = True  
def update():
    # 如果游戏已结束,则不再更新游戏,即暂停游戏 
    if finished or failed:
        return
    check_gameover()
    check_keys()
    eat_food()
    update_snake()
    check_status()

点击间隔

运行游戏时,如果蛇头方向向左移动(←),正常情况下按“→”键是无效的,但如果玩家在很短的时间内按“↑”再按“→”键,就会出现蛇头来不及向上移动,就改变为向右移动的情况,导致蛇头直接与蛇身相撞;

为避免该类问题,我们可以设置最小点击间隔,避免快速点击按键的情况出现

start_time = 0                                      # 初始点击时间
# 不用check_keys函数,改为使用内置的on_key_down函数
def on_key_down(key):
    global direction
    # 在间隔时间内,则return不执行键盘事件
    if not check_click_interval(time.time()):
        return
    # ←键被按下,且当前方向不为向右
    if key == keys.LEFT and direction != "R":
        direction = "L"
        snake_head.angle = 180
    # →键被按下,且当前方向不为向左
    elif key == keys.RIGHT and direction != "L":
        direction = "R"
        snake_head.angle = 0
    # ↑键被按下,且当前方向不为向下
    elif key == keys.UP and direction != "D":
        direction = "U"
        snake_head.angle = 90
    # ↓键被按下,且当前方向不为向上
    elif key == keys.DOWN and direction != "U":
        direction = "D"
        snake_head.angle = -90
# 点击间隔判定        
def check_click_interval(click_time,interval_time = 0.2):
    global start_time
    isAccord = False
    # 如果上一次点击间隔与记录的上次点击时间相差大于间隔限制时间,则修改标识为True,并更新start_time
    if click_time - start_time >= interval_time:
        start_time = click_time
        isAccord = True
    return isAccord
def update():
    if finished or failed:
        return
    check_gameover()
    # 移除check_keys
    # check_keys()
    eat_food()
    update_snake()
    check_status()

完整代码

import random
import time
SIZE = 15                                           # 每个格子的大小
WIDTH = SIZE * 30                                   # 游戏场景总宽度
HEIGHT = SIZE * 30                                  # 游戏场景总高度
direction = "R"                                     # 蛇头初始移动方向
counter = 0                                         # 循环标识
snake_head = Actor("snake_head",(30, 30))           # 绘制蛇头图标,初始坐标为(30,30)
length = 1                                          # 贪吃蛇当前的初始长度
MAX_LENGTH = 20                                     # 贪吃蛇最长长度(胜利条件)
body = []                                           # 贪吃蛇蛇身各部位位置,不含蛇头
finished = False                                    # 游戏结束标识
failed = False                                      # 游戏胜利标识
start_time = 0                                      # 初始点击时间
INTERVAL_TIME = 0.1                                 # 点击间隔           

food = Actor("snake_food")
# 在窗口内生成随机坐标,作为食物出现的位置
food.x = random.randint(2,WIDTH // SIZE - 2) * SIZE
food.y = random.randint(2,HEIGHT // SIZE - 2) * SIZE

def draw():
    screen.fill((255,255,255))                      # 设置背景色为白色
    snake_head.draw()                               # 绘制蛇头
    food.draw()                                     # 绘制食物
    # 通过循环列表绘制出所有蛇身
    for b in body:
        b.draw()
    # 绘制失败提示图像
    if finished:
        screen.draw.text("Game Over!",center=(WIDTH // 2,HEIGHT // 2),fontsize = 50,color = "red")
    # 绘制胜利提示图像
    if failed:
        screen.draw.text("Failed!",center=(WIDTH // 2,HEIGHT // 2),fontsize = 50,color = "red")
    
# 不用check_keys函数,改为使用内置的on_key_down函数
def on_key_down(key):
    global direction
    # 在间隔时间内,直接return
    if not check_click_interval(time.time()):
        return
    # ←键被按下,且当前方向不为向右
    if key == keys.LEFT and direction != "R":
        direction = "L"
        snake_head.angle = 180
    # →键被按下,且当前方向不为向左
    elif key == keys.RIGHT and direction != "L":
        direction = "R"
        snake_head.angle = 0
    # ↑键被按下,且当前方向不为向下
    elif key == keys.UP and direction != "D":
        direction = "U"
        snake_head.angle = 90
    # ↓键被按下,且当前方向不为向上
    elif key == keys.DOWN and direction != "U":
        direction = "D"
        snake_head.angle = -90

# 贪吃蛇移动位置各部位变化逻辑
def update_snake():
    global counter
    counter += 1
    # 每执行10次update函数,才执行一次下方代码,减缓蛇头位置变更速度
    if counter < 10:
        return
    else:
        counter = 0
    # 如果蛇身数量等于蛇的总长度,则移除列表记录的第一个蛇身,实际是移除蛇尾
    if len(body) == length:
        body.remove(body[0])
    # 在列表后追加当前蛇头的坐标,用于绘制新的蛇身;配合上面删除实现蛇身位置变更效果
    body.append(Actor("snake_body",(snake_head.x, snake_head.y)))
    
    # 蛇头移动逻辑,改变蛇头位置实现移动效果
    if direction == "L":
        snake_head.x -= SIZE
    elif direction == "R":
        snake_head.x += SIZE
    elif direction == "U":
        snake_head.y -= SIZE
    elif direction == "D":
        snake_head.y += SIZE
    
# 吃食物
def eat_food():
    global length
    # 如果当前食物坐标与蛇头坐标位置重叠
    if food.x == snake_head.x and food.y == snake_head.y:
        # 在窗口内随机坐标位置重新生成食物
        food.x = random.randint(2,WIDTH // SIZE - 2) * SIZE
        food.y = random.randint(2,HEIGHT // SIZE - 2) * SIZE
        length += 1                                 # 每吃一个食物,贪吃蛇长度+1
        
# 检测游戏是否结束
def check_gameover():
    global finished
    global failed
    # 如果蛇身长度大于等于10,则游戏胜利
    if len(body) >= MAX_LENGTH:
        failed = True
    # 遍历蛇身,判断是否存在与蛇头重叠的蛇身,有,则判定失败
    for n in range(len(body) - 1):
        if(body[n].x == snake_head.x and body[n].y == snake_head.y):
            finished = True     

def check_status():
    # 如果蛇头的位置超出窗口,则修改其对应坐标为另一边
    if snake_head.x - SIZE < 0 :
        snake_head.x = WIDTH - SIZE
    elif snake_head.x + SIZE > WIDTH:
        snake_head.x = SIZE
    elif snake_head.y - SIZE < 0:
        snake_head.y = HEIGHT - SIZE
    elif snake_head.y + SIZE > HEIGHT:
        snake_head.y = SIZE

# 点击间隔判定        
def check_click_interval(click_time):
    global start_time
    isAccord = False
    if click_time - start_time >= INTERVAL_TIME:
        start_time = click_time
        isAccord = True
    return isAccord
    
def update():
    # 如果游戏已结束,则不再更新游戏,即暂停游戏 
    if finished or failed:
        return
    check_gameover()
    eat_food()
    update_snake()
    check_status()