摘要
本次使用python语言编写了一个记忆游戏,主要利用pygame库的一些类方法实现图形界面的编辑,事件的发生,实现一个简单的翻牌的记忆游戏。
1.引言
在进行完python课程的学习后,为了更好的掌握这个语言,以及学习一些相关库的用法,故在此编写了一个简单的游戏—记忆游戏。此游戏开始时先通过快速的翻牌显示,来展示每个牌上的图案。然后玩家通过鼠标选择点击进行翻牌,连续两次翻出一样图案的牌视为成功,不一样的图案则会重新覆盖牌子,玩家需要重新选择,直到所有牌子图案都显示出来了,则玩家获胜。
2. 介绍系统的结构
实现的功能:鼠标点击翻开牌子,两个相同的图案就显示,不相同就重新覆盖,定义五种图形,七种颜色,游戏开始时候提示随机8个牌子。
2.1 系统的界面结构
2.1.1主窗口
界面显示的整体窗口是所有牌子的容器,默认大小为1000px(宽)*480px(长),默认颜色为红色
2.1.2 牌(盒子)
是图案的容器,默认大小为40px*40px,每张牌有五种图案与七种颜色搭配使用。
2.1.3 图案
为牌子上显示的图案,默认为五种图案,分别为:甜甜圈,方块,钻石,线,椭圆
2.1.4 颜色
是界面窗口,牌等展示的颜色,默认有十种颜色,分别为:GRAY,
NAYYYELLOW, WHITE, RED, GREEN, BLUE, YELLOW, ORANGE, PURPLE, CYAN.
2.1.5边框
是当鼠标移动到牌的时候,显示的边框,包裹着牌子。默认颜色为红色
2.1.6 获胜提示
当取得胜利时,会显示“You Are Win!”的话语。
2.2 系统的功能逻辑
图1 功能逻辑
2.3 使用的相关模块及方法
2.3.1 pygame模块
Pygame.Init() 初始化
Pygame.Time.clock()创建一个对象来帮助跟踪时间。
Pygame.display.set_mode()初始化图形显示,建立一个画布
Pygame.display.set_caption()设计标题名
Pygame.rect(left,
top, width, height) rect对象是用来存储矩形对象的,rect对象有一些虚拟属性,
Pygam.event()
Event.pos()
2.3.2 random模块
2.3.3 sys 模块
3. 实现代码。
3.1相关定义初始化。
3.1.1 引入的模块
模块有:random, pygame, sys
代码实现:
import random, pygame, sys
from pygame.locals import *
3.2.2 默认值的声明定义(变量名,[变量值])
帧数:(FPS, 60)
窗口的宽:(WINDOWWIDTH,1000)
窗口的长:(WINDOWHEIGHT,480)
翻牌的显示速度:(REVEALSPEED,
8)
图案盒子的大小(正方形牌):(BOSXIZE,40)
盒子(牌)之间的间距:(GAPSIZE,10)
窗口盒子(牌)排列的横排数:(BOARDWIDTH, 5)
窗口盒子(牌)排列的横排数:(BOARDHEIGHT, 2)
盒子(牌)区域的横向外边距:(XMARGIN)
盒子(牌)区域纵向外边距:(YMARGIN)
颜色定义:
(GRAY,(100,100,100)))
(NAVYYELLOW,(255,165, 0)))
(WHITE = (255, 255, 255))
(RED = (255, 0, 0))
(GREEN= (0, 255, 0))
(BLUE= (0, 0, 255))
(YELLOW= (255, 255, 0))
(ORANGE= (255, 128, 0))
(PURPLE= (255, 0, 255))
(CYAN= (0, 255, 255))
图案:
DONUT = 'donut' # 甜甜圈
SQUARE = 'square' # 方块
DIAMOND = 'diamond' # 钻石
LINES = 'lines' # 线
OVAL = 'oval' # 椭圆
储存颜色的元组(
ALLCOLORS)
储存图案的元组( ALLSHAPES)
窗口背景色:(BGOLOR)
闪烁的时候变换的背景颜色:(LIGHTBGCOLOR)
盒子(牌)的颜色:(BOXCOLOR)
鼠标选中的时候外边框的颜色:(HIGHLIGHTCOLOR,RED)
代码实现:
FPS = 60 # 每秒的帧数,开局提示
WINDOWWIDTH = 1000 # 窗口的宽度
WINDOWHEIGHT = 480 # 窗口的长
REVEALSPEED = 8 # 翻盖的速度
BOXSIZE = 40 # 图案盒子的大小
GAPSIZE = 10 # 图之间的间距
BOARDWIDTH = 5 # 横排个数
BOARDHEIGHT = 2 # 纵排个数
assert (BOARDWIDTH * BOARDHEIGHT) % 2 == 0, 'Board needs to have an even number of boxes for pairs of matches.'
XMARGIN = int((WINDOWWIDTH - (BOARDWIDTH * (BOXSIZE + GAPSIZE))) / 2)
YMARGIN = int((WINDOWHEIGHT - (BOARDHEIGHT * (BOXSIZE + GAPSIZE))) / 2)
# R G B
GRAY = (100, 100, 100)
NAVYYELLOW = (255, 165, 0)
WHITE = (255, 255, 255)
RED = (255, 0, 0)
GREEN = ( 0, 255, 0)
BLUE = ( 0, 0, 255)
YELLOW = (255, 255, 0)
ORANGE = (255, 128, 0)
PURPLE = (255, 0, 255)
CYAN = ( 0, 255, 255)
BGCOLOR = NAVYYELLOW # 背景色
LIGHTBGCOLOR = GRAY # 闪烁时候的背景颜色
BOXCOLOR = WHITE # 盒子的颜色
HIGHLIGHTCOLOR = RED # 鼠标选中时候外边框的颜色
DONUT = 'donut' # 甜甜圈
SQUARE = 'square' # 方块
DIAMOND = 'diamond' # 钻石
LINES = 'lines' # 线
OVAL = 'oval' # 椭圆
# 定义两个元组ALLCOLRS 和 ALLSHAPES 分别存储 颜色 和 图案。
ALLCOLORS = (RED, GREEN, BLUE, YELLOW, ORANGE, PURPLE, CYAN)
ALLSHAPES = (DONUT, SQUARE, DIAMOND, LINES, OVAL)
assert len(ALLCOLORS) * len(ALLSHAPES) * 2 >= BOARDWIDTH * BOARDHEIGHT, "Board is too big for the number of shapes/colors defined."
3.2.3 函数的定义实现
根据系统的逻辑实现,得出以下一些实现相关功能逻辑的函数:
(1)主函数main()
变量的定义:
全局变量 FPSCLOCK 和 DISPLAYSURE:
前者用来存储一个对象来跟踪时间,后者用来存储显示的对象
mousex 和 mousex :
分别用来存储鼠标事件的x和y坐标
Fontobj和testsurfaceobj:
分别用来存储pygame的字体对象和输出胜利提示的文本
函数的实现逻辑:
首先初始化跟踪事件和显示的事件,然后调用函数实现牌的窗口的初始化、牌的初始化、监控鼠标的动态位置数据,以及相关的点击事件,来实现整个游戏流程。代码实现如下:
def main():
global FPSCLOCK, DISPLAYSURF
pygame.init() #初始化
FPSCLOCK = pygame.time.Clock() # 创建一个对象来帮助跟踪时间
DISPLAYSURF = pygame.display.set_mode((WINDOWWIDTH, WINDOWHEIGHT)) # 初始化显示
mousex = 0 # 存储鼠标时间的x坐标
mousey = 0 # 储存鼠标时间的y坐标
pygame.display.set_caption('游戏') # 设计标题
# 设置胜利时候输出语句
fontObj = pygame.font.Font('freesansbold.ttf',32)
textSurfaceObj = fontObj.render('You Are Win!',True,GREEN,BLUE)
textRectObj = textSurfaceObj.get_rect()
textRectObj.center=(200,150)
#初始化界面随机的图案排列
mainBoard = getRandomizedBoard()
#初始化每个图案的默认值为False
revealedBoxes = generateRevealedBoxesData(False)
firstSelection = None # 储存点击的第一个图的(x,y)
DISPLAYSURF.fill(BGCOLOR) # 设置背景颜色
# 游戏开始的动画
startGameAnimation(mainBoard)
while True: # 主游戏循环
mouseClicked = False
DISPLAYSURF.fill(BGCOLOR) # 绘制窗口
drawBoard(mainBoard, revealedBoxes)
for event in pygame.event.get(): # event 处理循环
if event.type == QUIT or (event.type == KEYUP and event.key == K_ESCAPE):
pygame.quit()
sys.exit()
elif event.type == MOUSEMOTION:
mousex, mousey = event.pos
elif event.type == MOUSEBUTTONUP:
mousex, mousey = event.pos
mouseClicked = True
boxx, boxy = getBoxAtPixel(mousex, mousey)
if boxx != None and boxy != None:
# 鼠标位于一个盒子里的事件(显示红色边框框)
if not revealedBoxes[boxx][boxy]:
drawHighlightBox(boxx, boxy)
if not revealedBoxes[boxx][boxy] and mouseClicked:
revealBoxesAnimation(mainBoard, [(boxx, boxy)])
revealedBoxes[boxx][boxy] = True # 将盒子设置为显示
if firstSelection == None: # 判断当前框是否第一个单击的框
firstSelection = (boxx, boxy)
else: # 第二个点击的框
# 检查两个图是否匹配
icon1shape, icon1color = getShapeAndColor(mainBoard, firstSelection[0], firstSelection[1])
icon2shape, icon2color = getShapeAndColor(mainBoard, boxx, boxy)
if icon1shape != icon2shape or icon1color != icon2color:
# 不匹配,隐藏显示
pygame.time.wait(250) # 250毫秒的等待时间
coverBoxesAnimation(mainBoard, [(firstSelection[0], firstSelection[1]), (boxx, boxy)])
revealedBoxes[firstSelection[0]][firstSelection[1]] = False
revealedBoxes[boxx][boxy] = False
elif hasWon(revealedBoxes): # 判断是否找出所有的对
gameWonAnimation(mainBoard)
DISPLAYSURF.fill(YELLOW)
DISPLAYSURF.blit(textSurfaceObj,textRectObj)
pygame.time.wait(000)
# 重置面板
mainBoard = getRandomizedBoard()
revealedBoxes = generateRevealedBoxesData(False)
drawBoard(mainBoard, revealedBoxes)
pygame.display.update()
pygame.time.wait(1000)
# 重新开始游戏动画
startGameAnimation(mainBoard)
firstSelection = None # 重置 firstSelection变量
pygame.display.update()
FPSCLOCK.tick(FPS)
(2)getRandomizedBoard(val)
将传进来的值(布尔值)赋予每个牌,可用来界面牌的初始化(也就是实现将所有牌盖起来了),存储进序列revealedBoxes[],并返回此值。
代码实现:
def generateRevealedBoxesData(val):
revealedBoxes = []
for i in range(BOARDWIDTH):
revealedBoxes.append([val] * BOARDHEIGHT)
return revealedBoxes
(3)getRandomizedBoard()
根据初始化声明BOARDWIDTH和BORDHEIGHT的值确定牌的种类数,以及图案的个数。将颜色与图案的组合利用随机函数随机排序,存进序列中。
代码实现如下:
def getRandomizedBoard():
# 用每种颜色列出每种可能的形状
icons = []
for color in ALLCOLORS:
for shape in ALLSHAPES:
icons.append( (shape, color) )
random.shuffle(icons) # 随机排序图标列表的顺序
numIconsUsed = int(BOARDWIDTH * BOARDHEIGHT / 2) # 计算要多少个图标
icons = icons[:numIconsUsed] * 2 # 图标双倍
random.shuffle(icons) # 将序列的所有元素随机排序
# 用随机放置的图片创建一个列表
board = []
for x in range(BOARDWIDTH):
column = []
for y in range(BOARDHEIGHT):
column.append(icons[0])
del icons[0] # 移除指定的图标
board.append(column)
return board
(4) splitIntoGrounpsOs(groupsize,
thelist)
接受传进来的值(开局每次提示的个数),序列,拆分列表,根据定义的个数把序列随机拆分,每组就是定义的个数(此处根据默认定义,传进来的值应该为8)
代码实现:
def splitIntoGroupsOf(groupSize, theList):
# 拆分列表的逻辑实现,为了开局的每次展示图案(8个)
result = []
for i in range(0, len(theList), groupSize):
result.append(theList[i:i + groupSize])
return result
(5)leftTopCoordsOfBox(boxx, boxy)
将传进来的鼠标的横纵坐标转为以盒子边距为参考的像素,外边距已声明。
代码实现:
def leftTopCoordsOfBox(boxx, boxy):
# 将面板转换为像素坐标
left = boxx * (BOXSIZE + GAPSIZE) + XMARGIN
top = boxy * (BOXSIZE + GAPSIZE) + YMARGIN
return (left, top)
(6)getBoxAtPixel(x, y)
确定盒子(牌)的像素位置区域,根据盒子(牌)的大小,以及盒子在横排纵排的个数确定。
此区域分别用boxx,和boxy来表示横向,纵向。
代码实现:
def getBoxAtPixel(x, y):
for boxx in range(BOARDWIDTH):
for boxy in range(BOARDHEIGHT):
left, top = leftTopCoordsOfBox(boxx, boxy)
boxRect = pygame.Rect(left, top, BOXSIZE, BOXSIZE)
if boxRect.collidepoint(x, y):
return (boxx, boxy)
return (None, None)
(7)drawIcon(shape, color, boxx, boxy)
绘制牌子图案,根据图型,颜色,以及盒子的区域,画出每一张牌上的图案。
代码实现如下:
def drawIcon(shape, color, boxx, boxy):
quarter = int(BOXSIZE * 0.25) # 语法糖
half = int(BOXSIZE * 0.5) # 语法糖
left, top = leftTopCoordsOfBox(boxx, boxy) # 从面板坐标获取像素坐标
# 画形状
if shape == DONUT:
pygame.draw.circle(DISPLAYSURF, color, (left + half, top + half), half - 5)
pygame.draw.circle(DISPLAYSURF, BGCOLOR, (left + half, top + half), quarter - 5)
elif shape == SQUARE:
pygame.draw.rect(DISPLAYSURF, color, (left + quarter, top + quarter, BOXSIZE - half, BOXSIZE - half))
elif shape == DIAMOND:
pygame.draw.polygon(DISPLAYSURF, color, ((left + half, top), (left + BOXSIZE - 1, top + half), (left + half, top + BOXSIZE - 1), (left, top + half)))
elif shape == LINES:
for i in range(0, BOXSIZE, 4):
pygame.draw.line(DISPLAYSURF, color, (left, top + i), (left + i, top))
pygame.draw.line(DISPLAYSURF, color, (left + i, top + BOXSIZE - 1), (left + BOXSIZE - 1, top + i))
elif shape == OVAL:
pygame.draw.ellipse(DISPLAYSURF, color, (left, top + quarter, BOXSIZE, half))
(7)getShapeAndColor(board, boxx, boxy)
用来存储每张盒子(牌)的图形以及颜色。
代码实现如下:
def getShapeAndColor(board, boxx, boxy):
# 存储x,y的形状值 board[x][y][0]
# 存储x,y的颜色值 board[x][y][1]
return board[boxx][boxy][0], board[boxx][boxy][1]
(8)drawBoxCovers(board, boxes, coverage)
用来实现盒子(牌)的副盖(翻)
代码实现如下:
def drawBoxCovers(board, boxes, coverage):
# 绘制覆盖/显示的盒子,是一个列表
# 两个列表分别存储x&y的位置
for box in boxes:
left, top = leftTopCoordsOfBox(box[0], box[1])
pygame.draw.rect(DISPLAYSURF, BGCOLOR, (left, top, BOXSIZE, BOXSIZE))
shape, color = getShapeAndColor(board, box[0], box[1])
drawIcon(shape, color, box[0], box[1])
if coverage > 0: # 判断是否覆盖
pygame.draw.rect(DISPLAYSURF, BOXCOLOR, (left, top, coverage, BOXSIZE))
pygame.display.update()
FPSCLOCK.tick(FPS) # 覆盖(翻牌)的速度
(9)revealBoxesAnimation(board, boxesToReveal)
用来实现展示盒子(牌)图案的动画
代码实现:
def revealBoxesAnimation(board, boxesToReveal):
# 盒子展示的动画
for coverage in range(BOXSIZE, (-REVEALSPEED) - 1, -REVEALSPEED):
drawBoxCovers(board, boxesToReveal, coverage)
(10)coverBoxesAnimation(board, boxesToCover)
用来实现隐藏盒子(牌)的动画
代码实现如下:
def coverBoxesAnimation(board, boxesToCover):
# 盒子覆盖的动画
for coverage in range(0, BOXSIZE + REVEALSPEED, REVEALSPEED):
drawBoxCovers(board, boxesToCover, coverage)
(11)drawBoard(board, revealed)
用来实现盒子(牌)的覆盖或者展示,用if判断传进来的值若是展示则进行覆盖操作,若传进来的值是覆盖,则画上(展示)此位置的图案。
代码实现如下:def drawBoard(board, revealed):
# 绘制框
for boxx in range(BOARDWIDTH):
for boxy in range(BOARDHEIGHT):
left, top = leftTopCoordsOfBox(boxx, boxy)
if not revealed[boxx][boxy]:
# 画一个有盖的盒子.
pygame.draw.rect(DISPLAYSURF, BOXCOLOR, (left, top, BOXSIZE, BOXSIZE))
else:
# 绘制(显示)图标
shape, color = getShapeAndColor(board, boxx, boxy)
drawIcon(shape, color, boxx, boxy)
(12)drawHighlightBox(boxx, boxy)
绘制当鼠标挪动到盒子(牌)时周边的红色边框。
代码实现:
def drawHighlightBox(boxx, boxy):
left, top = leftTopCoordsOfBox(boxx, boxy)
pygame.draw.rect(DISPLAYSURF, HIGHLIGHTCOLOR, (left - 5, top - 5, BOXSIZE + 10, BOXSIZE + 10), 4)
(13)startGameAnimation(board)
实现开始游戏时的提示,默认设置为每次随机展示八个盒子(牌)
代码实现如下:def startGameAnimation(board):
# 一次随机展示八个盒子
coveredBoxes = generateRevealedBoxesData(False) # 覆盖每个盒子
boxes = []
for x in range(BOARDWIDTH):
for y in range(BOARDHEIGHT):
boxes.append( (x, y) )
random.shuffle(boxes)
boxGroups = splitIntoGroupsOf(8, boxes) # 每次展示八个图案
drawBoard(board, coveredBoxes)
for boxGroup in boxGroups:
revealBoxesAnimation(board, boxGroup)
coverBoxesAnimation(board, boxGroup)
(14)gameWonAnimation(board)
玩家获胜时,背景会两种颜色交替闪烁。
代码实现如下:
def gameWonAnimation(board):
# 当玩家获胜时闪烁背景色
coveredBoxes = generateRevealedBoxesData(True)
color1 = LIGHTBGCOLOR
color2 = BGCOLOR
for i in range(6):
color1, color2 = color2, color1 # 墙的颜色
DISPLAYSURF.fill(color1)
drawBoard(board, coveredBoxes)
pygame.display.update()
pygame.time.wait(180)
(15)hasWon(revealedBoxes)
判断是否游戏胜利,当所有牌都是展示的时候就是游戏胜利,此时返回布尔值 True。
代码实现如下:
def hasWon(revealedBoxes):
# 判断是否胜利,胜利返回True 失败返回Falee
for i in revealedBoxes:
if False in i:
return False # 如果有覆盖的盒子,则返回False
return True
4. 实验。
4.1游戏界面展示
图2 游戏主界面
4.2 盒子图案展示
图3 游戏图案展示
4.3 翻牌成功展示
图4 游戏翻牌展示
图5 游戏翻牌
4.4 游戏胜利展示
图6 游戏胜利