拓展上一项目【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()