Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

Commit 0fb19e2

Browse files
committed
m
1 parent afa5a23 commit 0fb19e2

File tree

12 files changed

+347
-0
lines changed

12 files changed

+347
-0
lines changed

‎yeke/README.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111
+ [py-counter](https://github.com/JustDoPython/python-examples/tree/master/yeke/py-counter) :不到 100 行 Python 代码写个计算器
1212
+ [py-tank](https://github.com/JustDoPython/python-examples/tree/master/yeke/py-tank) :用 Python 写个坦克大战
1313
+ [py-scratch](https://github.com/JustDoPython/python-examples/tree/master/yeke/py-scratch) :不到 50 行 Python 代码,做个刮刮卡
14+
+ [py-xxl](https://github.com/JustDoPython/python-examples/tree/master/yeke/py-xxl) :用 Python 写个消消乐小游戏
1415

1516
---
1617

‎yeke/py-xxl/__init__.py

Whitespace-only changes.

‎yeke/py-xxl/py_xxl.py

Lines changed: 346 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,346 @@
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()

‎yeke/py-xxl/resources/images/gem1.png

4.51 KB
Loading[フレーム]

‎yeke/py-xxl/resources/images/gem2.png

4.48 KB
Loading[フレーム]

‎yeke/py-xxl/resources/images/gem3.png

3.68 KB
Loading[フレーム]

‎yeke/py-xxl/resources/images/gem4.png

5.12 KB
Loading[フレーム]

‎yeke/py-xxl/resources/images/gem5.png

4.13 KB
Loading[フレーム]

‎yeke/py-xxl/resources/images/gem6.png

4.62 KB
Loading[フレーム]

‎yeke/py-xxl/resources/images/gem7.png

2.9 KB
Loading[フレーム]

0 commit comments

Comments
(0)

AltStyle によって変換されたページ (->オリジナル) /