不知道方向就先走着,最近又无所事事,准备照着别人的代码写一下 Mario 游戏,参照链接:https://github.com/mx0c/super-mario-python
虽然这位作者有写是受另一个项目启发,但是有些地方比如依赖关系也挺混乱的,但好在项目很完整,是用 pygame 写的,照着写总比自己瞎折腾容易吧。
先找出没有依赖其他文件的代码,可以作为后续其他代码的零件,这里选择 https://github.com/mx0c/super-mario-python/blob/master/classes/Spritesheet.py
文件结构直接按参考项目创建,主要用 cairo.ImageSurface 对象替代 pygame.image 对象,并另外实现 set_colorkey 指定颜色作为透明色。笔记和想法都直接写到注释里了。
# SpriteSheet.py
from cairo import ImageSurface, Context, Rectangle, FORMAT_ARGB32
from mario.classes.Utils import Utils
"""
cairo.ImageSurface 提供了一个灵活和强大的方式来在内存中创建和操作图像
在 Cairo 中,一切都被视为绘图操作,即使是对图像的修改也被视为在绘图上下文中进行的操作。
创建图像表面(Surface)的主要目的是为了提供一个统一的接口,使得对不同类型的绘图目标(比如窗口、PDF 文档、图像文件等)进行绘制操作时,能够使用相同的 API 和模式。
总之,将图像存储在 Surface 中的主要目的是为了提供一个统一的、灵活的、可移植的绘图接口,使得开发者可以更容易地进行绘图操作,并且可以轻松地将相同的绘图操作应用于不同的输出目标。
"""
class SpriteSheet:
def __init__(self, filename):
try:
# 将 PNG 格式的精灵序列图片转换为 Surface
self.sprite_sheet = ImageSurface.create_from_png(filename)
except Exception as e:
print(str(e))
raise SystemExit
def image_at(self, x, y, scale=1, color_key=None, is_column_row=True, tile_width=16, tile_height=16):
if is_column_row:
# x, y 作为行和列,取精灵图范围
rect = Rectangle(x * tile_width, y * tile_height, tile_width, tile_height)
else:
# x, y 作为坐标,取精灵图范围
rect = Rectangle(x, y, tile_width, tile_height)
# 从精灵序列图中提取指定区域并创建为新的 Surface,surface.get_width() 结果为 0
# surface = self.sprite_sheet.create_for_rectangle(*rect)
# 创建缩放尺寸的空 Surface
surface = ImageSurface(FORMAT_ARGB32, int(tile_width * scale), int(tile_height * scale))
# 创建与指定 Surface 相关联的 Cairo 绘图上下文,这样就可以在指定的 Surface 上进行绘制操作。
ctx = Context(surface)
# 缩放 surface。缩放操作仅对后续绘制的几何图形进行缩放变换,而不会立即对图形上下文中的路径或 Surface 进行填充或绘制。
ctx.scale(scale, scale)
# self.sprite_sheet 为源 Surface,源 Surface 的坐标参数是指定其在目标 Surface 上的绘制位置,即源左上角相对于目标左上角作为原点的坐标。
ctx.set_source_surface(self.sprite_sheet, -rect.x, -rect.y)
# 应用所有设置样式进行填充
ctx.paint()
if color_key:
if color_key == (-1, -1, -1):
# 取 surface 像素数据
data = surface.get_data()
# 取 surface 第一个点 rgb 像素值
color_key = tuple(data[:3])
else:
# surface data 实际排列为 bgra
color_key = color_key[::-1]
# 将 color_key 设为透明
surface = Utils.set_colorkey(surface, color_key)
return surface
# Utils.py
from cairo import ImageSurface, FORMAT_ARGB32
class Utils:
@staticmethod
def set_colorkey(surface, color_key):
# 获取 Surface 的像素数据
data = surface.get_data()
# 获取 Surface 的宽度和高度
width = surface.get_width()
height = surface.get_height()
# 遍历像素数据
for y in range(height):
for x in range(width):
# 获取当前像素的索引
index = y * width * 4 + x * 4
# 获取当前像素的颜色
pixel_color = (data[index], data[index + 1], data[index + 2])
# 如果当前像素的颜色与目标颜色相同,则将 ALPHA 值设为 0
if pixel_color == color_key:
data[index + 3] = 0
# 将修改透明值的 data 转为 ImageSurface 对象
return ImageSurface.create_for_data(data, FORMAT_ARGB32, width, height)
# test_SpriteSheet.py
import unittest
from mario.classes.SpriteSheet import SpriteSheet
# 仅包括功能测试,不包括异常测试
class TestSpriteSheet(unittest.TestCase):
def test_spritesheet_init(self):
ss = SpriteSheet("./img/tiles.png")
self.assertEquals(ss.sprite_sheet.get_width(), 528)
def test_spritesheet_image_at_is_column_row(self):
ss = SpriteSheet("./img/tiles.png")
surface = ss.image_at(0, 3)
# surface.write_to_png("./test/test_SpriteSheet.png")
self.assertEquals(surface.get_width(), 16)
def test_spritesheet_image_at_not_column_row(self):
ss = SpriteSheet("./img/tiles.png")
surface = ss.image_at(17, 0, is_column_row=False)
# surface.write_to_png("./test/test_SpriteSheet2.png")
self.assertEquals(surface.get_width(), 16)
def test_spritesheet_image_at_scale_zoom_in(self):
ss = SpriteSheet("./img/tiles.png")
surface = ss.image_at(0, 0, scale=10)
# surface.write_to_png("./test/test_SpriteSheet3.png")
self.assertEquals(surface.get_width(), 160)
def test_spritesheet_image_at_scale_zoom_out(self):
ss = SpriteSheet("./img/tiles.png")
surface = ss.image_at(0, 0, scale=0.5)
# surface.write_to_png("./test/test_SpriteSheet4.png")
self.assertEquals(surface.get_width(), 8)
def test_spritesheet_image_at_color_key_default(self):
ss = SpriteSheet("./img/tiles.png")
surface = ss.image_at(0, 0, color_key=(-1, -1, -1))
# surface.write_to_png("./test/test_SpriteSheet5.png")
self.assertEquals(tuple(surface.get_data()[:4]), (0, 74, 156, 0))
def test_spritesheet_image_at_color_key_custom(self):
ss = SpriteSheet("./img/tiles.png")
surface = ss.image_at(0, 0, color_key=(255, 206, 198))
# surface.write_to_png("./test/test_SpriteSheet6.png")
self.assertEquals(tuple(surface.get_data()[4:8]), (198, 206, 255, 0))
if __name__ == '__main__':
unittest.main(verbosity=2)