mcpField
M
math-essentials

math-essentials

github.com/jame581开发889169 次安装4 天前更新
代码仓库

关于 math-essentials

面向 Godot 4.3+ 开发者的游戏数学技能,附有 GDScript 和 C# 示例。内容覆盖向量、变换、插值、路径与随机数,并提供常见游戏数学配方及易错点说明。

主要功能

  1. 01向量运算与距离计算
  2. 02二维和三维变换
  3. 03插值、缓动与运动逼近
  4. 04曲线资源与路径跟随
  5. 05随机数和加权选择
  6. 06常见数学配方与问题排查

适用场景

  1. 01计算目标方向、距离和视野判定
  2. 02平滑跟随目标或精确移动到目标
  3. 03处理三维旋转与朝向插值
  4. 04生成可复现随机结果和程序化路径

安装

复制下面的命令,在终端中运行。

$npx skills add https://github.com/jame581/godotprompter --skill math-essentials

安装后在 Claude Code 中输入/math-essentials确认已加载。

包含的文件

除 SKILL.md 外,安装时一起下载的参考资料和脚本。

references/
curves-and-paths.md
game-math-recipes.md
random-numbers.md

详细介绍

Godot 4.3+ 中的游戏数学

所有示例均面向 Godot 4.3+,不使用已弃用的 API。先展示 GDScript,然后展示 C#。

**相关技能:**移动物理请参阅 player-controller,寻路数学请参阅 ai-navigation,摄像机插值请参阅 camera-system,缓动曲线请参阅 tween-animation,碰撞数学请参阅 physics-system。


1. 向量运算

常用向量方法
方法返回值描述
length()float向量的长度
length_squared()float长度的平方(速度更快,无需计算平方根)
normalized()Vector方向不变、长度为 1 的单位向量
distance_to(b)float两点之间的距离
distance_squared_to(b)float距离的平方(比较时速度更快)
direction_to(b)Vector从此点指向 b 的归一化方向
angle_to(b)float两个向量之间的弧度角
angle_to_point(b)float从此点到 b 的角度(2D)
dot(b)float点积
cross(b)float/Vector3叉积(2D 返回浮点数,3D 返回向量)
rotated(angle)Vector2按弧度旋转(2D)
move_toward(to, delta)Vector向目标移动,最多移动 delta
clamp(min, max)Vector限制各分量的范围
snapped(step)Vector对齐到网格
reflect(normal)Vector从表面反射
bounce(normal)Vector从表面弹回(反向反射)
slide(normal)Vector沿表面滑动
方向与距离
# Get direction from A to B (normalized)
var dir: Vector2 = global_position.direction_to(target.global_position)

# Get distance
var dist: float = global_position.distance_to(target.global_position)

# Use squared distance for comparisons (faster — avoids sqrt)
if global_position.distance_squared_to(target.global_position) < detection_range * detection_range:
    chase_target()
Vector2 dir = GlobalPosition.DirectionTo(target.GlobalPosition);
float dist = GlobalPosition.DistanceTo(target.GlobalPosition);

if (GlobalPosition.DistanceSquaredTo(target.GlobalPosition) < detectionRange * detectionRange)
    ChaseTarget();
点积

点积可用于判断两个向量的方向有多一致。

# Is the target in front of us? (dot > 0 = in front, < 0 = behind)
var forward: Vector2 = Vector2.RIGHT.rotated(rotation)
var to_target: Vector2 = global_position.direction_to(target.global_position)
var dot: float = forward.dot(to_target)

if dot > 0.7:  # roughly within ~45° cone
    print("Target is ahead")
elif dot < -0.7:
    print("Target is behind")
Vector2 forward = Vector2.Right.Rotated(Rotation);
Vector2 toTarget = GlobalPosition.DirectionTo(target.GlobalPosition);
float dot = forward.Dot(toTarget);

if (dot > 0.7f) GD.Print("Target is ahead");
叉积(3D)

叉积会生成一个垂直于两个输入向量的向量。

# Get the surface normal from two edge vectors
var edge1: Vector3 = vertex_b - vertex_a
var edge2: Vector3 = vertex_c - vertex_a
var normal: Vector3 = edge1.cross(edge2).normalized()
Vector3 edge1 = vertexB - vertexA;
Vector3 edge2 = vertexC - vertexA;
Vector3 normal = edge1.Cross(edge2).Normalized();

