Skip to content

数学工具类 ​

ModSDK 提供了一套数学工具类,用于处理三维向量、旋转和矩阵变换。这些类位于 common.utils.mcmath 模块中,是编写涉及空间计算的 Mod 时不可或缺的基础工具。

python
from common.utils.mcmath import Vector3, Quaternion, Matrix

坐标系约定

我的世界基岩版使用右手坐标系:

  • X 轴:东(+) / 西(-)
  • Y 轴:上(+) / 下(-)
  • Z 轴:南(+) / 北(-)

玩家面朝北方时,前方为 Z 轴负方向。

Vector3 ​

Vector3 表示三维向量或三维坐标点,是 ModSDK 中最常用的数学类型。实体坐标、运动方向、速度等场景都会用到它。

构造方式 ​

python
from common.utils.mcmath import Vector3

# 直接传入三个分量
v1 = Vector3(1.0, 2.0, 3.0)

# 从元组构造
pos_tuple = (10, 64, -20)
v2 = Vector3(pos_tuple)

预设静态方法 ​

快速获取常用向量,无需手动输入分量:

python
Vector3.Zero()      # (0, 0, 0)
Vector3.One()       # (1, 1, 1)
Vector3.Up()        # (0, 1, 0)
Vector3.Down()      # (0, -1, 0)
Vector3.Left()      # (-1, 0, 0)
Vector3.Right()     # (1, 0, 0)
Vector3.Forward()   # (0, 0, 1)
Vector3.Back()      # (0, 0, -1)

属性 ​

属性返回类型说明
xfloatX 分量(可读写)
yfloatY 分量(可读写)
zfloatZ 分量(可读写)
magnitudefloat向量长度(模),即 √(x²+y²+z²)
sqrMagnitudefloat向量长度的平方,比 magnitude 更高效,适合距离比较
normalizedVector3返回单位化后的新向量(不修改自身)

实例方法 ​

方法返回类型说明
Set(x, y, z)None设置向量分量
Normalize()None将自身单位化(就地修改)
ToString()str返回格式化字符串
Floor()Vector3各分量向下取整,返回新向量
ToTuple()tuple转为 (x, y, z) 元组,可直接传入引擎 API
CopyVector3()Vector3深拷贝,避免引用问题

静态方法 ​

方法返回类型说明
Distance(a, b)float两点之间的距离
Dot(a, b)float点积,可用于判断方向关系
Cross(a, b)Vector3叉积,返回垂直于两个向量的向量
Angle(fromVec, toVec)float两个向量之间的夹角(度)
Lerp(a, b, t)Vector3线性插值,t 取值 0~1
Min(a, b)Vector3各分量取最小值
Max(a, b)Vector3各分量取最大值
Reflect(dir, normal)Vector3反射向量
Project(vec, normal)Vector3将向量投影到指定方向上

运算符 ​

python
a = Vector3(1, 2, 3)
b = Vector3(4, 5, 6)

a + b          # Vector3(5, 7, 9)
a - b          # Vector3(-3, -3, -3)
a * 2.0        # Vector3(2, 4, 6)  标量乘法
a / 2.0        # Vector3(0.5, 1, 1.5)  标量除法
-a             # Vector3(-1, -2, -3)  取反
a == b         # False
a != b         # True
a[0]           # 1  按索引访问 (0=x, 1=y, 2=z)

避免浮点比较陷阱

由于浮点精度问题,== 比较可能不可靠。对于距离判断,建议使用 sqrMagnitude 与阈值比较:

python
if (a - b).sqrMagnitude < 0.01:
    # 视为相同位置
    pass

实战示例 ​

计算两个实体之间的距离:

python
import mod.server.extraServerApi as serverApi

comp = serverApi.GetEngineCompFactory().CreatePos(entity_a)
pos_a = comp.GetPos()

comp = serverApi.GetEngineCompFactory().CreatePos(entity_b)
pos_b = comp.GetPos()

vec_a = Vector3(pos_a)
vec_b = Vector3(pos_b)
dist = Vector3.Distance(vec_a, vec_b)
print("两实体距离: {}".format(dist))

获取从 A 指向 B 的单位方向:

python
direction = (vec_b - vec_a).normalized
print("方向向量: {}".format(direction.ToString()))

在两个位置之间进行平滑插值(用于动画):

