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

继续来研究怎么实现消除后的下移,大体思想都已经说了,开始编写函数。

先找到第一轮需要下移的元素。

def find_fall_stars(board):
    all_stars = []
    single_star = {}
    find_fall_star = False
    for column in range(columns):
        # 从底部往上找,每一个减少的row值都相当于往上走一行
        for row in range(rows-2, -1, -1):
            # 此位置星星可以下降,记录,使用字典记录类型,处于二维列表中的位置,然后放入all_stars中
            if board[row][column] != 'empty' and board[row+1][column] == 'empty':
                print(row, column)
                single_star['type'] = board[row][column]
                single_star['x'] = row
                single_star['y'] = column
                all_stars.append(single_star)
                single_star = {}
                find_fall_star = True
    # 返回当前找到的星星,同时返回标志位,为下次寻找作准备
    return find_fall_star, all_stars

先做一个单独往下移动一个固定位置的函数。

def one_column_falling_stars(board, star, speed):
    move_x = 0
    speed *= 0.01
    move_y = int(speed * tile_size)
    basex = star['x']
    basey = star['y']
    left_location = x_margin / 2 + (basey * tile_size) + 3
    top_location = y_margin / 2 + (basex * tile_size) + 3
    r = pygame.Rect((left_location + move_x, top_location + move_y, tile_size, tile_size))
    path = 'gems/' + board[basex][basey] + '.png'
    star = pygame.image.load(path)
    star = pygame.transform.scale(star, (tile_size - 5, tile_size - 5))
    display.blit(star, r)

给出要移动的星星坐标,因为必定是往下移动,所以最终偏差出现在y坐标上,同时speed即每次画出的图形下移的偏差,speed越大,看着移动越快。需要多次调用该函数,才能实现移动过程。

然后编写一个给定所有星星后下移一格的函数。

def animate_falling_stars(board, stars):
    speed = 0
    while speed < 110:
        display.fill(bg_color)
        draw_chest()
        draw_stars(board)
        for star in stars: # Draw each gem.
            one_column_falling_stars(board, star, speed)
        pygame.display.update()
        fps_clock.tick(fps)
        speed += 30

运行游戏,确实有移动了,但是因为这个只是动画过程,那么实际二维列表内容并没有发生变化,所以游戏界面出现的不是移动,而像是复制后移动,并且如果此刻重新画图,所有界面都恢复原样了。

请添加图片描述

现在考虑在绘制动画前先将二维列表里需要移动位置的星星删除,等动画完成后,把星星放到指定位置,完成数据交换,然后再查找下一轮可以移动的星星。

修改查找星星的函数,增加数据删除部分内容。这个数据处理过程隐隐觉得不是太妥…

def find_fall_stars(board):
    all_stars = []
    single_star = {}
    find_fall_star = False
    for column in range(columns):
        # 从底部往上找,每一个减少的row值都相当于往上走一行
        for row in range(rows-2, -1, -1):
            # 此位置星星可以下降,记录,使用字典记录类型,处于二维列表中的位置,然后放入all_stars中
            if board[row][column] != 'empty' and board[row+1][column] == 'empty':
                print(row, column)
                single_star['type'] = board[row][column]
                single_star['x'] = row
                single_star['y'] = column
                # 先从二维列表中清除原来的类型,主要是绘制动画需要
                board[row][column] = 'empty'
                all_stars.append(single_star)
                single_star = {}
                find_fall_star = True
    # 返回当前找到的星星,同时返回标志位,为下次寻找作准备
    return find_fall_star, all_stars

当动画完成后,需要将动画结束位置的星星类型放入实际的数据中。

def animate_falling_stars(board, stars):
    speed = 0
    while speed < 120:
        display.fill(bg_color)
        draw_chest()
        draw_stars(board)
        for star in stars: # Draw each gem.
            one_column_falling_stars(board, star, speed)
        pygame.display.update()
        fps_clock.tick(fps)
        speed += 30
    for star in stars:
        # 下移完成后交换数据
        board[star['x']+1][star['y']] = star['type']

因为当前只下移了一行,所以循环调用,直到没有可以移动的为止。

                    if remove_or_not:
                        remove_stars(main_board, need_to_removed)
                        while True:
                            print(main_board)
                            falling_or_not, falling_stars = find_fall_stars(main_board)
                            if falling_or_not:
                                animate_falling_stars(main_board, falling_stars)
                            else:
                                break

运行程序看看效果。

请添加图片描述

可以看到,效果还可以,因为没有编写补充星星的函数,所以会有空的。

秉承着消除几个星星增加几个星星的原则,我们开始增加新的星星。增加的过程也需要动画效果。

关于下落星星的思路是,根据empty位置蛇形寻找当前需要第一次需要下落的星星数,并随机产生星星,星星下标[x][-1]的位置,也就是游戏界面之上下落。完成当前下落后,再次寻找游戏界面的empty位置,重复之前的操作。

def show_new_fall_stars(board):
    limit_x = -1
    signal_star = {}
    new_stars = []
    # 从最后一行开始寻找
    for row in range(rows-1, -1, -1):
        # 每一行从左往右找
        for column in range(columns):
            # 找到当前最下面一行,最左边的空白点
            if board[row][column] == 'empty' and row > limit_x:
                signal_star['type'] = random.choice(stars)
                signal_star['x'] = -1
                signal_star['y'] = column
                new_stars.append(signal_star)
                signal_star = {}
    return new_stars

然后再循环里检查是否需要补空。

        if len(new_falling_stars):
            animate_falling_stars(main_board, new_falling_stars)
            while True:
                falling_or_not, falling_stars = find_fall_stars(main_board)
                if falling_or_not:
                    animate_falling_stars(main_board, falling_stars)
                else:
                    break

当完成第一次补空后,那么情况类似于之前消除的情况,所以使用与消除一样的方法,再次下落。完成一次这样的操作后,循环还会继续补空,直到所有的空位填补完成。

运行游戏,效果如下:

请添加图片描述

抛开已知未知bug不谈,目前我感觉已经进入尾声。接下来需要做的就是设定一种机制,让游戏结束。那么比较简单的就是采用定时机制,比如游戏有60秒时间游玩,看游玩过程中最多能拿多少分。可以考虑如果一次消除6个以上的星星,增加一点时间。还有就是,如果发生完全没法消除的情况,又该如何?

考虑有可能出现完全无法消除的情况,那么可以再游戏里增加检查机制,可以在游戏过程中,不断检查游戏状态,利用之前的消除算法,不停查找是否还有可以消除的星星,如果没有了,则重新生成棋盘。这个很简单,就不做了,我稍微偷个懒,修改一下游戏机制,即只要有2个以上相同的星星,就可以消除,那么游戏就可能出现无法消除的情况。把只消除2个星星的情况设定为无法得分或者扣分即可。

到此,剩下的部分就完全没有任何算法内容和难度了。就不放出来具体过程了,直接看图。

请添加图片描述

猜你喜欢

转载自blog.csdn.net/jackwsd/article/details/126555688