实验四
学号20242313 2024 - 2025 《Python 程序设计》实验 4 报告
课程:《Python 程序设计》
班级: 2423
姓名: 曾海鹏
学号:20242313
实验教师:王志强
实验日期:2025 年 5 月 14 日
必修 / 选修: 公选课
1. 实验内容
(1)使用面向对象编程思想设计并实现一个完整的游戏程序 ——《球球大作战》。
(2)实现游戏的核心功能,包括玩家控制、AI 对手、碰撞检测、分数系统和复活机制。
(3)优化游戏体验,确保复活位置分散、大小适中、距离合理,避免初始碰撞导致的不公平性。
(4)将程序代码托管到码云,并撰写规范的实验报告。
2. 实验过程及结果
2.1 设计原因与分析设计
设计原因:
球球大作战是我最开始接触且第一次喜欢上的游戏,而且在学习编程的时候有一次在B站上刷到过一个up主的视频,主要内容是如何用C语言手搓球球大作战这款游戏,当时感觉很新奇很震撼,如今学习了python,也想手搓一款简易版的球球大作战。
分析设计:
先使用豆包给我一个编写好的代码,使我对编写游戏有一个整体框架,在测试代码实用性时对其进行改错和优化,遇到问题(比如python环境问题,安装路径问题等)可以先自己思考问题,再询问豆包,对于一些过于专业的内容依旧不懂的寻求学霸或者老师的帮助。
本次实验采用面向对象编程思想,将球球大作战组件抽象为以下类:
GameObject:基类,定义物体的基本属性(位置、大小、颜色)和方法(绘制、碰撞检测)。
Food:食物类,随机生成在地图上,模拟球球大作战中的彩豆。
AIPlayer:AI 控制的球,具备自主移动和追逐逻辑,主要角色为人机。
Player:玩家控制的球,支持键盘或鼠标控制,主要通过wasd或箭头上下左右来控制球球的移动。
Game:游戏主类,管理游戏流程、食物刷新和场景设计等。
2.2 核心功能实现
(1)玩家控制与移动
支持两种控制方式:
键盘控制:使用方向键或 WASD 移动。
鼠标控制:按 M 键切换,玩家自动向鼠标指针方向移动。
(2)碰撞检测与分数系统
玩家与食物碰撞:玩家吞噬食物后变大并获得分数。
玩家与 AI 碰撞:若玩家尺寸大于 AI 的 1.2 倍,可吞噬 AI;反之则被吞噬(玩家死亡)。
AI 之间的碰撞:大球吃小球,符合常理。
(3)复活机制优化
预生成 10 个分散的复活点,确保彼此距离≥300 像素。
复活时随机选择未使用的位置,避免扎堆。
新生成的 AI 球大小限制在 15-25 之间,防止初始尺寸过大导致不公平。
2.3 主要设计过程
(1)pygame的安装和python环境的搭建
首先需要安装必要的库pygame,要求我们在终端pip install pygame。
而在此过程中可能会遇到pip 版本过低为 21.1.1,而最新版本是 25.0.1,建议升级 pip 以获取最新功能和安全补丁。
此时需要利用在Python 安装路径下的解释器来执行 pip 升级,在终端使用c:\program files\python38\python.exe -m pip install --upgrade pip来实现。
但PowerShell 可能无法识别c:\program\files\python38\python.exe这个路径,这就需要在 Windows 中添加路径到 PATH。通过以下步骤实现:
- 右键点击 "此电脑" → 选择 "属性" → 点击 "高级系统设置"
- 在弹出的窗口中,点击 "环境变量" 按钮
- 在 "系统变量"列表中,找到"Path"变量 → 点击"编辑"
- 点击 "新建" → 复制粘贴路径:
C:\Users\33834\AppData\Roaming\Python\Python38\Scripts
但安装好了pygame库,可能PyCharm 仍然无法找到已安装的pygame库,这通常是因为 PyCharm 使用的 Python 环境与安装 pygame 的环境不一致。这就需要确认 PyCharm 使用的 Python 环境,并检查 pygame 是否安装在该环境中,最后在 PyCharm 中手动安装 pygame即可。
- 在
Python Interpreter面板中,点击右侧的+按钮 - 在搜索框中输入
pygame - 选择最新版本(例如 2.6.1),然后点击
Install Package

在我的python中出现如下版本表示成功:

(2)代码的改正和优化
1.主要问题:在创建Food和AIPlayer类对象时出现了AttributeError: 'Food' object has no attribute 'size'的问题。

