继续来研究怎么实现消除后的下移,大体思想都已经说了,开始编写函数。
先找到第一轮需要下移的元素。
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个星星的情况设定为无法得分或者扣分即可。
到此,剩下的部分就完全没有任何算法内容和难度了。就不放出来具体过程了,直接看图。