netfishing/tests/ui_scaling_runtime_validation.gd

208 lines
6.7 KiB
GDScript3
Raw Normal View History

extends SceneTree
const UIPixelationPresenterType = preload(
"res://ui/ui_pixelation_presenter.gd"
)
const UIReferencePresentationType = preload(
"res://ui/ui_reference_presentation.gd"
)
const GameUIScene := preload("res://ui/game_ui.tscn")
const TARGET_SIZES: Array[Vector2i] = [
Vector2i(1280, 720),
Vector2i(1920, 1080),
Vector2i(2560, 1440),
Vector2i(3840, 2160),
Vector2i(640, 480),
Vector2i(1024, 768),
Vector2i(1729, 973),
Vector2i(2560, 1080),
Vector2i(3440, 1440),
]
func _initialize() -> void:
call_deferred("_run")
func _run() -> void:
var original_size: Vector2i = root.size
var presenter := UIPixelationPresenterType.new()
presenter.name = "UIPresentation"
presenter.stretch = true
var ui_viewport := SubViewport.new()
ui_viewport.name = "UIViewport"
var game_ui := GameUIScene.instantiate() as GameUI
var ui_root := game_ui.get_node("UIRoot") as Control
var canonical_stage := game_ui.get_node(
"UIRoot/CanonicalStage"
) as Control
var chat_ui := game_ui.get_node("UIRoot/ChatUI") as ChatUI
var player_menu := game_ui.get_node(
"UIRoot/CanonicalStage/PlayerMenu"
) as PlayerMenu
var hotbar := game_ui.get_node(
"UIRoot/CanonicalStage/Hotbar"
) as HotbarUI
var screen_fade := game_ui.get_node("UIRoot/ScreenFade") as Control
var title_screen := game_ui.get_node("UIRoot/TitleScreen") as TitleScreen
var title_content_stage := title_screen.get_node(
"ResponsiveTitleStage"
) as Control
var decorative_fish_layer := title_screen.get_node(
"DecorativeFishLayer"
) as Control
var decorative_bubble_layer := title_screen.get_node(
"DecorativeBubbleLayer"
) as Control
ui_viewport.add_child(game_ui)
presenter.add_child(ui_viewport)
root.add_child(presenter)
await process_frame
chat_ui.set_available(true)
print(
"UI viewport embedding: root=%s subviewport=%s"
% [root.gui_embed_subwindows, ui_viewport.gui_embed_subwindows]
)
for target_size: Vector2i in TARGET_SIZES:
root.size = target_size
await process_frame
await process_frame
var actual_size: Vector2i = root.size
var display_size := Vector2(actual_size)
var expected_scale: float = UIReferencePresentationType.get_scale(
display_size
)
var expected_stage_offset: Vector2 = UIReferencePresentationType.get_offset(
display_size
)
assert(presenter.position.is_equal_approx(Vector2.ZERO))
assert(ui_root.position.is_equal_approx(Vector2.ZERO))
var expected_visible_size: Vector2 = (
UIReferencePresentationType.get_visible_reference_size(display_size)
)
assert(ui_root.size.is_equal_approx(expected_visible_size))
assert(canonical_stage.size.is_equal_approx(
UIReferencePresentationType.REFERENCE_SIZE
))
assert(canonical_stage.position.is_equal_approx(
UIReferencePresentationType.get_stage_position(display_size)
))
assert(chat_ui.size.is_equal_approx(expected_visible_size))
assert(screen_fade.size.is_equal_approx(expected_visible_size))
assert(player_menu.position.is_equal_approx(Vector2.ZERO))
assert(player_menu.size.is_equal_approx(
UIReferencePresentationType.REFERENCE_SIZE
))
assert(hotbar.position.is_equal_approx(Vector2.ZERO))
assert(hotbar.size.is_equal_approx(
UIReferencePresentationType.REFERENCE_SIZE
))
assert(title_screen.size.is_equal_approx(expected_visible_size))
assert(title_content_stage.size.is_equal_approx(
UIReferencePresentationType.REFERENCE_SIZE
))
assert(title_content_stage.position.is_equal_approx(
canonical_stage.position
))
assert(decorative_fish_layer.size.is_equal_approx(
expected_visible_size
))
assert(decorative_bubble_layer.size.is_equal_approx(
expected_visible_size
))
var left_to_right_bounds: Vector2 = (
TitleScreen.get_decorative_fish_crossing_bounds(
expected_visible_size.x, 160.0, 1.0
)
)
var right_to_left_bounds: Vector2 = (
TitleScreen.get_decorative_fish_crossing_bounds(
expected_visible_size.x, 160.0, -1.0
)
)
assert(left_to_right_bounds.x < -160.0)
assert(left_to_right_bounds.y > expected_visible_size.x)
assert(right_to_left_bounds.x > expected_visible_size.x)
assert(right_to_left_bounds.y < -160.0)
assert(is_equal_approx(presenter.scale.x, presenter.scale.y))
assert(is_equal_approx(ui_root.scale.x, ui_root.scale.y))
var effective_scale: float = presenter.scale.x * ui_root.scale.x
assert(is_equal_approx(effective_scale, expected_scale))
assert((
canonical_stage.position * effective_scale
).distance_to(expected_stage_offset) <= 0.01)
var expected_render_height: int = mini(
roundi(UIReferencePresentationType.REFERENCE_SIZE.y * expected_scale),
PlayerSettings.get_ui_render_height(
PlayerSettings.DEFAULT_UI_PIXEL_SIZE,
roundi(UIReferencePresentationType.REFERENCE_SIZE.y),
),
)
var expected_render_scale: float = (
float(expected_render_height)
/ UIReferencePresentationType.REFERENCE_SIZE.y
)
var expected_viewport_size := Vector2i(
roundi(expected_visible_size.x * expected_render_scale),
roundi(expected_visible_size.y * expected_render_scale),
)
assert(presenter.get_ui_viewport_size() == expected_viewport_size)
assert(presenter.size.is_equal_approx(Vector2(expected_viewport_size)))
var chat_panel := chat_ui.get_node("ChatPanel") as Control
2026-08-02 13:31:10 -04:00
var clock_panel := chat_ui.get_node("WorldClockPanel") as Control
var weather_icon := chat_ui.get_node("WorldWeatherIcon") as Control
assert(chat_panel != null)
2026-08-02 13:31:10 -04:00
assert(clock_panel != null)
assert(weather_icon != null)
assert(chat_panel.visible)
assert(is_equal_approx(chat_panel.position.x, 0.0))
assert(is_equal_approx(
(chat_panel.position.y + chat_panel.size.y) * effective_scale,
display_size.y - ChatUI.BOTTOM_MARGIN * effective_scale,
))
2026-08-02 13:31:10 -04:00
assert(clock_panel.position.is_equal_approx(Vector2(
ChatUI.CLOCK_EDGE_MARGIN,
ChatUI.CLOCK_EDGE_MARGIN,
)))
assert(is_equal_approx(
weather_icon.position.y, ChatUI.CLOCK_EDGE_MARGIN
))
assert(weather_icon.position.x > clock_panel.position.x)
print(
(
"UI resize: requested=%s actual=%s viewport=%s container_scale=%.6f "
+ "root_scale=%.6f effective_scale=%.6f stage_offset=%s "
+ "chat_x=%.3f"
)
% [
target_size,
actual_size,
presenter.get_ui_viewport_size(),
presenter.scale.x,
ui_root.scale.x,
effective_scale,
expected_stage_offset,
chat_panel.position.x * effective_scale,
]
)
if OS.has_environment("NETFISHING_UI_SCALE_CAPTURE"):
await process_frame
var capture: Image = root.get_texture().get_image()
var capture_path: String = (
"%s-requested-%dx%d-actual-%dx%d.png"
) % [
OS.get_environment("NETFISHING_UI_SCALE_CAPTURE"),
target_size.x,
target_size.y,
actual_size.x,
actual_size.y,
]
assert(capture.save_png(capture_path) == OK)
presenter.queue_free()
root.size = original_size
await process_frame
print("UI scaling runtime validation: PASS")
quit()