学习python编写一个小游戏(2020-07-08)

摘要

本次使用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 游戏胜利

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 205,386评论 6 479
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 87,939评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 151,851评论 0 341
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,953评论 1 278
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,971评论 5 369
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,784评论 1 283
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,126评论 3 399
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,765评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 43,148评论 1 300
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,744评论 2 323
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,858评论 1 333
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,479评论 4 322
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,080评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,053评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,278评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,245评论 2 352
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,590评论 2 343