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)