module
Eagle::Mathf
Overview
Scalar helpers for game math: angles, interpolation, clamping, snapping and smoothing.
Everything takes any Number and returns Float32, so you can mix Int32
literals with Float32 fields without casting. The module is extend self,
so call functions as Mathf.lerp(...).
The most useful ones in day-to-day game code:
#lerp/#inverse_lerp/#remapconvert between ranges (health to bar width, distance to volume).#dampsmooths a value toward a target at the same speed regardless of frame rate. Use it instead ofa = lerp(a, b, 0.1), which runs faster at higher FPS.#move_towardsteps toward a target by a fixed amount without overshooting, which suits acceleration and cooldown timers.#angle_diff/#lerp_angleturn toward an angle the short way round.
class Turret < Node2D
@target : Vec2 = v2(400, 300)
def process(dt : Float32) : Nil
# Turn toward the target, smoothly and frame-rate independent.
want = (@target - position).angle
self.rotation = Mathf.lerp_angle(rotation, want, 1 - Math.exp(-8 * dt))
end
end
volume = Mathf.remap(120, 0, 400, 1.0, 0.0) # => 0.7 (quieter as distance grows)
speed = Mathf.move_toward(0, 300, 50) # => 50.0 (accelerate by 50 per call)
Extended Modules
Defined in:
eagle/math/math.crConstant Summary
-
EPS =
1e-6_f32 -
Default tolerance used by
#approx?. -
PI =
Math::PI.to_f32 -
π as a
Float32, so it mixes with engine fields without casting. -
TAU =
(Math::PI * 2).to_f32 -
One full turn in radians (2π). Handy for "spin once per second":
rotation += Mathf::TAU * dt.
Instance Method Summary
-
#angle_diff(from : Number, to : Number) : Float32
Shortest signed difference from from to to, in -π..π.
-
#approx?(a : Number, b : Number, eps = EPS) : Bool
True when a and b differ by at most eps.
-
#clamp(v : Number, lo : Number, hi : Number)
Restricts v to lo..hi.
-
#clamp01(v : Number) : Float32
Restricts v to 0..1 and returns a
Float32. -
#damp(a : Number, b : Number, lambda : Number, dt : Number) : Float32
Frame-rate independent smoothing: moves a toward b, and higher lambda moves faster.
- #damp(a : Vec2, b : Vec2, lambda : Number, dt : Number) : Vec2
- #damp(a : Vec3, b : Vec3, lambda : Number, dt : Number) : Vec3
-
#deg2rad(d : Number) : Float32
Converts degrees to radians.
-
#inverse_lerp(a : Number, b : Number, v : Number) : Float32
The inverse of
#lerp: where v sits between a and b, as a fraction. -
#lerp(a : Number, b : Number, t : Number) : Float32
Linear interpolation: returns a when t is 0, b when t is 1, and a proportional mix in between.
-
#lerp_angle(from : Number, to : Number, t : Number) : Float32
Interpolates between two angles the short way round, so 350° to 10° goes through 0° instead of spinning backwards.
-
#move_toward(from : Number, to : Number, delta : Number) : Float32
Moves from toward to by at most delta and never overshoots.
-
#ping_pong(v : Number, length : Number) : Float32
Bounces v back and forth between 0 and length, for patrols and pulsing effects.
-
#rad2deg(r : Number) : Float32
Converts radians to degrees, for display or for authoring in degrees.
-
#remap(v : Number, a : Number, b : Number, c : Number, d : Number) : Float32
Maps v from the range a..b onto the range c..d.
-
#sign(v : Number) : Int32
Returns -1, 0 or 1 depending on the sign of v.
-
#smoothstep(a : Number, b : Number, t : Number) : Float32
Hermite ease between 0 and 1 as t moves from a to b, with zero slope at both ends.
-
#snapped(v : Number, step : Number) : Float32
Rounds v to the nearest multiple of step, for grid snapping.
-
#wrap(v : Number, lo : Number, hi : Number)
Wraps v into the half-open range lo...hi, like a modulo that works for floats and negative numbers.
-
#wrap_angle(r : Number) : Float32
Normalizes an angle to -π..π.
Instance Method Detail
Shortest signed difference from from to to, in -π..π. Positive means turn clockwise in Eagle's y-down screen space.
True when a and b differ by at most eps. Use it instead of == on floats.
Restricts v to lo..hi. Keeps the input's numeric type.
Frame-rate independent smoothing: moves a toward b, and higher lambda moves faster. Around 5 feels soft, 10 to 20 feels snappy. Call it every frame with that frame's dt.
cam_x, player_x = 0.0, 250.0
cam_x = Mathf.damp(cam_x, player_x, 10, dt) # call every frame
The inverse of #lerp: where v sits between a and b, as a fraction.
Returns 0 when a equals b.
Mathf.inverse_lerp(10, 20, 15) # => 0.5
Linear interpolation: returns a when t is 0, b when t is 1, and a proportional mix in between. t is not clamped, so values outside 0..1 extrapolate.
Mathf.lerp(0, 100, 0.25) # => 25.0
Interpolates between two angles the short way round, so 350° to 10° goes through 0° instead of spinning backwards.
Moves from toward to by at most delta and never overshoots.
speed, max_speed, accel = 0.0, 300.0, 900.0
speed = Mathf.move_toward(speed, max_speed, accel * dt)
Bounces v back and forth between 0 and length, for patrols and pulsing effects.
x = Mathf.ping_pong(Clock.elapsed * 100, 300) # slides 0..300..0
Converts radians to degrees, for display or for authoring in degrees.
Maps v from the range a..b onto the range c..d.
bar_width = Mathf.remap(35, 0, 100, 0, 200) # health 35/100 => 70px bar
Hermite ease between 0 and 1 as t moves from a to b, with zero slope at both ends. Good for fades and soft thresholds.
Rounds v to the nearest multiple of step, for grid snapping. A step of 0 returns v unchanged.
Mathf.snapped(37, 16) # => 32.0
Wraps v into the half-open range lo...hi, like a modulo that works for floats and negative numbers. Useful for screen wrap-around.
Mathf.wrap(-10, 0, 800) # => 790.0