python
start = Vector3(0, 64, 0)
end = Vector3(10, 70, 10)

# t 从 0 到 1,表示插值进度
for i in range(11):
    t = i / 10.0
    current = Vector3.Lerp(start, end, t)
    print("t={:.1f} -> {}".format(t, current.ToString()))

Quaternion ​

Quaternion(四元数)用于表示旋转。相比欧拉角,四元数没有万向锁问题,并且支持平滑插值。在处理实体朝向、模型旋转等场景时推荐使用。

构造方式 ​

python
from common.utils.mcmath import Quaternion

# 直接传入四元数分量(一般不直接使用)
q1 = Quaternion(0, 0, 0, 1)

# 从欧拉角构造(推荐,单位为度)
# 参数含义:绕 X 轴旋转, 绕 Y 轴旋转, 绕 Z 轴旋转
q2 = Quaternion.Euler(0, 90, 0)  # 绕 Y 轴旋转 90 度

TIP

大多数情况下使用 Quaternion.Euler() 创建旋转即可,不需要手动设置四元数的 x, y, z, w 分量。

属性 ​

属性返回类型说明
xfloat四元数 X 分量
yfloat四元数 Y 分量
zfloat四元数 Z 分量
wfloat四元数 W 分量
eulerAnglesVector3转换为欧拉角(度),返回 Vector3

静态方法 ​

方法返回类型说明
Identity()Quaternion无旋转的四元数 (0,0,0,1)
Euler(x, y, z)Quaternion从欧拉角(度)创建四元数
AngleAxis(angle, axis)Quaternion绕指定轴旋转指定角度(度)
LookRotation(forward, up)Quaternion创建一个朝向 forward 方向的旋转,up 为参考上方向
Lerp(a, b, t)Quaternion线性插值,t 取值 0~1
Slerp(a, b, t)Quaternion球面插值,比 Lerp 更平滑,推荐用于旋转动画
Angle(a, b)float两个旋转之间的角度差(度)
Inverse(q)Quaternion逆旋转

运算符 ​

python
q1 = Quaternion.Euler(0, 90, 0)
q2 = Quaternion.Euler(90, 0, 0)

# 四元数相乘 = 组合旋转(先 q2 再 q1)
combined = q1 * q2

# 旋转一个向量
direction = Vector3.Forward()  # (0, 0, 1)
rotated = q1 * direction       # 将方向绕 Y 轴旋转 90 度

旋转顺序

四元数乘法不满足交换律,q1 * q2 和 q2 * q1 的结果不同。乘法顺序表示先应用右侧旋转,再应用左侧旋转。

实战示例 ​

旋转一个方向向量:

python
# 假设我们要将一个向前的方向向量绕 Y 轴旋转 45 度
forward = Vector3(0, 0, 1)
rotation = Quaternion.Euler(0, 45, 0)
new_direction = rotation * forward
print("旋转后方向: {}".format(new_direction.ToString()))
# 结果约为 (0.707, 0, 0.707)

平滑旋转过渡:

python
rot_start = Quaternion.Euler(0, 0, 0)
rot_end = Quaternion.Euler(0, 180, 0)

# 使用 Slerp 进行球面插值
for i in range(11):
    t = i / 10.0
    current = Quaternion.Slerp(rot_start, rot_end, t)
    angles = current.eulerAngles
    print("t={:.1f} -> Y轴旋转: {:.1f}度".format(t, angles.y))

Matrix ​

Matrix 表示 4×4 变换矩阵,可以同时表达平移、旋转和缩放。在绝大多数 Mod 开发场景中 Vector3 + Quaternion 已经足够,Matrix 仅在高级变换需求时使用。

python
from common.utils.mcmath import Matrix

核心方法 ​

方法返回类型说明
Matrix()Matrix构造空矩阵
Matrix.Identity()Matrix4×4 单位矩阵
SetTRS(pos, rot, scale)None设置平移(Vector3)、旋转(Quaternion)、缩放(Vector3)
Matrix.TRS(pos, rot, scale)Matrix静态方法,直接返回 TRS 矩阵
inverseMatrix逆矩阵(属性)
m * otherMatrix矩阵乘法,组合两个变换
python
# 创建一个 TRS 矩阵
pos = Vector3(10, 64, 10)
rot = Quaternion.Euler(0, 45, 0)
scale = Vector3.One()

