数学工具类
ModSDK 提供了一套数学工具类,用于处理三维向量、旋转和矩阵变换。这些类位于 common.utils.mcmath 模块中,是编写涉及空间计算的 Mod 时不可或缺的基础工具。
from common.utils.mcmath import Vector3, Quaternion, Matrix坐标系约定
我的世界基岩版使用右手坐标系:
- X 轴:东(+) / 西(-)
- Y 轴:上(+) / 下(-)
- Z 轴:南(+) / 北(-)
玩家面朝北方时,前方为 Z 轴负方向。
Vector3
Vector3 表示三维向量或三维坐标点,是 ModSDK 中最常用的数学类型。实体坐标、运动方向、速度等场景都会用到它。
构造方式
from common.utils.mcmath import Vector3
# 直接传入三个分量
v1 = Vector3(1.0, 2.0, 3.0)
# 从元组构造
pos_tuple = (10, 64, -20)
v2 = Vector3(pos_tuple)预设静态方法
快速获取常用向量,无需手动输入分量:
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)属性
| 属性 | 返回类型 | 说明 |
|---|---|---|
x | float | X 分量(可读写) |
y | float | Y 分量(可读写) |
z | float | Z 分量(可读写) |
magnitude | float | 向量长度(模),即 √(x²+y²+z²) |
sqrMagnitude | float | 向量长度的平方,比 magnitude 更高效,适合距离比较 |
normalized | Vector3 | 返回单位化后的新向量(不修改自身) |
实例方法
| 方法 | 返回类型 | 说明 |
|---|---|---|
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 | 将向量投影到指定方向上 |
运算符
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 与阈值比较:
if (a - b).sqrMagnitude < 0.01:
# 视为相同位置
pass实战示例
计算两个实体之间的距离:
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 的单位方向:
direction = (vec_b - vec_a).normalized
print("方向向量: {}".format(direction.ToString()))在两个位置之间进行平滑插值(用于动画):
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(四元数)用于表示旋转。相比欧拉角,四元数没有万向锁问题,并且支持平滑插值。在处理实体朝向、模型旋转等场景时推荐使用。
构造方式
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 分量。
属性
| 属性 | 返回类型 | 说明 |
|---|---|---|
x | float | 四元数 X 分量 |
y | float | 四元数 Y 分量 |
z | float | 四元数 Z 分量 |
w | float | 四元数 W 分量 |
eulerAngles | Vector3 | 转换为欧拉角(度),返回 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 | 逆旋转 |
运算符
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 的结果不同。乘法顺序表示先应用右侧旋转,再应用左侧旋转。
实战示例
旋转一个方向向量:
# 假设我们要将一个向前的方向向量绕 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)平滑旋转过渡:
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 仅在高级变换需求时使用。
from common.utils.mcmath import Matrix核心方法
| 方法 | 返回类型 | 说明 |
|---|---|---|
Matrix() | Matrix | 构造空矩阵 |
Matrix.Identity() | Matrix | 4×4 单位矩阵 |
SetTRS(pos, rot, scale) | None | 设置平移(Vector3)、旋转(Quaternion)、缩放(Vector3) |
Matrix.TRS(pos, rot, scale) | Matrix | 静态方法,直接返回 TRS 矩阵 |
inverse | Matrix | 逆矩阵(属性) |
m * other | Matrix | 矩阵乘法,组合两个变换 |
# 创建一个 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 结合使用,实现在玩家面朝方向上发射一个抛射物。
# -*- 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 可以避免开方运算,提升性能:
# 不推荐:每帧开方
if Vector3.Distance(a, b) < 5.0:
pass
# 推荐:比较距离的平方
if (a - b).sqrMagnitude < 25.0: # 5.0 * 5.0 = 25.0
pass