软件工程课程第二次个人作业

软件工程 https://edu.cnblogs.com/campus/fzu/SE2024
作业要求 https://edu.cnblogs.com/campus/fzu/SE2024/homework/13253
作业目标 做出一个类似“羊了个羊”的小游戏
学号 072208130
项目链接 Starryship/cats (github.com)

一、游戏设计

(一)页面设计

对于页面的设计,综合考虑了页面的美观性以及页面切换的合理性。设计了一个主页面,主页面有两个按钮,一个是游戏开始按钮,另一个是游戏记录按钮。点击游戏开始按钮可以进入游戏选关界面,一共设计了十二关,我这里把各选关按钮均匀排列,使页面排版更加合理美观。点击关卡按钮可以进入游戏页面,在游戏页面中,为了保证各小块图标的清晰度不被压缩,游玩区域就设计得足够大(为图中左侧区域),右侧区域则是我们的一些状态。右上方是计时按钮,记录游玩关卡的时间,右侧中间区域是我们的道具区,当我们在左侧游玩区是在是找不到卡关的解决方法时,我们可以点击道具区的方块解决我们的问题。右侧下方则是我们的状态(选快)区,我们从左侧选过来的方块都会放到这里。当我们胜利或者失败时,游戏会自动跳出来一个结果(result)页面,来表明你是成功的还是失败的,在该页面的左上角,我还设计了一个可以返回主页面的按钮还有一个退出游戏的按钮,充分考虑到了仍有游玩意愿或者想终止游戏的人。讲完了开始界面的start按钮,接下来要介绍的是record按钮,record按钮是我们的成功记录,里面按行将我们的通关记录展现了出来,包括关卡号还有通关时间。

页面的设计图如下:

pic1

(二)玩法设计

本项目游戏的玩法是类似于前段时间爆火的“羊了个羊”,不同的是,我更加丰富了其原本的玩法以及一些页面的优化。可以看到我们的关卡难度分为十二个等级,难度依次递增,其难度设计为随着关卡的增加,图片的种类和图片的数量会增加。其玩法是对于牌堆,从上到下可以依次点击,但是有被压住的牌不能进行点击,有三个相同的牌在卡槽里则这三张牌被消除,卡槽最大可容纳七张牌,如果我们左侧的牌全部消除完,则游戏胜利,如果不能消除的卡牌超过或等于卡槽的最大容量(本游戏设计的最大容量为7),则游戏失败。这就很考验我们合理规划,要找到一个最佳点击排队的顺序,以及合理利用道具,最终将左侧牌堆全部消除完。看起来确实是个很好玩的游戏呢!

二、详细介绍

(一)页面展示

首页

选关界面

游玩界面

这里展示了三个等级

这个很简单

这个会有点难

这个特别难

结束界面

胜利

失败

查看通关记录

动图展示(展示点击特效及关卡设置)

(二)玩法展示

通过点击上面的牌,将下面的牌拯救出来,三个牌九消去,知道左侧没有牌剩余

点击牌

简单玩法

速通玩法(并展示记录)

(三)技术实现

关卡选择

每一个level对应着相应的关卡。返回的第一个参数是层数,第二个参数是道具数量,第三个参数比如说list(range(1, kind+1)) * 48,range的终止条件减一的出来的结果就是图片种类数。

def level_select_s(level):
    if level ==1:
        return 6,5,list(range(1, 3)) * 48
    elif level ==2:
        return 7,4,list(range(1, 3)) * 72
    elif level ==3:
    	return 3,1,list(range(1, 6)) * 3
    elif level ==4:
        return 7,4,list(range(1, 4)) * 48
    elif level ==5:
        return 6,4,list(range(1, 5)) * 24
    elif level ==6:
        return 7,4,list(range(1,5)) * 36
    elif level ==7:
        return 5,5,list(range(1, 6)) * 12
    elif level ==8:
        return 7,4,list(range(1,7)) * 24
    elif level ==9:
        return 6,5,list(range(1, 9)) * 12
    elif level ==10:
        return 5,5,list(range(1, 11)) * 6
    elif level ==11:
        return 4,3,list(range(1, 12)) * 3
    elif level ==12:
        return 7,4,list(range(1,13)) * 12

