CS 41 课程九:游戏开发(5月24日)
一、课程基本信息
日期 :2023年5月24日
主题:🐍 Gaming(游戏开发)
课程安排
下周预览:
- 单元测试(Unit Testing)
- Pygame游戏开发
二、使用Python制作游戏
1. 游戏示例展示
Example Games(课堂演示各种Python游戏)
2. Pygame安装
安装说明:
- Pygame是非标准库包(上周学习内容)
- 需要使用pip安装
bash
pip install pygame
三、游戏循环(Game Loop)
1. 游戏循环概念
核心作用:
- 游戏控制的中心
- 更新游戏状态
- 每个循环周期就是一帧(frame)
基本结构:
python
running = True
while running:
# 1. 处理事件
# 2. 更新游戏状态
# 3. 绘制画面
# 4. 刷新显示
四、事件系统(Events)
1. 什么是事件?
定义:
- 用户操作,游戏循环围绕这些操作展开
- 通过事件处理器(event handler)访问
- 每个事件都有一个类型
2. 事件类型
常见事件类型:
| 事件类型 | 说明 |
|---|---|
QUIT |
退出事件 |
ACTIVEEVENT |
窗口激活事件 |
KEYDOWN |
按键按下 |
KEYUP |
按键释放 |
MOUSEMOTION |
鼠标移动 |
MOUSEBUTTONUP |
鼠标按钮释放 |
MOUSEBUTTONDOWN |
鼠标按钮按下 |
JOYAXISMOTION |
游戏手柄轴移动 |
JOYBALLMOTION |
游戏手柄球移动 |
JOYHATMOTION |
游戏手柄帽移动 |
JOYBUTTONUP |
游戏手柄按钮释放 |
JOYBUTTONDOWN |
游戏手柄按钮按下 |
VIDEORESIZE |
视频窗口调整大小 |
VIDEOEXPOSE |
视频窗口曝光 |
USEREVENT |
用户自定义事件 |
3. 事件队列
访问事件:
python
for event in pygame.event.get():
# 处理事件
事件对象示例:
python
<Event(768-KeyDown {
'unicode': '',
'key': 1073741906,
'mod': 0,
'scancode': 82,
'window': None
})>
<Event(769-KeyUp {
'unicode': '',
'key': 1073741906,
'mod': 0,
'scancode': 82,
'window': None
})>
特点:
- 事件存储在队列中
- 按顺序处理
- 包含详细的事件信息
五、第一个图形窗口
1. 完整代码示例
python
import pygame
pygame.init()
screen = pygame.display.set_mode((500, 500))
white = (255, 255, 255)
blue = (0, 0, 255)
running = True
while running:
for event in pygame.event.get():
if event.type == pygame.QUIT:
running = False
# 用白色填充背景
screen.fill(white)
# 在中心绘制蓝色实心圆
pygame.draw.circle(screen, blue, (250, 250), 75)
# 更新显示
pygame.display.update() # 或使用 pygame.display.flip()
# 游戏结束时
pygame.quit()
2. 代码详解
(1) 初始化窗口
python
import pygame
pygame.init()
screen = pygame.display.set_mode((500, 500))
- 导入pygame库
- 初始化pygame
- 创建500x500像素的窗口
(2) 定义颜色
python
white = (255, 255, 255)
blue = (0, 0, 255)
- 使用RGB元组表示颜色
- (R, G, B) 每个值范围0-255
(3) 初始化游戏循环
python
running = True
while running:
- 控制游戏运行状态
- 循环直到running变为False
(4) 获取事件队列
python
for event in pygame.event.get():
- 获取所有事件
- 遍历事件队列
(5) 检查事件类型
python
if event.type == pygame.QUIT:
running = False
- 检查是否点击关闭按钮
- 退出游戏循环
(6) 绘制图形
python
screen.fill(white)
pygame.draw.circle(screen, blue, (250, 250), 75)
screen.fill(white): 填充背景色pygame.draw.circle(): 绘制圆形- 参数1: 绘制表面
- 参数2: 颜色
- 参数3: (x, y) 圆心坐标
- 参数4: 半径
(7) 更新显示
python
pygame.display.update() # 或 pygame.display.flip()
- 刷新屏幕显示
- 显示所有绘制内容
运行代码:Let's run this code
六、制作贪吃蛇游戏
1. 绘制矩形基础
python
pygame.draw.rect(surface, color, rect)
参数说明:
surface: 绘制表面(screen)color: 颜色元组rect: 矩形[left, top, width, height]
示例:
python
# 绘制一个蛇的方块
pygame.draw.rect(screen, blue, [100, 100, 10, 10])
2. 键盘控制
检测按键事件
python
if event.type == pygame.KEYDOWN:
if event.key == pygame.K_LEFT:
# 向左移动
if event.key == pygame.K_RIGHT:
# 向右移动
if event.key == pygame.K_UP:
# 向上移动
if event.key == pygame.K_DOWN:
# 向下移动
按键常量:
pygame.K_LEFT: 左箭头pygame.K_RIGHT: 右箭头pygame.K_UP: 上箭头pygame.K_DOWN: 下箭头
3. 蛇的表示与移动
数据结构
python
# 使用坐标元组列表表示蛇
snake = [(100, 100), (100, 110), (100, 120)]
移动策略问题
方法1:更新所有坐标(复杂)
问题:蛇头需要向下移动snake_size
但需要遍历整个列表更新所有过去的坐标 :(
方法2:添加和删除(优雅)✓
步骤1: 在蛇头应该在的位置添加新方块
步骤2: 删除最后一个方块
图示:
移动前: [█][█][█]
↓
添加新头: [█][█][█][█]
↓
删除尾部: [█][█][█] (新位置)
4. 动画速度控制
时钟控制:
python
pygame.time.Clock().tick(15)
作用:
- 确保游戏循环最多以每秒15帧运行
- 放在循环内部
- 控制游戏速度,使其在不同电脑上表现一致
帧率说明:
python
# 慢速游戏
pygame.time.Clock().tick(10) # 10 FPS
# 正常速度
pygame.time.Clock().tick(15) # 15 FPS
# 快速游戏
pygame.time.Clock().tick(30) # 30 FPS
七、完整贪吃蛇代码实现
1. 常量和初始化
python
import time
import random
import pygame
# 常量和变量
white = (255, 255, 255)
blue = (50, 153, 213)
green = (0, 255, 0)
dis_width = 600
dis_height = 400
block_size = 10
snake_length = 15
# 初始化pygame窗口
pygame.init()
screen = pygame.display.set_mode((dis_width, dis_height))
pygame.display.set_caption('CS 41 PYTHON')
2. 辅助函数
(1) 初始化蛇
python
def initialize_snake(len, start_left, start_top):
initialize_snake = []
for i in range(len):
initialize_snake.append((start_left, start_top + (block_size * i)))
return initialize_snake
(2) 绘制蛇
python
def draw_snake(snake_coords):
for x, y in snake_coords:
pygame.draw.rect(screen, blue, [x, y, block_size, block_size])
(3) 更新蛇的位置
python
def update_snake(x, y, snake):
snake.append((x, y))
if len(snake) > snake_length:
del snake[0]
return snake
工作原理:
- 在蛇头位置添加新方块
- 如果蛇长度超过限制,删除尾部
- 实现"移动"效果
(4) 生成食物
python
def generate_food(snake):
while True:
food_x = random.randrange(0, dis_width, block_size)
food_y = random.randrange(0, dis_height, block_size)
if (food_x, food_y) not in snake:
return food_x, food_y
重要细节:
- 使用
random.randrange()生成随机坐标 - 步长为
block_size确保食物在网格上 - 检查食物不与蛇重叠
(5) 绘制食物
python
def draw_food(food_x, food_y):
pygame.draw.rect(screen, green, [food_x, food_y, block_size, block_size])
(6) 碰撞检测
python
def check_collision(snake_head, food_x, food_y):
if snake_head[0] == food_x and snake_head[1] == food_y:
return True
return False
3. 主游戏函数
python
def game():
global snake_length # 使用全局变量
running = True
x_change = 0
y_change = 0
x = dis_width / 2
y = dis_height / 2
snake = initialize_snake(snake_length, x, y)
food_x, food_y = generate_food(snake)
while running:
screen.fill(white)
draw_snake(snake)
draw_food(food_x, food_y)
for event in pygame.event.get():
if event.type == pygame.QUIT:
running = False
if event.type == pygame.KEYDOWN:
if event.key == pygame.K_LEFT:
x_change = -block_size
y_change = 0
if event.key == pygame.K_RIGHT:
x_change = block_size
y_change = 0
if event.key == pygame.K_UP:
x_change = 0
y_change = -block_size
if event.key == pygame.K_DOWN:
x_change = 0
y_change = block_size
x += x_change
y += y_change
snake = update_snake(x, y, snake)
if check_collision(snake[-1], food_x, food_y):
snake_length += 1
food_x, food_y = generate_food(snake)
draw_snake(snake)
pygame.time.Clock().tick(15)
pygame.display.flip()
pygame.quit()
if __name__ == "__main__":
game()
4. 游戏逻辑流程
每帧执行步骤:
1. 清空屏幕(填充白色)
2. 绘制蛇和食物
3. 处理事件(键盘输入、退出)
4. 更新蛇的位置
5. 检查是否吃到食物
6. 如果吃到食物:
- 增加蛇的长度
- 生成新食物
7. 重新绘制
8. 控制帧率(15 FPS)
9. 刷新显示
八、小组活动
活动说明
任务:为贪吃蛇游戏添加食物功能
准备工作:
- 确保已安装pygame:
pip install pygame - 从网站下载starter code
功能要求
-
食物碰撞:
- 当蛇碰到食物时,蛇增长1个单位
-
食物生成:
- 食物应该出现在窗口的随机位置
- 被吃掉后重新出现
-
提示:
- 思考蛇移动的倍数
- 食物坐标应该只在这些倍数位置上
-
扩展功能(可选):
- 如果想添加其他有趣的功能,随意发挥!
实现建议
关键点:
python
# 1. 食物坐标必须是block_size的倍数
food_x = random.randrange(0, dis_width, block_size)
food_y = random.randrange(0, dis_height, block_size)
# 2. 检测碰撞
if snake_head == (food_x, food_y):
snake_length += 1
# 生成新食物
# 3. 确保食物不在蛇身上
while (food_x, food_y) in snake:
# 重新生成
九、课程总结
Pygame核心概念
游戏开发三要素:
1. 游戏循环(Game Loop)
└─ 持续运行直到游戏结束
2. 事件处理(Event Handling)
└─ 响应用户输入
3. 绘制更新(Draw & Update)
└─ 显示游戏状态
游戏循环模式
python
# 标准游戏循环模式
pygame.init()
screen = pygame.display.set_mode((width, height))
clock = pygame.time.Clock()
running = True
while running:
# 1. 处理事件
for event in pygame.event.get():
if event.type == pygame.QUIT:
running = False
# 其他事件处理
# 2. 更新游戏逻辑
# 更新位置、检测碰撞等
# 3. 绘制
screen.fill(background_color)
# 绘制游戏元素
# 4. 刷新和控制帧率
pygame.display.flip()
clock.tick(FPS)
pygame.quit()
Pygame常用函数速查
初始化与窗口:
python
pygame.init()
screen = pygame.display.set_mode((width, height))
pygame.display.set_caption('Title')
事件处理:
python
pygame.event.get() # 获取所有事件
event.type # 事件类型
event.key # 按键值
绘制函数:
python
screen.fill(color) # 填充背景
pygame.draw.circle(screen, color, (x,y), r) # 圆形
pygame.draw.rect(screen, color, [x,y,w,h]) # 矩形
pygame.draw.line(screen, color, (x1,y1), (x2,y2)) # 线条
显示更新:
python
pygame.display.flip() # 刷新整个屏幕
pygame.display.update() # 更新特定区域
时间控制:
python
clock = pygame.time.Clock()
clock.tick(FPS) # 控制帧率
贪吃蛇实现要点
数据结构选择:
python
# 用列表存储蛇的所有方块
snake = [(x1, y1), (x2, y2), (x3, y3)]
# 蛇头:snake[-1]
# 蛇尾:snake[0]
移动算法:
python
# 添加新头
snake.append((new_x, new_y))
# 删除旧尾(如果没吃食物)
if not ate_food:
del snake[0]
网格对齐:
python
# 确保所有坐标都是block_size的倍数
x = random.randrange(0, width, block_size)
y = random.randrange(0, height, block_size)
可能的扩展功能
基础扩展:
- 添加得分显示
- 增加游戏难度(随时间加速)
- 添加边界检测(撞墙游戏结束)
- 添加自咬检测(蛇咬到自己)
进阶扩展 :
-
多个食物
-
特殊食物(加速/减速/双倍分数)
-
障碍物
-
暂停功能
-
最高分记录
-
音效和背景音乐
调试技巧
常见问题:
python
# 1. 窗口一闪而过
# 解决:确保有游戏循环和事件处理
# 2. 游戏太快/太慢
# 解决:调整clock.tick()的参数
# 3. 绘制不显示
# 解决:确保调用了pygame.display.flip()
# 4. 蛇移动不连续
# 解决:确保坐标是block_size的倍数
学习资源
官方文档:
- Pygame官网:https://www.pygame.org/
- Pygame文档:https://www.pygame.org/docs/
教程推荐:
- Pygame官方教程
- Real Python的Pygame系列
- YouTube上的游戏开发教程
关键要点
-
游戏循环是核心:
- 所有游戏都基于循环
- 每帧更新状态和绘制
-
事件驱动编程:
- 响应用户输入
- 处理各种事件类型
-
帧率控制重要:
- 使用Clock控制速度
- 保证不同设备体验一致
-
数据结构选择:
- 合适的数据结构简化逻辑
- 列表非常适合表示蛇
-
模块化设计:
- 将功能分解为函数
- 提高代码可读性和可维护性