这个作于属于哪个课程 软件工程
这个作业要求在哪里 作业要求
这个作业的目标 学习使用pygame和软件开发流程
学号 102202129

项目名称:金铲铲之战(1.0版本)

github主页链接:lin10-coding (github.com)

github作业链接:software-engineering/soft at main · lin10-coding/software-engineering (github.com)

一、游戏规则:

金铲铲之战是一款刺激的战斗消除类游戏

玩家可通过鼠标点击对应英雄卡牌将其收集起来,每收集三个相同的英雄碎片即可获得该英雄,收集足够多的英雄碎片帮助你的英雄升级并凑集强大羁绊,战胜敌人!

在备战(游戏)期间每收集三个英雄碎片即可增加得分 最终剩余的时间也将转换为得分!

游戏中有多种道具,合理利用可以帮助你增加游戏时间,提高得分,筹备更强阵容!

战胜敌人后可以提高段位,获得奖励,购买道具,增强实力!

现阶段游戏处于1.0测试版本,仅有收集英雄玩法,暂无对战玩法,敬请期待!

游戏内有隐藏彩蛋,触发后联系制作人有奖!

二、游戏演示

1游戏菜单主页

2游戏界面

3游戏操作

4道具使用

5游戏失败

三、小游戏实现步骤

1 游戏逻辑实现

将图片作为actor对象并创建多个对象放入列表中作为牌库,再将其按规律随机叠放

单独设计出可置放的位置,超过剩余seat数量则游戏失败

三个相同的对象在seat中则会消除

牌库代码

ts = list(range(1, 14))*18
random.shuffle(ts)
n = 0
for k in range(8):    #  8层
    for i in range(8-k):    #每层减1行
        for j in range(8-k):
            t = ts[n]        #获取排种类
            n += 1
            tile = Actor(f'tile{t}')       #使用tileX图片创建Actor对象
            tile.pos = 250 + (k * 0.5 + j) * tile.width, 100 + (k * 0.5 + i) * tile.height * 0.9    #设定位置
            tile.tag = t            #记录种类
            tile.layer = k          #记录层级
            tile.status = 1 if k == 7 else 0        #除了最顶层,状态都设置为0(不可点)这里是个简化实现
            tiles.append(tile)
for i in range(13):        #剩余牌放下面(为了凑整能通关)
    t = ts[n] 
    n += 1
    tile = Actor(f'tile{t}')
    tile.pos = 100+i * tile.width, 580
    tile.tag = t
    tile.layer = 0
    tile.status = 1
    tiles.append(tile)

for i in range(13):        #剩余牌放下面(为了凑整能通关)
    t = ts[n] 
    n += 1
    tile = Actor(f'tile{t}')
    tile.pos = 100+i * tile.width, 40
    tile.tag = t
    tile.layer = 0
    tile.status = 1
    tiles.append(tile)

鼠标响应代码

def on_mouse_down(pos):

    global docks
    
    if len(docks) >= seat or len(tiles) == 0:      #游戏结束不响应
        return
    for tile in reversed(tiles):    #逆序循环是为了先判断上方的牌,如果点击了就直接跳出,避免重复判定
        if tile.status == 1 and tile.collidepoint(pos):
            # 状态1可点,并且鼠标在范围内
            tile.status = 2
            tiles.remove(tile)
            diff = [t for t in docks if t.tag != tile.tag]    #获取牌堆内不相同的牌
            if len(docks) - len(diff) < 2:    #如果相同的牌数量<2,就加进牌堆
                docks.append(tile)
            else:             #否则用不相同的牌替换牌堆(即消除相同的牌)
                docks = diff
                score+=10
            for down in tiles:      #遍历所有的牌
                if down.layer == tile.layer - 1 and down.colliderect(tile):   #如果在此牌的下一层,并且有重叠
                    for up in tiles:      #那就再反过来判断这种被覆盖的牌,是否还有其他牌覆盖它
                        if up.layer == down.layer + 1 and up.colliderect(down):     #如果有就跳出
                            break
                    else:      #如果全都没有,说明它变成了可点状态
                        down.status = 1
            return

