我始终置信,使生存如此漂亮的,是咱们藏起来的真挚和童心。
六一节快到了,520能够不过,然而儿童节相对不能错过,毕竟谁还不是个几百个月的宝宝了,而且凭借本身智商,过这个节齐全不成问题,哈哈哈哈哈。
作为程序媛,当然要用代码来过节啦,那就一起用 Python 开发个连连看小游戏来欢度六一吧!
从零造轮子那是不可能的,毕竟 Python 齐全零根底,所以还是采取参考他人的例子的形式,从了解剖析外加革新的过程中,来学习新的常识~
先来看一下最初的成果:
实现步骤如下:
一. 装置依赖包
装置 pygame 依赖包python -m pip install pygame
二. 代码流程剖析
导入须要的依赖
import sys, timeimport math, randomimport threadingimport pygamefrom pygame.locals import *
申明全局的设置类,并实例化设置对象
# 全局的设置类class Settings: # __init__办法负责对象的初始化 def __init__(self): # 定义游戏窗口大小(800*640) self.screen_size = (self.screen_width, self.screen_height) = (800, 640) # 游戏中的块数排列(8行*10列) self.game_size = (self.game_row, self.game_col) = (8, 10) # 连连看的总块数 self.map_total = self.game_row * self.game_col # 定义元素个数(共16种图片) self.element_num = 16 self.bg_color = (220, 220, 220) self.title = '智障儿童连连看' self.win_image = './images/win.png' self.lose_image = './images/lose.png' self.grid_size = 80 self.scale_size = (76, 76) self.points = [] # 实例化设置对象settings = Settings()
申明全局变量
# 图像列表映射map_list = []#global map_listimage_list = []# 点击右上角间接完结游戏标记位is_exit = False# 工夫截止标记位time_out = False;
申明图片按钮类,即每一个打消的小方块
# 图片按钮类class ImageBtn: __checkable = True __checked = False def __init__(self, screen, image_path, x, y, number, element): self.x = x self.y = y # 元素标号(1-16) self.element = element self.number = number self.screen = screen # 图片原始大小为 200x200,所以要进行缩放 self.image = pygame.transform.scale(pygame.image.load(image_path), settings.scale_size) def __del__(self): pass def display(self): # 描边 if self.__checked: pygame.draw.rect(self.image, (0,205,205,255), (0,0,self.image.get_width()-1,self.image.get_height()-1), 2) self.screen.blit(self.image, (self.x, self.y)) def hide(self): self.__checked = False self.__checkable = False # 图片不可见 self.image.fill((255, 255,240)) def is_checkable(self): return self.__checkable def click(self): self.__checked = not self.__checked return self.__checked def reset(self): self.__checked = False def get_geometry(self): return (int(self.x), int(self.y), settings.scale_size[0], settings.scale_size[1])
定义最外围的 程度扫描办法 horizontal_scan 和 垂直扫描办法 vertical_scan
# 程度扫描def horizontal_scan(points): """ 程度标定 以 p1 和 p2 所在的两个行作为基准线,而后用垂直线在无效范畴内扫描, 一旦发现可行门路,间接返回。 hLine1 和 hLine2 别离是扫描的上边线和下边线 leftLimit 和 rightLimit 别离是扫描的右边线和左边线 """ # 设置的列数为10 column = settings.game_col p1_x = int(points[0].number % column) p1_y = int(points[0].number / column) p2_x = int(points[1].number % column) p2_y = int(points[1].number / column) # 如果 p1 和 p2 在同一行,则不符合要求 if p1_y == p2_y: return False # 记录两条程度基准线 # hLine1 为上水平线,hLine2 为下水平线 if p1_y < p2_y: hLine1 = p1_y hLine2 = p2_y else: hLine1 = p2_y hLine2 = p1_y # 初始化左、左边界限为 0 leftLimit = 0; rightLimit = column-1 # 寻找左边界线 i = p1_x # 第一次扫描 # 当 i 大于 0 时,才进入循环 while i > 0: # 判断右边点是否为空 if map_list[p1_y * column+ i - 1] != 0: break # 当右边点为空时会持续扫描下一个右边点 i -= 1 leftLimit = i; # 第二次扫描 i = p2_x while i > 0: if map_list[p2_y * column + i - 1] != 0: break i -= 1 # leftLimit 记录左边界线,该界限所在的点为空或p1、p2自身 if i > leftLimit: leftLimit = i # 如果 leftLimit 为 0,阐明p1、p2曾经在外界接通了,间接返回 if leftLimit == 0: return True # 寻找左边界限 i = p1_x while i < column-1: if map_list[p1_y * column + i + 1] != 0: break; i += 1 rightLimit = i i = p2_x while i < column-1: if map_list[p2_y * column + i + 1] != 0: break i += 1 if i < rightLimit: rightLimit = i if rightLimit == column-1: return True # Bug # 判断 leftLimit 和 rightLimit if leftLimit > rightLimit: # 如果左边界线超出左边界限,则无奈连贯 return False else: # 从左往右扫描 for i in range(leftLimit, rightLimit+1): #print("从左往右扫描:%d -> %d"%(i, rightLimit)) j = hLine1 + 1 for j in range(hLine1+1, hLine2): # 只有当前列有妨碍,马上跳出 if map_list[j * column + i] != 0: # 回退一行 #j -= 1 break j += 1 if j == hLine2: # 程度扫描胜利 return True return False; # 垂直扫描def vertical_scan(points): """ 垂直标定 以 p1 和 p2 所在的两个列作为基准线,而后用水平线在无效范畴内扫描, 一旦发现可行门路,间接返回。 """ row = settings.game_row column = settings.game_col p1_x = int(points[0].number % column) p1_y = int(points[0].number / column) p2_x = int(points[1].number % column) p2_y = int(points[1].number / column) # 如果 p1 和 p2 在同一列,则不符合要求 if p1_x == p2_x: return False # 记录两条垂直基准线 if p1_x < p2_x: vLine1 = p1_x # 左垂直线 vLine2 = p2_x # 右垂直线 else: vLine1 = p2_x vLine2 = p1_x # 初始化上、下边界线 topLimit = 0 bottomLimit = row-1 # 寻找上边界线 i = p1_y # 第一次扫描 # 当 i 大于 0 时,才进入循环 while i > 0: if map_list[p1_x + (i-1) * column] != 0: break # 判断上边点是否为空 i -= 1 # 当上边点为空时会持续扫面下一个上边点 topLimit = i # 第二次扫描 i = p2_y while i > 0: if map_list[p2_x + (i-1) * column] != 0: break i -= 1 # topLimit 记录上边界线,该界限所在的点为空或p1、p2自身 if i > topLimit: topLimit = i # 如果 topLimit 为 0,阐明p1、p2曾经在外界接通了,间接返回 if topLimit == 0: return True # 寻找下边界线 i = p1_y while i < row-1: if map_list[p1_x + (i+1) * column] != 0: break i += 1 bottomLimit = i i = p2_y while i < row-1: if map_list[p2_x + (i+1) * column] != 0: break i += 1 if i < bottomLimit: bottomLimit = i if bottomLimit == row-1: return True # 判断 topLimit 和 bottomLimit if topLimit > bottomLimit: # 如果上边界线超出下边界线,则无奈连贯 return False else: # 从上往下扫描 for i in range(topLimit, bottomLimit+1): j = vLine1 + 1 for j in range(vLine1+1, vLine2): # 只有以后行有妨碍,马上跳出 if map_list[i * column + j] != 0: # 回退一列 #j -= 1 break j += 1 if j == vLine2: # 垂直扫描胜利 return True return False
定义其余操作方法,包含 判断是否能打消,事件处理(完结事件和点击事件),构建游戏图片布局,查看所有图片是否全副打消,游戏倒计时实现 共5个办法。
# 判断是否打消def can_clear(points): # 如果两个元素不雷同,还连个屁 if points[0].element != points[1].element: return False else: # 如果两个元素垂直能够相连或者程度能够相连 if vertical_scan(points) or horizontal_scan(points): return True else: return False # 事件处理(完结事件,按钮点击事件)def check_event(btn_list): """ Key event capture and key_control """ for event in pygame.event.get(): if event.type == QUIT: global is_exit is_exit = True #print("exit...") sys.exit() # 判断是否按下按钮 if event.type == MOUSEBUTTONDOWN: pos = pygame.mouse.get_pos() #print("Mouse button down, position:", pos) for btn in btn_list: geo = btn.get_geometry() x = geo[0]; y = geo[1]; w = geo[2]; h = geo[3] # 判断是否在图片块范畴内 if pos[0] > x and pos[0] < x+w and pos[1] > y and pos[1] < y+h: #print("在图片块范畴内:", id(btn)) # 如果图片还没被打消 if btn.is_checkable(): if not btn.click(): # 点本人是有效的 settings.points.clear() break # 后面曾经记录了一个点 if settings.points != []: settings.points.append(btn) # 查看是否雷同、是否相连 if can_clear(settings.points): # 毁灭它们 for point in settings.points: map_list[point.number] = 0 point.number = 0 point.hide() else: # 不匹配,复原图片状态 for point in settings.points: point.reset() # 判断结束,革除记录的点 settings.points.clear() # 后面没有记录点 else: # 记录第一个点 settings.points.append(btn) # 如果图片曾经被打消 break # 构建游戏图片布局def build_map(): t_list = [] m_list = [] # 成对数据构建 for i in range(0, settings.map_total, 2): # 随机生成成对的图片元素标号(1-16),寄存于 tmp_list e = math.ceil(random.random()*settings.element_num) # double append t_list.append(e) t_list.append(e) # 对全副数据进行打乱 for i in range(0, settings.map_total, 1): # 将 tmp_list 中的图片元素随机排列在 m_list index = int(random.random()*(settings.map_total-i)) m_list.append(t_list[index]) # 删除已保留到m_list中的元素 t_list.pop(index) return m_list # 查看是否全副打消def is_over(): for each in map_list: if each > 0: return False return True # 倒计时实现(默认120秒,超时则判断为游戏失败)def game_clock(): clock_ = 120 while clock_>0: global is_exit if is_exit: break clock_ = clock_ - 1 time.sleep(1) print("倒计时还剩:", clock_) print("工夫进行,游戏完结啦!") global time_out time_out = True;
定义主函数调用
def main(): # 初始化 Pygame pygame.init() # 创立一个游戏窗口 screen = pygame.display.set_mode(settings.screen_size, 0, 0) # 设置窗口题目 pygame.display.set_caption(settings.title) # 设置倒计时计时器 t = threading.Thread(target=game_clock,args=()); t.start(); # 筹备图片元素 global map_list map_list = build_map() # 创立图片列表 for i in range(0, settings.map_total): x = int(i%settings.game_col) * settings.grid_size + (settings.grid_size -settings.scale_size[0])/2 y = int(i/settings.game_col) * settings.grid_size + (settings.grid_size -settings.scale_size[0])/2 element = './images/element_'+str(map_list[i])+'.png' image_list.append(ImageBtn(screen, element, x, y, i, map_list[i])) play = True i = 0 while True: # 背景色彩填充 screen.fill(settings.bg_color) # 如果倒计时完结,提醒游戏输了 if time_out: youlose = pygame.image.load(settings.lose_image) screen.blit(youlose, ((settings.screen_width-youlose.get_width())/2,(settings.screen_height-youlose.get_height())/2)) elif play: if is_over(): play = False for im in image_list: im.display() else: youwin = pygame.image.load(settings.win_image) screen.blit(youwin, ((settings.screen_width-youwin.get_width())/2,(settings.screen_height-youwin.get_height())/2)) pygame.display.update() # 查看按键事件 check_event(image_list) time.sleep(0.04)
这里有一个须要留神的中央是,咱们的倒计时是须要独自开一个线程执行的,不然会阻塞主线程:
# 设置倒计时计时器t = threading.Thread(target=game_clock,args=());t.start();
三. 总结和晋升
通过对这个案例的学习和革新,咱们大略理解了 pygame 的罕用写法,来补充下基础知识吧。
- pygame模块
Hello World疾速动手模板
#导入pygame库import pygame#导入一些罕用的函数和常量from pygame.locals import *#向sys模块的exit函数用来退出程序from sys import exit # 指定图像文件名称background_image_filename = 'back.jpg'mouse_image_filename = 'center.jpg' #初始化pygame,为应用硬件做筹备pygame.init()#创立窗口screen = pygame.display.set_mode((480, 480), 0, 32)#设置窗口题目pygame.display.set_caption("Hello, World!")#加载并转换图像background = pygame.image.load(background_image_filename).convert()mouse_cursor = pygame.image.load(mouse_image_filename).convert_alpha() #游戏主循环while True: for event in pygame.event.get(): #接管到退出事件后退出程序 if event.type == QUIT: exit() #将背景图画下来 screen.blit(background, (0,0)) #取得鼠标地位 x, y = pygame.mouse.get_pos() #计算光标的左上角地位 x-= mouse_cursor.get_width() / 2 y-= mouse_cursor.get_height() / 2 #把光标画下来 screen.blit(mouse_cursor, (x, y)) #刷新一下画面 pygame.display.update()
轻易找两张图片,重命名为代码中的名字即可,须要放在代码同层门路下。