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 assert(chat_panel != 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, )) 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()