mat = Matrix.TRS(pos, rot, scale)

TIP

如果你不确定是否需要 Matrix,那大概率不需要。优先使用 Vector3 和 Quaternion 的组合来完成空间变换。

综合实战:向玩家视线方向发射抛射物 ​

下面是一个完整的服务端脚本示例,将 Vector3 和 Quaternion 结合使用,实现在玩家面朝方向上发射一个抛射物。

python
# -*- coding: utf-8 -*-
import mod.server.extraServerApi as serverApi
from common.utils.mcmath import Vector3, Quaternion

ServerSystem = serverApi.GetServerSystemCls()

class ProjectileLauncherSystem(ServerSystem):
    def __init__(self, namespace, system_name):
        ServerSystem.__init__(self, namespace, system_name)
        self.ListenForEvent(
            serverApi.ModNamespace,
            serverApi.ModSystemName,
            'ServerItemUseOnEvent',
            self,
            self.OnItemUse
        )

    def OnItemUse(self, args):
        player_id = args.get('entityId')
        if not player_id:
            return

        # ---- 1. 获取玩家位置 ----
        pos_comp = serverApi.GetEngineCompFactory().CreatePos(player_id)
        player_pos = pos_comp.GetPos()
        if not player_pos:
            return
        origin = Vector3(player_pos)
        # 将发射点抬高到眼睛位置(约 1.62 格)
        origin.y += 1.62

        # ---- 2. 获取玩家旋转角度 ----
        rot_comp = serverApi.GetEngineCompFactory().CreateRot(player_id)
        rot_tuple = rot_comp.GetRot()
        if not rot_tuple:
            return
        # rot_tuple = (pitch, yaw)
        # pitch: 上下看的角度,向上为负,向下为正
        # yaw: 左右看的角度
        pitch, yaw = rot_tuple

        # ---- 3. 用 Quaternion 构造朝向旋转 ----
        # 先绕 Y 轴旋转 yaw,再绕 X 轴旋转 pitch
        rotation = Quaternion.Euler(pitch, yaw, 0)

        # ---- 4. 基础前方向量(MC 中面朝南为 Z+) ----
        forward = Vector3(0, 0, 1)

        # ---- 5. 将前方向量旋转到玩家视线方向 ----
        look_direction = rotation * forward
        look_direction = look_direction.normalized

        # ---- 6. 设定速度和发射偏移 ----
        speed = 2.0  # 每 tick 移动的格数
        velocity = look_direction * speed

        # 发射点稍微往前偏移,避免与玩家碰撞
        spawn_pos = origin + look_direction * 0.5

        # ---- 7. 创建抛射物实体 ----
        entity_comp = serverApi.GetEngineCompFactory().CreateProjectile(
            serverApi.GetLevelId()
        )
        proj_id = entity_comp.CreateProjectileEntity(
            player_id,                  # 发射者
            'minecraft:arrow',          # 抛射物标识符
            spawn_pos.ToTuple()         # 生成坐标(元组)
        )

        if proj_id:
            # 设置抛射物速度
            motion_comp = serverApi.GetEngineCompFactory().CreateActorMotion(
                proj_id
            )
            motion_comp.SetMotion(velocity.ToTuple())

            print("发射成功!方向: {} 速度: {}".format(
                look_direction.ToString(),
                velocity.ToString()
            ))

代码要点解析 ​

步骤用到的类/方法作用
获取位置Vector3(tuple)将引擎返回的元组转为 Vector3 便于计算
构造旋转Quaternion.Euler(pitch, yaw, 0)用玩家的俯仰角和偏航角构造旋转
旋转方向rotation * forward将基础前方向量旋转到玩家的实际视线方向
计算速度direction * speed标量乘法,将方向缩放为速度向量
传回引擎vector.ToTuple()引擎 API 接受元组格式,用 ToTuple 转换

性能提示

在 tick 级别的高频计算中(如每帧更新),使用 sqrMagnitude 代替 magnitude 可以避免开方运算,提升性能:

python
# 不推荐:每帧开方
if Vector3.Distance(a, b) < 5.0:
    pass

# 推荐:比较距离的平方
if (a - b).sqrMagnitude < 25.0:  # 5.0 * 5.0 = 25.0
    pass