上篇python连连看与记忆翻牌游戏(1)讲了连连看的核心判断实现。(最后的remove边界判断有点问题,没有先判断两者是否相等。感谢@井老师提醒)
原来的:
def remove(p1,p2):
# p1 和 p2 都在边界,可以直接删除
if (p1[0] == p2[0]) and (p1[0]==0 or p1[0]==len(array)-1):
print('上下边界可以直接删除')
elif (p1[1] == p2[1]) and (p1[1]==0 or p1[1]==len(array[0])-1):
print('上下边界可以直接删除')
这里直接前面加一句判断是否是同一个点:
def remove(p1,p2):
if p1[0]==p2[0] and p1[1]==p2[1]:
return False
# p1 和 p2 都在边界,可以直接删除
if (p1[0] == p2[0]) and (p1[0]==0 or p1[0]==len(array)-1):
print('上下边界可以直接删除')
elif (p1[1] == p2[1]) and (p1[1]==0 or p1[1]==len(array[0])-1):
print('上下边界可以直接删除')
今天我们主要用pygame实现连连看的界面以及基本的操作(实际连连看的消除下篇实现)。
视频效果(点击相同的两个可以消除):
步骤:
1.素材准备(我准备了两套):
resources
resources2
2.界面参数设置
class Config:
ROW_COUNT = 7
COLUMN_COUNT = 8
WIDTH = 80
HEIGHT = 80
MARGIN = 5
SCREEN_WIDTH = (WIDTH + MARGIN) * COLUMN_COUNT + MARGIN
SCREEN_HEIGHT = (HEIGHT + MARGIN) * ROW_COUNT + MARGIN
BG_COLOR = (200, 200, 200)
RESOURCE_DIR = 'resources'
3.界面分区
pygame的界面是一个整体,这里人为通过左边给界面划分区域,实现每一格可以独立控制。
在之前的生成游戏地图文件文章中有写过,不理解的可以看看,实际项目融合了里面的部分代码。
4.加载图片
首先把每张图片看成一个独立的单元,这里通过类封装其数据以及方法。
class ImageButton:
def __init__(self,screen,imgobj,x,y):
self.checked = False
self.checkable = True
self.screen = screen
self.imgobj = imgobj
self.x = x
self.y = y
def draw(self):
if self.checkable:
if self.checked:
pygame.draw.rect(self.imgobj, (255, 0, 0), [0, 0, Config.WIDTH, Config.HEIGHT], 4)
else:
pygame.draw.rect(self.imgobj, (0, 0, 0), [0, 0, Config.WIDTH, Config.HEIGHT], 4)
self.screen.blit(self.imgobj, (self.x, self.y))
主逻辑中加载图片素材,同时将其变成按钮对象。
class MyGame():
def __init__(self):
...
def load_imgs_obj(self):
imgs = os.listdir(Config.RESOURCE_DIR)
self.all_imgs = 4*imgs.copy() # 将图片复制4次
random.shuffle(self.all_imgs)
for img in self.all_imgs:
img_obj = pygame.image.load(f'{Config.RESOURCE_DIR}/{img}')
img_obj = pygame.transform.scale(img_obj, (Config.WIDTH, Config.HEIGHT))
self.all_imgs_obj.append(img_obj)
def init_imgs_obj(self):
for row in range(Config.ROW_COUNT):
for column in range(Config.COLUMN_COUNT):
x = (Config.MARGIN + Config.WIDTH) * column + Config.MARGIN
y = (Config.MARGIN + Config.HEIGHT) * row + Config.MARGIN
obj = ImageButton(self.screen,self.all_imgs_obj[row * Config.COLUMN_COUNT + column],x,y)
self.all_imgs_button.append(obj)
5.逻辑实现。
这里可以想象自己在玩这个游戏,首先游戏启动后,你会移动鼠标点击一张图片。然后找到和其一样的图片继续点击,相同图片就消失,不同图片则没有反应。
问题:
计算机如何知道你点击的是哪张图片,以及点击的前后顺序等?
解决:创建一个字典或者列表记录下来即可。
#存储两个点击的图片
self.match_img = {
1: {
"point": None, # 表示被点图片坐标
"pbtn_img": None # 被点击按钮对应的图片名
},
2: {
"point": None,
"pbtn_img": None
}
}
怎么获取对应的图片?根据鼠标点击的坐标,计算出点击图片的行列值,然后根据索引得到图片名,并将结果保存到字典中
def get_img(self,x,y):
column = int(x // (Config.WIDTH + Config.MARGIN))
row = int(y // (Config.HEIGHT + Config.MARGIN))
if row < Config.ROW_COUNT and column < Config.COLUMN_COUNT:
return (row, column)
# 鼠标检测
if event.type == pygame.MOUSEBUTTONDOWN:
self.pos_x, self.pos_y = pygame.mouse.get_pos()
if self.clicked_num <2:
point = self.get_img(self.pos_x,self.pos_y)
self.clicked_num += 1
# 存储按钮对应的图片位置和图片
self.match_img[self.clicked_num]["point"] = point
self.match_img[self.clicked_num]["pbtn_img"] = self.all_imgs[point[0]*Config.COLUMN_COUNT+point[1]]
最后判断是不是相同的图片,且不在一个位置。
def judge(self):
#判断图片是否配对
if self.clicked_num == 2:
#重置点击按钮次数
self.clicked_num = 0
#判断是否相同
if self.match_img[1]["pbtn_img"] == self.match_img[2]["pbtn_img"] and self.match_img[1]['point'] != self.match_img[2]['point']:
print('匹配成功')
# 隐藏按钮图片 设置为不可点击
self.all_imgs_button[self.match_img[1]["point"][0] * Config.COLUMN_COUNT + self.match_img[1]["point"][1]].checkable = False
self.all_imgs_button[self.match_img[2]["point"][0] * Config.COLUMN_COUNT + self.match_img[2]["point"][1]].checkable = False
else:
#恢复图片原始状态
print('不匹配')
self.all_imgs_button[self.match_img[1]["point"][0] * Config.COLUMN_COUNT + self.match_img[1]["point"][1]].checked = False
self.all_imgs_button[self.match_img[2]["point"][0] * Config.COLUMN_COUNT + self.match_img[2]["point"][1]].checked = False
#清空已保存的两张图片按钮信息
self.match_img[1]["point"] = self.match_img[2]["point"] = None
self.match_img[1]["pbtn_img"] = self.match_img[2]["pbtn_img"] = None
完整代码:
import pygame
import os
import random
class Config:
ROW_COUNT = 4
COLUMN_COUNT = 8
WIDTH = 80
HEIGHT = 80
MARGIN = 5
SCREEN_WIDTH = (WIDTH + MARGIN) * COLUMN_COUNT + MARGIN
SCREEN_HEIGHT = (HEIGHT + MARGIN) * ROW_COUNT + MARGIN
BG_COLOR = (200, 200, 200)
RESOURCE_DIR = 'resources2'
class ImageButton:
def __init__(self,screen,imgobj,x,y):
self.checked = False
self.checkable = True
self.screen = screen
self.imgobj = imgobj
self.x = x
self.y = y
def draw(self):
if self.checkable:
if self.checked:
pygame.draw.rect(self.imgobj, (255, 0, 0), [0, 0, Config.WIDTH, Config.HEIGHT], 4)
else:
pygame.draw.rect(self.imgobj, (0, 0, 0), [0, 0, Config.WIDTH, Config.HEIGHT], 4)
self.screen.blit(self.imgobj, (self.x, self.y))
class MyGame():
def __init__(self):
pygame.init()
self.screen = pygame.display.set_mode([Config.SCREEN_WIDTH, Config.SCREEN_HEIGHT])
self.clock = pygame.time.Clock()
self.grid = [[0 for _column in range(Config.COLUMN_COUNT)] for _row in range(Config.ROW_COUNT)]
#存储两个点击的图片
self.match_img = {
1: {
"point": None, # 表示被点图片坐标
"pbtn_img": None # 被点击按钮对应的图片名
},
2: {
"point": None,
"pbtn_img": None
}
}
self.clicked_num = 0
self.all_imgs = None
self.all_imgs_obj = []
self.all_imgs_button = []
self.pos_x = -1
self.pos_y = -1
self.load_imgs_obj()
self.init_imgs_obj()
def load_imgs_obj(self):
imgs = os.listdir(Config.RESOURCE_DIR)
self.all_imgs = 4*imgs.copy()
random.shuffle(self.all_imgs)
for img in self.all_imgs:
img_obj = pygame.image.load(f'{Config.RESOURCE_DIR}/{img}')
img_obj = pygame.transform.scale(img_obj, (Config.WIDTH, Config.HEIGHT))
self.all_imgs_obj.append(img_obj)
def init_imgs_obj(self):
for row in range(Config.ROW_COUNT):
for column in range(Config.COLUMN_COUNT):
x = (Config.MARGIN + Config.WIDTH) * column + Config.MARGIN
y = (Config.MARGIN + Config.HEIGHT) * row + Config.MARGIN
obj = ImageButton(self.screen,self.all_imgs_obj[row * Config.COLUMN_COUNT + column],x,y)
self.all_imgs_button.append(obj)
def get_img(self,x,y):
column = int(x // (Config.WIDTH + Config.MARGIN))
row = int(y // (Config.HEIGHT + Config.MARGIN))
if row < Config.ROW_COUNT and column < Config.COLUMN_COUNT:
return (row, column)
def process_event(self):
for event in pygame.event.get():
if event.type == pygame.QUIT:
exit()
# 鼠标左键检测
if event.type == pygame.MOUSEBUTTONDOWN:
self.pos_x, self.pos_y = pygame.mouse.get_pos()
if self.clicked_num <2:
point = self.get_img(self.pos_x,self.pos_y)
self.clicked_num += 1
# 存储按钮对应的图片位置和图片
self.match_img[self.clicked_num]["point"] = point
self.match_img[self.clicked_num]["pbtn_img"] = self.all_imgs[point[0]*Config.COLUMN_COUNT+point[1]]
self.all_imgs_button[self.match_img[1]["point"][0] * Config.COLUMN_COUNT + self.match_img[1]["point"][1]].checked = True
def judge(self):
#判断图片是否配对
if self.clicked_num == 2:
#重置点击按钮次数
self.clicked_num = 0
#判断是否相同
if self.match_img[1]["pbtn_img"] == self.match_img[2]["pbtn_img"] and self.match_img[1]['point'] != self.match_img[2]['point']:
print('匹配成功')
# 隐藏按钮图片 设置为不可点击
self.all_imgs_button[self.match_img[1]["point"][0] * Config.COLUMN_COUNT + self.match_img[1]["point"][1]].checkable = False
self.all_imgs_button[self.match_img[2]["point"][0] * Config.COLUMN_COUNT + self.match_img[2]["point"][1]].checkable = False
else:
#恢复图片原始状态
print('不匹配')
self.all_imgs_button[self.match_img[1]["point"][0] * Config.COLUMN_COUNT + self.match_img[1]["point"][1]].checked = False
self.all_imgs_button[self.match_img[2]["point"][0] * Config.COLUMN_COUNT + self.match_img[2]["point"][1]].checked = False
#清空已保存的两张图片按钮信息
self.match_img[1]["point"] = self.match_img[2]["point"] = None
self.match_img[1]["pbtn_img"] = self.match_img[2]["pbtn_img"] = None
def on_draw(self):
self.screen.fill(Config.BG_COLOR)
for imgbtn in self.all_imgs_button:
imgbtn.draw()
def on_update(self):
self.judge()
pygame.display.update()
self.clock.tick(30)
def run(self):
while True:
self.process_event()
self.on_draw()
self.on_update()
if __name__ == '__main__':
game = MyGame()
game.run()
上面的代码改改,可以变成记忆翻牌的游戏,加个遮挡即可。
下篇预告:将本篇的代码结合上一篇的连连看核心代码,实现完整可玩的连连看的游戏。
(全文完)