Python 现代开发学习笔记

(十一)图像、透明区域与资源路径

1. 从文件载入图像,再按尺寸复用

将方块换成图像时,仍然是把一份 Surface 放到屏幕上。Images 负责一次载入原图,之后按需要的尺寸生成并保存缩放结果:

# 项目文件:dodge_project/src/dodgegame/assets.py
# 资源以模块位置为起点加载,与运行命令所在目录无关;缩放结果按尺寸复用。
import logging
from pathlib import Path

import pygame

ASSET_DIR = Path(__file__).resolve().parent / "assets"
logger = logging.getLogger(__name__)


class Images:
    def __init__(self):
        self.originals = {
            name: pygame.image.load(ASSET_DIR / f"{name}.png").convert_alpha()
            for name in ("player", "obstacle")
        }
        self.cache = {}

    def get(self, name, size):
        key = name, tuple(size)
        if key not in self.cache:
            self.cache[key] = pygame.transform.smoothscale(self.originals[name], size)
        return self.cache[key]

image.load 读取文件,convert_alpha 建立适合当前显示、保留逐像素透明度的图像;因此先建立窗口,再准备这些资源。smoothscale 交回指定宽高的新 Surface,缓存键由图像名字和尺寸组成,后续相同请求直接复用。

资源路径从模块文件的位置取得,不依赖终端当前在哪个目录。配套的 player.png、obstacle.png 和两段短音效都已放在包内,生成代码也保留在 tools/make_assets.py,方便修改形状、颜色和声音。

资源从模块位置定位,原图载入一次,同一尺寸的缩放图重复使用而不在每帧重读文件

2. 用像素掩码区分透明部分

Rect 包围整张图像,透明角也包含在里面。若希望只按不透明像素判断,可以从图像建立 Mask:

# 项目文件:demos/mask_collision.py
# 两张透明图像的Rect相同,圆点却分离;平移第二张后,实际不透明像素才重叠。
import pygame

a = pygame.Surface((20, 20), pygame.SRCALPHA)
b = pygame.Surface((20, 20), pygame.SRCALPHA)
pygame.draw.circle(a, "white", (4, 10), 3)
pygame.draw.circle(b, "white", (16, 10), 3)
mask_a, mask_b = pygame.mask.from_surface(a), pygame.mask.from_surface(b)
rect_a, rect_b = a.get_rect(), b.get_rect()
print(rect_a.colliderect(rect_b), mask_a.overlap(mask_b, (0, 0)) is not None)
rect_b.x = -12
offset = rect_b.x - rect_a.x, rect_b.y - rect_a.y
print(rect_a.colliderect(rect_b), mask_a.overlap(mask_b, offset) is not None)

第一行是 True False:两个 Rect 重叠,但圆点分离。第二张向左平移 12 后得到 True True,圆点也重叠了。

mask_a.overlap(mask_b, offset) 的 offset 表示 b 左上角相对 a 的位置,交回某个重叠像素坐标或 None。它细分的是此刻的图像形状;前面的移动时间判断解决的是两次采样之间的路径。完整游戏使用矩形作为玩家和障碍的碰撞形状,F1 可以把实际使用的框画出来。

两个图像外框重叠而圆点分离,平移后不透明像素才在同一位置重叠

笔记正文与全部题解 · Ctrl / ⌘ K