pygame 小游戏准备工作要做足&pygame 游戏开场动画渲染学习

721 阅读8分钟

「这是我参与11月更文挑战的第9天,活动详情查看:2021最后一次更文挑战

本系列专栏将通过不断编写游戏的方式,带你夯实 Python 知识。

这篇博客的目的

本文继续为你补充 pygame 中的一些小知识点,后面开始写一个知名案例,较于其他博客,本篇博客拆解及其细致。

导入 pygame 中常用的常量 使用下述代码,可以导入 pygame 中常用的常量。

from pygame.locals import *

locals 中内置了如下常量。

['ACTIVEEVENT', 'ANYFORMAT', 'APPACTIVE', 'APPFOCUSMOUSE', 'APPINPUTFOCUS', 'ASYNCBLIT', 'AUDIODEVICEADDED', 'AUDIODEVICEREMOVED', 'AUDIO_ALLOW_ANY_CHANGE', ...]

总数可以通过下述代码获取,达到了 551 个。

import pygame
from pygame.locals import *
print(len(dir(pygame.locals)))

所以通过代码导入之后,此 551 个常量,不需要在通过 pygame.x 方式进行调用了,可以直接使用常量名称。

下面三种方式调用的数据是一致的。

print(pygame.BLEND_MAX)
print(pygame.locals.BLEND_MAX)
print(BLEND_MAX)

多应用自定义常量 pygame 进行游戏编写时,经常会遇到需要数字变量的时候,此时可以设置为下述内容:

WIDTH = 400
HEIGHT = 300
FPS = 30

熟练使用该形式代码,可以大幅度的增加代码的可读性。 对于部分字符串,也可以声明成常量,便于后续的使用。

SQUARE = 'square'
LINES = 'lines'

Memory Puzzle 记忆拼图 该案例来源自书籍中的案例,互联网已经有相关的完整代码,本阶段学习将对该案例进行拆解,重新梳理实现逻辑。

游戏主框架部分代码 本部分决定了游戏的一个主体结构,也是编写过程中最简单的点。

import pygame
import random
import sys
from pygame.locals import *

FPS = 30
WIDTH = 600
HEIGHT = 400

# 颜色常量
WHITE = (255, 255, 255)
TURQUOISE = (64, 224, 208)

# 场景颜色
BGCOLOR = TURQUOISE


# 游戏运行入口函数
def main():
    # pygame 模块初始化
    pygame.init()
    # pygame 时钟初始化
    FPSCLOCK = pygame.time.Clock()

    # 设置游戏窗口
    SCREEN = pygame.display.set_mode((WIDTH, HEIGHT))

    # 游戏窗口标题
    pygame.display.set_caption("记忆拼图")

    # 背景填充
    SCREEN.fill(BGCOLOR)
    # 游戏主循环
    while True:
        # 事件处理
        for event in pygame.event.get():
            # 按下 ESC 键或者点击关闭,退出程序
            if event.type == QUIT or (event.type == KEYDOWN and event.key == K_ESCAPE):
                pygame.quit()
                sys.exit()

        # 更新屏幕
        pygame.display.update()
        # 设置帧率
        FPSCLOCK.tick(FPS)


if __name__ == '__main__':
    main()

上述代码基于之前的博客内容实现,运行效果如下:

pygame 小游戏前的准备工作要做足

设置方块

方块目前参考案例给出的是 10 x 7 个,我们需要做的就是生成 70 个不同的形状与颜色的数据。本部分核心用到的代码在图后。

pygame 小游戏前的准备工作要做足

代码中主要的注释部分都已经提供,编写的时候从 main_board = get_random_board() 函数调用开始进行编写。

import pygame
import random
import sys
from pygame.locals import *

FPS = 30
WIDTH = 600
HEIGHT = 400

# 设置横向盒子的数量
BOARDWIDTH = 10
# 设置纵向盒子的数量
BOARDHEIGHT = 7

# 颜色常量
WHITE = (255, 255, 255)
TURQUOISE = (64, 224, 208)
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 = TURQUOISE

# 定义形状
DONUT = 'donut'  # 甜甜圈
SQUARE = 'square'  # 方形
DIAMOND = 'diamond'  # 钻石
LINES = 'lines'  # 多条线
OVAL = 'oval'  # 椭圆