2 界面设计实现

分为菜单界面和主游戏界面及胜利失败后结算界面,菜单界面包含开始游戏,商店和退出游戏,成功或失败后均有继续按钮

游戏界面及备战席绘制代码

def on_mouse_down(pos):

    global docks
    
    if len(docks) >= seat or len(tiles) == 0:      #游戏结束不响应
        return
    for tile in reversed(tiles):    #逆序循环是为了先判断上方的牌,如果点击了就直接跳出,避免重复判定
        if tile.status == 1 and tile.collidepoint(pos):
            # 状态1可点,并且鼠标在范围内
            tile.status = 2
            tiles.remove(tile)
            diff = [t for t in docks if t.tag != tile.tag]    #获取牌堆内不相同的牌
            if len(docks) - len(diff) < 2:    #如果相同的牌数量<2,就加进牌堆
                docks.append(tile)
            else:             #否则用不相同的牌替换牌堆(即消除相同的牌)
                docks = diff
                score+=10
            for down in tiles:      #遍历所有的牌
                if down.layer == tile.layer - 1 and down.colliderect(tile):   #如果在此牌的下一层,并且有重叠
                    for up in tiles:      #那就再反过来判断这种被覆盖的牌,是否还有其他牌覆盖它
                        if up.layer == down.layer + 1 and up.colliderect(down):     #如果有就跳出
                            break
                    else:      #如果全都没有,说明它变成了可点状态
                        down.status = 1
            return

开始及结束游戏界面代码

def draw():
    if gamestatus==1:
        global score
        screen.clear()
        screen.blit('back1', (0, 0))      #背景图
        timebutton.draw()
        screen.draw.text('time: ' + str(round(timer, 0)), (900,10), color=(255,255,255),fontsize=40)
        screen.draw.text('seats: ' , (50,700), color=(255,255,255),fontsize=30)
        remain=seat-len(docks)
        screen.draw.text('remains:' + str(round(remain, 0)), (250,640), color=(255,255,255),fontsize=30)
        screen.draw.text('scores:' + str(round(score, 0))+'+'+str(round(timer, 0)), (900,50), color=(255,255,255),fontsize=50)

    if gamestatus==0:
        screen.clear()
        screen.blit('back1',(0,0))
        beginbutton.draw()
        storebutton.draw()
        exitbutton.draw()
        logo.draw()
        logo2.draw()

通过不同的游戏状态绘制菜单 主游戏 失败/成功等界面

四、小游戏实现代码

git链接:金铲铲之战1.0) -images文件在这

游戏代码

import pgzrun
import pygame
import random
import os


font = pygame.font.SysFont('simHei',20)

TITLE = '金铲铲之战1.0' 
WIDTH = 1200
HEIGHT = 800

timer=300 #游戏倒计时
#buttons
logo=Actor('logo')
logo.pos=400,300

logo2=Actor('logo2')
logo2.pos=700,600

beginbutton=Actor('star')
beginbutton.pos=1060,500

storebutton=Actor('store')
storebutton.pos=1060,600

exitbutton=Actor('exit')
exitbutton.pos=1060,700

timebutton=Actor('addtime')
timebutton.pos=1060,400

playagain=Actor('playagain')
playagain.pos=1000,600


# 自定义游戏常量
T_WIDTH = 60
T_HEIGHT = 66
gamestatus=0
seat=7
remain=7
score=0

# 下方牌堆的位置
DOCK = Rect((120, 700), (T_WIDTH*seat, T_HEIGHT))

# 上方的所有牌
tiles = []
# 牌堆里的牌
docks = []

