mcpField
M
math-essentials

math-essentials

github.com/jame581開発889169 回インストール4 日前に更新
リポジトリ

math-essentials について

Godot 4.3+開発者向けのゲーム数学スキルで、GDScriptとC#の例を掲載しています。ベクトル、変換、補間、パス、乱数に加え、実用レシピと注意点を扱います。

主な機能

  1. 01ベクトル演算と距離判定
  2. 022D・3D変換
  3. 03補間、イージング、移動処理
  4. 04Curveリソースとパス追従
  5. 05乱数と重み付き選択
  6. 06定番レシピとトラブルシューティング

活用シーン

  1. 01ターゲットの方向、距離、正面判定の計算
  2. 02ターゲットへの滑らかな追従や正確な移動
  3. 033D回転と向きの補間処理
  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)float2 点間の距離
distance_squared_to(b)float距離の二乗(比較時に高速)
direction_to(b)Vectorこの点から b への正規化された方向
angle_to(b)float2 つのベクトル間の角度(ラジアン)
angle_to_point(b)floatこの点から b への角度(2D)
dot(b)float内積
cross(b)float/Vector3外積(2D では float、3D ではベクトルを返す)
rotated(angle)Vector2ラジアン単位で回転(2D)
move_toward(to, delta)Vectordelta 以内の距離で目標に近づける
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();
内積

内積を使うと、2 つのベクトルがどの程度同じ方向を向いているかがわかります。

# 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)

外積を求めると、入力した 2 つのベクトルに垂直なベクトルが得られます。

# 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. 乱数生成

1 回限りのランダム性には、グローバル関数(randf()、randi() % N、randf_range(a, b))を使用します。シード指定による再現可能なランダム性(プロシージャル生成、リプレイ、セーブ状態)には RandomNumberGenerator を使用します。重み付き選択には、累積和方式またはエイリアス法を使用します。

各パターンの GDScript 全文に加え、プロシージャル生成のノイズ用 FastNoiseLite については、references/random-numbers.md を参照してください。


6. よく使うゲーム数学のレシピ

5 つのレシピ:ターゲットを見る(2D の Vector2.angle_to_point)、点を中心に旋回する(極座標)、正弦波で揺らす(浮遊する UI 要素、宝物)、角度を折り返す(-PI..PI の正規化)、デッドゾーン付きの制限付き接近(アナログ入力と小さな入力の無視)。

各レシピですぐに使えるコードについては、references/game-math-recipes.md を参照してください。


7. よくある落とし穴

症状原因修正
lerp が目標に到達しない毎フレーム lerp(a, b, speed * delta) を使用している正確に到達させるには move_toward() を使用します
回転が 180° の位置で跳ねるlerp_angle ではなく lerp を使用している角度の補間には必ず 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() を使用します
再起動後にランダム結果が繰り返される固定シードで RandomNumberGenerator を使用しているGodot 4.x ではグローバル RNG のシードが自動設定されます。RandomNumberGenerator では randomize() を使用するか、seed を設定してください
ノイズ値がすべて約 0 になるfrequency が低すぎるFastNoiseLite.frequency を上げます(0.01~0.1 を試してください)

8. 実装チェックリスト

  • パフォーマンス向上のため、距離の比較には distance_squared_to() を使用している
  • 角度の補間には lerp() ではなく lerp_angle() を使用している
  • 3D の前方向は +z ではなく -transform.basis.z である
  • ターゲットに正確に到達する必要がある場合は、move_toward() を使用している
  • lerp(a, b, speed * delta) はフレームレートに依存する滑らかな追従であり、正確な移動ではないと理解している
  • 決定論的な乱数やシード付き乱数(プロシージャル生成、リプレイ)には RandomNumberGenerator を使用している
  • ノイズベースの生成には、適切な周波数とシードを指定して FastNoiseLite を使用している
  • 戦利品テーブルや確率ベースのシステムには、重み付きランダム選択を使用している
  • パスの追従には、progress または progress_ratio を指定して PathFollow2D/3D を使用している
  • 3D の回転補間にはオイラー角ではなく、クォータニオンの球面線形補間(slerp)を使用している

コメント