78 lines
3.2 KiB
GDScript3
78 lines
3.2 KiB
GDScript3
|
|
extends Node
|
||
|
|
|
||
|
|
# Forgejo #228: the HUD is proportionally three times too big on a PC.
|
||
|
|
#
|
||
|
|
# The layout is authored in CANVAS units against a portrait phone. With
|
||
|
|
# stretch "canvas_items"/"expand" and a 1920x1080 base, a 720x1280 phone
|
||
|
|
# scales by 0.375 and so gets a canvas 1920x3413 units tall - while ANY
|
||
|
|
# 16:9-or-wider screen lands on 1920x1080 units whatever its pixel count.
|
||
|
|
# The canvas is three times shorter, so a 148-unit button goes from 4.3% of
|
||
|
|
# the screen's height to 13.7% of it. Nothing collides; everything is just
|
||
|
|
# enormous, and the thumb sticks in particular.
|
||
|
|
#
|
||
|
|
# Each HUD element's offsets are measured FROM ITS OWN ANCHOR, so scaling
|
||
|
|
# them scales the element's size and its distance from the edge it hangs off
|
||
|
|
# together. That is the whole trick: one multiplier per control, no layout
|
||
|
|
# rules to re-derive, and anchoring keeps working untouched.
|
||
|
|
|
||
|
|
# The canvas the HUD was drawn for. Not a magic number: 1280 / (720 / 1920).
|
||
|
|
const AUTHORED_CANVAS_HEIGHT: float = 3413.0
|
||
|
|
|
||
|
|
# Below this the controls stop being worth pressing. A landscape phone gets
|
||
|
|
# the same short canvas as a desktop, and there the sticks are still thumb
|
||
|
|
# targets - so the shrink is bounded rather than purely proportional.
|
||
|
|
const MIN_SCALE: float = 0.45
|
||
|
|
|
||
|
|
# Authored offsets and sizes, captured before anything is touched.
|
||
|
|
# Re-reading them after a resize would measure a previous scaling and let the
|
||
|
|
# HUD ratchet away to nothing - the same trap NeonPanel documents.
|
||
|
|
var _authored: Dictionary = {}
|
||
|
|
|
||
|
|
|
||
|
|
func _ready() -> void:
|
||
|
|
var layer: Node = get_parent()
|
||
|
|
for child in layer.get_children():
|
||
|
|
if child == self or not (child is Control):
|
||
|
|
continue
|
||
|
|
var c: Control = child
|
||
|
|
# Full-bleed things - the modal menus, the breach overlay - hang off
|
||
|
|
# every edge at once and have nothing to shrink.
|
||
|
|
if is_equal_approx(c.anchor_left, 0.0) and is_equal_approx(c.anchor_right, 1.0) \
|
||
|
|
and is_equal_approx(c.anchor_top, 0.0) and is_equal_approx(c.anchor_bottom, 1.0):
|
||
|
|
continue
|
||
|
|
_authored[c] = [c.offset_left, c.offset_top,
|
||
|
|
c.offset_right - c.offset_left, c.offset_bottom - c.offset_top]
|
||
|
|
get_viewport().size_changed.connect(_apply)
|
||
|
|
_apply()
|
||
|
|
|
||
|
|
|
||
|
|
func hud_scale() -> float:
|
||
|
|
var height: float = get_viewport().get_visible_rect().size.y
|
||
|
|
if height <= 0.0:
|
||
|
|
return 1.0
|
||
|
|
# Only ever shrinks. A canvas TALLER than the phone's is not a reason to
|
||
|
|
# inflate the HUD over the game.
|
||
|
|
return clampf(height / AUTHORED_CANVAS_HEIGHT, MIN_SCALE, 1.0)
|
||
|
|
|
||
|
|
|
||
|
|
func _apply() -> void:
|
||
|
|
var k: float = hud_scale()
|
||
|
|
for control in _authored:
|
||
|
|
if not is_instance_valid(control):
|
||
|
|
continue
|
||
|
|
var a: Array = _authored[control]
|
||
|
|
var c: Control = control
|
||
|
|
# Position moves toward the edge the element hangs off; the RECT keeps
|
||
|
|
# its authored size and the node is scaled instead.
|
||
|
|
#
|
||
|
|
# Scaling the rect alone was wrong: half the HUD draws its own
|
||
|
|
# contents at fixed canvas sizes - ResourceReadout lays its rows out
|
||
|
|
# in code - so a shrunken rect kept full-size content inside it, and
|
||
|
|
# the build button slid up underneath the resource list. Scaling the
|
||
|
|
# node takes the drawn content with it.
|
||
|
|
c.pivot_offset = Vector2.ZERO
|
||
|
|
c.offset_left = float(a[0]) * k
|
||
|
|
c.offset_top = float(a[1]) * k
|
||
|
|
c.offset_right = c.offset_left + float(a[2])
|
||
|
|
c.offset_bottom = c.offset_top + float(a[3])
|
||
|
|
c.scale = Vector2(k, k)
|