俄罗斯方块

  1 import random, time, pygame, sys
  2 from pygame.locals import *
  3 
  4 FPS = 25
  5 WINDOWWIDTH = 640#整个游戏屏幕的宽
  6 WINDOWHEIGHT = 480#整个游戏屏幕的高
  7 BOXSIZE = 20#每个小格子的宽和高
  8 BOARDWIDTH = 10#游戏窗口本身有10个方块的宽度
  9 BOARDHEIGHT = 20#游戏窗口本身有20个方块的高度
 10 BLANK = '.'#表示空白空格
 11 
 12 #每当玩家按下向左或向右箭头键的时候,下落的砖块都应该分别向左或向右移动一个方块。然而,玩家也可以保持按住了向左箭头键或向右箭头键以使得下落的砖块持续移动。
 13 MOVESIDEWAYSFREQ = 0.15 #按向左箭头键或向右箭头键每次持续按下超过0.15秒的时候,砖块相应的移动一个空格
 14 MOVEDOWNFREQ = 0.1     #按向下头键每次持续按下超过0.1秒的时候,砖块向下一个空格
 15 XMARGIN = int((WINDOWWIDTH - BOARDWIDTH * BOXSIZE) / 2)#(0INDOWWIDTH是总窗口的宽度-游戏界面一行上的方块个数*每个方块的宽度)/2窗口左边或右边剩下的像素数
 16 TOPMARGIN = WINDOWHEIGHT - (BOARDHEIGHT * BOXSIZE) - 5#TOPMARGIN:游戏窗口上面剩下的像素数=总窗口的高度-(游戏界面一列上的方块个数*每个方块的高度)-5
 17 #               R    G    B
 18 WHITE       = (255, 255, 255)#白色
 19 GRAY        = (185, 185, 185)#灰色
 20 BLACK       = (  0,   0,   0)#黑色
 21 RED         = (155,   0,   0)#红色
 22 GREEN       = (  0, 155,   0)#绿色
 23 BLUE        = (  0,   0, 155)#蓝色
 24 YELLOW      = (155, 155,   0)#黄色
 25 BORDERCOLOR = BLUE#边界颜色
 26 BGCOLOR = BLACK#背景颜色
 27 TEXTCOLOR = WHITE#文字颜色
 28 COLORS      = (BLUE,GREEN,RED,YELLOW) #方块四种颜色,存于COLORS元组中
 29 TEMPLATEWIDTH = 5#砖块模板宽 
 30 TEMPLATEHEIGHT = 5#砖块模板高
 31 
 32 S_SHAPE_TEMPLATE = [['.....',  #S形状的模板
 33                      '.....',
 34                      '..OO.',
 35                      '.OO..',
 36                      '.....'],
 37                     ['.....',  #S逆时针变化的形状
 38                      '..O..',
 39                      '..OO.',
 40                      '...O.',
 41                      '.....']]
 42 
 43 Z_SHAPE_TEMPLATE = [['.....', #Z形模板
 44                      '.....',
 45                      '.OO..',
 46                      '..OO.',
 47                      '.....'],
 48                     ['.....',
 49                      '..O..',
 50                      '.OO..',
 51                      '.O...',
 52                      '.....']]
 53 
 54 I_SHAPE_TEMPLATE = [['..O..', #I型模板
 55                      '..O..',
 56                      '..O..',
 57                      '..O..',
 58                      '.....'],
 59                     ['.....',
 60                      '.....',
 61                      'OOOO.',
 62                      '.....',
 63                      '.....']]
 64 
 65 O_SHAPE_TEMPLATE = [['.....', #O型模板
 66                      '.....',
 67                      '.OO..',
 68                      '.OO..',
 69                      '.....']]
 70 
 71 J_SHAPE_TEMPLATE = [['.....', #J型模板
 72                      '.O...',
 73                      '.OOO.',
 74                      '.....',
 75                      '.....'],
 76                     ['.....',
 77                      '..OO.',
 78                      '..O..',
 79                      '..O..',
 80                      '.....'],
 81                     ['.....',
 82                      '.....',
 83                      '.OOO.',
 84                      '...O.',
 85                      '.....'],
 86                     ['.....',
 87                      '..O..',
 88                      '..O..',
 89                      '.OO..',
 90                      '.....']]
 91 
 92 L_SHAPE_TEMPLATE = [['.....', #L型模板
 93                      '...O.',
 94                      '.OOO.',
 95                      '.....',
 96                      '.....'],
 97                     ['.....',
 98                      '..O..',
 99                      '..O..',
100                      '..OO.',
101                      '.....'],
102                     ['.....',
103                      '.....',
104                      '.OOO.',
105                      '.O...',
106                      '.....'],
107                     ['.....',
108                      '.OO..',
109                      '..O..',
110                      '..O..',
111                      '.....']]
112 
113 T_SHAPE_TEMPLATE = [['.....', #T型模板
114                      '..O..',
115                      '.OOO.',
116                      '.....',
117                      '.....'],
118                     ['.....',
119                      '..O..',
120                      '..OO.',
121                      '..O..',
122                      '.....'],
123                     ['.....',
124                      '.....',
125                      '.OOO.',
126                      '..O..',
127                      '.....'],
128                     ['.....',
129                      '..O..',
130                      '.OO..',
131                      '..O..',
132                      '.....']]
133 
134 PIECES = {'S': S_SHAPE_TEMPLATE, #PIECES是一个字典,它储存了所有不同的模板(列表)。每个模板都拥有一个形状所有可能的旋转(列表)。
135           'Z': Z_SHAPE_TEMPLATE,
136           'J': J_SHAPE_TEMPLATE,
137           'L': L_SHAPE_TEMPLATE,
138           'I': I_SHAPE_TEMPLATE,
139           'O': O_SHAPE_TEMPLATE,
140           'T': T_SHAPE_TEMPLATE}
141 
142 def main(): #main()函数还负责创建了一些其他的全局常量,并且显示了在游戏运行的时候出现的初始屏幕。
143     global FPSCLOCK, DISPLAYSURF, BASICFONT, BIGFONT
144     pygame.init()#在inport pygame之后 调用其他函数之前总要调用这个函数
145     FPSCLOCK = pygame.time.Clock()#pygame.time.Clock()创建pygame.time.Clock对象
146     DISPLAYSURF = pygame.display.set_mode((WINDOWWIDTH, WINDOWHEIGHT)) #pygame.display.set_mode()的参数是一个元组,该元祖中有两个参数,
147     #即:创建窗口的宽和高,单位是像素,该函数返回pygame.Surface对象
148     BASICFONT = pygame.font.Font('freesansbold.ttf', 18)#字体
149     BIGFONT = pygame.font.Font('freesansbold.ttf', 100)#字体
150     pygame.display.set_caption('Tetromino')#设置窗口标题
151     showTextScreen('Tetromino')#设置在开始界面显示的文字
152 
153     while True: #游戏循环,该游戏循坏会在一秒之内多次检查是否发生了任何新的事件,例如:点击鼠标或按下键盘
154         pygame.mixer.music.load('tetrisc.mp3')#加载音乐 
155         pygame.mixer.music.play(-1, 0.0)#播放音乐
156         runGame()#调用runGame()开始游戏,当游戏失败的时候,runGame()将返回main(),
157         pygame.mixer.music.stop()#然后main()会停止背景音乐
158         showTextScreen('Game Over')#并显示游戏结束屏幕。当玩家按下一个键,showTextScreen()函数将返回,程序回到main()中的的第一行,重新开始游戏
159 
160 def runGame():#实际的游戏代码都在runGame中
161 #在游戏开始并且砖块开始下落前,我们需要将一些变量初始化为游戏开始时候的值。
162     board = getBlankBoard()#创建一个空白游戏板数据结构
163     lastMoveDownTime = time.time()#lastMoveDownTime最后按向下方向键的时间
164     lastMoveSidewaysTime = time.time()#lastMoveSidewaysTime最后按左右向键的时间
165     lastFallTime = time.time()#最后下落砖块的时间
166     movingDown = False #没有按下向下方向键
167     movingLeft = False #没有按下向左方向键
168     movingRight = False #没有按下向右方向键
169     score = 0 #得分
170     level, fallFreq = calculateLevelAndFallFreq(score)#计算关卡数和下落频率,因为此时score=0,所以经计算后level=1,fallFreq=0.25
171     fallingPiece = getNewPiece() #fallingPiece变量将设置为能够被玩家操作的当前下落的砖块
172     nextPiece = getNewPiece() #nextPice为在屏幕的Next部分出现的砖块,即下一个将要下落的砖块
173 
174     while True: # 游戏主循环,它负责砖块在落向底部的过程中,游戏主要部分的代码
175         if fallingPiece == None:#在下落的砖块已经着陆之后,fallingPiece变量设置为None
176             fallingPiece = nextPiece#这意味着nextPiece中的砖块将会复制到fallingPiece中。
177             nextPiece = getNewPiece()#生成新的新的nextPiece砖块,砖块可以通过getNewPiece()函数生成。
178             lastFallTime = time.time() #该变量也重新设置为当前时间,以便砖块能够在fallFreq中所设置的那么多秒之内下落。
179             if not isValidPosition(board, fallingPiece):
180                  #但是,如果游戏板已经填满了,isValidPosition()将返回False,导致这是一个无效的位置,那么,我们知道游戏板已经填满了,玩家失败了。
181                 return #在这种情况下 runGame()函数将被返回。
182 
183         for event in pygame.event.get():  #事件处理循环负责玩家旋转下落的砖块,移动下落的砖块。
184                 #松开一个剪头键将会把movingLeft或movingRight或movingDown变量设置为False,表示玩家不再想
185                 #要让砖块朝着该方向移动。随后的代码将会根据这些“moving”变量中的Boolean值来确定做什么。
186             if event.type == KEYUP:#当按键弹起的时候响应KEYUP事件
187                 if (event.key == K_LEFT):#判断当前弹起的按键是否为左方向键
188                     movingLeft = False #是的话置为False,表示玩家不再想要让砖块朝着该方向移动。
189                 elif (event.key == K_RIGHT):#同上
190                     movingRight = False
191                 elif (event.key == K_DOWN):#同上
192                     movingDown = False
193 
194             elif event.type == KEYDOWN:#当按键按下的时候响应KEYDOWN事件
195                 if (event.key == K_LEFT) and isValidPosition(board, fallingPiece, adjX=-1):
196                 #当按下的按键为向左方向键,并且向左移动一个位置有效
197                     fallingPiece['x'] = fallingPiece['x'] -1 #左移
198                     movingLeft = True #将movingLeft变量设置为True,并且为了确保落下的砖块不会既向左又向右移动
199                     movingRight = False #将 movingRight设置为False
200                     lastMoveSidewaysTime = time.time() #lastMoveSidewaysTime更改为当前时间
201 #设置了 movingLeft,movingRigh以便玩家能够只是按住方向键以保持砖块移动。如果movingLeft变量设置为True,程序就知道已经按下了向左箭头键并且没有松开它。
202 
203                 elif (event.key == K_RIGHT ) and isValidPosition(board, fallingPiece, adjX=1): #同上
204                     fallingPiece['x'] =fallingPiece['x'] + 1
205                     movingRight = True
206                     movingLeft = False
207                     lastMoveSidewaysTime = time.time()
208 
209                 #按向上箭头将会把砖块为其下一个旋转状态。代码所需要做的只是fallingPiece字典中的'rotation'键的值增加1。然而,如果增加'rotation'键的值
210                 #大于旋转的总数目,那么用该形状可能旋转的总数目(这就是len(PIECES[fallingPiece['shape']的含义)来模除它,然后,这个值将回滚到从0开始。
211                 elif event.key == K_UP :
212                     fallingPiece['rotation'] = (fallingPiece['rotation'] + 1) % len(PIECES[fallingPiece['shape']])
213                     if not isValidPosition(board, fallingPiece):
214                     #由于新的旋转位置与游戏板上已有的一些方块重叠而导致新的旋转位置无效,那么,
215                     #我们想要通过从fallingPiece['rotation']减去1而切换回最初的旋转。我们也可以使用len(PIECES[fallingPiece['shape']])来模除
216                     #它,以便如果新的值为-1,模除将其改为列表中的最后一次旋转。????
217                         fallingPiece['rotation'] = (fallingPiece['rotation'] - 1) % len(PIECES[fallingPiece['shape']])
218 
219                 #如果按下了向下键,说明玩家想要砖块下落得比正常速度更快一些。
220                 elif (event.key == K_DOWN ):
221                     movingDown = True # movingDown设置为True
222                     if isValidPosition(board, fallingPiece, adjY=1):#下一个位置有效
223                         fallingPiece['y'] =  fallingPiece['y'] +1  #移动
224                     lastMoveDownTime = time.time() #lastMoveDownTime重新设置为当前时间。随后将检查这些变量,以确保只要按下向下箭头键的时候,砖块就
225                     #会以较快的速率下降
226 
227         if (movingLeft or movingRight) and time.time() - lastMoveSidewaysTime > MOVESIDEWAYSFREQ:#MOVESIDEWAYSFREQ = 0.15 按向左或向右超过0.15秒
228             if movingLeft and isValidPosition(board, fallingPiece, adjX=-1):#如果是向左方向键,并且向左一个位置有效
229                 fallingPiece['x'] =fallingPiece['x'] - 1#左移动一个位置
230             elif movingRight and isValidPosition(board, fallingPiece, adjX=1):#如果是向右方向键,并且向左一个位置有效
231                 fallingPiece['x'] =fallingPiece['x'] + 1#右移动一个位置
232             lastMoveSidewaysTime = time.time() #将lastMoveSidewaysTime更新为当前时间。
233 
234         if movingDown and time.time() - lastMoveDownTime > MOVEDOWNFREQ and isValidPosition(board, fallingPiece, adjY=1):
235         #MOVEDOWNFREQ = 0.1 按向下方向键超过0.1秒,并且向下一个位置有效
236             fallingPiece['y'] = fallingPiece['y'] + 1#向下移动一个位置
237             lastMoveDownTime = time.time()#将laslastMoveDownTime更新为当前时间。
238 
239         #让砖块自然落下
240         if time.time() - lastFallTime > fallFreq:#fallFreq向下移动的速率
241             if not isValidPosition(board, fallingPiece, adjY=1):#当砖块下一个位置无效时,即表示砖块当前已经着陆了。
242                 addToBoard(board, fallingPiece) #在游戏板数据结构中记录这个着陆的砖块
243                 score=score + removeCompleteLines(board)# removeCompleteLines()将负责删除掉游戏板上任何已经填充完整的行,并且将方块向下推动。
244                 #removeCompleteLines()函数还会返回一个整数值,表明消除了多少行,以便我们将这个数字加到得分上。
245                 level, fallFreq = calculateLevelAndFallFreq(score)#由于分数已经修改了,我们调用calculateLevelAndFallFreq()函数来更新当前的关卡以及砖块下落得频率。
246                 fallingPiece = None#最后我们将fallingPiece变量设置为None,以表示下一个砖块应该变为新的下落砖块,并且应该生成一个随机的新砖块作为下一个砖块。??????
247             else:
248                 # 如果砖块没有着陆,我们直接将其Y位置向下设置一个空格,并且将lastFallTime重置为当前时间
249                 fallingPiece['y'] = fallingPiece['y'] +1
250                 lastFallTime = time.time()
251 
252         # drawing everything on the screen
253         DISPLAYSURF.fill(BGCOLOR)
254         drawBoard(board)
255         drawStatus(score, level)
256         drawNextPiece(nextPiece)
257         if fallingPiece != None:#砖块没有下落到底部
258             drawPiece(fallingPiece)
259         pygame.display.update()
260         FPSCLOCK.tick(FPS)
261 
262 def makeTextObjs(text, font, color):
263     surf = font.render(text, True, color)
264     return surf, surf.get_rect()
265 
266 def checkForKeyPress():
267     # checkForKeyPress()函数和它在Wormy游戏中所做的事件相同。首先,它调用checkForQuit()来处理任何的QUIT事件(或者是专门针对Esc键的KEYUP事件),如果有任何这样的事件
268     #就会终止程序。然后,它从事件队列中提取出所有的KEYDOWN, KEYUP事件。它会忽略掉任何的KEYDOWN事件(针对pygame.event.get()指定了KEYDOWN,从而从事件队列中清除掉该类事件)。
269     #如果事件队列中没有KEYUP事件,那么该函数返回None。
270     for event in pygame.event.get([KEYDOWN, KEYUP]):
271         if event.type == KEYDOWN:
272             continue
273         return event.key
274     return None
275 
276 def calculateLevelAndFallFreq(score):#每次玩家填满一行,起分数都将增加1分。每增加10分,游戏就进入下一个关卡,砖块下落得会更快。关卡和下落的频率都是通过传递
277     level = int(score / 10) + 1 #给该函数的分数来计算的。要计算关卡,我们使用int()来舍入除以10以后的分数。因此如果分数是0-9之间的任何数字,int()调用会将其
278     fallFreq = 0.27 - (level * 0.02) #舍入到0。代码这里的+1部分,是因为我们想要第一个关卡作为第一关,而不是第0关。当分数达到10分的时候,int(10/10)将会计算为1
279     return level, fallFreq #并且+1将会使得关卡变为2
280     #为了计算下落的频率,我们首先有一个基准值0.27(这意味着每0.27秒,砖块会自然地下落一次)。然后,我们将关卡值乘以0.02,并且从基准时间0.27中减去它。因此,对于关卡1,
281     #我们从0.27中减去0.02*1得到0.25。在关卡2中我们减去0.02*2得到0.23。对于每一个关卡来说,砖块下落的速度都比之前的关卡块了0.02秒。
282 
283 #getNewPiece()函数产生一个随机的砖块,放置于游戏板的顶部(设置'y'=-2)。
284 def getNewPiece():
285     shape = random.choice(list(PIECES.keys()))#PIECES是一个字典,它的键为代表形状的字母,值为一个形状所有可能的旋转(列表的列表)。
286     #PIECES.keys()返回值是(['Z','J','L','I','O','T'])的元组,list(PIECES.keys())返回值是['Z','J','L','I','O','T']列表
287     #这样转换是因为random.choice()函数只接受列表值作为其参数。 random.choice()函数随机地返回列表中的一项的值,即可能是'Z'。
288     newPiece = {'shape': shape,
289                 'rotation': random.randint(0, len(PIECES[shape]) - 1), #rotation:随机出砖块是多个旋转形装的哪个
290                 #PIECES['Z']的返回值为[[形状],[形状]],len(PIECES['z'])的返回值为2 2-1=1 random.randint(0,1)随机范围是[0,1]
291                 'x': int(BOARDWIDTH / 2) - int(TEMPLATEWIDTH / 2), #'x'代表砖块5x5数据结构左上角第一个方格的横坐标,键的值总是要设置为游戏板的中间。
292                 'y': -2,  #'x'代表砖块5x5数据结构左上角第一个方格的纵坐标,'y'键的值总是要设置为-2以便将其放置到游戏板上面一点点(游戏板的首行是0行)
293                 'color': random.randint(0, len(COLORS) - 1)#COLORS:不同颜色的一个元组
294                 }
295     return newPiece#getNewPiece()函数返回newPiece字典
296 
297 #给游戏板数据结构添加砖块
298 def addToBoard(board, piece): #游戏板数据结构用来记录之前着陆的砖块。该函数所做的事情是接受一个砖块数据结构,并且将其上的有效砖块添加到游戏板数据结构中
299     for x in range(TEMPLATEWIDTH): #该函数这在一个砖块着陆之后进行
300         for y in range(TEMPLATEHEIGHT):#嵌套for遍历了5x5砖块数据结构,当找到一个有效砖块时,将其添加到游戏板中
301             if PIECES[piece['shape']][piece['rotation']][y][x] != BLANK:
302                 board[x + piece['x']][y + piece['y']] = piece['color'] #游戏板数据结构的值有两种形式:数字(表示砖块颜色),'.'即空白,表示该处没有有效砖块
303 
304 def getBlankBoard(): #创建一个新的游戏板数据结构。
305     board = [] #创建一个空白的游戏板
306     for i in range(BOARDWIDTH):# range(10)=[0,9]    BOARDWIDTH=10   BLANK = '.' #表示空白空格
307         board.append([BLANK] * BOARDHEIGHT)
308     #board[0]-board[9]每一个变量的值都是20个.组成的列表   
309     return board
310 
311 def isOnBoard(x, y):#isOnBoard()函数检查参数x,y坐标是否存在于游戏板上
312     return x >= 0 and x < BOARDWIDTH and y < BOARDHEIGHT#BOARDWIDTH=10,BOARDHEIGHT=20
313 
314 def isValidPosition(board, piece, adjX=0, adjY=0):#board:游戏板 piece:砖块 adjX,adjY表示5x5砖块左上角方块的坐标
315 #isValidPositio,n()如果砖块中所有的方块都在游戏板上并且没有和游戏板上任何方块重叠,那么返回True
316 #isValidPosition()函数还有名为adjX和adjY的可选参数。通常,isValidPosition()函数检查作为第二个参数传递的砖块对象所提供的位置数据(此时adjX=0, adjY=0)。
317 #然而,有时候我们不想要检查砖块的当前位置,而是要检查该位置之上的一些空格
318 ##如果给adjX递-1.那么它不会检查砖块的数据结构中的位置的有效性,而是检查砖块所处的位置的左边一个空格是否是有效的。
319 #给adjX传递1的话,将会检查砖块右边的一个空格。还有一个可选的adjY参数。传递-1给adjY,将会检查砖块当前所处位置的上面一个空格,
320 #而给adjY传递值3将会检查砖块所在位置下面的3个空格。
321     for x in range(TEMPLATEWIDTH): #TEMPLATEWIDTH=5 TEMPLATEWIDTH=5
322         for y in range(TEMPLATEHEIGHT):# 遍历砖块模板的所有方块
323             isAboveBoard = y + piece['y'] + adjY  < 0  #模板还没完全进入游戏板
324             if isAboveBoard or PIECES[piece['shape']][piece['rotation']][y][x] == BLANK:#在5x5模板中不等于'.'的方块,即有效方块
325                 continue
326             if not isOnBoard(x + piece['x'] + adjX, y + piece['y'] + adjY):#有效砖块不在游戏板上
327                 return False
328             if board[x + piece['x'] + adjX][y + piece['y'] + adjY] != BLANK:#有效砖块和游戏板上的方块重叠
329                 return False
330     return True
331 
332 def isCompleteLine(board, y):#判断y行是否填满,填满返回True
333     for x in range(BOARDWIDTH):#遍历该行的所有砖块
334         if board[x][y] == BLANK:#如果存在空白,则没填满
335             return False
336     return True
337 
338 def removeCompleteLines(board):#删除所有填满行,每删除一行要将游戏板上该行之上的所有方块都下移一行。返回删除的行数
339     numLinesRemoved = 0
340     y = BOARDHEIGHT - 1 # BOARDHEIGHT=20-1=19即从最低行开始
341     while y >= 0:#注意当删除一行时y没有生变化,因为此时它的值已经更新为新的一行了
342         if isCompleteLine(board, y):#如果该行填满
343             for pullDownY in range(y, 0, -1):  #range(y, 0, -1)范围[y,1]
344                 for x in range(BOARDWIDTH):
345                     board[x][pullDownY] = board[x][pullDownY-1]#将删除的行之上的每一行的值都复制到下一行
346             for x in range(BOARDWIDTH):#删除第一行
347                 board[x][0]=BLANK
348             numLinesRemoved=numLinesRemoved+1
349         else:
350             y =y- 1 #移到下一行
351     return numLinesRemoved
352 
353 def convertToPixelCoords(boxx, boxy):#将游戏板上方块的坐标转化成像素坐标
354     return (XMARGIN + (boxx * BOXSIZE)), (TOPMARGIN + (boxy * BOXSIZE))#XMARGIN为游戏板左顶点的横坐标,TOPMARGIN为游戏板左顶点的纵坐标
355 
356 def drawBoard(board):#绘制游戏板边界
357     pygame.draw.rect(DISPLAYSURF, BORDERCOLOR, (XMARGIN - 3, TOPMARGIN - 7, (BOARDWIDTH * BOXSIZE) + 8, (BOARDHEIGHT * BOXSIZE) + 8), 5)
358     #pygame.draw.rect(DISPLAYSURF对象,RED颜色,(x,y,width,height),线的宽度) rect:矩形 x,y表示左上角的坐标 width表示矩形的宽度 height表示高度
359     #线的宽度为0(默认)表示全部填充,为1会画很细的线
360     pygame.draw.rect(DISPLAYSURF, BGCOLOR, (XMARGIN, TOPMARGIN, BOXSIZE * BOARDWIDTH, BOXSIZE * BOARDHEIGHT)) #填充游戏板的背景颜色
361     for x in range(BOARDWIDTH):#遍历游戏板
362         for y in range(BOARDHEIGHT):
363             drawBox(x, y, board[x][y])#这个函数会自动找出有效方块并绘制
364 
365 #绘制一个砖块的一个有效方块(每个砖块有个有效方块)
366 def drawBox(boxx, boxy, color, pixelx=None, pixely=None):#绘制一个有效方块
367     if color == BLANK: #如果这不是一个有效方块,这是5x5一个空白
368         return
369     if pixelx == None and pixely == None:
370         pixelx, pixely = convertToPixelCoords(boxx, boxy)#将游戏板上方块的坐标转化成像素坐标
371     pygame.draw.rect(DISPLAYSURF, COLORS[color], (pixelx + 1, pixely + 1, BOXSIZE - 1, BOXSIZE - 1))#留出1像素的空白,这样才能在砖块中看到组成砖块
372     #的有效方块,不然砖块看起来就只有一片颜色。
373 
374 #绘制一个砖块
375 def drawPiece(piece, pixelx=None, pixely=None):#pixelx, pixely为5x5砖块数据结构左上角在游戏板上的的坐标
376     shapeToDraw = PIECES[piece['shape']][piece['rotation']]#PIECES[piece['shape']][piece['rotation']]为一个图形的一种旋转方式
377     if pixelx == None and pixely == None: 
378     #然而,'Next'砖块并不会绘制到游戏板上。在这种情况下,我们忽略砖块数据结构中包含的位置信息,而是让drawPiece()函数的调用者
379 #为pixelx何pixely参数传递实参,以指定应该将砖块确切地绘制到窗口上的什么位置。
380         pixelx, pixely = convertToPixelCoords(piece['x'], piece['y'])#将砖块坐标转换为像素坐标。
381     for x in range(TEMPLATEWIDTH): #遍历5x5砖块数据结构
382         for y in range(TEMPLATEHEIGHT):
383             if shapeToDraw[y][x] != BLANK:
384                 drawBox(None, None, piece['color'], pixelx+(x * BOXSIZE), pixely + (y * BOXSIZE))
385                 #还记得吗?有效方块左上角在游戏板中的坐标=有效方块左上角在方块板数据结构中的坐标+方块数据额结构左上角在游戏板中的坐标,这里这不过换成了像素格式
386 
387 def drawNextPiece(piece):
388     nextSurf = BASICFONT.render('Next:', True, TEXTCOLOR)
389     nextRect = nextSurf.get_rect()
390     nextRect.topleft = (WINDOWWIDTH - 120, 80)
391     DISPLAYSURF.blit(nextSurf, nextRect)
392     drawPiece(piece, pixelx=WINDOWWIDTH-120, pixely=100)
393 
394 def drawStatus(score, level):
395     scoreSurf = BASICFONT.render('Score: %s' % score, True, TEXTCOLOR)
396     scoreRect = scoreSurf.get_rect()
397     scoreRect.topleft = (WINDOWWIDTH - 150, 20)
398     DISPLAYSURF.blit(scoreSurf, scoreRect)
399     levelSurf = BASICFONT.render('Level: %s' % level, True, TEXTCOLOR)
400     levelRect = levelSurf.get_rect()
401     levelRect.topleft = (WINDOWWIDTH - 150, 50)
402     DISPLAYSURF.blit(levelSurf, levelRect)
403 
404 def showTextScreen(text):
405     titleSurf, titleRect = makeTextObjs(text, BIGFONT, TEXTCOLOR)
406     titleRect.center = (int(WINDOWWIDTH / 2) - 3, int(WINDOWHEIGHT / 2) - 3)
407     DISPLAYSURF.blit(titleSurf, titleRect)
408     pressKeySurf, pressKeyRect = makeTextObjs('Press a key to play.', BASICFONT, TEXTCOLOR)
409     pressKeyRect.center = (int(WINDOWWIDTH / 2), int(WINDOWHEIGHT / 2) + 100)
410     DISPLAYSURF.blit(pressKeySurf, pressKeyRect)
411     while checkForKeyPress() == None:
412         pygame.display.update()
413         FPSCLOCK.tick()
414 
415 if __name__ == '__main__':
416     main()

原文地址:https://www.cnblogs.com/zhou-huilin/p/14057635.html