# 初始化牌组,13*18张牌随机打乱
ts = list(range(1, 14))*18
random.shuffle(ts)
n = 0
for k in range(8):    # 7层
    for i in range(8-k):    #每层减1行
        for j in range(8-k):
            t = ts[n]        #获取排种类
            n += 1
            tile = Actor(f'tile{t}')       #使用tileX图片创建Actor对象
            tile.pos = 250 + (k * 0.5 + j) * tile.width, 100 + (k * 0.5 + i) * tile.height * 0.9    #设定位置
            tile.tag = t            #记录种类
            tile.layer = k          #记录层级
            tile.status = 1 if k == 7 else 0        #除了最顶层,状态都设置为0(不可点)这里是个简化实现
            tiles.append(tile)
for i in range(13):        #剩余牌放下面(为了凑整能通关)
    t = ts[n] 
    n += 1
    tile = Actor(f'tile{t}')
    tile.pos = 100+i * tile.width, 580
    tile.tag = t
    tile.layer = 0
    tile.status = 1
    tiles.append(tile)

for i in range(13):        #剩余牌放下面(为了凑整能通关)
    t = ts[n] 
    n += 1
    tile = Actor(f'tile{t}')
    tile.pos = 100+i * tile.width, 40
    tile.tag = t
    tile.layer = 0
    tile.status = 1
    tiles.append(tile)

def update():
    global gamestatus
    global timer
    if timer <=0:
        return 
    timer-=1/60

# 游戏帧绘制函数
def draw():
    if gamestatus==1:
        global score
        screen.clear()
        screen.blit('back1', (0, 0))      #背景图
        timebutton.draw()
        screen.draw.text('time: ' + str(round(timer, 0)), (900,10), color=(255,255,255),fontsize=40)
        screen.draw.text('seats: ' , (50,700), color=(255,255,255),fontsize=30)
        remain=seat-len(docks)
        screen.draw.text('remains:' + str(round(remain, 0)), (250,640), color=(255,255,255),fontsize=30)
        screen.draw.text('scores:' + str(round(score, 0))+'+'+str(round(timer, 0)), (900,50), color=(255,255,255),fontsize=50)
        for tile in tiles:
            #绘制上方牌组
            tile.draw()
            if tile.status == 0:
                screen.blit('mask', tile.topleft)     #不可点的添加遮罩
        for i, tile in enumerate(docks):
            #绘制排队,先调整一下位置(因为可能有牌被消掉)
            tile.left = (DOCK.x + i * T_WIDTH)
            tile.top = DOCK.y
            tile.draw()

        # 超过7张,失败
        if len(docks) >= seat:
            screen.clear()
            screen.blit('end', (0, 0))
            screen.draw.text('your scores:' + str(round(score, 0)), (900,50), color=(255,255,255),fontsize=50)
            screen.draw.text('you lost!!!!' , (100,50), color=(255,255,255),fontsize=90)
            playagain.draw()
        if timer <= 0:
            screen.clear()
            screen.blit('end', (0, 0))
            screen.draw.text('your scores:' + str(round(score, 0)), (900,50), color=(255,255,255),fontsize=50)
            screen.draw.text('you lost!!!!' , (100,50), color=(255,255,255),fontsize=90)
            playagain.draw()
        # 没有剩牌,胜利
        if len(tiles) == 0:
            screen.blit('win', (0, 0))
            score+=timer
    if gamestatus==0:
        screen.clear()
        screen.blit('back1',(0,0))
        beginbutton.draw()
        storebutton.draw()
        exitbutton.draw()
        logo.draw()
        logo2.draw()

