软件工程第二次作业
这个作业属于哪个课程 | https://edu.cnblogs.com/campus/fzu/SE2024 |
---|---|
这个作业要求在哪里 | https://edu.cnblogs.com/campus/fzu/SE2024/homework/13253 |
这个作业的目标 | 增强python语言的编程能力,学会运用AIGC工具辅助写代码;初步掌握开发步骤 |
学号 | 102201317 |
一、项目展示
源代码已上传至github仓库:
https://github.com/qingmu177/my_homework/tree/software-engineering_second-homework
二、项目介绍
一 所使用的技术和特殊的算法
运用了Pygame库。主要用来进行图像加载与缩放,窗口渲染,对事件处理
主算法展示:
# 主菜单函数
def main_menu():
"""显示主菜单"""
pygame.init() # 初始化pygame
screen = pygame.display.set_mode((WINDOW_WIDTH, WINDOW_HEIGHT)) # 创建窗口
pygame.display.set_caption('石了个石 - 主菜单') # 设置窗口标题
font = pygame.font.Font(pygame.font.match_font('simhei'), 50) # 创建主标题字体
small_font = pygame.font.Font(pygame.font.match_font('simhei'), 30) # 创建菜单选项字体
menu_options = ["简单模式", "普通模式", "困难模式", "排行榜", "退出"] # 菜单选项
selected_option = 0 # 当前选中的选项索引
while True:
background = load_background_image(MAIN_BG) # 使用主菜单背景
screen.blit(background, (0, 0)) # 绘制背景
title_text = font.render("石了个石_陈石石石", True, BLACK) # 渲染标题文本
screen.blit(title_text, (WINDOW_WIDTH // 2 - title_text.get_width() // 2, 100)) # 显示标题文本
# 显示菜单选项
for index, option in enumerate(menu_options):
color = RED if index == selected_option else WHITE # 选中的选项为红色,其他为白色
menu_text = small_font.render(option, True, color) # 渲染菜单文本
screen.blit(menu_text, (WINDOW_WIDTH // 2 - menu_text.get_width() // 2, 300 + index * 50)) # 显示菜单选项
for event in pygame.event.get(): # 处理事件
if event.type == QUIT: # 如果退出事件
pygame.quit() # 退出pygame
sys.exit() # 退出程序
elif event.type == KEYDOWN: # 如果按下键盘
if event.key == K_UP: # 向上箭头
selected_option = (selected_option - 1) % len(menu_options) # 选择上一个选项
elif event.key == K_DOWN: # 向下箭头
selected_option = (selected_option + 1) % len(menu_options) # 选择下一个选项
elif event.key == K_RETURN: # 回车键
if selected_option == 0: # 简单模式
game_loop(difficulty="easy") # 启动简单模式游戏
elif selected_option == 1: # 普通模式
game_loop(difficulty="normal") # 启动普通模式游戏
elif selected_option == 2: # 困难模式
game_loop(difficulty="hard") # 启动困难模式游戏
elif selected_option == 3: # 排行榜
show_leaderboard(screen) # 显示排行榜
elif selected_option == 4: # 退出
pygame.quit() # 退出pygame
sys.exit() # 退出程序
pygame.display.flip() # 更新显示
# 游戏循环函数,包含难度级别
def game_loop(difficulty):
"""游戏主循环"""
pygame.init() # 初始化pygame
play_surface = pygame.display.set_mode((WINDOW_WIDTH, WINDOW_HEIGHT)) # 创建游戏窗口
pygame.display.set_caption(f'石了个石 - {difficulty.capitalize()} 模式') # 设置窗口标题
# 根据难度设置图像数量和时间限制
if difficulty == "easy":
used_image_count = 7
time_limit = 180
elif difficulty == "normal":
used_image_count = 10
time_limit = 180
elif difficulty == "hard":
used_image_count = 15
time_limit = 90
images = load_sheep_images(used_image_count) # 加载图像
font = pygame.font.Font(pygame.font.match_font('simhei'), 25) # 创建游戏字体
fps_clock = pygame.time.Clock() # 创建时钟对象
grid_cols = 8 # 网格列数
grid_rows = 5 # 网格行数
offset_x = (WINDOW_WIDTH - (grid_cols * (ICON_SIZE + 10))) // 2 # 水平偏移量
offset_y = (WINDOW_HEIGHT - (grid_rows * (ICON_SIZE + 10))) // 2 # 垂直偏移量
max_item_count = 7 # 最大物品数量
item_count = 3 # 当前物品数量
store = [0] * max_item_count # 存储区初始化
score = 0 # 当前得分
total_score = 0 # 总得分
seconds = time_limit # 剩余时间
start_ticks = pygame.time.get_ticks() # 记录游戏开始时间
# 生成游戏网格
layers = generate_solvable_grid(used_image_count, grid_cols, grid_rows)
selected_icons = [] # 选中的图标列表
move_history = [] # 移动历史记录
while True:
background = load_background_image(GAME_BG) # 使用游戏背景
play_surface.blit(background, (0, 0)) # 绘制背景
seconds = time_limit - (pygame.time.get_ticks() - start_ticks) // 1000 # 计算剩余时间
# 绘制图标网格
for r in range(grid_rows): # 遍历行
for c in range(grid_cols): # 遍历列
x = offset_x + c * (ICON_SIZE + 10) # 计算图标的x坐标
base_y = offset_y + r * (ICON_SIZE + 10) # 计算图标的基准y坐标
if layers[r][c]: # 如果该格子有物品
stack_height = len(layers[r][c]) # 堆叠高度
for level in range(stack_height): # 遍历堆叠的每一层
icon_index = layers[r][c][level] # 获取当前层的图标索引
y = base_y - level * int(ICON_SIZE * OVERLAP_PERCENTAGE) # 计算图标的y坐标
play_surface.blit(images[icon_index], (x, y)) # 绘制图标
# 绘制存储区
storage_x = WINDOW_WIDTH // 2 - (max_item_count * ICON_SIZE) // 2 # 存储区x坐标
storage_y = WINDOW_HEIGHT - 100 # 存储区y坐标
for i in range(max_item_count): # 遍历存储区
pygame.draw.rect(play_surface, STORAGE_BOX_COLOR, # 绘制存储框
(storage_x + i * ICON_SIZE, storage_y, ICON_SIZE, ICON_SIZE))
if store[i]: # 如果存储区有物品
play_surface.blit(images[store[i] - 1], (storage_x + i * ICON_SIZE, storage_y)) # 绘制物品
# 显示分数和计时器
mission_text = f"总分: {total_score}" # 总分文本
score_text = f"当前分数: {score}" # 当前分数文本
countdown_text = f"时间: {seconds}" # 剩余时间文本
play_surface.blit(font.render(mission_text, True, RED), (10, 10)) # 显示总分
play_surface.blit(font.render(score_text, True, GREEN), (10, 40)) # 显示当前分数
play_surface.blit(font.render(countdown_text, True, GREEN), (WINDOW_WIDTH / 2 - 50, 10)) # 显示剩余时间
for event in pygame.event.get(): # 处理事件
if event.type == QUIT: # 如果退出事件
pygame.quit() # 退出pygame
sys.exit() # 退出程序
if event.type == MOUSEBUTTONUP: # 鼠标点击事件
(mouse_x, mouse_y) = event.pos # 获取鼠标点击位置
for r in range(grid_rows): # 遍历行
for c in range(grid_cols): # 遍历列
x = offset_x + c * (ICON_SIZE + 10) # 计算图标的x坐标
base_y = offset_y + r * (ICON_SIZE + 10) # 计算图标的基准y坐标
if layers[r][c]: # 如果该格子有物品
top_image_y = base_y - (len(layers[r][c]) - 1) * int(ICON_SIZE * OVERLAP_PERCENTAGE) # 顶层图标y坐标
# 检查鼠标点击位置是否在图标上
if x < mouse_x < x + ICON_SIZE and top_image_y < mouse_y < top_image_y + ICON_SIZE:
clicked_icon = layers[r][c].pop() # 获取被点击的图标
move_history.append((r, c, clicked_icon + 1)) # 记录移动历史
for i in range(7): # 将图标放入存储区
if store[i] == 0: # 找到空位
store[i] = clicked_icon + 1 # 放入图标
break
# 检查是否消除
if store.count(clicked_icon + 1) == 3:
store = [0 if icon == clicked_icon + 1 else icon for icon in store] # 清空对应的图标
score += 10 # 每次消除得分
total_score += 10 # 更新总分
if score > 20: # 每20分增加一个存储位
item_count = min(item_count + 1, max_item_count) # 增加物品数量
score = 0 # 重置当前得分
if event.type == KEYDOWN: # 按键事件
if event.key == K_BACKSPACE: # 用户按下退格键来悔棋
undo_last_move(layers, store, move_history) # 进行悔棋操作
if seconds <= 0: # 如果时间结束
show_defeat_screen(play_surface) # 显示失败界面
if all(store) and not any(store.count(icon) == 3 for icon in store if icon != 0): # 存储区已满且没有消除
show_defeat_screen(play_surface) # 显示失败界面
if not any(layers[r][c] for r in range(grid_rows) for c in range(grid_cols)): # 如果网格没有物品
show_victory_screen(play_surface, total_score) # 显示胜利界面
pygame.display.flip() # 刷新窗口
fps_clock.tick(30) # 控制帧率
二 前端设计与特色功能
界面设计以简洁、易用为原则,通过合理的图像布局和色彩搭配增强用户的游戏体验。
主菜单界面:包含5个菜单,简单模式、普通模式、困难模式、排行榜、退出。
通过红色高亮提示用户所选中的菜单,用户按下【enter】键进入菜单。
游戏界面:
游戏背景通过GAME_BG加载,所有游戏元素)都采用居中的布局方式
如图:
胜利与失败界面:
胜利和失败界面分别使用了不同的背景图像,通过绿色和红色字体分别展示胜利或失败的状态。界面颜色搭配明确,强化视觉冲击力。
难度设置:
分为简单、普通、困难三种难度,不同难度下图标种类不同,限定时间也不同(困难模式时间为90s图标为15种,其他模式180s,图标最多10种);
如图
增添了道具,撤销功能,用户点击【backspace】键即可“悔棋”
如图:
三、测试
一 图标过多问题:
图标过多且过于重复降低了用户兴趣。
评价:测试用例满足程序测试了需求;
解决:重新编写图标生成逻辑,使图表数量合理。
二 增添背景后图标大小异常问题
评价:测试用例满足程序测试了需求;
解决:重新编写背景添加逻辑,达到图标大小正常的目标
四、AIGC表格/心得体会
项目内容 | 学习到的内容和心得体会 |
---|---|
Pygame 初始化和窗口创建 | 学习了如何使用 pygame 库初始化游戏环境和创建窗口。如何设置窗口标题和大小。 |
图像绘制和界面更新 | 学会了如何加载图像和调整图像大小。 |
图像加载与处理 | 学习了如何计算图像的绘制位置并处理重叠问题。 |
游戏逻辑和计时 | 掌握了如何实现游戏计时和逻辑处理。通过 pygame.time.get_ticks() 记录时间,计算剩余时间,并在游戏中添加计时功能。了解了如何处理游戏胜利和失败的条件,并相应地调用不同的界面显示函数 |
排行榜和分数管理 | 学习了使用文件读写操作(open, readlines, write)来保存和加载分数。 |
可解的网格生成 | 学会了如何生成可解的游戏网格。了解了如何计算每种图像的数量,并使用 shuffle 打乱图像列表以生成随机网格。掌握了如何创建游戏网格并确保网格是可解的。 |
心得体会:
一个项目的开发是很复杂的,我掌握了 pygame 的基本使用,增强了我在游戏开发中的综合能力。通过实际的编码和调试,我学会了如何将不同的功能集成到一个完整的游戏中,并初步学会了处理实际开发中的各种问题。
五、PSP表格
任务 | 工作内容 | 时间 |
---|---|---|
需求分析 | 确定游戏功能需求、界面设计、游戏逻辑等。 | 1h |
界面设计 | 设计游戏主菜单、游戏界面、胜利和失败界面等。 | 2h |
主菜单实现、游戏逻辑实现、胜利,失败界面实现、存储区和悔棋功能实现 | 逐步调试代码,确保游戏逻辑按照预期运行。 | 4h |
测试修改代码 | 编写测试用例,进行详细测试,并记录问题。 | 3h |
总结评价:
需求分析用的时间过长。
测试代码所耗费时间过多,还需改进。