ALLCOLORS = (RED, GREEN, BLUE, YELLOW, ORANGE, PURPLE, CYAN)

ALLSHAPES = (DONUT, SQUARE, DIAMOND, LINES, OVAL)


# 游戏运行入口函数
def main():
    # pygame 模块初始化
    pygame.init()
    # pygame 时钟初始化
    FPSCLOCK = pygame.time.Clock()

    # 设置游戏窗口
    SCREEN = pygame.display.set_mode((WIDTH, HEIGHT))

    # 游戏窗口标题
    pygame.display.set_caption("记忆拼图")

    # ********************
    # 游戏中的 board 数据生成
    main_board = get_random_board()
    # ********************

    # 背景填充
    SCREEN.fill(BGCOLOR)

    # 游戏主循环
    while True:
        # 事件处理
        for event in pygame.event.get():
            # 按下 ESC 键或者点击关闭,退出程序
            if event.type == QUIT or (event.type == KEYDOWN and event.key == K_ESCAPE):
                pygame.quit()
                sys.exit()

        # 更新屏幕
        pygame.display.update()
        # 设置帧率
        FPSCLOCK.tick(FPS)


# 游戏中的拼图数据生成函数主体
def get_random_board():
    # 全列举所有的颜色和形状,进行组合之后的结果存在 icons 中
    icons = []
    for color in ALLCOLORS:
        for shape in ALLSHAPES:
            icons.append((shape, color))

    # 列表乱序
    random.shuffle(icons)

    # 计算需要多少种的元素(也就是整个 BORDER 里所有 box(目前有 70 个) 的一半)
    num_icons_used = int(BOARDWIDTH * BOARDHEIGHT / 2)
    # 将上面得到的元素复制一份(得到的结果就可以两两配对)
    icons = icons[:num_icons_used] * 2

    # 再次打乱 icons 里的元素的顺序
    random.shuffle(icons)

    # 创建用来存放 board 数据的结构,将上面处理好的 icons 分组放入 board 中
    board = []
    for x in range(BOARDWIDTH):
        # 列集合
        column = []
        for y in range(BOARDHEIGHT):
            # 每次都添加第一个元素
            # print(icons[0])
            column.append(icons[0])
            # 删除已经复制的元素
            del icons[0]
        board.append(column)
    # print(board)
    return board

if __name__ == '__main__':
    main()

代码运行之后,生成的结构体如下,10 行 7 列,好像和一开始的设置有些出入,后面如果碰到问题,在进行修改。

[[('lines', (0, 255, 0)), ('square', (255, 255, 0)), ('donut', (0, 255, 0)), ('donut', (0, 255, 255)), ('square', (255, 128, 0)), ('diamond', (255, 128, 0)), ('square', (255, 255, 0))],
[('diamond', (0, 255, 255)), ('oval', (255, 0, 0)), ('donut', (255, 128, 0)), ('diamond', (255, 255, 0)), ('square', (0, 0, 255)), ('oval', (255, 128, 0)), ('oval', (255, 0, 0))],
[('oval', (255, 0, 255)), ('donut', (0, 255, 255)), ('donut', (255, 255, 0)), ('donut', (0, 0, 255)), ('square', (255, 0, 0)), ('square', (0, 255, 255)), ('diamond', (0, 255, 0))],
[('square', (255, 0, 255)), ('oval', (255, 128, 0)), ('oval', (255, 255, 0)), ('oval', (255, 255, 0)), ('oval', (0, 255, 255)), ('lines', (0, 255, 0)), ('lines', (255, 0, 0))],
[('lines', (255, 255, 0)), ('lines', (255, 255, 0)), ('lines', (0, 255, 255)), ('donut', (255, 0, 0)), ('donut', (0, 255, 0)), ('oval', (255, 0, 255)), ('lines', (255, 128, 0))],
[('diamond', (255, 0, 0)), ('donut', (255, 255, 0)), ('diamond', (0, 0, 255)), ('diamond', (255, 0, 255)), ('square', (0, 255, 255)), ('oval', (0, 0, 255)), ('diamond', (0, 255, 0))],
[('diamond', (0, 0, 255)), ('donut', (255, 0, 255)), ('oval', (0, 255, 0)), ('lines', (0, 255, 255)), ('square', (0, 0, 255)), ('square', (0, 255, 0)), ('oval', (0, 255, 255))],
[('donut', (0, 0, 255)), ('lines', (255, 0, 255)), ('diamond', (255, 255, 0)), ('square', (255, 0, 0)), ('oval', (0, 0, 255)), ('lines', (255, 0, 255)), ('square', (255, 0, 255))],
[('diamond', (255, 128, 0)), ('lines', (0, 0, 255)), ('lines', (0, 0, 255)), ('donut', (255, 0, 255)), ('donut', (255, 128, 0)), ('lines', (255, 0, 0)), ('lines', (255, 128, 0))],
[('oval', (0, 255, 0)), ('donut', (255, 0, 0)), ('square', (0, 255, 0)), ('square', (255, 128, 0)), ('diamond', (0, 255, 255)), ('diamond', (255, 0, 0)), ('diamond', (255, 0, 255))]]