# 鼠标点击响应
def on_mouse_down(pos):

    if beginbutton.collidepoint(pos):
        global gamestatus
        global timer
        timer=300
        gamestatus=1

    if timebutton.collidepoint(pos):
        
        global seat
        global remain
        global score
        timer+=60
        seat+=2
        remain+=2
        score-=60
    
    if exitbutton.collidepoint(pos):
        pygame.quit()

    if playagain.collidepoint(pos):
        gamestatus=0
        
    global docks
    
    if len(docks) >= seat or len(tiles) == 0:      #游戏结束不响应
        return
    for tile in reversed(tiles):    #逆序循环是为了先判断上方的牌,如果点击了就直接跳出,避免重复判定
        if tile.status == 1 and tile.collidepoint(pos):
            # 状态1可点,并且鼠标在范围内
            tile.status = 2
            tiles.remove(tile)
            diff = [t for t in docks if t.tag != tile.tag]    #获取牌堆内不相同的牌
            if len(docks) - len(diff) < 2:    #如果相同的牌数量<2,就加进牌堆
                docks.append(tile)
            else:             #否则用不相同的牌替换牌堆(即消除相同的牌)
                docks = diff
                score+=10
            for down in tiles:      #遍历所有的牌
                if down.layer == tile.layer - 1 and down.colliderect(tile):   #如果在此牌的下一层,并且有重叠
                    for up in tiles:      #那就再反过来判断这种被覆盖的牌,是否还有其他牌覆盖它
                        if up.layer == down.layer + 1 and up.colliderect(down):     #如果有就跳出
                            break
                    else:      #如果全都没有,说明它变成了可点状态
                        down.status = 1
            return


os.environ['SDL_VIDEO_CENTERED'] = '1' 
pgzrun.go()



五、AIGC

AIGC表格:

子任务 借助何种AIGC技术,实现了什么功能 效果如何
图层堆叠 牌库堆叠问题 效果一般,为平面层,自己调为交错堆叠
检查是否能被消除 消除逻辑 给出借鉴和参考,对主逻辑编写有很大帮助
功能图像图标 借助AI生成完成美工 达到预期
鼠标事件处理 给出范例及个别代码 帮助举一反三实现功能
游戏时间 倒计时代码及处理方式 达到预期但需要微调
得分 得分计算代码位置 达到预期
整体代码调整 优化代码提高性能 达到预期

六、测试实例

测试游戏主菜单等功能

import pgzrun
import pygame
import random
import os


font = pygame.font.SysFont('simHei',20)

TITLE = '金铲铲之战1.0' 
WIDTH = 1200
HEIGHT = 800

timer=300 #游戏倒计时
#buttons
logo=Actor('logo')
logo.pos=400,300

logo2=Actor('logo2')
logo2.pos=700,600

beginbutton=Actor('star')
beginbutton.pos=1060,500

storebutton=Actor('store')
storebutton.pos=1060,600

exitbutton=Actor('exit')
exitbutton.pos=1060,700

timebutton=Actor('addtime')
timebutton.pos=1060,400

playagain=Actor('playagain')
playagain.pos=1000,600


# 自定义游戏常量
T_WIDTH = 60
T_HEIGHT = 66
gamestatus=0
seat=7
remain=7
score=0

# 下方牌堆的位置
DOCK = Rect((120, 700), (T_WIDTH*seat, T_HEIGHT))

# 上方的所有牌
tiles = []
# 牌堆里的牌
docks = []

# 初始化牌组,13*18张牌随机打乱
ts = list(range(1, 14))*18
random.shuffle(ts)
n = 0
for k in range(8):    # 7层
    for i in range(8-k):    #每层减1行
        for j in range(8-k):
            t = ts[n]        #获取排种类
            n += 1
            tile = Actor(f'tile{t}')       #使用tileX图片创建Actor对象
            tile.pos = 250 + (k * 0.5 + j) * tile.width, 100 + (k * 0.5 + i) * tile.height * 0.9    #设定位置
            tile.tag = t            #记录种类
            tile.layer = k          #记录层级
            tile.status = 1 if k == 7 else 0        #除了最顶层,状态都设置为0(不可点)这里是个简化实现
            tiles.append(tile)
for i in range(13):        #剩余牌放下面(为了凑整能通关)
    t = ts[n] 
    n += 1
    tile = Actor(f'tile{t}')
    tile.pos = 100+i * tile.width, 580
    tile.tag = t
    tile.layer = 0
    tile.status = 1
    tiles.append(tile)