2. 变换

Transform2D

2D 变换包含位置、旋转和缩放。

# Get the global transform
var xform: Transform2D = global_transform

# Convert between local and global space
var local_point: Vector2 = to_local(global_point)
var world_point: Vector2 = to_global(local_point)

# Apply transform to a point
var transformed: Vector2 = xform * Vector2(10, 0)  # point in local space → global

# Inverse transform
var local: Vector2 = xform.affine_inverse() * global_point
Transform2D xform = GlobalTransform;
Vector2 localPoint = ToLocal(globalPoint);
Vector2 worldPoint = ToGlobal(localPoint);
Vector2 transformed = xform * new Vector2(10, 0);
Vector2 local = xform.AffineInverse() * globalPoint;
Transform3D 与 Basis
# Basis holds rotation and scale as 3 column vectors
var basis: Basis = global_transform.basis

# Forward direction (looking along -Z in Godot)
var forward: Vector3 = -basis.z
var right: Vector3 = basis.x
var up: Vector3 = basis.y

# Look at a target
look_at(target.global_position, Vector3.UP)

# Rotate around an axis
rotate_y(deg_to_rad(90.0))
rotate_object_local(Vector3.UP, deg_to_rad(45.0))

# Interpolate between two transforms (smooth transition)
var a: Transform3D = $Start.global_transform
var b: Transform3D = $End.global_transform
global_transform = a.interpolate_with(b, 0.5)  # halfway
Basis basis = GlobalTransform.Basis;
Vector3 forward = -basis.Z;
Vector3 right = basis.X;
Vector3 up = basis.Y;

LookAt(target.GlobalPosition, Vector3.Up);
RotateY(Mathf.DegToRad(90.0f));

Transform3D a = GetNode<Node3D>("Start").GlobalTransform;
Transform3D b = GetNode<Node3D>("End").GlobalTransform;
GlobalTransform = a.InterpolateWith(b, 0.5f);
is_orthonormal()(Godot 4.7+)

Basis.is_orthonormal()(const)在基底正交(各轴彼此垂直)且 归一化(每个轴的长度均为 1.0)时返回 true,在物理计算中尤其有用。它与 orthonormalized() 互为补充:先检查,只有累积的浮点误差导致基底不再归一化时,才重新正交归一化。

if not global_transform.basis.is_orthonormal():
    global_transform.basis = global_transform.basis.orthonormalized()
// GodotSharp has no IsOrthonormal(); compare with the orthonormalized basis instead.
Basis basis = GlobalTransform.Basis;
if (!basis.IsEqualApprox(basis.Orthonormalized()))
{
    GlobalTransform = new Transform3D(basis.Orthonormalized(), GlobalPosition);
}

3. 插值

lerp — 线性插值
# Interpolate between two values (t = 0.0 to 1.0)
var mid: float = lerp(0.0, 100.0, 0.5)   # 50.0
var pos: Vector2 = lerp(start_pos, end_pos, 0.75)  # 75% of the way

# Smooth following — lerp with delta for frame-rate independence
func _process(delta: float) -> void:
    position = position.lerp(target_position, 5.0 * delta)
float mid = Mathf.Lerp(0.0f, 100.0f, 0.5f);
Vector2 pos = startPos.Lerp(endPos, 0.75f);

public override void _Process(double delta)
{
    Position = Position.Lerp(targetPosition, 5.0f * (float)delta);
}

警告:lerp(a, b, speed * delta) 与帧率有关,并且永远无法完全到达目标。需要精确移动时,请改用 move_toward()。

move_toward — 定速接近
# Move exactly `speed * delta` units toward target each frame
position.x = move_toward(position.x, target_x, speed * delta)

# Vector version
position = position.move_toward(target_position, speed * delta)
float newX = Mathf.MoveToward(Position.X, targetX, speed * (float)delta);
Position = Position.MoveToward(targetPosition, speed * (float)delta);
slerp — 球面插值

用于平滑旋转插值(沿弧线旋转,而非直线)。

