拓展上一项目【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 - SIZEelif snake_head.x + SIZE > WIDTH:snake_head.x = SIZEelif snake_head.y - SIZE < 0:snake_head.y = HEIGHTelif 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 finishedglobal failed# 如果蛇身长度大于等于10,则游戏胜利if len(body) >= 10:failed = Truefor 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:returncheck_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_timeisAccord = False# 如果上一次点击间隔与记录的上次点击时间相差大于间隔限制时间,则修改标识为True,并更新start_timeif click_time - start_time >= interval_time:start_time = click_timeisAccord = Truereturn isAccord
def update():if finished or failed:returncheck_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) * SIZEdef 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# 在间隔时间内,直接returnif 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 countercounter += 1# 每执行10次update函数,才执行一次下方代码,减缓蛇头位置变更速度if counter < 10:returnelse: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 -= SIZEelif direction == "R":snake_head.x += SIZEelif direction == "U":snake_head.y -= SIZEelif 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) * SIZEfood.y = random.randint(2,HEIGHT // SIZE - 2) * SIZElength += 1 # 每吃一个食物,贪吃蛇长度+1# 检测游戏是否结束
def check_gameover():global finishedglobal 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 - SIZEelif snake_head.x + SIZE > WIDTH:snake_head.x = SIZEelif snake_head.y - SIZE < 0:snake_head.y = HEIGHT - SIZEelif snake_head.y + SIZE > HEIGHT:snake_head.y = SIZE# 点击间隔判定
def check_click_interval(click_time):global start_timeisAccord = Falseif click_time - start_time >= INTERVAL_TIME:start_time = click_timeisAccord = Truereturn isAccorddef update():# 如果游戏已结束,则不再更新游戏,即暂停游戏 if finished or failed:returncheck_gameover()eat_food()update_snake()check_status()