当前位置:   article > 正文

【Python小游戏】面向对象编程,使用tkinter制作一个俄罗斯方块小游戏_python tkinter实现俄罗斯方块

python tkinter实现俄罗斯方块

目录

一. 游戏介绍

二. 代码重点

2.1 格子矩阵

2.2 当前方块

2.3 方块位置

2.4 绑定键盘事件

2.5 分数计算

三. 代码


一. 游戏介绍

《俄罗斯方块》(Tetris,俄文:Тетрис)是一款由俄罗斯人阿列克谢·帕基特诺夫于1984年6月发明的休闲游戏。

该游戏曾经被多家公司代理过。经过多轮诉讼后,该游戏的代理权最终被任天堂获得。任天堂对于俄罗斯方块来说意义重大,因为将它与GB搭配在一起后,获得了巨大的成功。

《俄罗斯方块》的基本规则是移动、旋转和摆放游戏自动输出的各种方块,使之排列成完整的一行或多行并且消除得分。


二. 代码重点

2.1 格子矩阵

        程序中的一个核心变量是记录棋盘位置的numpy.ndarray对象grid,这里是用来记录每个格子的值,用以在前端画图的时候展示,四面的墙的值为-1,中间空间里面,空白的地方的值为0,有方块的位置的值为方块所对应的数值(大于0),程序通过判断grid里面的数值来对tkinter前端进行不同颜色的填充。

2.2 当前方块

        另一个非常重要的变量是this_block,表示的是当前选中的方块(BLOCK_1~BLOCK_7),每次的选择都是随机性的,BLOCK_1~BLOCK_7都是简单的类,里面主要有三个变量:BLOCK_COLOR、BLOCK_NUM、BLOCK_LOCATION,其中,BLOCK_COLOR是方块的颜色,BLOCK_NUM是方块的号码。这里最重要的是BLOCK_LOCATION,列表类型,表示的是方块的初始位置,但是由于方块会有变形这个动作,因此这里列出来了所有方块变形后对应的初始位置。同时维护一个变量change_num,通过维护change_num来改变方块的初始位置。

  1. self.BLOCK_1 = BLOCK_1()
  2. self.BLOCK_2 = BLOCK_2()
  3. self.BLOCK_3 = BLOCK_3()
  4. self.BLOCK_4 = BLOCK_4()
  5. self.BLOCK_5 = BLOCK_5()
  6. self.BLOCK_6 = BLOCK_6()
  7. self.BLOCK_7 = BLOCK_7()
  8. self.block_list = [self.BLOCK_1, self.BLOCK_2, self.BLOCK_3, self.BLOCK_4, self.BLOCK_5, self.BLOCK_6, self.BLOCK_7]
  9. self.this_block = self.block_list[random.randint(0,len(self.block_list)-1)]

2.3 方块位置

        方块移动的重点是维护一个变量this_block_location,表示的是当前方块的位置,所有的移动操作(方块的生成、自动下落、加速下落、左右移动、方块变形)都是对这个this_block_location进行修改。

        考虑到这里的方块会有变形,因此这里的this_block_location的维护方式是这样的:首先是有一个原始的位置this_block.BLOCK_LOCATION,以及对列和对行的偏移值变量block_col_offset和block_row_offset。在自动下落、加速下落中是对block_row_offset值的修改,在左右移动中是对block_col_offset的修改,在方块的生成、方块变形中,是对方块原始位置this_block.BLOCK_LOCATION的值的修改。

        This_block_location的值的构成是由this_block.BLOCK_LOCATION和block_col_offset、block_row_offset构成,构成如下:self.this_block_location = [[i[0] + self.block_col_offset, i[1] + self.block_row_offset] for i in self.this_block.BLOCK_LOCATION[self.choose_one]]

方块的初始位置图如下:

2.4 绑定键盘事件

        另一个重点是绑定键盘事件,这里的绑定键盘事件主要有三种:

  1. 左右移动。这里主要是看方块的移动是否在空间内(即不能撞墙);
  2. 向下移动。这里是看方块最终停留的位置;
  3. 方块变形。将当前的方块进行指定方向变形。

        三种都是涉及到方块当前位置的更改,其中主要要考虑的是一个碰撞检验,即移动/变形后的方块位置是否没有其他的方块相撞、或者是在内部空间内。

2.5 分数计算

        另外还有一个重要的部分是分数的计算,在俄罗斯方块中,每次消除的得分和同时消除的行数相关

连续消除1行——100分

连续消除2行——200分

