零基础Python贪吃蛇游戏开发教程(二)
第三部分:Snake类详解 - 面向对象编程入门
在上一篇文章中,我们学习了游戏的基础设置。现在让我们深入探讨Snake类的实现,这是理解面向对象编程(OOP)的绝佳示例。
1. 什么是面向对象编程?
想象一下你要建造一栋房子。在面向过程编程中,你会关注"如何一步步建造";而在面向对象编程中,你会先设计"房子"这个概念,包括它的属性(大小、颜色)和方法(开门、关窗)。
class Snake:
"""贪吃蛇类"""
# 类的内容
class关键字定义了一个类,它是创建对象的蓝图。类名通常采用首字母大写的驼峰命名法。(单词首字母大写连在一起)
2.__init__方法:构造器
def __init__(self):
self.reset()
__init__是特殊方法(双下划线开头),称为构造器或初始化方法- 当创建Snake对象时,Python会自动调用这个方法
self参数代表对象实例本身,用于访问对象的属性和方法
3.self关键字:对象的"自我意识"
self.positions = [] # 蛇的位置列表
self.direction = UP # 当前移动方向
self就像是对象说"我的":
self.positions:我的位置列表self.direction:我的移动方向self.score:我的分数
每次调用对象的方法时,Python会自动传递这个对象作为self参数。
4.reset方法:初始化蛇的状态
def reset(self):
"""初始化或重置蛇的状态"""
self.length = 3
# 初始位置在屏幕中心偏上
start_x = GRID_WIDTH // 2
start_y = GRID_HEIGHT // 2
# 初始位置列表:蛇头在上,身体向下延伸
self.positions = [(start_x, start_y)]
for i in range(1, self.length):
new_y = (start_y + i) % GRID_HEIGHT
self.positions.append((start_x, new_y))
self.direction = UP
self.next_direction = UP
self.grow_pending = 0
self.score = 0
self.is_alive = True
关键点解析:
- 蛇的位置表示:
self.positions = [(x1, y1), (x2, y2), (x3, y3)]
使用列表存储每个身体部分的网格坐标,第一个元素是蛇头。
- 穿墙效果的实现:
new_y = (start_y + i) % GRID_HEIGHT
%取模运算符确保坐标始终在网格范围内。
- 两个方向变量:
self.direction = UP # 当前方向
self.next_direction = UP # 下一个方向
为什么需要两个?防止玩家在同一帧内快速反向操作,导致蛇"自杀"。
5.move方法:蛇的移动逻辑
def move(self):
"""移动蛇"""
if not self.is_alive:
return
self.update_direction()
head_x, head_y = self.get_head_position()
dir_x, dir_y = self.direction
# 计算新坐标并实现穿墙效果
new_x = (head_x + dir_x) % GRID_WIDTH
new_y = (head_y + dir_y) % GRID_HEIGHT
new_position = (new_x, new_y)
# 碰撞检测:检查是否撞到自己
if new_position in self.positions[1:]:
self.is_alive = False
return
# 移动蛇
self.positions.insert(0, new_position)
# 如果不需要增长,移除尾部
if self.grow_pending > 0:
self.grow_pending -= 1
else:
self.positions.pop()
关键点解析:
- 坐标计算:
new_x = (head_x + dir_x) % GRID_WIDTH
当前位置 + 方向向量 = 新位置。取模运算实现穿墙。
- 碰撞检测:
if new_position in self.positions[1:]:
检查新位置是否在蛇身中(从第二个元素开始检查,排除蛇头)。
- 列表操作:
self.positions.insert(0, new_position) # 头部添加新位置
self.positions.pop() # 移除尾部位置(如果没增长)
这是贪吃蛇移动的核心:头部长出新段,尾部移除旧段。
6.增长机制
def grow(self):
"""蛇增长"""
self.grow_pending += 1
self.score += SCORE_INCREMENT
为什么不直接增加蛇的长度?因为:
- 蛇吃到食物后,要在下一次移动时才增长
grow_pending记录待增长的数量- 在
move()方法中处理实际增长
这种延迟执行是游戏编程的常见技巧!
7.蛇的绘制
def draw(self, surface: pygame.Surface):
"""绘制蛇"""
for i, (x, y) in enumerate(self.positions):
rect = pygame.Rect(
x * GRID_SIZE,
y * GRID_SIZE,
GRID_SIZE,
GRID_SIZE
)
# 蛇头
if i == 0:
pygame.draw.rect(surface, SNAKE_HEAD, rect)
# 绘制眼睛...
# 蛇身
else:
pygame.draw.rect(surface, SNAKE_BODY, rect.inflate(-4, -4))
关键点解析:
- enumerate函数:
for i, (x, y) in enumerate(self.positions):
同时获得索引i和坐标(x, y),用于区分蛇头和蛇身。
- pygame.Rect:
pygame.Rect(x, y, width, height)
表示矩形区域,是pygame中重要的基础类。
- rect.inflate(-4, -4):
缩小矩形4像素,让身体部分看起来有间隙。
8.小练习:理解面向对象
- 在Snake类中添加一个新属性
name,表示蛇的名字 - 创建一个
change_color方法,可以动态改变蛇的颜色 - 添加一个
speed属性,并根据长度调整移动速度
第四部分:Food类详解
食物类相对简单,但体现了几个重要概念:
1.依赖注入
class Food:
def __init__(self, snake_positions: List[Tuple[int, int]] = None):
self.position = (0, 0)
self.snake_positions = snake_positions or []
self.randomize_position()
- 传入
snake_positions参数,避免食物生成在蛇身上 snake_positions or []:如果参数为None,使用空列表- 这种设计使Food类更加灵活,可以独立测试
2.随机位置生成
def randomize_position(self):
"""随机生成食物位置,确保不在蛇身上"""
max_attempts = GRID_WIDTH * GRID_HEIGHT
for _ in range(max_attempts):
new_position = (
random.randint(0, GRID_WIDTH - 1),
random.randint(0, GRID_HEIGHT - 1)
)
if new_position not in self.snake_positions:
self.position = new_position
return
# 如果找不到合适位置(几乎不可能),使用备用位置
self.position = (GRID_WIDTH // 2, GRID_HEIGHT // 2)
关键点解析:
-
random.randint(a, b):
返回[a, b]范围内的随机整数 -
避免无限循环:
max_attempts = GRID_WIDTH * GRID_HEIGHT
设置最大尝试次数,避免在蛇占满屏幕时程序卡死
- 防御性编程:
即使理论上不可能发生,也要有备用方案
3.圆的绘制
def draw(self, surface: pygame.Surface):
"""绘制食物"""
x, y = self.position
center_x = x * GRID_SIZE + GRID_SIZE // 2
center_y = y * GRID_SIZE + GRID_SIZE // 2
# 绘制圆形食物
pygame.draw.circle(
surface,
FOOD_COLOR,
(center_x, center_y),
GRID_SIZE // 2 - 2
)
# 添加高光效果
pygame.draw.circle(
surface,
(255, 255, 255),
(center_x - 3, center_y - 3),
3
)
关键点解析:
- 坐标转换:
网格坐标 → 像素坐标:
center_x = x * GRID_SIZE + GRID_SIZE // 2
- 绘制圆形:
pygame.draw.circle(表面, 颜色, 圆心, 半径)
- 高光效果:
添加一个小白点,模拟光反射,增加立体感
4.小练习:扩展食物类
- 创建不同类型的食物(不同颜色、不同分数)
- 添加食物闪烁效果(提示:使用
pygame.time.get_ticks()) - 让食物在一段时间后消失并重新生成
面向对象编程的核心概念
1. 封装
将数据(属性)和操作数据的方法封装在类中,隐藏内部实现细节。
2. 继承(本游戏未使用,但很重要)
class SpecialFood(Food): # SpecialFood继承自Food
def __init__(self):
super().__init__() # 调用父类的初始化方法
self.color = (255, 215, 0) # 金色
3. 多态(本游戏未使用,但很重要)
不同类的对象对同一消息做出不同响应。
4.类与对象的关系
# 类:蓝图
class Dog:
def __init__(self, name):
self.name = name
def bark(self):
return "汪汪!"
# 对象:根据蓝图创建的具体实例
my_dog = Dog("旺财") # 创建一个Dog对象
print(my_dog.name) # 访问属性
print(my_dog.bark()) # 调用方法
总结
今天我们学习了:
关键知识点:
- 面向对象编程基础:类、对象、属性、方法
- self关键字:代表对象实例本身
- 构造器__init__:对象初始化
- 游戏逻辑实现:蛇的移动、碰撞检测、增长机制
- 图形绘制:矩形、圆形、坐标转换
重要编程技巧:
- 延迟执行:使用
grow_pending处理蛇的增长 - 防御性编程:设置最大尝试次数,提供备用方案
- 依赖注入:使类更加灵活和可测试
- 列表操作:
insert()、pop()、enumerate()的使用
思维训练:
- 为什么游戏中的蛇用列表表示?
- 如果不用
grow_pending,如何实现增长? - 穿墙效果除了取模运算,还有其他实现方式吗?
拓展思考:游戏AI的可能性
想象一下,如果我们想让蛇自动寻找食物:
- 需要哪些信息?(蛇头位置、食物位置、障碍物位置)
- 可以使用什么算法?(BFS、A*寻路算法)
- 如何避免蛇陷入死循环?
在下篇文章中,我们将讲解Game类,这是整个游戏的控制中心,学习事件处理、游戏状态管理和主循环的实现。
编程哲学:面向对象编程的核心思想是"万物皆对象"。在游戏开发中,每个游戏元素都可以看作对象,它们有自己的状态和行为,通过消息传递进行交互。
动手实践是最好的学习方式!尝试修改代码,观察变化,你会对概念有更深刻的理解。
利用AI迭代学习贪吃蛇代码(三)
利用AI迭代学习贪吃蛇代码(一)
38



被折叠的 条评论
为什么被折叠?