原因分析: 尝试时使用self.size作为随机位置的边界,但此时self.size还没有被设置(因为super().__init__还未执行)。
解决方案:
在调用父类构造函数之前,先初始化size属性:
代码修改后:
# 定义食物类,继承自游戏物体基类
class Food(GameObject):
def __init__(self):
# 先初始化size,再调用父类构造函数
size = random.randint(FOOD_SIZE_MIN, FOOD_SIZE_MAX)
color = YELLOW # 食物统一为黄色
# 随机生成食物位置(使用已初始化的size)
x = random.randint(size, WIDTH - size)
y = random.randint(size, HEIGHT - size)
# 调用父类构造函数
super().__init__(x, y, size, color)
# 定义AI控制的球类,继承自游戏物体基类
class AIPlayer(GameObject):
def __init__(self):
# 先初始化size,再调用父类构造函数
size = random.randint(AI_SIZE_MIN, AI_SIZE_MAX)
color = random.choice(COLORS) # 随机颜色
# 随机生成AI球位置(使用已初始化的size)
x = random.randint(size, WIDTH - size)
y = random.randint(size, HEIGHT - size)
# 调用父类构造函数
super().__init__(x, y, size, color)
self.speed = max(1, size / 10) # 大小影响速度,越大越慢
2.优化方面:
主要在原来代码基础上将球球的运动空间设置的大一些,最好超出屏幕,设置球球的AI玩家大球能够吃掉小球,死亡后可以复活为小球。
运动空间设置方面:修改WIDTH, HEIGHT = 800, 600改为2600,2600大小即可。
AI玩家方面:改进 AIPlayer 类,让 AI 球更智能,大球追逐小球,小球躲避大球。
复活方面:添加初始化代码,更新AI球位置并显示死亡提示,其他基本保持不变。
优化时常见的问题主要是使用方向键用不了,
我个人认为以下这段代码是解决问题最为关键的:
它移除静态方法装饰器,保留实例方法,但确保不访问实例属性。
class Game:
def __init__(self):
self.clock = pygame.time.Clock()
self.running = True
self.paused = False
self.player = Player()
self.food = [Food() for _ in range(30)]
self.ai_players = [AIPlayer() for _ in range(10)]
self.food_spawn_timer = 0
self.ai_spawn_timer = 0
self.control_mode = "keyboard"
self.death_timer = 0
self.player_alive = True
self.respawn_points = self._generate_respawn_points(10) # 正确调用
self.used_respawns = set() # 记录已使用的复活点
def _generate_respawn_points(self, count):
"""生成一组分散的复活点"""
points = []
for _ in range(count):
max_attempts = 100
for _ in range(max_attempts):
x = random.randint(RESPAWN_AREA_MARGIN, WORLD_WIDTH - RESPAWN_AREA_MARGIN)
y = random.randint(RESPAWN_AREA_MARGIN, WORLD_HEIGHT - RESPAWN_AREA_MARGIN)
if all(math.sqrt((x-p[0])**2 + (y-p[1])** 2) > RESPAWN_MIN_DISTANCE for p in points):
points.append((x, y))
break
return points
2.4 源代码实现
以下是游戏的初始代码:
import pygame
import random
import math
import sys
# 初始化pygame
pygame.init()
# 确保中文显示正常
pygame.font.init()
font_path = pygame.font.match_font('simsun') # 使用系统中的宋体
if not font_path:
font_path = pygame.font.get_default_font() # 如果没有找到宋体,使用默认字体
font = pygame.font.Font(font_path, 24)
# 游戏窗口设置
WIDTH, HEIGHT =2600,2600
screen = pygame.display.set_mode((WIDTH, HEIGHT))
pygame.display.set_caption("球球大作战 - 简化版")
# 颜色定义
WHITE = (255, 255, 255)
BLACK = (0, 0, 0)
RED = (255, 0, 0)
GREEN = (0, 255, 0)
BLUE = (0, 0, 255)
YELLOW = (255, 255, 0)
COLORS = [RED, GREEN, BLUE, YELLOW, (255, 165, 0), (128, 0, 128), (0, 128, 128)]
# 游戏常量
FPS = 60
PLAYER_INIT_SIZE = 20 # 玩家初始大小
PLAYER_SPEED = 5 # 玩家移动速度
FOOD_SIZE_MIN = 5 # 食物最小大小
FOOD_SIZE_MAX = 15 # 食物最大大小
AI_SIZE_MIN = 15 # AI球最小大小
AI_SIZE_MAX = 40 # AI球最大大小
FOOD_SPAWN_RATE = 50 # 食物生成速率
AI_SPAWN_RATE = 200 # AI球生成速率
SCORE_PER_FOOD = 1 # 每个食物的分数
SCORE_PER_AI = 5 # 每个AI球的分数
# 定义游戏物体基类
class GameObject:
def __init__(self, x, y, size, color):
self.x = x # x坐标
self.y = y # y坐标
self.size = size # 物体大小(半径)
self.color = color # 物体颜色
def draw(self, surface):
"""在指定表面绘制物体"""
pygame.draw.circle(surface, self.color, (int(self.x), int(self.y)), self.size)
def is_colliding_with(self, other):
"""检测与另一个物体是否碰撞"""
distance = math.sqrt((self.x - other.x) ** 2 + (self.y - other.y) ** 2)
return distance < (self.size + other.size)
# 定义食物类,继承自游戏物体基类
class Food(GameObject):
def __init__(self):
# 先初始化size,再调用父类构造函数
size = random.randint(FOOD_SIZE_MIN, FOOD_SIZE_MAX)
color = YELLOW # 食物统一为黄色
# 随机生成食物位置(使用已初始化的size)
x = random.randint(size, WIDTH - size)
y = random.randint(size, HEIGHT - size)
# 调用父类构造函数
super().__init__(x, y, size, color)
# 定义AI控制的球类,继承自游戏物体基类
class AIPlayer(GameObject):
def __init__(self):
# 先初始化size,再调用父类构造函数
size = random.randint(AI_SIZE_MIN, AI_SIZE_MAX)
color = random.choice(COLORS) # 随机颜色
# 随机生成AI球位置(使用已初始化的size)
x = random.randint(size, WIDTH - size)
y = random.randint(size, HEIGHT - size)
# 调用父类构造函数
super().__init__(x, y, size, color)
self.speed = max(1, size / 10) # 大小影响速度,越大越慢
def move_towards(self, target_x, target_y):
"""向目标位置移动"""
# 计算方向向量
dx = target_x - self.x
dy = target_y - self.y
distance = math.sqrt(dx ** 2 + dy ** 2)
if distance > 0:
# 标准化方向向量并乘以速度
self.x += (dx / distance) * self.speed
self.y += (dy / distance) * self.speed
# 边界检测,防止AI球移出屏幕
self.x = max(self.size, min(self.x, WIDTH - self.size))
self.y = max(self.size, min(self.y, HEIGHT - self.size))
# 定义玩家控制的球类,继承自游戏物体基类
class Player(GameObject):
def __init__(self):
# 玩家初始位置在屏幕中心
x = WIDTH // 2
y = HEIGHT // 2
size = PLAYER_INIT_SIZE
color = BLUE # 玩家球为蓝色
super().__init__(x, y, size, color)
self.score = 0 # 玩家分数
self.speed = PLAYER_SPEED # 玩家移动速度
def move_with_keyboard(self, keys):
"""通过键盘控制移动"""
dx, dy = 0, 0
if keys[pygame.K_w] or keys[pygame.K_UP]:
dy -= self.speed
if keys[pygame.K_s] or keys[pygame.K_DOWN]:
dy += self.speed
if keys[pygame.K_a] or keys[pygame.K_LEFT]:
dx -= self.speed
if keys[pygame.K_d] or keys[pygame.K_RIGHT]:
dx += self.speed
# 更新位置
self.x += dx
self.y += dy
# 边界检测,防止玩家球移出屏幕
self.x = max(self.size, min(self.x, WIDTH - self.size))
self.y = max(self.size, min(self.y, HEIGHT - self.size))
def move_with_mouse(self, mouse_x, mouse_y):
"""通过鼠标控制移动"""
# 计算方向向量
dx = mouse_x - self.x
dy = mouse_y - self.y
distance = math.sqrt(dx ** 2 + dy ** 2)
if distance > 0:
# 标准化方向向量并乘以速度
self.x += (dx / distance) * self.speed
self.y += (dy / distance) * self.speed
# 边界检测
self.x = max(self.size, min(self.x, WIDTH - self.size))
self.y = max(self.size, min(self.y, HEIGHT - self.size))
def eat(self, other):
"""吞噬其他物体"""
if self.size > other.size:
# 计算吞噬后增加的大小(基于被吞噬物体的大小)
size_increase = other.size * 0.5
self.size += size_increase
# 根据被吞噬物体类型增加分数
if isinstance(other, Food):
self.score += SCORE_PER_FOOD
elif isinstance(other, AIPlayer):
self.score += SCORE_PER_AI
return True
return False
# 游戏主类
class Game:
def __init__(self):
self.clock = pygame.time.Clock()
self.running = True
self.paused = False
self.player = Player()
self.food = [Food() for _ in range(10)] # 初始生成10个食物
self.ai_players = [AIPlayer() for _ in range(5)] # 初始生成5个AI球
self.food_spawn_timer = 0
self.ai_spawn_timer = 0
self.control_mode = "keyboard" # 控制模式:keyboard或mouse
def process_events(self):
"""处理游戏事件"""
for event in pygame.event.get():
if event.type == pygame.QUIT:
self.running = False
elif event.type == pygame.KEYDOWN:
if event.key == pygame.K_ESCAPE:
self.running = False
elif event.key == pygame.K_SPACE:
self.paused = not self.paused
elif event.key == pygame.K_m:
self.control_mode = "mouse" if self.control_mode == "keyboard" else "keyboard"
def update(self):
"""更新游戏状态"""
if self.paused:
return
# 根据控制模式移动玩家
if self.control_mode == "keyboard":
keys = pygame.key.get_pressed()
self.player.move_with_keyboard(keys)
else:
mouse_x, mouse_y = pygame.mouse.get_pos()
self.player.move_with_mouse(mouse_x, mouse_y)
# 更新AI球位置(AI球会朝向玩家移动)
for ai in self.ai_players:
ai.move_towards(self.player.x, self.player.y)
# 食物生成逻辑
self.food_spawn_timer += 1
if self.food_spawn_timer >= FOOD_SPAWN_RATE:
self.food.append(Food())
self.food_spawn_timer = 0
# AI球生成逻辑
self.ai_spawn_timer += 1
if self.ai_spawn_timer >= AI_SPAWN_RATE:
self.ai_players.append(AIPlayer())
self.ai_spawn_timer = 0
# 碰撞检测:玩家吞噬食物
food_to_remove = []
for food_item in self.food:
if self.player.is_colliding_with(food_item):
if self.player.eat(food_item):
food_to_remove.append(food_item)
for item in food_to_remove:
self.food.remove(item)
# 碰撞检测:玩家吞噬AI球
ai_to_remove = []
for ai_item in self.ai_players:
if self.player.is_colliding_with(ai_item):
if self.player.eat(ai_item):
ai_to_remove.append(ai_item)
for item in ai_to_remove:
self.ai_players.remove(item)
# 碰撞检测:AI球之间的互动(简单逻辑:大AI球吞噬小AI球)
ai_to_remove = [] # 记录需要移除的AI球
# 使用副本进行循环,避免修改原始列表
for i in range(len(self.ai_players)):
for j in range(i + 1, len(self.ai_players)):
# 检查索引是否仍然有效
if i >= len(self.ai_players) or j >= len(self.ai_players):
break
ai1 = self.ai_players[i]
ai2 = self.ai_players[j]
if ai1.is_colliding_with(ai2):
if ai1.size > ai2.size and ai2 not in ai_to_remove:
ai1.size += ai2.size * 0.3
ai_to_remove.append(ai2) # 标记为移除
elif ai2.size > ai1.size and ai1 not in ai_to_remove:
ai2.size += ai1.size * 0.3
ai_to_remove.append(ai1) # 标记为移除
# 统一移除标记的AI球
for ai in ai_to_remove:
if ai in self.ai_players: # 确保元素还在列表中
self.ai_players.remove(ai)
def render(self):
"""渲染游戏画面"""
screen.fill(WHILE) # 清屏为黑色
# 绘制所有食物
for food_item in self.food:
food_item.draw(screen)
# 绘制所有AI球
for ai_item in self.ai_players:
ai_item.draw(screen)
# 绘制玩家球
self.player.draw(screen)
# 绘制分数和游戏信息
score_text = font.render(f"分数: {self.player.score}", True, WHITE)
screen.blit(score_text, (10, 10))
size_text = font.render(f"大小: {int(self.player.size)}", True, WHITE)
screen.blit(size_text, (10, 40))
control_text = font.render(f"控制方式: {'鼠标' if self.control_mode == 'mouse' else '键盘'} (按M切换)", True,
WHITE)
screen.blit(control_text, (10, 70))
pause_text = font.render(f"游戏状态: {'已暂停' if self.paused else '进行中'} (按空格暂停)", True, WHITE)
screen.blit(pause_text, (10, 100))
pygame.display.flip() # 更新显示
def run(self):
"""运行游戏主循环"""
while self.running:
self.process_events()
self.update()
self.render()
self.clock.tick(FPS) # 控制帧率
# 程序入口
if __name__ == "__main__":
game = Game()
game.run()
pygame.quit()
sys.exit()
之后是游戏的进阶代码:(能够复活,大范围活动,AI玩家可以大球吃小球等)
import pygame
import random
import math
import sys
# 初始化pygame
pygame.init()
# 确保中文显示正常
pygame.font.init()
font_path = pygame.font.match_font('simsun') # 使用系统中的宋体
if not font_path:
font_path = pygame.font.get_default_font() # 如果没有找到宋体,使用默认字体
font = pygame.font.Font(font_path, 24)
# 游戏窗口设置
WINDOW_WIDTH, WINDOW_HEIGHT = 1200, 800 # 实际窗口大小
WORLD_WIDTH, WORLD_HEIGHT = 5000, 5000 # 更大的游戏世界(超出屏幕)
screen = pygame.display.set_mode((WINDOW_WIDTH, WINDOW_HEIGHT))
pygame.display.set_caption("球球大作战 - 增强版")
# 颜色定义
WHITE = (255, 255, 255)
BLACK = (0, 0, 0)
RED = (255, 0, 0)
GREEN = (0, 255, 0)
BLUE = (0, 0, 255)
YELLOW = (255, 255, 0)
COLORS = [RED, GREEN, BLUE, YELLOW, (255, 165, 0), (128, 0, 128), (0, 128, 128)]
# 游戏常量
FPS = 60
PLAYER_INIT_SIZE = 20 # 玩家初始大小
PLAYER_SPEED = 5 # 玩家移动速度
FOOD_SIZE_MIN = 5 # 食物最小大小
FOOD_SIZE_MAX = 15 # 食物最大大小
AI_SIZE_MIN = 15 # AI球最小大小
AI_SIZE_MAX = 40 # AI球最大大小
FOOD_SPAWN_RATE = 50 # 食物生成速率
AI_SPAWN_RATE = 200 # AI球生成速率
SCORE_PER_FOOD = 1 # 每个食物的分数
SCORE_PER_AI = 5 # 每个AI球的分数
RESPAWN_TIME = 120 # 复活时间(帧数)
RESPAWN_MIN_DISTANCE = 300 # 复活点之间的最小距离
RESPAWN_AREA_MARGIN = 50 # 复活区域边缘留白
# 定义游戏物体基类
class GameObject:
def __init__(self, x, y, size, color):
self.x = x # x坐标
self.y = y # y坐标
self.size = size # 物体大小(半径)
self.color = color # 物体颜色
def is_on_screen(self, camera_x, camera_y):
"""检查物体是否在屏幕范围内"""
return (self.x + self.size > camera_x and self.x - self.size < camera_x + WINDOW_WIDTH and
self.y + self.size > camera_y and self.y - self.size < camera_y + WINDOW_HEIGHT)
def draw(self, surface, camera_x, camera_y):
"""只在屏幕范围内绘制物体"""
if self.is_on_screen(camera_x, camera_y):
# 计算在屏幕上的相对位置
screen_x = self.x - camera_x
screen_y = self.y - camera_y
pygame.draw.circle(surface, self.color, (int(screen_x), int(screen_y)), self.size)
def is_colliding_with(self, other):
"""检测与另一个物体是否碰撞"""
distance = math.sqrt((self.x - other.x) ** 2 + (self.y - other.y) ** 2)
return distance < (self.size + other.size)
# 定义食物类
class Food(GameObject):
def __init__(self):
size = random.randint(FOOD_SIZE_MIN, FOOD_SIZE_MAX)
color = YELLOW
x = random.randint(size, WORLD_WIDTH - size)
y = random.randint(size, WORLD_HEIGHT - size)
super().__init__(x, y, size, color)
# 定义AI控制的球类
class AIPlayer(GameObject):
def __init__(self):
size = random.randint(AI_SIZE_MIN, AI_SIZE_MIN + 10) # 限制初始大小
color = random.choice(COLORS)
x = random.randint(size, WORLD_WIDTH - size)
y = random.randint(size, WORLD_HEIGHT - size)
super().__init__(x, y, size, color)
self.speed = max(1, 5 - size / 10) # 小球更快,大球更慢
def move(self, player, ai_players, food_list):
# 寻找最近的目标
nearest_target = None
nearest_distance = float('inf')
# 优先追逐比自己小的AI球
for ai in ai_players:
if ai.size < self.size * 0.9: # 只追逐明显更小的球
distance = math.sqrt((self.x - ai.x) ** 2 + (self.y - ai.y) ** 2)
if distance < nearest_distance:
nearest_distance = distance
nearest_target = ai
# 如果没有更小的AI球,考虑食物
if nearest_target is None:
for food in food_list:
distance = math.sqrt((self.x - food.x) ** 2 + (self.y - food.y) ** 2)
if distance < nearest_distance:
nearest_distance = distance
nearest_target = food
# 如果没有食物,考虑玩家
if nearest_target is None and player.size < self.size * 0.9:
nearest_target = player
# 如果有目标,向目标移动
if nearest_target:
dx = nearest_target.x - self.x
dy = nearest_target.y - self.y
distance = math.sqrt(dx ** 2 + dy ** 2)
if distance > 0:
self.x += (dx / distance) * self.speed
self.y += (dy / distance) * self.speed
# 边界检测
self.x = max(self.size, min(self.x, WORLD_WIDTH - self.size))
self.y = max(self.size, min(self.y, WORLD_HEIGHT - self.size))
# 定义玩家控制的球类
class Player(GameObject):
def __init__(self, x=None, y=None):
# 如果没有指定位置,使用屏幕中心
if x is None:
x = WORLD_WIDTH // 2
if y is None:
y = WORLD_HEIGHT // 2
super().__init__(x, y, PLAYER_INIT_SIZE, BLUE)
self.score = 0
self.speed = PLAYER_SPEED
def move_with_keyboard(self, keys):
dx, dy = 0, 0
if keys[pygame.K_w] or keys[pygame.K_UP]:
dy -= self.speed
if keys[pygame.K_s] or keys[pygame.K_DOWN]:
dy += self.speed
if keys[pygame.K_a] or keys[pygame.K_LEFT]:
dx -= self.speed
if keys[pygame.K_d] or keys[pygame.K_RIGHT]:
dx += self.speed
self.x += dx
self.y += dy
self.x = max(self.size, min(self.x, WORLD_WIDTH - self.size))
self.y = max(self.size, min(self.y, WORLD_HEIGHT - self.size))
def move_with_mouse(self, mouse_x, mouse_y, camera_x, camera_y):
# 计算世界坐标中的鼠标位置
world_x = mouse_x + camera_x
world_y = mouse_y + camera_y
dx = world_x - self.x
dy = world_y - self.y
distance = math.sqrt(dx ** 2 + dy ** 2)
if distance > 0:
self.x += (dx / distance) * self.speed
self.y += (dy / distance) * self.speed
self.x = max(self.size, min(self.x, WORLD_WIDTH - self.size))
self.y = max(self.size, min(self.y, WORLD_HEIGHT - self.size))
def eat(self, other):
if self.size > other.size:
size_increase = other.size * 0.5
self.size += size_increase
if isinstance(other, Food):
self.score += SCORE_PER_FOOD
elif isinstance(other, AIPlayer):
self.score += SCORE_PER_AI
return True
return False
# 游戏主类
class Game:
def __init__(self):
self.clock = pygame.time.Clock()
self.running = True
self.paused = False
self.player = Player()
self.food = [Food() for _ in range(30)]
self.ai_players = [AIPlayer() for _ in range(10)]
self.food_spawn_timer = 0
self.ai_spawn_timer = 0
self.control_mode = "keyboard"
self.death_timer = 0
self.player_alive = True
self.respawn_points = self._generate_respawn_points(10) # 正确调用
self.used_respawns = set() # 记录已使用的复活点
def _generate_respawn_points(self, count):
"""生成一组分散的复活点"""
points = []
for _ in range(count):
max_attempts = 100
for _ in range(max_attempts):
x = random.randint(RESPAWN_AREA_MARGIN, WORLD_WIDTH - RESPAWN_AREA_MARGIN)
y = random.randint(RESPAWN_AREA_MARGIN, WORLD_HEIGHT - RESPAWN_AREA_MARGIN)
if all(math.sqrt((x - p[0]) ** 2 + (y - p[1]) ** 2) > RESPAWN_MIN_DISTANCE for p in points):
points.append((x, y))
break
return points
def _get_respawn_position(self):
"""获取一个未被使用的复活点位置"""
# 如果所有复活点都被使用了,清空记录
if len(self.used_respawns) >= len(self.respawn_points):
self.used_respawns.clear()
# 随机选择一个未被使用的复活点
available = [i for i in range(len(self.respawn_points)) if i not in self.used_respawns]
index = random.choice(available)
self.used_respawns.add(index)
return self.respawn_points[index]
def process_events(self):
for event in pygame.event.get():
if event.type == pygame.QUIT:
self.running = False
elif event.type == pygame.KEYDOWN:
if event.key == pygame.K_ESCAPE:
self.running = False
elif event.key == pygame.K_SPACE:
self.paused = not self.paused
elif event.key == pygame.K_m:
self.control_mode = "mouse" if self.control_mode == "keyboard" else "keyboard"
def update(self):
if self.paused:
return
if not self.player_alive:
self.death_timer += 1
if self.death_timer >= RESPAWN_TIME:
# 从复活点生成新玩家
x, y = self._get_respawn_position()
self.player = Player(x, y)
self.player_alive = True
self.death_timer = 0
return
# 根据控制模式移动玩家
if self.control_mode == "keyboard":
keys = pygame.key.get_pressed()
self.player.move_with_keyboard(keys)
else:
mouse_x, mouse_y = pygame.mouse.get_pos()
camera_x = max(0, min(WORLD_WIDTH - WINDOW_WIDTH, self.player.x - WINDOW_WIDTH // 2))
camera_y = max(0, min(WORLD_HEIGHT - WINDOW_HEIGHT, self.player.y - WINDOW_HEIGHT // 2))
self.player.move_with_mouse(mouse_x, mouse_y, camera_x, camera_y)
# 更新AI球位置
for ai in self.ai_players[:]:
ai.move(self.player, self.ai_players, self.food)
# 食物生成逻辑
self.food_spawn_timer += 1
if self.food_spawn_timer >= FOOD_SPAWN_RATE:
self.food.append(Food())
self.food_spawn_timer = 0
# AI球生成逻辑
self.ai_spawn_timer += 1
if self.ai_spawn_timer >= AI_SPAWN_RATE:
# 从复活点生成新AI球
x, y = self._get_respawn_position()
new_ai = AIPlayer()
new_ai.x, new_ai.y = x, y
self.ai_players.append(new_ai)
self.ai_spawn_timer = 0
# 碰撞检测:玩家吞噬食物
food_to_remove = []
for food_item in self.food:
if self.player.is_colliding_with(food_item):
if self.player.eat(food_item):
food_to_remove.append(food_item)
for item in food_to_remove:
self.food.remove(item)
# 碰撞检测:玩家与AI球
for ai in self.ai_players[:]:
if self.player.is_colliding_with(ai):
if self.player.size > ai.size:
self.player.eat(ai)
self.ai_players.remove(ai)
# 从复活点生成新AI球
x, y = self._get_respawn_position()
new_ai = AIPlayer()
new_ai.x, new_ai.y = x, y
self.ai_players.append(new_ai)
elif ai.size > self.player.size * 1.2:
# 玩家死亡
self.player_alive = False
# AI球之间的碰撞
ai_to_remove = []
for i in range(len(self.ai_players)):
for j in range(i + 1, len(self.ai_players)):
if i >= len(self.ai_players) or j >= len(self.ai_players):
break
ai1 = self.ai_players[i]
ai2 = self.ai_players[j]
if ai1.is_colliding_with(ai2):
if ai1.size > ai2.size * 1.1:
ai1.size += ai2.size * 0.2
ai_to_remove.append(ai2)
elif ai2.size > ai1.size * 1.1:
ai2.size += ai1.size * 0.2
ai_to_remove.append(ai1)
# 移除被吃掉的AI球,并生成新的
for ai in ai_to_remove:
if ai in self.ai_players:
self.ai_players.remove(ai)
# 从复活点生成新AI球
x, y = self._get_respawn_position()
new_ai = AIPlayer()
new_ai.x, new_ai.y = x, y
self.ai_players.append(new_ai)
def render(self):
screen.fill(BLACK)
# 计算相机偏移(跟随玩家)
camera_x = max(0,
min(WORLD_WIDTH - WINDOW_WIDTH, self.player.x - WINDOW_WIDTH // 2)) if self.player_alive else 0
camera_y = max(0, min(WORLD_HEIGHT - WINDOW_HEIGHT,
self.player.y - WINDOW_HEIGHT // 2)) if self.player_alive else 0
# 绘制所有食物
for food_item in self.food:
if food_item.is_on_screen(camera_x, camera_y):
food_item.draw(screen, camera_x, camera_y)
# 绘制所有AI球
for ai_item in self.ai_players:
if ai_item.is_on_screen(camera_x, camera_y):
ai_item.draw(screen, camera_x, camera_y)
# 绘制玩家
if self.player_alive:
self.player.draw(screen, camera_x, camera_y)
else:
# 显示死亡提示
death_text = font.render("你已死亡,即将复活...", True, WHITE)
screen.blit(death_text, (WINDOW_WIDTH // 2 - death_text.get_width() // 2,
WINDOW_HEIGHT // 2 - death_text.get_height() // 2))
# 绘制分数和游戏信息
score_text = font.render(f"分数: {self.player.score if self.player_alive else 0}", True, WHITE)
screen.blit(score_text, (10, 10))
size_text = font.render(f"大小: {int(self.player.size) if self.player_alive else 0}", True, WHITE)
screen.blit(size_text, (10, 40))
control_text = font.render(f"控制方式: {'鼠标' if self.control_mode == 'mouse' else '键盘'} (按M切换)", True,
WHITE)
screen.blit(control_text, (10, 70))
pause_text = font.render(f"游戏状态: {'已暂停' if self.paused else '进行中'} (按空格暂停)", True, WHITE)
screen.blit(pause_text, (10, 100))
# 绘制世界边界
pygame.draw.rect(screen, WHITE,
(max(0, -camera_x), max(0, -camera_y),
min(WINDOW_WIDTH, WORLD_WIDTH), min(WINDOW_HEIGHT, WORLD_HEIGHT)), 2)
pygame.display.flip()
def run(self):
while self.running:
self.process_events()
self.update()
self.render()
self.clock.tick(FPS)
# 程序入口
if __name__ == "__main__":
game = Game()
game.run()
pygame.quit()
sys.exit()
2.4实验知识点分析
通过本次实验,我主要学习了面向对象编程,列表 ,集合以及调用一些函数,如random.randint(),print()等等,还有一些专业的知识如游戏循环 ,碰撞检测, 用户交互等,虽然没有那么熟练,但是有所收获。
2.5 测试结果
(1)功能测试
启动游戏后,玩家可通过键盘或鼠标控制球体移动。
成功吞噬食物和 AI 球后,玩家尺寸增大,分数增加。
被更大的 AI 球吞噬后,玩家会在随机位置复活,且新生成的 AI 球尺寸适中。
(2)复活机制测试
通过调试模式观察复活点分布(按 D 键显示):
复活点均匀分布在地图上,彼此距离较远。
玩家和 AI 复活时会选择未被使用的位置,有效避免了扎堆现象。
测试截图


3. 实验过程中遇到的问题和解决过程
- 问题 1:游戏无法移动
原因:Food和AIPlayer类初始化时使用了未定义的self.size属性。
解决方案:在调用父类构造函数前先初始化size,并使用局部变量计算位置。 - 问题 2:复活位置扎堆,体验不佳
原因:复活点随机生成,未考虑与其他物体的距离。
解决方案:
预生成一组分散的复活点,确保彼此距离≥300 像素。
使用记录系统避免重复使用同一复活点。 - 问题 3:初始 AI 球尺寸过大,导致玩家易死亡
原因:AI 球生成时随机范围过大(15-40)。
解决方案:限制复活的 AI 球尺寸在 15-25 之间,降低初始难度。
4. 代码托管

将代码推送到码云仓库:
https://gitee.com/jame-zhp_0/pythonhomework/blob/e14eb2c421dc6f4bd3d75f5688100aa3b08035c7/.idea/qiqiu2.py
https://gitee.com/jame-zhp_0/pythonhomework/blob/e14eb2c421dc6f4bd3d75f5688100aa3b08035c7/.idea/qiuqiu.py
5. 参考资料
- 《Python 游戏编程入门》(作者:Mike Dawson)
- Pygame 官方文档:https://www.pygame.org/docs/
- Cryptography 模块文档:https://pycryptodome.readthedocs.io/
- AI人工智能体豆包。
6.全课总结与建议
6.1 课程总结与体会
一学期的 Python 课程结束了,收获颇丰。通过学习,我从对 Python 一无所知到能编写简单程序,兴趣愈发浓厚。我学会了变量定义、数据输入输出、循环控制、条件语句、函数定义、文件读写等知识。
当然,学习中也遇到过代码报错、结果不符等问题,但独立写对代码时的成就感令人愉悦。我意识到自己课下练习不足,很多知识掌握不牢,实践太少。
对于学习编程语言,自学也很重要,课上内容基础,实际运用中的许多内容和第三方库需要自行查阅资料学习。通过学习,我不仅提升了编程能力,还对项目管理、系统设计、性能优化等方面有了更深刻的理解。
我也很幸运遇到一位这么可爱幽默的python老师,从令人眼花缭乱的九宫格签到,到每日特殊的百词斩单词邂逅,还有那充满童趣的卡卡西头像,最令我难忘的还是课上摇人没一个人会,老师那莞尔一笑真的很有亲和力。
最后一节课上放的那首《富丰路7号》终将令人铭记!
6.2 意见与建议
教学建议
我个人觉得课程主要靠学生的自觉性,建议老师针对当天讲的知识点布置相应的作业,而且我觉得python还是满有难度的,没有常常做项目,不能够成为一个python高手。

浙公网安备 33010602011771号