核心代码

该代码实现了每张牌之间的叠放和点击关系,只有点击上层下层才会被释放变得可点击,该代码就是利用该思想完成的这个点击关系。

def handle_mouse_click(pos):
    global docks

    if exit_button_rect.collidepoint(pos):
        pygame.quit()
        return

    if len(docks) >= 7 or len(tiles) == 0:  # 游戏结束时不处理点击
        # game_over = True
        return


    for tile in reversed(tiles):  # 从最上层的牌开始判断
        rect = pygame.Rect(tile['pos'][0], tile['pos'][1], TILE_WIDTH, TILE_HEIGHT)
        if tile['status'] == 1 and rect.collidepoint(pos):
            tile['status'] = 2
            tiles.remove(tile)

            unmatched_tiles = [t for t in docks if t['tag'] != tile['tag']]

            if len(docks) - len(unmatched_tiles) < 2:
                docks.append(tile)
            else:
                docks = unmatched_tiles

            for down_tile in tiles:
                if down_tile['layer'] == tile['layer'] - 1 and pygame.Rect(down_tile['pos'][0], down_tile['pos'][1],
                                                                           TILE_WIDTH, TILE_HEIGHT).colliderect(
                        pygame.Rect(tile['pos'][0], tile['pos'][1], TILE_WIDTH, TILE_HEIGHT)):
                    for up_tile in tiles:
                        if up_tile['layer'] == down_tile['layer'] + 1 and pygame.Rect(up_tile['pos'][0],
                                                                                      up_tile['pos'][1], TILE_WIDTH,
                                                                                      TILE_HEIGHT).colliderect(
                                pygame.Rect(down_tile['pos'][0], down_tile['pos'][1], TILE_WIDTH, TILE_HEIGHT)):
                            break
                    else:
                        down_tile['status'] = 1  # 如果没有其他牌覆盖,则变为可点击状态

            break

驱动代码

这部分代码就是将各界面组织起来,并使程序完成相对应的点击任务,实现了绘图还有鼠标事件的关联驱动。

def main():
    global level_select,tiles,selected_level,record,show_record
    pygame.init()
    clock = pygame.time.Clock()
    running = True
    game_started = False  # 游戏是否开始标志
    game_over = False  # 游戏结束标志
    start_time = None  # 计时起始时间
    # level_select = True
    level_select = False
    selected_level=0#选择关卡
    tiles = make_titles(selected_level+1)
    record=[]
    elapsed_time2=0
    time_record_control=False
    show_record=False
    # count=0
    while running:
        screen.fill(WHITE)
        mouse_pos = pygame.mouse.get_pos()
        is_clicked = False


        for event in pygame.event.get():
            if event.type == pygame.QUIT:
                running = False
            elif event.type == pygame.MOUSEBUTTONDOWN:
                if game_over:
                    handle_game_over_click(event.pos)
                    game_over = False
                    game_started = False
                    level_select=True
                elif not game_started and button_rect.collidepoint(event.pos):
                    game_started = True
                    level_select=True
                elif not show_record and button_record.collidepoint(event.pos):
                    show_record = True
                elif game_started and level_select:
                    handle_level_select_click(event.pos)
                    start_time = pygame.time.get_ticks()  # 开始计时
                elif level_select:
                    handle_record(event.pos)
                elif game_started and not level_select:
                    handle_mouse_click(event.pos)

        if len(docks) >= 7 or len(tiles) == 0:
            level_select=False
            game_over = True  #


        if len(tiles) == 0 and time_record_control:

            record.append((selected_level, elapsed_time2))

            time_record_control = False


        if not game_started:
            draw_start(screen, mouse_pos, is_clicked)

        elif game_over:
            draw_game_over_screen(screen, mouse_pos, is_clicked)
        elif show_record:
            draw_record(screen)

        elif level_select:
            draw_level_select_screen(screen, mouse_pos, is_clicked)

        else:
            draw(tiles)  # 绘制游戏内容
            time_record_control = True
            elapsed_time = (pygame.time.get_ticks() - start_time) // 1000  # 计算经过时间
            elapsed_time2 = round((pygame.time.get_ticks() - start_time) / 1000,3)

            font = pygame.font.SysFont(None, 60)
            time_text = font.render(f"Time: {elapsed_time}s", True, (0, 0, 0))
            screen.blit(time_text, (1000, 50))  # 显示计时器


        # print(record)
        pygame.display.update()
        clock.tick(60)  # 控制帧率


    pygame.quit()