for i in range(13):        #剩余牌放下面(为了凑整能通关)
    t = ts[n] 
    n += 1
    tile = Actor(f'tile{t}')
    tile.pos = 100+i * tile.width, 40
    tile.tag = t
    tile.layer = 0
    tile.status = 1
    tiles.append(tile)

def update():
    global gamestatus
    global timer
    if timer <=0:
        return 
    timer-=1/60

# 游戏帧绘制函数
def draw():

    if gamestatus==0:
        screen.clear()
        screen.blit('back1',(0,0))
        beginbutton.draw()
        storebutton.draw()
        exitbutton.draw()
        logo.draw()
        logo2.draw()

# 鼠标点击响应
def on_mouse_down(pos):

    if beginbutton.collidepoint(pos):
        global gamestatus
        global timer
        timer=300
        gamestatus=1

    if timebutton.collidepoint(pos):
        
        global seat
        global remain
        global score
        timer+=60
        seat+=2
        remain+=2
        score-=60
    
    if exitbutton.collidepoint(pos):
        pygame.quit()

    if playagain.collidepoint(pos):
        gamestatus=0
        
    global docks
    
    if len(docks) >= seat or len(tiles) == 0:      #游戏结束不响应
        return
    for tile in reversed(tiles):    #逆序循环是为了先判断上方的牌,如果点击了就直接跳出,避免重复判定
        if tile.status == 1 and tile.collidepoint(pos):
            # 状态1可点,并且鼠标在范围内
            tile.status = 2
            tiles.remove(tile)
            diff = [t for t in docks if t.tag != tile.tag]    #获取牌堆内不相同的牌
            if len(docks) - len(diff) < 2:    #如果相同的牌数量<2,就加进牌堆
                docks.append(tile)
            else:             #否则用不相同的牌替换牌堆(即消除相同的牌)
                docks = diff
                score+=10
            for down in tiles:      #遍历所有的牌
                if down.layer == tile.layer - 1 and down.colliderect(tile):   #如果在此牌的下一层,并且有重叠
                    for up in tiles:      #那就再反过来判断这种被覆盖的牌,是否还有其他牌覆盖它
                        if up.layer == down.layer + 1 and up.colliderect(down):     #如果有就跳出
                            break
                    else:      #如果全都没有,说明它变成了可点状态
                        down.status = 1
            return


os.environ['SDL_VIDEO_CENTERED'] = '1' 
pgzrun.go()



七、AIGC表格

学到的内容 内容描述 心得体会
开发流程管理 如何合理分配时间,避免开发过程中出现时间延误。 高效的开发流程管理可以减少整体开发时间,提升工作效率。
调试和修改 认识到调试和修改的复杂性,及其对项目时间的影响。 提前进行详细的单元测试和代码审查,可以减少调试时间和修改成本
测试的重要性 了解在项目完成后进行全面测试的重要 完善的测试能确保项目质量,及时发现和修复潜在的问题。
需求分析技巧 在项目初期明确需求的重要性,能够快速理解并整理需求。 清晰的需求分析能够减少后续开发中的问题,提高效率。
原型构建方法 通过快速构建原型模型来验证设计思路和功能实现。 原型构建帮助更快地确定设计方向,避免在开发过程中反复修改。

八、PSP表格

PSP 预估耗时 实际耗时
项目分析,明确需求 1 0.5
构建原型模型 1 0.2
项目准备工作 0.3 0.1
投入开发 6 4
调试和修改 2 6
测试 1 0.5
复盘与总结 1 1
维护 continue continue

个人评价及心得:过分追求功能的实现及代码实操,在前期部署及原型构建上花的时间不够多导致实际工作中代码问题很多,修改调试中花了较多的时间精力,浪费了较多时间,本次作业后学会了pygame及软件开发逻辑,收获颇丰