1+ import os
2+ import sys
3+ import time
4+ import pygame
5+ import random
6+
7+ WIDTH = 400
8+ HEIGHT = 400
9+ NUMGRID = 8
10+ GRIDSIZE = 36
11+ XMARGIN = (WIDTH - GRIDSIZE * NUMGRID ) // 2
12+ YMARGIN = (HEIGHT - GRIDSIZE * NUMGRID ) // 2
13+ ROOTDIR = os .getcwd ()
14+ FPS = 30
15+
16+ # 拼图类
17+ class Puzzle (pygame .sprite .Sprite ):
18+ def __init__ (self , img_path , size , position , downlen , ** kwargs ):
19+ pygame .sprite .Sprite .__init__ (self )
20+ self .image = pygame .image .load (img_path )
21+ self .image = pygame .transform .smoothscale (self .image , size )
22+ self .rect = self .image .get_rect ()
23+ self .rect .left , self .rect .top = position
24+ self .downlen = downlen
25+ self .target_x = position [0 ]
26+ self .target_y = position [1 ] + downlen
27+ self .type = img_path .split ('/' )[- 1 ].split ('.' )[0 ]
28+ self .fixed = False
29+ self .speed_x = 10
30+ self .speed_y = 10
31+ self .direction = 'down'
32+ # 拼图块移动
33+ def move (self ):
34+ if self .direction == 'down' :
35+ self .rect .top = min (self .target_y , self .rect .top + self .speed_y )
36+ if self .target_y == self .rect .top :
37+ self .fixed = True
38+ elif self .direction == 'up' :
39+ self .rect .top = max (self .target_y , self .rect .top - self .speed_y )
40+ if self .target_y == self .rect .top :
41+ self .fixed = True
42+ elif self .direction == 'left' :
43+ self .rect .left = max (self .target_x , self .rect .left - self .speed_x )
44+ if self .target_x == self .rect .left :
45+ self .fixed = True
46+ elif self .direction == 'right' :
47+ self .rect .left = min (self .target_x , self .rect .left + self .speed_x )
48+ if self .target_x == self .rect .left :
49+ self .fixed = True
50+ # 获取坐标
51+ def getPosition (self ):
52+ return self .rect .left , self .rect .top
53+ # 设置坐标
54+ def setPosition (self , position ):
55+ self .rect .left , self .rect .top = position
56+
57+ # 游戏类
58+ class Game ():
59+ def __init__ (self , screen , font , gem_imgs , ** kwargs ):
60+ self .screen = screen
61+ self .font = font
62+ self .gem_imgs = gem_imgs
63+ self .reset ()
64+ # 开始游戏
65+ def start (self ):
66+ clock = pygame .time .Clock ()
67+ # 遍历整个游戏界面更新位置
68+ overall_moving = True
69+ # 指定某些对象个体更新位置
70+ individual_moving = False
71+ gem_selected_xy = None
72+ gem_selected_xy2 = None
73+ swap_again = False
74+ add_score = 0
75+ add_score_showtimes = 10
76+ time_pre = int (time .time ())
77+ # 游戏主循环
78+ while True :
79+ for event in pygame .event .get ():
80+ if event .type == pygame .QUIT or (event .type == pygame .KEYUP and event .key == pygame .K_ESCAPE ):
81+ pygame .quit ()
82+ sys .exit ()
83+ elif event .type == pygame .MOUSEBUTTONUP :
84+ if (not overall_moving ) and (not individual_moving ) and (not add_score ):
85+ position = pygame .mouse .get_pos ()
86+ if gem_selected_xy is None :
87+ gem_selected_xy = self .checkSelected (position )
88+ else :
89+ gem_selected_xy2 = self .checkSelected (position )
90+ if gem_selected_xy2 :
91+ if self .swapGem (gem_selected_xy , gem_selected_xy2 ):
92+ individual_moving = True
93+ swap_again = False
94+ else :
95+ gem_selected_xy = None
96+ if overall_moving :
97+ overall_moving = not self .dropGems (0 , 0 )
98+ # 移动一次可能拼出多个 3 连块
99+ if not overall_moving :
100+ res_match = self .isMatch ()
101+ add_score = self .removeMatched (res_match )
102+ if add_score > 0 :
103+ overall_moving = True
104+ if individual_moving :
105+ gem1 = self .getGemByPos (* gem_selected_xy )
106+ gem2 = self .getGemByPos (* gem_selected_xy2 )
107+ gem1 .move ()
108+ gem2 .move ()
109+ if gem1 .fixed and gem2 .fixed :
110+ res_match = self .isMatch ()
111+ if res_match [0 ] == 0 and not swap_again :
112+ swap_again = True
113+ self .swapGem (gem_selected_xy , gem_selected_xy2 )
114+ else :
115+ add_score = self .removeMatched (res_match )
116+ overall_moving = True
117+ individual_moving = False
118+ gem_selected_xy = None
119+ gem_selected_xy2 = None
120+ self .screen .fill ((255 , 255 , 220 ))
121+ self .drawGrids ()
122+ self .gems_group .draw (self .screen )
123+ if gem_selected_xy :
124+ self .drawBlock (self .getGemByPos (* gem_selected_xy ).rect )
125+ if add_score :
126+ self .drawAddScore (add_score )
127+ add_score_showtimes -= 1
128+ if add_score_showtimes < 1 :
129+ add_score_showtimes = 10
130+ add_score = 0
131+ self .remaining_time -= (int (time .time ()) - time_pre )
132+ time_pre = int (time .time ())
133+ self .showRemainingTime ()
134+ self .drawScore ()
135+ if self .remaining_time <= 0 :
136+ return self .score
137+ pygame .display .update ()
138+ clock .tick (FPS )
139+ # 初始化
140+ def reset (self ):
141+ # 随机生成各个块
142+ while True :
143+ self .all_gems = []
144+ self .gems_group = pygame .sprite .Group ()
145+ for x in range (NUMGRID ):
146+ self .all_gems .append ([])
147+ for y in range (NUMGRID ):
148+ gem = Puzzle (img_path = random .choice (self .gem_imgs ), size = (GRIDSIZE , GRIDSIZE ), position = [XMARGIN + x * GRIDSIZE , YMARGIN + y * GRIDSIZE - NUMGRID * GRIDSIZE ], downlen = NUMGRID * GRIDSIZE )
149+ self .all_gems [x ].append (gem )
150+ self .gems_group .add (gem )
151+ if self .isMatch ()[0 ] == 0 :
152+ break
153+ # 得分
154+ self .score = 0
155+ # 拼出一个的奖励
156+ self .reward = 10
157+ # 设置总时间
158+ self .remaining_time = 500
159+ # 显示剩余时间
160+ def showRemainingTime (self ):
161+ remaining_time_render = self .font .render ('倒计时: %ss' % str (self .remaining_time ), 1 , (85 , 65 , 0 ))
162+ rect = remaining_time_render .get_rect ()
163+ rect .left , rect .top = (WIDTH - 190 , 15 )
164+ self .screen .blit (remaining_time_render , rect )
165+ # 显示得分
166+ def drawScore (self ):
167+ score_render = self .font .render ('分数:' + str (self .score ), 1 , (85 , 65 , 0 ))
168+ rect = score_render .get_rect ()
169+ rect .left , rect .top = (55 , 15 )
170+ self .screen .blit (score_render , rect )
171+ # 显示加分
172+ def drawAddScore (self , add_score ):
173+ score_render = self .font .render ('+' + str (add_score ), 1 , (255 , 100 , 100 ))
174+ rect = score_render .get_rect ()
175+ rect .left , rect .top = (250 , 250 )
176+ self .screen .blit (score_render , rect )
177+ # 生成新的拼图块
178+ def generateNewGems (self , res_match ):
179+ if res_match [0 ] == 1 :
180+ start = res_match [2 ]
181+ while start > - 2 :
182+ for each in [res_match [1 ], res_match [1 ]+ 1 , res_match [1 ]+ 2 ]:
183+ gem = self .getGemByPos (* [each , start ])
184+ if start == res_match [2 ]:
185+ self .gems_group .remove (gem )
186+ self .all_gems [each ][start ] = None
187+ elif start >= 0 :
188+ gem .target_y += GRIDSIZE
189+ gem .fixed = False
190+ gem .direction = 'down'
191+ self .all_gems [each ][start + 1 ] = gem
192+ else :
193+ gem = Puzzle (img_path = random .choice (self .gem_imgs ), size = (GRIDSIZE , GRIDSIZE ), position = [XMARGIN + each * GRIDSIZE , YMARGIN - GRIDSIZE ], downlen = GRIDSIZE )
194+ self .gems_group .add (gem )
195+ self .all_gems [each ][start + 1 ] = gem
196+ start -= 1
197+ elif res_match [0 ] == 2 :
198+ start = res_match [2 ]
199+ while start > - 4 :
200+ if start == res_match [2 ]:
201+ for each in range (0 , 3 ):
202+ gem = self .getGemByPos (* [res_match [1 ], start + each ])
203+ self .gems_group .remove (gem )
204+ self .all_gems [res_match [1 ]][start + each ] = None
205+ elif start >= 0 :
206+ gem = self .getGemByPos (* [res_match [1 ], start ])
207+ gem .target_y += GRIDSIZE * 3
208+ gem .fixed = False
209+ gem .direction = 'down'
210+ self .all_gems [res_match [1 ]][start + 3 ] = gem
211+ else :
212+ gem = Puzzle (img_path = random .choice (self .gem_imgs ), size = (GRIDSIZE , GRIDSIZE ), position = [XMARGIN + res_match [1 ]* GRIDSIZE , YMARGIN + start * GRIDSIZE ], downlen = GRIDSIZE * 3 )
213+ self .gems_group .add (gem )
214+ self .all_gems [res_match [1 ]][start + 3 ] = gem
215+ start -= 1
216+ # 移除匹配的元素
217+ def removeMatched (self , res_match ):
218+ if res_match [0 ] > 0 :
219+ self .generateNewGems (res_match )
220+ self .score += self .reward
221+ return self .reward
222+ return 0
223+ # 游戏界面的网格绘制
224+ def drawGrids (self ):
225+ for x in range (NUMGRID ):
226+ for y in range (NUMGRID ):
227+ rect = pygame .Rect ((XMARGIN + x * GRIDSIZE , YMARGIN + y * GRIDSIZE , GRIDSIZE , GRIDSIZE ))
228+ self .drawBlock (rect , color = (255 , 165 , 0 ), size = 1 )
229+ # 画矩形 block 框
230+ def drawBlock (self , block , color = (255 , 0 , 0 ), size = 2 ):
231+ pygame .draw .rect (self .screen , color , block , size )
232+ # 下落特效
233+ def dropGems (self , x , y ):
234+ if not self .getGemByPos (x , y ).fixed :
235+ self .getGemByPos (x , y ).move ()
236+ if x < NUMGRID - 1 :
237+ x += 1
238+ return self .dropGems (x , y )
239+ elif y < NUMGRID - 1 :
240+ x = 0
241+ y += 1
242+ return self .dropGems (x , y )
243+ else :
244+ return self .isFull ()
245+ # 是否每个位置都有拼图块了
246+ def isFull (self ):
247+ for x in range (NUMGRID ):
248+ for y in range (NUMGRID ):
249+ if not self .getGemByPos (x , y ).fixed :
250+ return False
251+ return True
252+ # 检查有无拼图块被选中
253+ def checkSelected (self , position ):
254+ for x in range (NUMGRID ):
255+ for y in range (NUMGRID ):
256+ if self .getGemByPos (x , y ).rect .collidepoint (* position ):
257+ return [x , y ]
258+ return None
259+ # 是否有连续一样的三个块
260+ def isMatch (self ):
261+ for x in range (NUMGRID ):
262+ for y in range (NUMGRID ):
263+ if x + 2 < NUMGRID :
264+ if self .getGemByPos (x , y ).type == self .getGemByPos (x + 1 , y ).type == self .getGemByPos (x + 2 , y ).type :
265+ return [1 , x , y ]
266+ if y + 2 < NUMGRID :
267+ if self .getGemByPos (x , y ).type == self .getGemByPos (x , y + 1 ).type == self .getGemByPos (x , y + 2 ).type :
268+ return [2 , x , y ]
269+ return [0 , x , y ]
270+ # 根据坐标获取对应位置的拼图对象
271+ def getGemByPos (self , x , y ):
272+ return self .all_gems [x ][y ]
273+ # 交换拼图
274+ def swapGem (self , gem1_pos , gem2_pos ):
275+ margin = gem1_pos [0 ] - gem2_pos [0 ] + gem1_pos [1 ] - gem2_pos [1 ]
276+ if abs (margin ) != 1 :
277+ return False
278+ gem1 = self .getGemByPos (* gem1_pos )
279+ gem2 = self .getGemByPos (* gem2_pos )
280+ if gem1_pos [0 ] - gem2_pos [0 ] == 1 :
281+ gem1 .direction = 'left'
282+ gem2 .direction = 'right'
283+ elif gem1_pos [0 ] - gem2_pos [0 ] == - 1 :
284+ gem2 .direction = 'left'
285+ gem1 .direction = 'right'
286+ elif gem1_pos [1 ] - gem2_pos [1 ] == 1 :
287+ gem1 .direction = 'up'
288+ gem2 .direction = 'down'
289+ elif gem1_pos [1 ] - gem2_pos [1 ] == - 1 :
290+ gem2 .direction = 'up'
291+ gem1 .direction = 'down'
292+ gem1 .target_x = gem2 .rect .left
293+ gem1 .target_y = gem2 .rect .top
294+ gem1 .fixed = False
295+ gem2 .target_x = gem1 .rect .left
296+ gem2 .target_y = gem1 .rect .top
297+ gem2 .fixed = False
298+ self .all_gems [gem2_pos [0 ]][gem2_pos [1 ]] = gem1
299+ self .all_gems [gem1_pos [0 ]][gem1_pos [1 ]] = gem2
300+ return True
301+ def __repr__ (self ):
302+ return self .info
303+
304+ # 初始化游戏
305+ def gameInit ():
306+ pygame .init ()
307+ screen = pygame .display .set_mode ((WIDTH , HEIGHT ))
308+ pygame .display .set_caption ('消消乐' )
309+ # 加载字体
310+ font = pygame .font .Font (os .path .join (ROOTDIR , 'resources/simsun.ttc' ), 25 )
311+ # 加载图片
312+ gem_imgs = []
313+ for i in range (1 , 8 ):
314+ gem_imgs .append (os .path .join (ROOTDIR , 'resources/images/gem%s.png' % i ))
315+ game = Game (screen , font , gem_imgs )
316+ while True :
317+ score = game .start ()
318+ flag = False
319+ # 设置退出、重新开始
320+ while True :
321+ for event in pygame .event .get ():
322+ if event .type == pygame .QUIT :
323+ pygame .quit ()
324+ sys .exit ()
325+ if event .type == pygame .KEYUP and event .key == pygame .K_r :
326+ flag = True
327+ if flag :
328+ break
329+ screen .fill ((255 , 255 , 220 ))
330+ text0 = '最终得分: %s' % score
331+ text1 = '按 R 键重新开始'
332+ y = 140
333+ for idx , text in enumerate ([text0 , text1 ]):
334+ text_render = font .render (text , 1 , (85 , 65 , 0 ))
335+ rect = text_render .get_rect ()
336+ if idx == 0 :
337+ rect .left , rect .top = (100 , y )
338+ elif idx == 1 :
339+ rect .left , rect .top = (100 , y )
340+ y += 60
341+ screen .blit (text_render , rect )
342+ pygame .display .update ()
343+ game .reset ()
344+
345+ if __name__ == '__main__' :
346+ gameInit ()
0 commit comments