碰撞

Pyxen 使用基于网格的碰撞系统。碰撞体位于空间网格上,引擎每帧检测它们之间的重叠。这使碰撞检测简单、快速且确定性。

网格地图和网格碰撞体

GridMap 定义空间世界——一个给定单元大小的图块网格。GridBody 是存在于该网格中的碰撞矩形。

拥有 GridMap 的实体必须是拥有 GridBody 的实体的父节点。引擎在单个 GridMap 内独立处理所有碰撞,这意味着你可以在同一个世界中运行多个不同的 GridMap,它们之间不会相互影响。

level = world.spawn(
    map=GridMap(rows=20, columns=20, size=(16, 16), image="tileset")
)

player = world.spawn(
    parent=level,
    body=GridBody(pos=(32, 32), size=(16, 16), tag=1, mask=(2,))
)

实体位置

实体的位置 (x, y) 由 GridMap 碰撞处理自动推导。当引擎处理碰撞体移动时,会更新实体的变换以匹配碰撞体的最终位置。对于拥有 GridBody 的实体,你不手动设置 x/y——碰撞系统拥有它们的位置。

移动碰撞体

要移动带碰撞体的实体,设置碰撞体的 move 属性。引擎会尝试移动并处理碰撞:

def update():
    player.body.move = (0, 0)
    if input.keyboard.right.down:
        player.body.move = (2, 0)

引擎移动碰撞体、检查碰撞并调整最终位置。

标签和掩码

标签和掩码控制哪些碰撞体可以相互碰撞。

当一个移动中的碰撞体遇到另一个碰撞体或图块,且其标签在掩码中时,它会沿着它滑动而不是穿过。这给你带来了平滑的实体碰撞,可以沿着墙壁和障碍物滑动。

player = world.spawn(
    parent=level,
    body=GridBody(pos=(0, 0), size=(16, 16), tag=1, mask=(2, 3))
)

enemy = world.spawn(
    parent=level,
    body=GridBody(pos=(0, 0), size=(16, 16), tag=2, mask=(1,))
)

玩家与标签 2 和 3(敌人和墙壁)碰撞。敌人与标签 1(玩家)碰撞。标签为 0 或没有匹配掩码的碰撞体会互相穿过。

碰撞回调

当碰撞体碰到图块时,引擎调用 on_hit_tile:

def on_hit_tile(entity, tile):
    # entity hit a solid tile
    pass

当两个碰撞体重叠时,引擎调用 on_hit_body:

def on_hit_body(entity, other):
    if entity == player:
        entity.health.value -= 1

这些在碰撞阶段运行,在 update() 之后、渲染之前。

图块地图碰撞

GridMap 定义了一个图块网格,图块可以使用图块标签标记为实体:

level.map.set(row=0, column=5, tile=(1, 0), tag=2)

掩码包含该标签的任何碰撞体都会沿该图块滑动。

工作原理

碰撞系统是基于网格的,不是像素精确的。碰撞体是在网格中移动的轴对齐矩形。模拟在每个 GridMap 内独立进行——每个 GridMap 实体运行自己独立的碰撞处理。这使碰撞:

完整 API 参见 GridBody 和 GridMap。