• 关于用pygame来编写类满天星游戏的全记录


    编写类满天星游戏,目前自己设定需要用到的算法内容,包括第一点点击某一个图标后,自动检查图标上下左右是有与之相同的图形,如果包括自身在内有三个以上的图形,则可以消除。第二,当界面中有图形消除后,那么需要根据列来下移图标,同时产生新的图标。第三,自动检查界面中是否还有能够匹配的图案,如果没有,那么重新生成游戏。第四,游戏机制设定为倒计时,在倒计时完成前,尽可能多消除,多得分。并且考虑每次消除4个以上,按比例增加时间。

    首先完成最简单的部分,配置pygame的基本参数,并且绘制游戏界面,也就是画横纵线。

    考虑把游戏界面画成一个10*10的方格,并且在上方留出空位显示等分等信息,具体还需不需要别的,待定…

    直接使用之前贪食蛇的画格子和初始化的代码,先略作修改。

    import random
    import time
    import pygame
    
    # 30帧
    fps = 30
    fps_clock = pygame.time.Clock()
    screen_width = 1024
    screen_height = 768
    # 分辨率,标题
    display = pygame.display.set_mode((screen_width, screen_height), 0, 32)
    pygame.display.set_caption('好似满天星')
    tile_size = 60
    tile_width = 60
    tile_height = 60
    
    
    x_margin = 400
    y_margin = 100
    
    # 列
    columns = 10
    # 行
    rows = 11
    
    # 配色RGB
    white = (255, 255, 255)
    black = 'black'
    bg_color = 'sky blue'
    border_color = white
    body_color = 'purple'
    inter_body_color = 'green'
    line_color = white
    directions = ['up', 'down', 'left', 'right']
    
    
    text_color = (144, 59, 28)
    my_score = 0
    snake = []
    
    
    # 画格子
    def draw_chest():
        for i in range(rows + 1):
            pygame.draw.line(display, border_color, (x_margin/2, y_margin/2 + i * tile_size),
                             (x_margin/2 + (columns * tile_size), y_margin/2 + i * tile_size), 2)
    
        for j in range(columns + 1):
            pygame.draw.line(display, border_color, (x_margin/2 + j * tile_size, y_margin/2),
                             (x_margin/2 + j * tile_size, y_margin/2 + (rows * tile_size)), 2)
    
    
    if __name__ == '__main__':
        pygame.init()
        bad_touch = pygame.mixer.Sound('badswap.wav')
        match_three = pygame.mixer.Sound('match3.wav')
        match_more_than_three = pygame.mixer.Sound('match4.wav')
        display.fill(bg_color)
        while True:
            draw_chest()
            for event in pygame.event.get():
                if event.type == pygame.QUIT:
                    pygame.quit()
            pygame.display.update()
            fps_clock.tick(fps)
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65

    目前不知道后续还有些什么需求,所以简单修改一下,后期有需要再来修改代码。

    运行结果如下:

    请添加图片描述

    有一个大体的框架了,接下来就是把星星放到游戏界面当中来了。嗯,因为我们有星星,所以我找了免费的饼干来代替星星。这些饼干也是五子连珠的棋子😄。

    请添加图片描述

    一共有七种饼干,考虑到游戏的难度,我们一开始只使用其中的四种。

    把之前snake = []删除,新创建一个列表,用来存放"星星"

    stars = [‘Cookie’, ‘Croissant’, ‘Cupcake’, ‘Danish’]

    在方框内放满“星星”。

    生成一个二维列表,列表中随机放入“星星”

    def make_stars():
        board = []
        for i in range(rows):
            temp = []
            for j in range(columns):
                star = random.choice(stars)
                temp.append(star)
            board.append(temp)
        return board
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

    还需要绘制星星的函数,在绘制星星之前呢,需要确定星星的位置,同时还是按照下标来计算游戏界面横纵坐标,那么:

    def get_left_top_location(x, y):
        left_position = y * tile_size + x_margin/2
        top_position = x * tile_size + y_margin/2
        return left_position, top_position
    
    
    def draw_stars(board):
        for i in range(len(board)):
            for j in range(len(board[i])):
                path = 'gems/' + board[i][j] + '.png'
                star = pygame.image.load(path)
                star = pygame.transform.scale(star, (tile_size-5, tile_size-5))
                left, top = get_left_top_location(i, j)
                # 微调一下,这样图片比较居中,谁让钉在左上角呢
                display.blit(star, (left+3, top+3))
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15

    在主程序中增加绘制星星代码

    if __name__ == '__main__':
        pygame.init()
        bad_touch = pygame.mixer.Sound('badswap.wav')
        match_three = pygame.mixer.Sound('match3.wav')
        match_more_than_three = pygame.mixer.Sound('match4.wav')
        display.fill(bg_color)
        main_board = make_stars()
        print(main_board)
        while True:
            draw_chest()
            draw_stars(main_board)
            for event in pygame.event.get():
                if event.type == pygame.QUIT:
                    pygame.quit()
            pygame.display.update()
            fps_clock.tick(fps)
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16

    运行程序

    请添加图片描述

    感觉“星星”太少了,所以还是再加点吧。

    stars = [‘Cookie’, ‘Croissant’, ‘Cupcake’, ‘Danish’, ‘Donut’, ‘Ginger’, ‘Macaroon’]

    请添加图片描述

    感觉好多了。接下来开始消除吧。

    消除部分,比较简单,需要编写的内容主要是当鼠标点击时,我们需要知道是哪个位置的哪个“星星”被点击了,也就是说我们需要定位到main_board的具体下标是多少。是[0][0],[1][2],或者是别的。然后我们确定位置和“星星后”,找到该位置“星星”的上下左右,看能否找到相同的“星星”,如果有且数量大于或者等于3,那么可以实现消除。同时为了以后动画效果,我准备使用一个列表中带字典变量的方式来存储这些相同的“星星”的位置和种类信息。

    先完成第一步,对应二维列表下标。

    # 将游戏中的界面坐标转换为main_board中的位置
    def get_spot_clicked(board, x, y):
        for i in range(len(board)):
            for j in range(len(board[i])):
                # 注意在二维数组里我们的查询顺序是比如maze[3][5]代表第四行第六列,但是对应游戏界面的坐标则3用来计算y坐标,5用来计算x坐标
                left, top = get_left_top_location(i, j)
                tile_rect = pygame.Rect(left, top, tile_width, tile_height)
                if tile_rect.collidepoint(x, y):
                    return i, j
        return None, None
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10

    然后在主循环中加入鼠标响应代码。根据鼠标点击位置,打印出二维列表下标,看看效果。

                if event.type == pygame.QUIT:
                    pygame.quit()
                elif event.type == pygame.MOUSEBUTTONUP:
                    # 鼠标左键点击后抬起
                    if event.button == 1:
                        pos_x, pos_y = event.pos
                        print(get_spot_clicked(main_board, pos_x, pos_y))
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    实际测试,鼠标点击位置和二维列表下标匹配。

    接下来则需要开始寻找点击位置四个方向上是否有相同"星星"。

    # 查找"星星"的相同情况
    def check_around(board, star_pos):
        store_stars = set()
        has_three_stars = False
        shape_type = board[star_pos[0]][star_pos[1]]
        # 查找上下两个方向是否有与当前棋子类型一样的棋子
        store_stars.add(star_pos)
        # 向上的方向上寻找
        star_i = star_pos[0] - 1
        up_and_down_star = set()
        while star_i >= 0 and board[star_i][star_pos[1]] == shape_type:
            store_stars.add((star_i, star_pos[1]))
            star_i -= 1
        # 向下的方向寻找
        star_i = star_pos[0] + 1
        while star_i < rows and board[star_i][star_pos[1]] == shape_type:
            store_stars.add((star_i, star_pos[1]))
            star_i += 1
        # 向左的方向上寻找
        star_j = star_pos[1] - 1
        while star_j >= 0 and board[star_pos[0]][star_j] == shape_type:
            store_stars.add((star_pos[0], star_j))
            star_j -= 1
        # 向右的方向寻找
        star_j = star_pos[1] + 1
        while star_j < columns and board[star_pos[0]][star_j] == shape_type:
            store_stars.add((star_pos[0], star_j))
            star_j += 1
        if len(store_stars) >= 3:
            has_three_stars = True
        return store_stars, has_three_stars
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31

    该函数返回所有需要消除的点的二维列表下标。再编写一个函数用于清除对坐标的二维列表中的内容。把需要清除的位置设定为empty。然后在主循环中调用。

    def remove_stars(board, removed_stars):
        for star in removed_stars:
            board[star[0]][star[1]] = 'empty'
    
    • 1
    • 2
    • 3
                    if event.button == 1:
                        pos_x, pos_y = event.pos
                        x, y = get_spot_clicked(main_board, pos_x, pos_y)
                        need_to_removed, remove_or_not = check_around(main_board, (x, y))
                        if remove_or_not:
                            remove_stars(main_board, need_to_removed)
                            display.fill(bg_color)
                            draw_chest()
                            draw_stars(main_board)
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

    运行程序。

    程序报错了,因为绘制“星星”的函数并没有考虑到二维列表的内容为empty的情况,所以需要修改这个函数内容。

    def draw_stars(board):
        for i in range(len(board)):
            for j in range(len(board[i])):
                if board[i][j] != 'empty':
                    path = 'gems/' + board[i][j] + '.png'
                    star = pygame.image.load(path)
                    star = pygame.transform.scale(star, (tile_size-5, tile_size-5))
                    left, top = get_left_top_location(i, j)
                    display.blit(star, (left+3, top+3))
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

    运行程序
    请添加图片描述
    确实能消除了,但是出现了一个意外情况,比如上图[4][3]这个位置的甜甜圈有三个相同的图形,我所编写的算法里,只是检查了点击处的上下左右,那么如果我点击[4][3]这个位置,[4][2],[4][3],[5][3]的内容都会消失,这个没问题,但是如果我点击的是[5][3],那么根据算法,该位置上有左右仅有一个相同的,所以不会发生消除情况。这貌似与满天星游戏方式不同,所以,我们需要修改消除算法。算法应该修改为从一个位置出发,寻找上下左右相同的图标,如果找到,再以该位置作为中心,继续寻找上下左右是否有相同点,直到没有为止。可以考虑使用回溯+栈的方式来修改算法。

    # 查找"星星"的相同情况
    def check_around(board, star_pos):
        stars_temp = []
        store_stars = set()
        has_three_stars = False
        shape_type = board[star_pos[0]][star_pos[1]]
        # 查找上下两个方向是否有与当前棋子类型一样的棋子
        store_stars.add(star_pos)
        stars_temp.append(star_pos)
        while len(stars_temp):
            current_star = stars_temp[0]
            del stars_temp[0]
            # 向上的方向上寻找
            star_i = current_star[0] - 1
    
            while star_i >= 0 and board[star_i][current_star[1]] == shape_type:
                if (star_i, current_star[1]) not in store_stars:
                    store_stars.add((star_i, current_star[1]))
                    stars_temp.append((star_i, current_star[1]))
                star_i -= 1
            # 向下的方向寻找
            star_i = current_star[0] + 1
            while star_i < rows and board[star_i][current_star[1]] == shape_type:
                if (star_i, current_star[1]) not in store_stars:
                    store_stars.add((star_i, current_star[1]))
                    stars_temp.append((star_i, current_star[1]))
                star_i += 1
            # 向左的方向上寻找
            star_j = current_star[1] - 1
            while star_j >= 0 and board[current_star[0]][star_j] == shape_type:
                if (current_star[0], star_j) not in store_stars:
                    store_stars.add((current_star[0], star_j))
                    stars_temp.append((current_star[0], star_j))
                star_j -= 1
            # 向右的方向寻找
            star_j = current_star[1] + 1
            while star_j < columns and board[current_star[0]][star_j] == shape_type:
                if (current_star[0], star_j) not in store_stars:
                    store_stars.add((current_star[0], star_j))
                    stars_temp.append((current_star[0], star_j))
                star_j += 1
            if len(store_stars) >= 2:
                has_three_stars = True
        #print('stars: ', store_stars)
        #input()
    
        return store_stars, has_three_stars
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47

    修改以后,从任意点击位置开始,都可以发现所有相同“星星了”

    马上开始比较麻烦的地方了,动画下移。

    关于动画下移的思路则是首先,按列寻找需要下移的“星星”,找到以后,无论这个星星(不加引号了,太麻烦),下面有多少空间都只移动一格,找到所有的需要移动的星星,动画移动一格,然后再寻找可以移动的星星,移动一格,再寻找,直到没有可以移动的为止。

    待续…

  • 相关阅读:
    【muduo源码剖析】Poller/EPollPoller设计分析
    SpringBoot-16-模块开发-首页登陆并权限拦截
    Java泛型的总结
    LeetCode【第2575题】
    口碑超好的挂耳式耳机盘点,这几款蓝牙耳机值得一看!
    Linux基础系列(三)——压缩命令、网络命令、关机重启
    MySQL 的 InnoDB 存储引擎简介
    & 和 && 的区别。| 和 || 的区别
    进阶版JavaScript学习-第三期
    加权平均的重要作用
  • 原文地址:https://blog.csdn.net/jackwsd/article/details/126547486