(四)测试

三、制作过程

子任务 借助何种AIGC技术 实现了什么功能 效果如何
游戏界面设计 ChatGPT 页面的设计 效果不太好,询问了好多次都没有能达到一个理想的结果,要么是回答太复杂了要么是不符合我的要求,后来是自己设计的页面。
pygame设计游戏的逻辑 ChatGPT 知识前提 使我了解到了该怎么设计游戏,以及各部分代码如何连接在一起
代码规范 ChatGPT 使代码可读性和可维护性增强 让我更加清楚每一部分衔接的逻辑,让我思路更加清晰
图片素材获取 ChatGPT、豆包、Craiyon 背景图还有按钮图的美化 效果非常好,图片精美程度从大到小依次是ChatGPT、豆包、Craiyon
界面状态控制 ChatGPT 使用状态量控制页面的切换 效果还不错,虽然生成的代码会有一点小bug,但是给了我一条不错的思路
按钮点击反馈 ChatGPT 是点击到的逻辑块能正确做出相应的指示 这个还不错,知识缺少可以帮我补充这一部分知识
算法实现 ChatGPT 控制下层猫猫块不能被点击 效果不错,给了我一条很不错的思路,但是小bug改的很麻烦
数据存储 ChatGPT 记录每一个猫猫块的位置还有状态 效果很好,解决了我对于位置如何存储的疑惑
注释 ChatGPT 实现代码自动的注释 效果不太行,注释完的代码运行报错,它还修改了我的代码,可能是想让我的代码更加规范吧,但是改的效果不太好

问题解决(ChatGPT)

还有很多就不一一上传了

图片收集(ChatGPT)

图片收集(豆包)



图片收集(Craiyon)

图片汇总

本来时想用动物图的做图标的,但是后面发现图片尺寸缩小后失真特别严重,整体放上去特别难看,就改为猫猫图了



背景音乐

音乐链接:bgmbgm2bgm3

四、PSP表格

阶段 计划时间 (分钟) 实际时间 (分钟) 阶段总结
计划 30 40 明确了项目的目标和需求,稍微超时
设计 120 300 设计不太顺利较为顺利,花了较多时间,主要是刚开始选错图片了
代码实现 300 600 遇到一些Bug,修复花费了较多时间,还有一些功能实现也占了比较多的时间
编译与测试 120 60 测试通过,基本没有问题
总结与报告撰写 240 240 总结分析了开发过程中的问题与经验

五、上传github

