资料合集下载链接:
https://pan.quark.cn/s/472bbdfcd014
游戏玩法回顾与功能分析
在我们敲下第一行代码之前,先来回顾一下贪吃蛇的核心玩法,这正是我们项目需求的来源。
1. 基本操作:玩家通过方向键(或W/S/A/D)控制蛇头的移动方向。
2. 成长机制:蛇会自动向前移动,当蛇头碰到“食物”时,蛇的身体会变长一节。
3. 食物刷新:食物被吃掉后,会在地图的随机位置重新生成。
4. 失败条件:
• 蛇头撞到游戏边界(墙壁)。
• 蛇头撞到自己的身体。
5. 游戏结束:一旦满足失败条件,游戏结束。
模块化设计:谋定而后动
一个好的程序离不开清晰的设计。根据笔记的指导,我们将项目划分为以下几个核心模块,让我们的代码结构更加清晰:
• 视图(View)模块:负责创建游戏窗口、绘制地图边界、渲染蛇和食物。这是用户能直接看到的部分。
• 蛇(Snake)模块:管理蛇的一切。包括它的初始位置、身体坐标、移动逻辑、成长逻辑以及死亡判断。
• 食物(Food)模块:负责食物的生成(随机位置)和状态。
• 控制(Control)模块:接收并处理玩家的键盘输入,改变蛇的行进方向。
• 主控(Main)模块:作为游戏的大脑,组织和调度以上所有模块,形成完整的游戏循环。
现在,让我们开始动手实现吧!
环境准备
本项目使用Python语言和curses库。curses是一个标准的Python库,用于在类Unix系统(Linux, macOS)上创建基于文本的用户界面(TUI)。
• 对于Linux/macOS用户:curses通常是Python自带的,无需额外安装。
• 对于Windows用户:curses不是标准库的一部分。你需要安装一个替代包windows-curses。
pip install windows-curses
代码实现:一步步构建贪吃蛇
我们将把所有代码写在一个Python文件中。让我们开始吧!
1. 初始化环境和视图模块
首先,我们需要导入curses和random库,并初始化游戏窗口。这部分代码实现了视图模块的基础。
import curses
import random
# 1. 初始化屏幕
# stdscr 是 standard screen 的缩写,代表整个终端窗口
stdscr = curses.initscr()
# 关闭光标显示
curses.curs_set(0)
# 获取屏幕的宽和高
sh, sw = stdscr.getmaxyx()
# 创建一个新的窗口,大小为 sh, sw,起始位置在 (0, 0)
win = curses.newwin(sh, sw, 0, 0)
# 开启键盘输入模式
win.keypad(1)
# 设置窗口刷新延迟,单位是毫秒。这里设置为100ms,即每秒刷新10次
win.timeout(100)
代码解析:
• curses.initscr():初始化curses环境,返回一个代表整个屏幕的窗口对象。
• curses.curs_set(0):隐藏闪烁的光标,让界面更清爽。
• curses.newwin(...):创建一个新的游戏窗口,我们所有的绘制操作都将在这个窗口上进行。
• win.keypad(1):允许我们使用方向键等特殊按键。
• win.timeout(100):这是一个关键函数。它让win.getch()(获取键盘输入)变成非阻塞的。程序会等待100毫秒,如果期间没有按键,就继续执行下去,实现了蛇的自动移动。
2. 蛇与食物模块的初始化
接下来,我们要创建蛇和食物。这对应了蛇模块和食物模块的初始化部分。
# 2. 蛇模块 和 食物模块 初始化
# 蛇头的初始位置(屏幕中央)
snk_x = sw // 4
snk_y = sh // 2
# 蛇的身体,用一个列表来存储坐标。列表的第一个元素是蛇头。
# 这就是笔记中提到的“二维数组表示蛇的位置”
snake = [
[snk_y, snk_x], # 蛇头
[snk_y, snk_x - 1], # 蛇身
[snk_y, snk_x - 2] # 蛇尾
]
# 食物的初始位置
food = [sh // 2, sw // 2]
# 在窗口中绘制第一个食物,'π' 是我们选择的食物字符
win.addch(int(food[0]), int(food[1]), 'π')
# 蛇的初始移动方向
key = curses.KEY_RIGHT
代码解析:
• 我们用一个列表snake来表示蛇。列表中的每个元素是[y, x]坐标。snake[0]永远是蛇头。这种数据结构非常直观,方便我们进行后续的移动和增长操作。
• 食物food也用一个[y, x]坐标表示。
• win.addch(y, x, char)是curses的绘图函数,可以在指定坐标绘制一个字符。
• key变量存储了蛇当前的前进方向,初始为向右。
3. 游戏主循环:控制与逻辑的核心
这是整个游戏的心脏,它负责接收输入、更新状态、判断逻辑并刷新屏幕。
# 3. 游戏主循环
while True:
# 3.1 控制模块:获取下一个按键
next_key = win.getch()
# 如果没有按键,next_key会是-1,此时保持原有方向;否则更新方向
key = key if next_key == -1 else next_key
# 3.2 蛇模块:判断游戏是否结束(撞墙或撞自己)
# 撞墙判断
if snake[0][0] in [0, sh-1] or
snake[0][1] in [0, sw-1] or
snake[0] in snake[1:]: # 撞自己判断
curses.endwin() # 关闭curses环境
print("Game Over!")
quit()
# 3.3 蛇模块:计算蛇头的新位置
new_head = [snake[0][0], snake[0][1]]
if key == curses.KEY_DOWN:
new_head[0] += 1
if key == curses.KEY_UP:
new_head[0] -= 1
if key == curses.KEY_LEFT:
new_head[1] -= 1
if key == curses.KEY_RIGHT:
new_head[1] += 1
# 3.4 蛇模块:移动蛇(在列表头部插入新蛇头)
snake.insert(0, new_head)
# 3.5 蛇与食物模块:判断是否吃到食物
if snake[0] == food:
# 吃到食物,蛇变长(不移除蛇尾),生成新食物
food = None
while food is None:
nf = [
random.randint(1, sh - 2),
random.randint(1, sw - 2)
]
# 确保新食物不会生成在蛇身上
food = nf if nf not in snake else None
win.addch(food[0], food[1], 'π')
else:
# 没吃到食物,蛇正常移动(移除蛇尾)
tail = snake.pop()
win.addch(tail[0], tail[1], ' ') # 在原蛇尾位置画一个空格,实现擦除效果
# 3.6 视图模块:绘制新的蛇头
win.addch(snake[0][0], snake[0][1], curses.ACS_CKBOARD)
代码解析:
• while True: 创建了一个无限循环,游戏会一直运行直到结束条件满足。
• 控制模块 (win.getch()): 获取键盘输入。注意我们只在有新按键时才更新key,这防止了玩家快速反向操作(比如从右立刻到左)导致蛇直接撞到自己。
• 死亡判断:
• snake[0][0] in [0, sh-1] 检查蛇头是否碰到了上下边界。
• snake[0][1] in [0, sw-1] 检查蛇头是否碰到了左右边界。
• snake[0] in snake[1:] 检查蛇头坐标是否存在于蛇身的其余部分,这是判断是否“咬到自己”的精髓。
• 移动逻辑:我们先计算出下一个蛇头new_head的位置,然后用snake.insert(0, new_head)将它加到蛇身体列表的最前面。
• 成长逻辑:
• 如果new_head的位置和food一样,说明吃到了食物。此时,我们不执行snake.pop(),这样蛇的列表长度就增加了1,实现了“变长”。然后我们重新生成一个随机位置的食物。
• 如果没吃到,就执行snake.pop(),移除列表的最后一个元素(蛇尾),并用空格擦除它在屏幕上的痕跡,保持蛇的长度不变。
• 视图渲染:win.addch(snake[0][0], snake[0][1], curses.ACS_CKBOARD) 在新蛇头的位置画一个方块字符,代表蛇的身体。
4. 整合完整代码
将以上所有部分组合起来,就是我们完整的贪吃蛇游戏代码。
# snake_game.py
import curses
import random
def main(stdscr):
# --- 1. 初始化环境和视图模块 ---
curses.curs_set(0)
sh, sw = stdscr.getmaxyx()
win = curses.newwin(sh, sw, 0, 0)
win.keypad(1)
win.timeout(100) # 游戏速度,数值越小越快
# --- 2. 蛇与食物模块的初始化 ---
snk_x = sw // 4
snk_y = sh // 2
snake = [
[snk_y, snk_x],
[snk_y, snk_x - 1],
[snk_y, snk_x - 2]
]
food = [sh // 2, sw // 2]
win.addch(food[0], food[1], 'π')
key = curses.KEY_RIGHT
score = 0
# --- 3. 游戏主循环 ---
while True:
# 绘制分数和边框
win.border(0)
win.addstr(0, 2, f'Score: {score} ')
next_key = win.getch()
# 防止蛇反向移动
if next_key != -1:
if (key == curses.KEY_RIGHT and next_key != curses.KEY_LEFT) or
(key == curses.KEY_LEFT and next_key != curses.KEY_RIGHT) or
(key == curses.KEY_UP and next_key != curses.KEY_DOWN) or
(key == curses.KEY_DOWN and next_key != curses.KEY_UP):
key = next_key
# 判断游戏是否结束
if snake[0][0] in [0, sh - 1] or
snake[0][1] in [0, sw - 1] or
snake[0] in snake[1:]:
curses.endwin()
print(f"Game Over! Final Score: {score}")
return
new_head = [snake[0][0], snake[0][1]]
if key == curses.KEY_DOWN:
new_head[0] += 1
if key == curses.KEY_UP:
new_head[0] -= 1
if key == curses.KEY_LEFT:
new_head[1] -= 1
if key == curses.KEY_RIGHT:
new_head[1] += 1
snake.insert(0, new_head)
if snake[0] == food:
score += 10
food = None
while food is None:
nf = [
random.randint(1, sh - 2),
random.randint(1, sw - 2)
]
food = nf if nf not in snake else None
win.addch(food[0], food[1], 'π')
else:
tail = snake.pop()
win.addch(tail[0], tail[1], ' ')
win.addch(snake[0][0], snake[0][1], curses.ACS_CKBOARD)
win.refresh()
if __name__ == "__main__":
curses.wrapper(main)
新增内容解析:
• 我们把代码封装在main函数中,并使用curses.wrapper(main)来启动。这是一个推荐的做法,wrapper会自动处理curses的初始化和善后工作(比如程序出错时恢复终端状态)。
• 增加了score计分功能。
• 增加了win.border(0)来绘制游戏边界。
• 优化了方向键的判断,防止蛇直接180度掉头。
运行效果
将以上代码保存为snake_game.py,然后在终端中运行:
python snake_game.py
你将看到如下的界面:
1. 游戏开始
你的终端会变成一个游戏窗口,一条短蛇和一个食物出现在屏幕上。
+------------------------------------------------------------------------------+
| Score: 0 |
| |
| |
| |
| ■■■ |
| |
| π |
| |
| |
+------------------------------------------------------------------------------+
2. 游戏进行中
使用方向键控制蛇移动,吃到食物后,蛇会变长,分数增加,食物会刷新到新的位置。
+------------------------------------------------------------------------------+
| Score: 30 |
| |
| ■ |
| ■ |
| ■■■■■■■■■■■ |
| ■ |
| ■ |
| ■ |
| π |
+------------------------------------------------------------------------------+
3. 游戏结束
当蛇撞到墙壁或自己时,程序退出,终端恢复正常,并打印出最终得分。
$ python snake_game.py
Game Over! Final Score: 30
$

















暂无评论内容