本篇代码先到此为止,下篇继续完善。

本系列专栏将通过不断编写游戏的方式,带你夯实 Python 知识。

这篇博客的目的

今天的主要目标就是绘制 10*7 个小方块,最终实现的效果图如下所示。

pygame 游戏开场动画渲染学习,绘制 10*7=70 个小方块

逻辑实现

在界面上进行正方形绘制,主要搞定坐标即可,方块颜色白色,宽度设计为 40 像素。

核心函数调用为:

# 游戏开场动画
start_game_animation(main_board)

函数主体内容

# 游戏入场动画函数
def start_game_animation(board):
    # 默认获取到的都是 False
    covered_boxes = generate_revealed_boxes(False)
    draw_board(board, covered_boxes)

该函数内部调用了 generate_revealed_boxes()draw_board() 两个函数。

generate_revealed_boxes()

# 生成一个 10*7 的数组,用来存放 box 的状态
def generate_revealed_boxes(val):
    revealed_boxes = []
    for i in range(BOARDWIDTH):
        revealed_boxes.append([val] * BOARDHEIGHT)

    return revealed_boxes

该函数会返回一个 7x10 结构,值都为 False 的二维列表。

[[False, False, False, False, False, False, False],
 [False, False, False, False, False, False, False],
 ……

draw_board() 该函数用于绘制白色方块。

# 绘制默认 box
def draw_board(board, revealed):

    for boxx in range(BOARDWIDTH):
        for boxy in range(BOARDHEIGHT):
            left, top = lefttop_coords_box(boxx, boxy)
            if not revealed[boxx][boxy]:
                # 绘制一个被覆盖的box
                pygame.draw.rect(SCREEN, BOXCOLOR, (left, top, BOXSIZE, BOXSIZE))
            else:
                pass

这里又出现了一个新的函数 lefttop_coords_box(boxx, boxy),函数用途是传入方块的 x 坐标和 y 坐标,绘制一个矩形。

坐标转换函数内容如下:

# 计算出 box 的左上角的像素坐标
def lefttop_coords_box(boxx, boxy):
    # 将数字坐标转化成像素坐标
    left = boxx * (BOXSIZE + GAPSIZE) + XMARGIN
    top = boxy * (BOXSIZE + GAPSIZE) + YMARGIN
    return (left, top)

用到的全局常量,具体设置参考下述代码,重点理解 XMARGINYMARGIN ,这两个值为的是将渲染出的整体区域进行居中展示。

FPS = 30
WIDTH = 640
HEIGHT = 480

# 设置横向盒子的数量
BOARDWIDTH = 10
# 设置纵向盒子的数量
BOARDHEIGHT = 7
# 设置box的大小
BOXSIZE = 40
# 设置box间的间隔
GAPSIZE = 10
# 计算距离 x 轴边缘的距离
XMARGIN = int((WIDTH - (BOARDWIDTH * (BOXSIZE + GAPSIZE))) / 2)
# 计算距离 y 轴边缘的距离
YMARGIN = int((HEIGHT - (BOARDHEIGHT * (BOXSIZE + GAPSIZE))) / 2)

本系列专栏属于番外篇,希望你能学到新知识。 有任何疑问,都可以联系橡皮擦进行解决,一起做游戏吧 本专栏每天的练习量大概在 1 小时左右,整篇博客节奏会比较快,毕竟咱们是有基础的人。