连续消除3行——400分

连续消除4行——800分

        因此,这里需要判断连续消除的行数是多少。


三. 代码

  1. from tkinter import *
  2. from tkinter import messagebox
  3. import time
  4. import random
  5. import numpy as np
  6. class BLOCK_1():
  7. '''
  8. “田”字形方块
  9. '''
  10. def __init__(self):
  11. self.BLOCK_LOCATION = [[[0, 0], [0, 1], [1, 0], [1, 1]]]
  12. self.BLOCK_COLOR = 'blue'
  13. self.BLOCK_NUM = 1
  14. class BLOCK_2():
  15. '''
  16. “一”字型方块
  17. '''
  18. def __init__(self):
  19. self.BLOCK_LOCATION = [[[0, 0], [0, 1], [0, 2], [0, 3]], [[-1, 0], [0, 0], [1, 0], [2, 0]]]
  20. self.BLOCK_COLOR = 'yellow'
  21. self.BLOCK_NUM = 2
  22. class BLOCK_3():
  23. '''
  24. 反“Z”字型方块
  25. '''
  26. def __init__(self):
  27. self.BLOCK_LOCATION = [[[0, 0], [0, 1], [1, 0], [-1, 1]], [[0, 1], [0, 2], [-1, 0], [-1, 1]]]
  28. self.BLOCK_COLOR = 'pink'
  29. self.BLOCK_NUM = 3
  30. class BLOCK_4():
  31. '''
  32. “L”字型方块
  33. '''
  34. def __init__(self):
  35. self.BLOCK_LOCATION = [[[0, 0], [0, 1], [0, 2], [1, 2]], [[-1, 0], [-1, 1], [0, 0], [1, 0]],
  36. [[1, 0], [1, 1], [1, 2], [0, 0]], [[1, 0], [1, 1], [0, 1], [-1, 1]]]
  37. self.BLOCK_COLOR = 'green'
  38. self.BLOCK_NUM = 4
  39. class BLOCK_5():
  40. '''
  41. “Z”字型方块
  42. '''
  43. def __init__(self):
  44. self.BLOCK_LOCATION = [[[0, 0], [0, 1], [1, 1], [-1, 0]], [[0, 0], [0, 1], [-1, 1], [-1, 2]]]
  45. self.BLOCK_COLOR = 'purple'
  46. self.BLOCK_NUM = 5
  47. class BLOCK_6():
  48. '''
  49. 反“L”字型方块
  50. '''
  51. def __init__(self):
  52. self.BLOCK_LOCATION = [[[0, 0], [0, 1], [0, 2], [-1, 2]], [[-1, 0], [-1, 1], [0, 1], [1, 1]],
  53. [[-1, 0], [-1, 1], [-1, 2], [0, 0]], [[-1, 0], [0, 0], [1, 0], [1, 1]]]
  54. self.BLOCK_COLOR = 'orange'
  55. self.BLOCK_NUM = 6
  56. class BLOCK_7():
  57. '''
  58. “T”字型方块
  59. '''
  60. def __init__(self):
  61. self.BLOCK_LOCATION = [[[0, 0], [0, 1], [0, 2], [-1, 1]], [[0, 0], [0, 1], [1, 1], [-1, 1]],
  62. [[0, 0], [0, 1], [1, 1], [0, 2]], [[-1, 1], [0, 1], [1, 1], [0, 2]]]
  63. self.BLOCK_COLOR = 'white'
  64. self.BLOCK_NUM = 7
  65. class Tetris():
  66. def __init__(self):
  67. self.width = 360 # 界面宽度
  68. self.height = 700 # 界面高度
  69. self.grid_size = 30 # 单格子边长
  70. self.row_num = 20 # 行数
  71. self.col_num = 12 # 列数
  72. self.speed = 500 # 格子下降速度
  73. self.total_score = 0 # 总分数
  74. self.inner_row_num = self.row_num - 2 # 内部格子空间行数
  75. self.inner_col_num = self.col_num - 2 # 内部格子空间列数
  76. self.grid = np.full((self.row_num, self.col_num), 0) # 记录格子空间的矩阵
  77. self.grid_init()
  78. self.BLOCK_1 = BLOCK_1()
  79. self.BLOCK_2 = BLOCK_2()
  80. self.BLOCK_3 = BLOCK_3()
  81. self.BLOCK_4 = BLOCK_4()
  82. self.BLOCK_5 = BLOCK_5()
  83. self.BLOCK_6 = BLOCK_6()
  84. self.BLOCK_7 = BLOCK_7()
  85. self.block_list = [self.BLOCK_1, self.BLOCK_2, self.BLOCK_3, self.BLOCK_4, self.BLOCK_5, self.BLOCK_6, self.BLOCK_7]
  86. self.color_dic = {1: self.BLOCK_1.BLOCK_COLOR, 2: self.BLOCK_2.BLOCK_COLOR, 3: self.BLOCK_3.BLOCK_COLOR,
  87. 4: self.BLOCK_4.BLOCK_COLOR, 5: self.BLOCK_5.BLOCK_COLOR, 6: self.BLOCK_6.BLOCK_COLOR,
  88. 7: self.BLOCK_7.BLOCK_COLOR, 0: 'black', -1: 'grey'} # 颜色对照表
  89. self.tk = Tk()
  90. self.tk.title('俄罗斯方块')
  91. self.tk.geometry(f'{self.width}x{self.height}')
  92. self.canvas = Canvas(self.tk, width = self.width, height = self.height, background='#FFFFFF')
  93. self.canvas.pack()
  94. self.canvas.focus_set() # 聚焦
  95. self.canvas.bind("<KeyPress-Left>", self.move)
  96. self.canvas.bind("<KeyPress-Right>", self.move)
  97. self.canvas.bind("<KeyPress-Down>", self.move)
  98. self.canvas.bind("<KeyPress-Up>", self.block_change)
  99. self.draw_grid()
  100. self.start_game()
  101. self.tk.mainloop()
  102. def grid_init(self):
  103. # 墙体赋值
  104. for i in range(0, self.row_num):
  105. self.grid[i][0] = -1
  106. self.grid[i][self.col_num - 1] = -1
  107. for i in range(0, self.col_num):
  108. self.grid[0][i] = -1
  109. self.grid[self.row_num - 1][i] = -1
  110. def draw_grid(self):
  111. # 画格子
  112. for i in range(0, self.row_num):
  113. for j in range(0, self.col_num):
  114. self.canvas.create_rectangle(j * self.grid_size + 2, i * self.grid_size + 2,
  115. (j + 1) * self.grid_size - 2, (i + 1) * self.grid_size - 2,
  116. fill=self.color_dic[self.grid[i][j]])
  117. self.label_text = StringVar()
  118. self.label_text.set('当前分数:' + str(self.total_score))
  119. self.label_score = Label(self.tk, textvariable=self.label_text, font=('宋体', 20), foreground='#000000', background='#FFFFFF').place_configure(x=70, y=620)
  120. def block_change(self, event):
  121. # 方块变形
  122. tmp_change_num = self.change_num + 1
  123. tmp_choose_one = tmp_change_num % len(self.this_block.BLOCK_LOCATION)
  124. tmp_block_location = [[i[0] + self.block_col_offset, i[1] + self.block_row_offset] for i in
  125. self.this_block.BLOCK_LOCATION[tmp_choose_one]]
  126. for i in tmp_block_location:
  127. if self.grid[i[1], i[0]] != 0 and i not in self.this_block_location:
  128. return
  129. self.change_num += 1
  130. self.choose_one = self.change_num % len(self.this_block.BLOCK_LOCATION)
  131. for i in self.this_block_location:
  132. self.grid[i[1], i[0]] = 0
  133. self.this_block_location = [[i[0] + self.block_col_offset, i[1] + self.block_row_offset] for i in
  134. self.this_block.BLOCK_LOCATION[self.choose_one]]
  135. self.canvas.delete('all')
  136. for i in self.this_block_location:
  137. self.grid[i[1], i[0]] = self.this_block.BLOCK_NUM
  138. self.draw_grid()
  139. def get_random_block(self):
  140. # 获取随机的格子,开始下落
  141. self.this_block = self.block_list[random.randint(0,len(self.block_list)-1)]
  142. self.block_col_offset = 5
  143. self.block_row_offset = 1
  144. self.choose_one = 0
  145. self.this_block_location = [[i[0] + self.block_col_offset, i[1] + self.block_row_offset] for i in self.this_block.BLOCK_LOCATION[self.choose_one]]
  146. self.start_judge = True
  147. self.change_num = 0
  148. if self.if_game_end():
  149. messagebox.showinfo('提示', '游戏结束')
  150. self.tk.destroy()
  151. def move(self, event):
  152. # 键盘事件,移动
  153. if event.keysym == 'Left':
  154. next_block_location = [[i[0] - 1, i[1]] for i in self.this_block_location]
  155. for i in next_block_location:
  156. if i not in self.this_block_location and self.grid[i[1], i[0]] != 0:
  157. return
  158. for i in self.this_block_location:
  159. self.grid[i[1], i[0]] = 0
  160. self.block_col_offset -= 1
  161. self.this_block_location = [[i[0] + self.block_col_offset, i[1] + self.block_row_offset] for i in self.this_block.BLOCK_LOCATION[self.choose_one]]
  162. elif event.keysym == 'Right':
  163. next_block_location = [[i[0] + 1, i[1]] for i in self.this_block_location]
  164. for i in next_block_location:
  165. if i not in self.this_block_location and self.grid[i[1], i[0]] != 0:
  166. return
  167. for i in self.this_block_location:
  168. self.grid[i[1], i[0]] = 0
  169. self.block_col_offset += 1
  170. self.this_block_location = [[i[0] + self.block_col_offset, i[1] + self.block_row_offset] for i in self.this_block.BLOCK_LOCATION[self.choose_one]]
  171. elif event.keysym == 'Down':
  172. for i in self.this_block_location:
  173. self.grid[i[1], i[0]] = 0
  174. while self.if_block_end() == False:
  175. self.block_row_offset += 1
  176. self.this_block_location = [[i[0] + self.block_col_offset, i[1] + self.block_row_offset] for i in self.this_block.BLOCK_LOCATION[self.choose_one]]
  177. self.canvas.delete('all')
  178. for i in self.this_block_location:
  179. self.grid[i[1], i[0]] = self.this_block.BLOCK_NUM
  180. self.draw_grid()
  181. def start_game(self):
  182. # 开始游戏
  183. self.get_random_block()
  184. self.block_move()
  185. def block_eliminate(self):
  186. # 方块清除
  187. tmplist = self.grid.tolist()
  188. continuous_row = 0
  189. score = 0
  190. for i in range(1, self.inner_row_num + 1):
  191. tmpls = tmplist[i][1: self.inner_col_num + 1]
  192. if min(tmpls) > 0:
  193. continuous_row += 1
  194. for j in range(i, 1, -1):
  195. self.grid[j] = self.grid[j - 1]
  196. else:
  197. if continuous_row == 1:
  198. score += 100
  199. elif continuous_row == 2:
  200. score += 200
  201. elif continuous_row == 3:
  202. score += 400
  203. elif continuous_row >= 4:
  204. score += 800
  205. continuous_row = 0
  206. if continuous_row == 1:
  207. score += 100
  208. elif continuous_row == 2:
  209. score += 200
  210. elif continuous_row == 3:
  211. score += 400
  212. elif continuous_row >= 4:
  213. score += 800
  214. continuous_row = 0
  215. return score
  216. def if_game_end(self):
  217. # 判断游戏是否结束
  218. for i in self.this_block_location:
  219. if self.grid[i[1], i[0]] > 0:
  220. return True
  221. return False
  222. def if_block_end(self):
  223. # 判断方块是否下落完毕
  224. next_block_location = [[i[0], i[1] + 1] for i in self.this_block_location]
  225. for i in next_block_location:
  226. next_block_x = i[1]
  227. next_block_y = i[0]
  228. if i not in self.this_block_location and self.grid[next_block_x, next_block_y] != 0:
  229. return True
  230. return False
  231. def block_move(self):
  232. # 方块的自动下落
  233. if self.if_block_end() == True:
  234. self.total_score += self.block_eliminate()
  235. self.get_random_block()
  236. for i in self.this_block_location:
  237. self.grid[i[1], i[0]] = 0
  238. if self.start_judge != True:
  239. self.block_row_offset += 1
  240. self.this_block_location = [[i[0] + self.block_col_offset, i[1] + self.block_row_offset] for i in self.this_block.BLOCK_LOCATION[self.choose_one]]
  241. self.canvas.delete('all')
  242. for i in self.this_block_location:
  243. self.grid[i[1], i[0]] = self.this_block.BLOCK_NUM
  244. self.draw_grid()
  245. self.start_judge = False
  246. if self.if_block_end() == False:
  247. self.canvas.after(self.speed, self.block_move)
  248. else:
  249. self.get_random_block()
  250. self.canvas.after(self.speed, self.block_move)
  251. if __name__ == '__main__':
  252. tetris = Tetris()

本文内容由网友自发贡献,转载请注明出处:【wpsshop博客】
推荐阅读
相关标签
  

闽ICP备14008679号