还是继续找文件依赖少的代码,有些必要的被依赖文件也需要先完成。
Font.py 中 Font 类居然继承 Spritesheet 类来加载字符对应图片,还和 Sprites 类的功能差不多,总感觉哪里不太对。https://github.com/mx0c/super-mario-python/blob/5623213c2eb001d8dabd95eb7035d7545e86ccf6/classes/Font.py
Math.py 中写了名为二维向量的类,但只是简单记录了坐标。
Tile.py 只是画红色边框矩形,实际用途未知。
GaussianBlur.py 高斯模糊,其视觉效果就像是经过一个毛玻璃在观察图像。来源:https://baike.baidu.com/item/%E9%AB%98%E6%96%AF%E6%A8%A1%E7%B3%8A/10885810 。wxPython 提供了方便的接口,直接调用 wx.Image.Blur 方法即可实现。
entities 目录中 EntityBase.py 主要实现了实体的状态的记录和更新。几个状态走跳跑都在 traits 目录单独创建了类,每个类方法还都不一样,这是丐版有限状态机吧。
Camera.py 摄像机,这里的功能是记录坐标,跟随实体移动坐标。
基本都没法写测试啊,后面待完成的是声音、鼠标键盘输入、碰撞检测、实体和主要显示类:面板、关卡、菜单、暂停画面。
代码:
# Font.py
from mario.classes.SpriteSheet import SpriteSheet
class Font(SpriteSheet):
def __init__(self, file_path, size):
SpriteSheet.__init__(self, filename=file_path)
self.chars = " !\"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_`abcdefghijklmnopqrstuvwxyz{|}~"
# 字符与字符图片 Sprite 对象组成的键值对
self.char_sprites = self.load_font()
def load_font(self):
font = {}
row = 0
column = 0
for char in self.chars:
# 字符图片中字符有 16 列
if column == 16:
column = 0
row += 1
font.update(
{
char: self.image_at(
column,
row,
scale=2,
color_key=(0, 0, 0),
tile_width=8,
tile_height=8
)
}
)
column += 1
return font
# Maths.py
class Vec2D:
def __init__(self, x=0, y=0):
self.x = x
self.y = y
# Tile.py
from cairo import Context
class Tile:
def __init__(self, sprite, rect):
self.sprite = sprite
self.rect = rect
def draw_rect(self, screen):
try:
# 等定义 screen 后再修改
ctx = Context(screen)
# 创建矩形路径,只是在 Context 中当前路径添加一个矩形描述,并不会实际绘制
ctx.rectangle(*self.rect)
# 设置绘制颜色,后续描边和填充都会用到,rgb 取值 0 到 1
ctx.set_source_rgb(1, 0, 0)
# 后续绘制的线条宽度
ctx.set_line_width(1)
# 描边,实际绘制矩形
ctx.stroke()
except Exception:
pass
# GaussianBlur.py
import wx
import wx.lib.wxcairo
class GaussianBlur:
def __init__(self, blur_radius=2):
self.blur_radius = blur_radius
def filter(self, original_surface):
# 将原始 wx.ImageSurface 对象转为 wx.Image 对象
image = wx.lib.wxcairo.BitmapFromImageSurface(original_surface).ConvertToImage()
# 调用模糊方法
blurred_image = image.Blur(self.blur_radius)
# 转回 Surface
blurred_surface = wx.lib.wxcairo.ImageSurfaceFromBitmap(blurred_image.ConvertToBitmap())
return blurred_surface
# EntityBase.py
from wx import Rect
from mario.classes.Maths import Vec2D
class EntityBase(object):
def __init__(self, x, y, gravity):
self.vel = Vec2D()
self.rect = Rect(x * 32, y * 32, 32, 32) # 标准 tile 尺寸
self.gravity = gravity # 重力
self.traits = None # 状态
self.alive = True
self.active = True
self.bouncing = False
self.time_after_death = 5
self.timer = 0
self.type = ""
self.on_ground = False
self.obey_gravity = True
def apply_gravity(self):
# 下落的纵轴量更大,相当于添加了重力加速度
if self.obey_gravity:
self.vel.y += self.gravity
def update_traits(self):
# 几个状态走跳跑都单独创建了类,每个类方法还都不一样,这是丐版有限状态机吧
for trait in self.traits.values():
try:
trait.update()
except AttributeError:
pass
def get_pos_index(self):
# 按 32 * 32 尺寸划分网格的索引
return Vec2D(self.rect.x // 32, self.rect.y // 32)
def get_pos_index_float(self):
return Vec2D(self.rect.x / 32.0, self.rect.y / 32.0)
# Camera.py
from mario.classes.Maths import Vec2D
class Camera:
def __init__(self, pos, entity):
self.pos = Vec2D(pos.x, pos.y)
self.entity = entity # EntityBase 对象
self.x = self.pos.x * 32
self.y = self.pos.y * 32
def move(self):
# 跟随实体移动
xpos_float = self.entity.get_pos_index_float().x
if 10 < xpos_float < 50:
self.pos.x = -xpos_float + 10
self.x = self.pos.x * 32
self.y = self.pos.y * 32