# Quaternion slerp for smooth 3D rotation
var current_quat: Quaternion = global_transform.basis.get_rotation_quaternion()
var target_quat: Quaternion = target_transform.basis.get_rotation_quaternion()
var result: Quaternion = current_quat.slerp(target_quat, 5.0 * delta)
global_transform.basis = Basis(result)
Quaternion currentQuat = GlobalTransform.Basis.GetRotationQuaternion();
Quaternion targetQuat = targetTransform.Basis.GetRotationQuaternion();
Quaternion result = currentQuat.Slerp(targetQuat, 5.0f * (float)delta);
GlobalTransform = new Transform3D(new Basis(result), GlobalPosition);
smoothstep — S 形缓动
# Returns 0.0 when x <= from, 1.0 when x >= to, smooth curve between
var t: float = smoothstep(0.0, 10.0, distance)  # 0→1 as distance goes 0→10

# Useful for soft thresholds (fog density, volume falloff)
var fog_intensity: float = smoothstep(50.0, 100.0, camera_distance)
cubic_interpolate — 平滑路径跟随
# Smooth interpolation using 4 control points (catmull-rom style)
var point: Vector2 = p1.cubic_interpolate(p2, p0, p3, t)
# p0 = before start, p1 = start, p2 = end, p3 = after end
插值对比
函数速度到达目标平滑用途
lerp(a, b, t)可变仅在 t=1 时到达是UI 过渡、混合
move_toward()恒定是否移动、计时器
slerp()可变仅在 t=1 时到达是旋转混合
smoothstep()S 曲线软阈值是雾效、体积、阈值
cubic_interpolate()可变仅在 t=1 时到达非常平滑路径、摄像机轨道

4. 曲线和路径

Curve 资源用于表示随时间变化的值(例如伤害衰减曲线)。Path2D / Path3D 用于定义空间路径,可由 PathFollow2D / PathFollow3D 采样——适用于移动平台、导弹制导和摄像机轨道。

关于 Curve 设置、Path 节点和 PathFollow 属性,请参阅 references/curves-and-paths.md。


5. 随机数生成

全局函数(randf()、randi() % N、randf_range(a, b))用于一次性随机取值。RandomNumberGenerator 用于可设定种子、可复现的随机取值(程序化生成、回放、存档状态)。可通过累积和法或别名法进行加权选择。

关于每种模式的完整 GDScript 代码,以及用于程序化生成噪声的 FastNoiseLite,请参阅 references/random-numbers.md。


6. 常用游戏数学示例

五个示例:朝向目标(2D Vector2.angle_to_point)、绕点旋转(极坐标)、正弦波浮动(浮动 UI 元素、宝藏)、角度归一化(-PI..PI 规范化)、带死区的钳制逼近(模拟输入 + 忽略微小输入)。

每个示例的即用代码请参阅 references/game-math-recipes.md。


7. 常见问题

症状原因修复方法
lerp 始终无法到达目标每帧使用 lerp(a, b, speed * delta)使用 move_toward() 实现精确抵达
旋转在 180° 处跳变使用了 lerp,而非 lerp_angle角度插值始终使用 lerp_angle()
对象朝向错误(3D)忘记 Godot 使用 -Z 作为前向前向为 -global_transform.basis.z
距离检查太慢对许多对象调用 distance_to使用 distance_squared_to,并与 range * range 比较
方向意外变成 (0, 0)零向量的 normalized() 会返回零使用前检查 length() > 0(或 is_zero_approx())
变换插值效果不正确对欧拉角进行插值,而非四元数使用 Quaternion.slerp() 或 Transform3D.interpolate_with()
重启后随机结果重复使用了固定种子的 RandomNumberGeneratorGodot 4.x 会自动为全局 RNG 设定种子;对于 RandomNumberGenerator,请使用 randomize() 或设置 seed
噪声值都约为 0frequency 太低提高 FastNoiseLite.frequency(尝试 0.01–0.1)

8. 实现检查清单

  • 距离比较使用 distance_squared_to() 以提升性能
  • 角度插值使用 lerp_angle(),而非 lerp()
  • 3D 前向方向为 -transform.basis.z,而非 +z
  • 需要精确到达目标时使用 move_toward()
  • 理解 lerp(a, b, speed * delta) 是依赖帧率的平滑跟随,而非精确移动
  • 使用 RandomNumberGenerator 实现确定性/可设定种子的随机效果(程序化生成、回放)
  • 基于噪声的生成使用 FastNoiseLite,并设置合适的频率和种子
  • 战利品表和基于概率的系统使用加权随机选择
  • 路径跟随使用 PathFollow2D/3D 和 progress 或 progress_ratio
  • 3D 旋转插值使用四元数球面线性插值,而非欧拉角

评论

支持 Markdown