项目以及上传到github咯,有想玩的可以clone下来玩玩,不会玩的可以让作者来教你(我我)哦“ψ(`∇´)ψ
链接:Starryship/cats (github.com)

六、总结

感觉本次项目可以做的方向有很多,重点是发挥自己的想象力以及如何用好一些工具,但受时间限制,只能做一个简单的demo。本次项目的重点主要是在于界面的美化还有游戏的玩设计。对于本项目用到的所有图片均为AI生成,重点注意了美术设计,尽可能的让界面变得美观,使人看到这个游戏就有玩下去的欲望,虽然有些地方任然不够好看,但是大体上还是蛮不错的。然后就是游戏的玩法,玩法的核心其实就是“羊了个羊”的玩法,从上到下依次点击方块进行消除,趣味性还是蛮大的。但是对于原版“羊了个羊”,第二关就变得特别难,我就做了相应的调整,进行选关操作,难度层层递进,每攻下一关都有很大的成就感。如果闲着无聊的话,还可以开启竞速模式,对每一关或者某一关进行速通挑战,记录会存在电脑上,可以通过record按钮查看,对于你个人的反应还有规划的训练真是一个不错的选择,这么想想还真是不错呢!(≧∇≦)/

七、完整代码

import pygame
import random

# 初始化 Pygame
pygame.init()

# 缩放系数
SCALE_FACTOR = 1.6  # 1.5 倍放大

# 游戏属性常量定义
TITLE = '猫了个猫'
WIDTH, HEIGHT = (1500, 800)
TILE_WIDTH, TILE_HEIGHT = int(60 * SCALE_FACTOR), int(60 * SCALE_FACTOR)

# 设置屏幕
screen = pygame.display.set_mode((WIDTH, HEIGHT))
pygame.display.set_caption(TITLE)

# 定义颜色
WHITE = (255, 255, 255)


# 加载资源并调整尺寸
background = pygame.transform.scale(pygame.image.load('image2/back.jpeg'), (WIDTH, HEIGHT))
mask = pygame.transform.scale(pygame.image.load('image2/mask.png'), (TILE_WIDTH, TILE_HEIGHT))
end_img = pygame.transform.scale(pygame.image.load('image2/lose.jpg'), (WIDTH, HEIGHT))
win_img = pygame.transform.scale(pygame.image.load('image2/win.jpg'), (WIDTH, HEIGHT))
exit_button = pygame.transform.scale(pygame.image.load('image2/tile2.png'), (50, 50))  # 退出按钮
f = pygame.transform.scale(pygame.image.load('image2/frame6.png'), (TILE_WIDTH+35, TILE_HEIGHT+35))
frame=pygame.transform.scale(pygame.image.load('image2/frame5.jpeg'), (TILE_WIDTH+30, TILE_HEIGHT+30))
cat_select=pygame.transform.scale(pygame.image.load('image2/cat_select2.jpeg'), (WIDTH, HEIGHT))
time_man=pygame.transform.scale(pygame.image.load('image2/tile.png'), (190, 100))
record_cat=pygame.transform.scale(pygame.image.load('image2/back22.jpg'), (WIDTH, HEIGHT))

#首页加载
back_start = pygame.transform.scale(pygame.image.load('image2/cat_sleep.jpeg'), (WIDTH, HEIGHT))

# 牌堆区域(DOCK)
# DOCK = pygame.Rect(int(90 * SCALE_FACTOR), int(564 * SCALE_FACTOR), TILE_WIDTH * 7, TILE_HEIGHT)
DOCK = pygame.Rect(600,600,TILE_WIDTH * 7, TILE_HEIGHT)

#按钮位置
exit_button_rect = exit_button.get_rect(topleft=(WIDTH - 50- 10, 10))  # 右上角
# exit_button_rect = exit_button.get_rect(topleft=(WIDTH , 10))  # 右上角

docks = []


def level_select_s(level):
    if level ==3:
        return 3,1,list(range(1, 6)) * 3
    elif level ==1:
        return 6,5,list(range(1, 3)) * 48
    elif level ==2:
        return 7,4,list(range(1, 3)) * 72
    elif level ==4:
        return 7,4,list(range(1, 4)) * 48
    elif level ==5:
        return 6,4,list(range(1, 5)) * 24
    elif level ==6:
        return 7,4,list(range(1,5)) * 36
    elif level ==7:
        return 5,5,list(range(1, 6)) * 12
    elif level ==8:
        return 7,4,list(range(1,7)) * 24
    elif level ==9:
        return 6,5,list(range(1, 9)) * 12
    elif level ==10:
        return 5,5,list(range(1, 11)) * 6
    elif level ==11:
        return 4,3,list(range(1, 12)) * 3
    elif level ==12:
        return 7,4,list(range(1,13)) * 12



def make_titles(level):



    # 游戏中的所有牌及牌堆
    tiles = []

    # level=3

    l,leftover,tile_numbers=level_select_s(level)



    random.shuffle(tile_numbers)

    # 创建牌
    tile_index = 0
    for layer in range(l):  # 7层
        for row in range(l - layer):  # 每层行数递减
            for col in range(l - layer):
                tile_type = tile_numbers[tile_index]
                tile_index += 1
                tile = {
                    "image": pygame.transform.scale(pygame.image.load(f'cats/cat{tile_type}.jpg'), (TILE_WIDTH, TILE_HEIGHT)),
                    "pos": [int((20 + (layer * 0.5 + col) * 60) * SCALE_FACTOR), int((30 + (layer * 0.5 + row) * 66 * 0.9) * SCALE_FACTOR)],  # 放大位置
                    "tag": tile_type,
                    "layer": layer,
                    "status": 1 if layer == l-1 else 0  # 最上层可点击
                }
                tiles.append(tile)

    # 剩余的4张牌放置到牌堆底部
    for i in range(leftover):
        tile_type = tile_numbers[tile_index]
        tile_index += 1
        tile = {
            "image": pygame.transform.scale(pygame.image.load(f'cats/cat{tile_type}.jpg'), (TILE_WIDTH, TILE_HEIGHT)),
            "pos": [870+i*60*SCALE_FACTOR, 400],  # 放大位置
            "tag": tile_type,
            "layer": 0,
            "status": 1
        }
        tiles.append(tile)
    return tiles

# 加载背景音乐
pygame.mixer.music.load('music/bgm.mp3')
pygame.mixer.music.play(-1)  # 循环播放

# 游戏帧绘制函数
def draw(elapsed_time):
    screen.blit(background, (0, 0))  # 绘制背景
    # screen.blit(f, (750,DOCK.y))
    for tile in tiles:
        screen.blit(frame,(tile["pos"][0]-15,tile["pos"][1]-15))
        screen.blit(tile['image'], tile['pos'])  # 绘制牌
        if tile['status'] == 0:
            screen.blit(mask, tile['pos'])  # 不可点击的牌显示遮罩

    for i in range(7):
        dock_pos = (755+i * (TILE_WIDTH+10),590)
        screen.blit(pygame.transform.scale(f,(109,109)), dock_pos)

    for i, tile in enumerate(docks):

        # dock_pos = (DOCK.x + i * TILE_WIDTH+173, DOCK.y+6)
        dock_pos = (i * (TILE_WIDTH+10)+755+15,590+15)

        screen.blit(pygame.transform.scale(tile['image'],(80,80)), dock_pos)

    screen.blit(time_man, (990, 30))
    screen.blit(exit_button, exit_button_rect.topleft)


# 鼠标点击处理
def handle_mouse_click(pos):
    global docks

    if exit_button_rect.collidepoint(pos):
        pygame.quit()
        return

    if len(docks) >= 7 or len(tiles) == 0:  # 游戏结束时不处理点击
        # game_over = True
        return


    for tile in reversed(tiles):  # 从最上层的牌开始判断
        rect = pygame.Rect(tile['pos'][0], tile['pos'][1], TILE_WIDTH, TILE_HEIGHT)
        if tile['status'] == 1 and rect.collidepoint(pos):
            tile['status'] = 2
            tiles.remove(tile)

            unmatched_tiles = [t for t in docks if t['tag'] != tile['tag']]

            if len(docks) - len(unmatched_tiles) < 2:
                docks.append(tile)
            else:
                docks = unmatched_tiles

            for down_tile in tiles:
                if down_tile['layer'] == tile['layer'] - 1 and pygame.Rect(down_tile['pos'][0], down_tile['pos'][1],
                                                                           TILE_WIDTH, TILE_HEIGHT).colliderect(
                        pygame.Rect(tile['pos'][0], tile['pos'][1], TILE_WIDTH, TILE_HEIGHT)):
                    for up_tile in tiles:
                        if up_tile['layer'] == down_tile['layer'] + 1 and pygame.Rect(up_tile['pos'][0],
                                                                                      up_tile['pos'][1], TILE_WIDTH,
                                                                                      TILE_HEIGHT).colliderect(
                                pygame.Rect(down_tile['pos'][0], down_tile['pos'][1], TILE_WIDTH, TILE_HEIGHT)):
                            break
                    else:
                        down_tile['status'] = 1  # 如果没有其他牌覆盖,则变为可点击状态

            break



BUTTON_WIDTH = 280
BUTTON_HEIGHT = 75
BUTTON_COLOR = (218, 198, 165)  # 绿色按钮
BUTTON_TEXT_COLOR = (255, 255, 255)  # 白色文字

# 定义按钮的矩形区域
button_rect = pygame.Rect((WIDTH // 2 - BUTTON_WIDTH // 2, HEIGHT // 2 - BUTTON_HEIGHT // 2-50), (BUTTON_WIDTH, BUTTON_HEIGHT))
button_record = pygame.Rect((WIDTH // 2 - BUTTON_WIDTH // 2, HEIGHT // 2 - BUTTON_HEIGHT // 2+40), (BUTTON_WIDTH, BUTTON_HEIGHT))


def draw_start(screen, mouse_pos, is_clicked):

    screen.blit(back_start, (0, 0))

    # 检查鼠标是否在按钮范围内
    if button_rect.collidepoint(mouse_pos):
        if is_clicked:
            color1 = (100, 200, 100)  # 点击时颜色
        else:
            color1 = (206, 179, 131)  # 悬停时颜色
    else:
        color1 = BUTTON_COLOR  # 正常按钮颜色

    if button_record.collidepoint(mouse_pos):
        if is_clicked:
            color2 = (100, 200, 100)  # 点击时颜色
        else:
            color2 = (206, 179, 131)  # 悬停时颜色
    else:
        color2 = BUTTON_COLOR  # 正常按钮颜色



    # 绘制按钮的外边框
    border_color = (0, 0, 0)  # 边框颜色
    pygame.draw.rect(screen, border_color, button_rect, border_radius=15)  # 边框
    # 绘制按钮本身(带圆角)
    pygame.draw.rect(screen, color1, button_rect.inflate(-4, -4), border_radius=15)  # -4让边框可见
    # 创建并绘制按钮文字
    font = pygame.font.SysFont(None, 36)
    text = font.render("Start !", True, BUTTON_TEXT_COLOR)
    text_rect = text.get_rect(center=button_rect.center)
    screen.blit(text, text_rect)  # 显示文字


    # 绘制按钮的外边框
    border_color = (0, 0, 0)  # 边框颜色
    pygame.draw.rect(screen, border_color, button_record, border_radius=15)  # 边框
    # 绘制按钮本身(带圆角)
    pygame.draw.rect(screen, color2, button_record.inflate(-4, -4), border_radius=15)  # -4让边框可见
    # 创建并绘制按钮文字
    font = pygame.font.SysFont(None, 36)
    text = font.render("Record", True, BUTTON_TEXT_COLOR)
    text_rect = text.get_rect(center=button_record.center)
    screen.blit(text, text_rect)  # 显示文字





# 定义按钮的宽度、高度和颜色
BUTTON_WIDTH_b = 200
BUTTON_HEIGHT_b = 60
BUTTON_COLOR2 = (0, 255, 0)  # 按钮颜色
BUTTON_TEXT_COLOR2 = (255, 255, 255)  # 文字颜色

# 回到主页按钮的位置
home_button_rect = pygame.Rect(10, 10, BUTTON_WIDTH_b, BUTTON_HEIGHT_b)

# 退出游戏按钮的位置
exit_button_rect2 = pygame.Rect(250, 10, BUTTON_WIDTH_b, BUTTON_HEIGHT_b)


def draw_game_over_screen(screen, mouse_pos, is_clicked):


    # 游戏结束判定
    if len(docks) >= 7:
        screen.blit(end_img, (0, 0))  # 绘制游戏结束图
    elif len(tiles) == 0:
        screen.blit(win_img, (0, 0))  # 绘制胜利图


    # 检查鼠标是否在按钮范围内
    if home_button_rect.collidepoint(mouse_pos):
        if is_clicked:
            color = (100, 200, 100)  # 点击时颜色
        else:
            color = (206, 179, 131)  # 悬停时颜色
    else:
        color = BUTTON_COLOR  # 正常按钮颜色

    # 检查鼠标是否在按钮范围内
    if exit_button_rect2.collidepoint(mouse_pos):
        if is_clicked:
            color2 = (100, 200, 100)  # 点击时颜色
        else:
            color2 = (206, 179, 131)  # 悬停时颜色
    else:
        color2 = BUTTON_COLOR  # 正常按钮颜色



    # 绘制回到主页按钮
    pygame.draw.rect(screen, color, home_button_rect)
    font = pygame.font.SysFont(None, 36)
    home_text = font.render("Home", True, BUTTON_TEXT_COLOR2)
    home_text_rect = home_text.get_rect(center=home_button_rect.center)
    screen.blit(home_text, home_text_rect)

    # 绘制退出游戏按钮
    pygame.draw.rect(screen, color2, exit_button_rect2)
    exit_text = font.render("Exit", True, BUTTON_TEXT_COLOR2)
    exit_text_rect = exit_text.get_rect(center=exit_button_rect2.center)
    screen.blit(exit_text, exit_text_rect)




def handle_game_over_click(pos):
    global selected_level
    if home_button_rect.collidepoint(pos):
        # 玩家点击了回到主页按钮
        reset_game(selected_level)  # 重置游戏状态并返回主页
    elif exit_button_rect2.collidepoint(pos):
        # 玩家点击了退出游戏按钮
        pygame.quit()
        exit()

# 定义按钮的宽度、高度、颜色和关卡数
LEVEL_BUTTON_WIDTH_s = 200
LEVEL_BUTTON_HEIGHT_s = 60
BUTTON_COLOR3 = (0, 255, 0)
BUTTON_TEXT_COLOR3 = (255, 255, 255)

# 定义关卡数量
TOTAL_LEVELS_R = 3
TOTAL_LEVELS_C = 4

# 创建关卡按钮的矩形
level_buttons = []
for i in range(TOTAL_LEVELS_R):
    for j in range(TOTAL_LEVELS_C):

        # button_rect = pygame.Rect(WIDTH // 2 - LEVEL_BUTTON_WIDTH_s // 2, 200 + i * (LEVEL_BUTTON_HEIGHT_s + 20), LEVEL_BUTTON_WIDTH_s, LEVEL_BUTTON_HEIGHT_s)
        button_rect3 = pygame.Rect(200+j*(LEVEL_BUTTON_WIDTH_s+100), 250 + i * (LEVEL_BUTTON_HEIGHT_s + 20), LEVEL_BUTTON_WIDTH_s, LEVEL_BUTTON_HEIGHT_s)
        level_buttons.append(button_rect3)


def draw_level_select_screen(screen,mouse_pos, is_clicked):
    screen.blit(cat_select, (0, 0))


    font = pygame.font.SysFont(None, 36)

    # 绘制每个关卡按钮
    for i, button_rect in enumerate(level_buttons):

        # 检查鼠标是否在按钮范围内
        if button_rect.collidepoint(mouse_pos):
            if is_clicked:
                color = (100, 200, 100)  # 点击时颜色
            else:
                color = (206, 179, 131)  # 悬停时颜色
        else:
            color = BUTTON_COLOR  # 正常按钮颜色

        pygame.draw.rect(screen, color, button_rect)
        level_text = font.render(f"Level {i + 1}", True, BUTTON_TEXT_COLOR)
        text_rect = level_text.get_rect(center=button_rect.center)
        screen.blit(level_text, text_rect)

def draw_record(screen):
    screen.blit(record_cat, (0, 0))  # 绘制背景

    font = pygame.font.SysFont(None, 50)

    # 遍历 record 列表,逐行显示内容
    for i, entry in enumerate(record):
        text = font.render(f"record{i + 1}    level:{entry[0]}    time:{entry[1]}s", True, (255, 255, 255))  # 将 record 里的内容绘制成文字
        screen.blit(text, (125, 100 + i * 40))  # 将每个记录绘制到不同的位置,避免重叠


def handle_level_select_click(pos):
    global selected_level, level_select

    for i, button_rect in enumerate(level_buttons):
        if button_rect.collidepoint(pos):
            selected_level = i + 1  # 玩家选择的关卡
            reset_game(selected_level)  # 重置游戏状态
            # game_started = True  # 开始游戏
            level_select = False
            # print(selected_level)
            break

def handle_record(pos):
    global show_record

    if button_record.collidepoint(pos):
        show_record = True


def reset_game(level):
    global game_started, game_over, tiles, docks, start_time
    game_started = False
    game_over = False
    tiles = make_titles(level)  # 重新初始化游戏的牌组
    docks = []  # 重置牌堆
    start_time = None  # 重置计时器


def main():
    global level_select,tiles,selected_level,record,show_record
    pygame.init()
    clock = pygame.time.Clock()
    running = True
    game_started = False  # 游戏是否开始标志
    game_over = False  # 游戏结束标志
    start_time = None  # 计时起始时间
    # level_select = True
    level_select = False
    selected_level=0#选择关卡
    tiles = make_titles(selected_level+1)
    record=[]
    elapsed_time2=0
    time_record_control=False
    show_record=False
    # count=0
    while running:
        screen.fill(WHITE)
        mouse_pos = pygame.mouse.get_pos()
        is_clicked = False


        for event in pygame.event.get():
            if event.type == pygame.QUIT:
                running = False
            elif event.type == pygame.MOUSEBUTTONDOWN:
                if game_over:
                    handle_game_over_click(event.pos)
                    game_over = False
                    game_started = False
                    level_select=True
                elif not game_started and button_rect.collidepoint(event.pos):
                    game_started = True
                    level_select=True
                elif not show_record and button_record.collidepoint(event.pos):
                    show_record = True
                elif game_started and level_select:
                    handle_level_select_click(event.pos)
                    start_time = pygame.time.get_ticks()  # 开始计时
                elif level_select:
                    handle_record(event.pos)
                elif game_started and not level_select:
                    handle_mouse_click(event.pos)

        if len(docks) >= 7 or len(tiles) == 0:
            level_select=False
            game_over = True  #


        if len(tiles) == 0 and time_record_control:

            record.append((selected_level, elapsed_time2))

            time_record_control = False


        if not game_started:
            draw_start(screen, mouse_pos, is_clicked)

        elif game_over:
            draw_game_over_screen(screen, mouse_pos, is_clicked)
        elif show_record:
            draw_record(screen)

        elif level_select:
            draw_level_select_screen(screen, mouse_pos, is_clicked)

        else:
            draw(tiles)  # 绘制游戏内容
            time_record_control = True
            elapsed_time = (pygame.time.get_ticks() - start_time) // 1000  # 计算经过时间
            elapsed_time2 = round((pygame.time.get_ticks() - start_time) / 1000,3)

            font = pygame.font.SysFont(None, 60)
            time_text = font.render(f"Time: {elapsed_time}s", True, (0, 0, 0))
            screen.blit(time_text, (1000, 50))  # 显示计时器


        # print(record)
        pygame.display.update()
        clock.tick(60)  # 控制帧率


    pygame.quit()


if __name__ == "__main__":
    main()
posted @ 2024-09-18 23:57  starryship  阅读(47)  评论(0编辑  收藏  举报