软工第二次作业
Github仓库链接:https://github.com/Yolaineyan/Yolaineyan
1. 游戏功能实现
界面设计:
使用Pygame或其他图形库设计游戏界面,包括主菜单、游戏界面和结束界面。
游戏逻辑:
实现图案的生成与分三层摆放,确保图案能够被合理匹配和消除。
玩家通过点击选择图案并消除,当所有图案被消除时游戏成功,当图案超出框内时失败。
难度设置:难度分为简单,普通,困难,三种模式。
2. 代码要求
使用GitHub Copilot或其他AIGC工具生成至少30%的代码,并在注释中标注自动生成的部分。
确保代码结构清晰,具有良好的可读性和注释。
- 游戏初始化
使用 Pygame 初始化游戏,并创建窗口。窗口的宽高被设为 1920x1080 的一半以符合用户的缩放要求。
加载了多种游戏资源(图片、音乐等),包括背景图片、牌组图片、按钮图片以及背景音乐。 - 自定义类
定义了 CustomTile 类,用来表示游戏中的每一张牌。每张牌都包含图片、位置、类型(tag)、层级(layer),以及状态(status)。
状态 status:代表是否可以点击,1 表示可以点击,0 表示被遮挡不可点击。 - 难度选择
difficulty_select() 函数处理游戏开始前的难度选择。通过加载不同难度按钮,用户可以点击选择“easy”、“normal” 或 “hard”模式。直到用户点击选择某个难度按钮,才会进入游戏。 - 牌组初始化
init_tile_group() 函数负责生成一组 12x12 的牌,并且随机打乱顺序放置在游戏界面上。
牌分为多层,初始有四层牌,每一层的牌数逐层减少。
牌的种类有六种,通过随机打乱顺序后按规则摆放在游戏画面中。 - 游戏主循环
main() 函数为游戏的主循环,调用难度选择函数,并不断刷新屏幕,检查用户的输入和操作。
在每一帧,游戏会处理玩家点击事件,绘制当前的游戏界面。 - 游戏的绘制
draw() 函数负责在每一帧绘制当前游戏状态,包括:
牌的展示:牌堆与遮罩,显示的牌是否可点击。
玩家已点击的牌会放入卡槽中。
若卡槽中牌的数量达到7张,显示结束画面(失败)。
若所有牌都已消除,则显示胜利画面。 - 鼠标点击响应
on_mouse_down() 处理用户的鼠标点击事件,负责:
处理点击“返回主菜单”按钮,重置游戏并返回主菜单。
当玩家点击牌时,牌从桌面移动到卡槽中,根据不同难度规则来判断是否能消除这些牌:
easy 模式:相同牌达到1张即可消除。
normal 模式:相同牌达到2张即可消除。
hard 模式:相同牌达到3张才可消除。
还包括点击“清空道具”功能,清空当前卡槽中的牌。 - 返回主菜单
return_to_menu() 函数用于处理返回主菜单的逻辑,重置所有游戏状态,并重新开始游戏。 - 游戏音乐
在游戏启动时,背景音乐会循环播放,增加游戏的氛围感。
import pygame
import random
import sys
# 定义游戏相关属性
TITLE = '羊了个羊小游戏'
WIDTH = 1920/2 # 放大后的宽度
HEIGHT = 1080/2 # 放大后的高度
FPS = 120
# 自定义游戏常量
T_WIDTH = 55 # 牌的宽度
T_HEIGHT = 55 # 牌的高度
# 下方牌堆的位置
DOCK = pygame.Rect((300, 485), (T_WIDTH *6, T_HEIGHT)) # 调整牌堆的位置
# 上方的所有牌
tiles = []
# 牌堆里的牌
docks = []
# 难度设置
DIFFICULTY = ''
# 清空道具的属性
CLEAR_ITEM_IMAGE = pygame.image.load('images/clear_item.png') # 假设你有一个清空道具的图片
CLEAR_ITEM_RECT = pygame.Rect((WIDTH - 200, 20), (100, 100)) # 调整道具的位置
has_clear_item = True # 玩家是否拥有清空道具的标志
# 返回主菜单按钮
BACK_BUTTON_IMAGE = pygame.image.load('images/back_button.png') # 假设你有一个返回按钮的图片
BACK_BUTTON_RECT = pygame.Rect((10, 10), (100, 50)) # 按钮位置和大小
# 初始化 Pygame
pygame.init()
screen = pygame.display.set_mode((WIDTH, HEIGHT)) # 设置为窗口模式,并放大
pygame.display.set_caption(TITLE)
clock = pygame.time.Clock()
# 加载背景和遮罩图像
background = pygame.image.load('images/back.png')
background = pygame.transform.scale(background, (WIDTH, HEIGHT)) # 背景适配屏幕
mask = pygame.image.load('images/mask.png')
mask = pygame.transform.scale(mask, (T_WIDTH, T_HEIGHT)) # 遮罩也需要适配
end = pygame.image.load('images/end.png')
end = pygame.transform.scale(end, (WIDTH/2, HEIGHT/2)) # 结束界面适配
win = pygame.image.load('images/win.png')
win = pygame.transform.scale(win, (WIDTH/2, HEIGHT/2)) # 胜利界面适配
# 加载按钮图片
easy_button = pygame.image.load('images/easy_button.png')
normal_button = pygame.image.load('images/normal_button.png')
hard_button = pygame.image.load('images/hard_button.png')
# 播放背景音乐
pygame.mixer.music.load('music/bgm.mp3')
pygame.mixer.music.play(-1)
# 自定义牌类
class CustomTile:
def __init__(self, image, rect, tag, layer, status):
self.image = image
self.rect = rect
self.tag = tag
self.layer = layer
self.status = status
# 难度选择界面
def difficulty_select():
global DIFFICULTY
easy_rect = easy_button.get_rect(center=(WIDTH // 2, HEIGHT // 2 - 150)) # 调整按钮位置
normal_rect = normal_button.get_rect(center=(WIDTH // 2, HEIGHT // 2))
hard_rect = hard_button.get_rect(center=(WIDTH // 2, HEIGHT // 2 + 150))
DIFFICULTY = '' # 初始化难度为空字符串
while DIFFICULTY not in ['easy', 'normal', 'hard']:
screen.fill((0, 0, 0)) # 用黑色填充屏幕
screen.blit(background, (0, 0)) # 绘制背景图像
screen.blit(easy_button, easy_rect) # 绘制简单模式按钮
screen.blit(normal_button, normal_rect) # 绘制普通模式按钮
screen.blit(hard_button, hard_rect) # 绘制困难模式按钮
pygame.display.update() # 更新屏幕显示
for event in pygame.event.get():
if event.type == pygame.QUIT:
pygame.quit()
sys.exit()
elif event.type == pygame.MOUSEBUTTONDOWN:
if easy_rect.collidepoint(event.pos):
DIFFICULTY = 'easy'
elif normal_rect.collidepoint(event.pos):
DIFFICULTY = 'normal'
elif hard_rect.collidepoint(event.pos):
DIFFICULTY = 'hard'
return DIFFICULTY
# 初始化牌组,12*12张牌随机打乱
def init_tile_group():
ts = ['zuanshi', 'hongshi', 'tiedin', 'lvbaoshi', 'meitan', 'qingshi'] * 6
random.shuffle(ts)
n = 0
for k in range(4): # 4层
for i in range(4 - k): # 每层减1行
for j in range(4 - k):
t = ts[n] # 获取排种类
n += 1
tile_image = pygame.image.load(f'images/{t}.png') # 加载对应的图片
tile_image = pygame.transform.scale(tile_image, (T_WIDTH, T_HEIGHT))
tile_rect = tile_image.get_rect()
tile_rect.topleft = (350 + (k * 0.5 + j) * T_WIDTH, 150 + (k * 0.5 + i) * T_HEIGHT * 0.9)
tile = CustomTile(tile_image, tile_rect, t, k, 1 if k == 3 else 0)
tiles.append(tile)
for i in range(6): # 剩余的6张牌放下面(为了凑整能通关)
t = ts[n]
n += 1
tile_image = pygame.image.load(f'images/{t}.png')
tile_image = pygame.transform.scale(tile_image, (T_WIDTH, T_HEIGHT))
tile_rect = tile_image.get_rect()
tile_rect.topleft = (300 + i * T_WIDTH, 375) # 调整底部位置
tile = CustomTile(tile_image, tile_rect, t, 0, 1)
tiles.append(tile)
# 游戏帧绘制函数
def draw():
screen.blit(background, (0, 0))
for tile in tiles:
screen.blit(tile.image, tile.rect)
if tile.status == 0:
screen.blit(mask, tile.rect)
for i, tile in enumerate(docks):
tile.rect.left = DOCK.x + i * T_WIDTH
tile.rect.top = DOCK.y
screen.blit(tile.image, tile.rect)
if len(docks) >= 7:
screen.blit(end, (0, 0))
if not tiles:
screen.blit(win, (0, 0))
if has_clear_item:
screen.blit(CLEAR_ITEM_IMAGE, CLEAR_ITEM_RECT)
screen.blit(BACK_BUTTON_IMAGE, BACK_BUTTON_RECT) # 绘制返回主菜单按钮
pygame.display.flip()
# 鼠标点击响应
def on_mouse_down(pos):
global docks, has_clear_item
if BACK_BUTTON_RECT.collidepoint(pos):
return_to_menu() # 调用返回主菜单函数
if len(docks) >= 7 or not tiles:
return
if has_clear_item and CLEAR_ITEM_RECT.collidepoint(pos):
has_clear_item = False # 消耗道具
# 将卡槽里的牌放到底部牌堆的上方
bottom_deck_start_y = 485 - T_HEIGHT # 底部牌堆的上方起始y坐标
for i, tile in enumerate(docks):
tile.status = 1 # 重置状态为可点击
# 计算应该放置的位置(底部牌堆的上方)
new_x = 300 + i * T_WIDTH
new_y = bottom_deck_start_y + (i // 7) * T_HEIGHT
tile.rect.topleft = (new_x, new_y)
tiles.append(tile) # 加入到tiles列表
docks.clear() # 清空卡槽
return
for tile in reversed(tiles): # 逆序循环是为了先判断上方的牌
if tile.status == 1 and tile.rect.collidepoint(pos):
tile.status = 2
tiles.remove(tile)
docks.append(tile)
# 根据难度检查是否可以消除
if DIFFICULTY == 'easy':
# 简单模式下,一张相同的卡片就可以消除
if len([t for t in docks if t.tag == tile.tag]) >= 1:
docks = [t for t in docks if t.tag != tile.tag]
elif DIFFICULTY == 'normal':
# 普通模式下,需要两张相同的卡片
if len([t for t in docks if t.tag == tile.tag]) >= 2:
docks = [t for t in docks if t.tag != tile.tag]
elif DIFFICULTY == 'hard':
# 困难模式下,需要三张相同的卡片
if len([t for t in docks if t.tag == tile.tag]) >= 3:
docks = [t for t in docks if t.tag != tile.tag]
for down in tiles:
if down.layer == tile.layer - 1 and down.rect.colliderect(tile.rect):
for up in tiles:
if up.layer == down.layer + 1 and up.rect.colliderect(down.rect):
break
else:
down.status = 1
return
# 返回主菜单
def return_to_menu():
global tiles, docks, DIFFICULTY, has_clear_item
tiles.clear()
docks.clear()
DIFFICULTY = ''
has_clear_item = True
difficulty_select() # 重新进入难度选择界面
init_tile_group() # 重新初始化牌组
# 游戏主循环
def main():
difficulty_select() # 调用难度选择界面
init_tile_group() # 初始化牌组
has_clear_item = True # 玩家开始时拥有清空道具
running = True
while running:
for event in pygame.event.get():
if event.type == pygame.QUIT:
running = False
elif event.type == pygame.MOUSEBUTTONDOWN:
on_mouse_down(event.pos)
draw()
clock.tick(FPS)
pygame.quit()
# 启动游戏
main()
3. 结合AIGC
子任务 | 何种AIGC技术 | 效果 |
---|---|---|
难度设置 | ChatGPT-4.0 | 成功设置了三种难度 |
音乐播放 | 无 | 使音乐能流畅 循环播放 |
道具 | copilot | 实现了将卡槽中的卡作为使用卡片 |
返回功能 | ChatGPT-4.0 | 能在游戏中回到主界面 |
4. 扩展功能(附加分)
增加道具功能,使卡槽中的牌回到上方道具区以使用,以增加游戏趣味性。
提供多种难度模式,设置了三种不同的难度,为玩家提供选择。
5.AIGC表格
学习内容 | 具体技术/工具 | 心得体会 |
---|---|---|
使用 Pygame 开发小游戏 | Pygame, Python | 理解了 Pygame 的事件处理、图像绘制等机制。 |
优化 UI 界面并提升用户体验 | 界面设计, 事件处理 | 学会了通过调整按钮布局和图片来优化用户体验。 |
处理项目中的图像加载和优化 | Pygame, 图像处理 | 深刻认识到图像大小调整和加载速度对性能的影响。 |
实现音频播放与音效管理 | Pygame, 音频处理 | 掌握了音频在游戏中的应用,理解了 Pygame 的音频模块。 |
6.PSP表格
PSP 阶段 | 任务内容 | 预估耗时(分钟) | 实际耗时(分钟) | 个人评价与改进建议 |
---|---|---|---|---|
计划 | 明确项目需求与目标 | 30 | 40 | 在明确需求时花费时间比预期长,未来应更高效地与需求相关方沟通。 |
开发 | 初步设计方案 | 40 | 35 | 设计方案较为顺利,但部分细节需要进一步优化。 |
开发 | 编写程序主结构和初始化部分 | 60 | 75 | 编写过程中遇到一些逻辑问题,导致实际时间延长。未来应更关注初始结构设计。 |
开发 | 实现游戏界面、图像加载与处理功能 | 120 | 130 | 游戏界面设计符合需求,但图像加载速度可以进一步优化。需要考虑性能提升。 |
开发 | 音频处理与音效播放功能 | 90 | 85 | 音效实现符合预期,时间估算合理,但在音效管理上还可进一步优化。 |
开发 | 处理用户输入事件与交互逻辑 | 100 | 110 | 用户输入处理较为顺利,但交互逻辑复杂,未来需要在设计阶段预留更多时间。 |
测试 | 单元测试与调试 | 80 | 120 | 测试和调试阶段耗时较长,未来需要在编码阶段减少潜在错误。 |
改进 | 优化图像加载速度与整体性能 | 60 | 50 | 优化过程顺利,未来可以考虑更多自动化工具来加速调试过程。 |
总结 | 总结与项目文档撰写 | 30 | 40 | 项目总结耗时略长,未来应优化总结结构,提高总结效率。 |
总计 | - | 610 | 685 | 总体上时间预估偏差不大,但在调试和测试阶段需要加强自动化工具的使用。 |