我始终置信,使生存如此漂亮的,是咱们藏起来的真挚和童心。
六一节快到了,520 能够不过,然而儿童节相对不能错过,毕竟谁还不是个几百个月的宝宝了,而且凭借本身智商,过这个节齐全不成问题,哈哈哈哈哈。
作为程序媛,当然要用代码来过节啦,那就一起用 Python 开发个连连看小游戏来欢度六一吧!
从零造轮子那是不可能的,毕竟 Python 齐全零根底,所以还是采取参考他人的例子的形式,从了解剖析外加革新的过程中,来学习新的常识~
先来看一下最初的成果:
实现步骤如下:
一. 装置依赖包
装置 pygame 依赖包 python -m pip install pygame
二. 代码流程剖析
导入须要的依赖
import sys, time
import math, random
import threading
import pygame
from 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_list
image_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()
轻易找两张图片,重命名为代码中的名字即可,须要放在代码同层门路下。