Files
CosmicClash/Game/scripts/hud_attitude_indicator.gd
Josh Creek 04691aaa48 chore(multiplayer): Phase 0 refactors + graphics/perf settings groundwork
Lands the non-networked Phase 0 tasks from multiplayer-todo.md (ship/camera/
arena refactors, sim constants, background FPS handling) plus a first pass
at exposing graphics/performance settings (presets, resolution scaling,
vsync, FPS cap, perf overlay) and a GPU profiling harness for the
real-hardware follow-up in task 0.15b.
2026-08-19 22:37:17 +01:00

109 lines
4.2 KiB
GDScript

class_name HudAttitudeIndicator
extends HudInstrument
# Aircraft-style attitude indicator (artificial horizon): the sky/ground disc
# rolls and the pitch ladder scrolls behind a fixed ship symbol, so pitch and
# roll are readable at a glance. Pure _draw code — no textures. A dumb display:
# HUDController pushes values in via set_attitude; it never reads the ship.
const SKY_COLOR := Color(0.10, 0.24, 0.42, 0.9)
const GROUND_COLOR := Color(0.22, 0.15, 0.10, 0.9)
const LINE_COLOR := Color(0.85, 0.92, 1.0, 0.9)
const SHIP_SYMBOL_COLOR := Color(1.0, 0.8, 0.2)
const RING_COLOR := Color(0.5, 0.85, 1.0, 0.7)
# Instrument scale: degrees of pitch visible from centre to rim.
const PITCH_RANGE := 45.0
var _target_pitch := 0.0
var _target_roll := 0.0
var _pitch := 0.0
var _roll := 0.0
func set_attitude(pitch_deg: float, roll_deg: float) -> void:
_target_pitch = pitch_deg
_target_roll = roll_deg
# Below this, both axes have visually settled — skip the redraw.
const REDRAW_EPSILON_DEG := 0.05
func _process(delta: float) -> void:
var t := _smoothing_weight(delta)
var new_pitch := lerpf(_pitch, _target_pitch, t)
var new_roll := lerp_angle_deg(_roll, _target_roll, t)
var changed := absf(new_pitch - _pitch) > REDRAW_EPSILON_DEG \
or absf(angle_delta_deg(_roll, new_roll)) > REDRAW_EPSILON_DEG
_pitch = new_pitch
_roll = new_roll
if changed:
_throttled_redraw(delta)
func _draw() -> void:
var center := size / 2.0
var radius := minf(size.x, size.y) / 2.0 - 10.0
var px_per_deg := radius / PITCH_RANGE
# Screen y grows downward, so nose-up (positive pitch) pushes the horizon
# down the instrument (+y) and reveals more sky.
var horizon_y := clampf(_pitch * px_per_deg, -radius, radius)
var roll_rad := deg_to_rad(_roll)
# Everything inside the dial is drawn in the horizon frame: rotated
# opposite to roll so the horizon stays level while the instrument banks.
draw_set_transform(center, -roll_rad, Vector2.ONE)
# Ground disc, then the sky as the circle segment above the horizon chord.
draw_circle(Vector2.ZERO, radius, GROUND_COLOR)
var sky := PackedVector2Array()
for i in 97:
# Start sampling at the bottom of the circle (excluded region) so the
# kept arc through the top is contiguous.
var ang := PI / 2.0 + float(i) * TAU / 96.0
var p := Vector2(cos(ang), sin(ang)) * radius
if p.y < horizon_y:
sky.append(p)
if sky.size() >= 3:
draw_colored_polygon(sky, SKY_COLOR)
# Horizon line, clipped to the dial.
if absf(horizon_y) < radius:
var half_chord := sqrt(radius * radius - horizon_y * horizon_y)
draw_line(Vector2(-half_chord, horizon_y), Vector2(half_chord, horizon_y), LINE_COLOR, 2.0)
# Pitch ladder: rungs every 10 degrees, labelled, scrolling with pitch.
var font := ThemeDB.fallback_font
for ladder_deg: int in [-30, -20, -10, 10, 20, 30]:
var y := horizon_y - ladder_deg * px_per_deg
if absf(y) > radius - 12.0:
continue
var half_w := 22.0 if ladder_deg % 20 == 0 else 14.0
draw_line(Vector2(-half_w, y), Vector2(half_w, y), LINE_COLOR, 1.0)
draw_string(font, Vector2(half_w + 4.0, y + 4.0), str(absi(ladder_deg)),
HORIZONTAL_ALIGNMENT_LEFT, -1, 10, LINE_COLOR)
# Roll pointer: triangle at the dial's top edge in the horizon frame, so
# it swings against the fixed tick marks as the ship banks.
draw_colored_polygon(PackedVector2Array([
Vector2(0, -radius + 2.0), Vector2(-5, -radius + 12.0), Vector2(5, -radius + 12.0),
]), SHIP_SYMBOL_COLOR)
# Back to the instrument frame for the fixed furniture.
draw_set_transform(center, 0.0, Vector2.ONE)
# Roll scale ticks at 0, +/-30, +/-60 degrees around the top of the dial.
for tick_deg: int in [-60, -30, 0, 30, 60]:
var ang := deg_to_rad(float(tick_deg)) - PI / 2.0
var dir := Vector2(cos(ang), sin(ang))
var tick_len := 8.0 if tick_deg == 0 else 5.0
draw_line(dir * radius, dir * (radius + tick_len), RING_COLOR, 2.0)
# Fixed ship symbol: wings + centre dot, what the ladder is read against.
draw_line(Vector2(-34, 0), Vector2(-10, 0), SHIP_SYMBOL_COLOR, 3.0)
draw_line(Vector2(10, 0), Vector2(34, 0), SHIP_SYMBOL_COLOR, 3.0)
draw_line(Vector2(-10, 0), Vector2(0, 5), SHIP_SYMBOL_COLOR, 3.0)
draw_line(Vector2(0, 5), Vector2(10, 0), SHIP_SYMBOL_COLOR, 3.0)
draw_arc(Vector2.ZERO, radius, 0.0, TAU, 96, RING_COLOR, 2.0, true)