Claude
Skills
Sign in
Back

godot-adapt-mobile-to-desktop

Included with Lifetime
$97 forever

Expert patterns for scaling mobile games to desktop including mouse/keyboard controls, increased resolution and graphical fidelity, expanded UI layouts, settings menus, window management, and platform-specific features. Use when creating desktop ports or cross-platform releases. Trigger keywords: mouse_controls, keyboard_shortcuts, resolution_scaling, graphics_settings, fullscreen_toggle, window_modes, Steam_integration, desktop_optimization.

Designscripts

What this skill does


# Adapt: Mobile to Desktop

Expert guidance for scaling mobile games to desktop platforms.

## NEVER Do

- **NEVER keep touch-only controls** — Add mouse/keyboard alternatives. Touch controls on desktop feel awkward and limit precision.
- **NEVER lock to mobile resolution** — Desktop can handle 1920x1080+ and higher frame rates. Upscale UI, increase render distance.
- **NEVER hide graphics settings** — Desktop players expect quality options (resolution, VSync, shadows, anti-aliasing).
- **NEVER use mobile-sized UI** — Touch targets (44pt) are too large for mouse. Reduce button/text size by 30-50%.
- **NEVER forget window management** — Players expect fullscreen, borderless, maximize, and multi-monitor support.

---

## Available Scripts

> **MANDATORY**: Read the appropriate script before implementing the corresponding pattern.

### [mouse_capture_look.gd](scripts/mouse_capture_look.gd)
Expert Mouse Capture Controller that completely overrides mobile touch/swipe logic by accumulating `InputEventMouseMotion.relative` against pitch/yaw variables while clamped.

### [dynamic_window_manager.gd](scripts/dynamic_window_manager.gd)
Crucial lifecycle manager handling the expectation of PC gamers to toggle between Windowed, Fullscreen Exclusive, and modern Borderless Fullscreen via `DisplayServer` flags.

### [keybinding_remapper.gd](scripts/keybinding_remapper.gd)
Complete runtime input remapper. Mobile relies on hardcoded touch zones, but PC requires the ability to swap WASD to custom keycodes via `InputMap` and saving to `ConfigFile`.

### [cursor_state_manager.gd](scripts/cursor_state_manager.gd)
Hardware cursor state machine. Replaces the default OS arrow with custom `Texture2D` hardware cursors and handles hiding the cursor during combat while freeing it in menus.

### [uncapped_framerate.gd](scripts/uncapped_framerate.gd)
Expert VSync and FPS unlocker. Mobile locks at 60FPS to save battery; PC gamers expect the ability to disable VSync and unlock `Engine.max_fps` for 144Hz+ monitors.

### [resolution_dropdown.gd](scripts/resolution_dropdown.gd)
Query engine utilizing `DisplayServer.screen_get_size` to build an OptionButton of supported native 16:9, 21:9, and 4K resolutions without exceeding the user's physical monitor.

### [desktop_ui_scaler.gd](scripts/desktop_ui_scaler.gd)
Recursive SceneTree crawler that scales down massive thumb-sized mobile buttons by a percentage shrink factor specifically on Desktop builds while retaining their anchor points.

### [scroll_wheel_zoom.gd](scripts/scroll_wheel_zoom.gd)
Replaces the mobile Pinch-to-Zoom gesture with discrete physical mouse wheel ticks, smoothly interpolating the Camera2D zoom continuously via `delta`.

### [quit_confirmation.gd](scripts/quit_confirmation.gd)
Hooks `get_tree().set_auto_accept_quit(false)` to intercept the OS-level 'X' window button, pausing the game and prompting the user to save instead of instantly terminating like mobile.

### [multi_monitor_handling.gd](scripts/multi_monitor_handling.gd)
Advanced PC window placement script that queries the mouse position to identify the active screen on multi-monitor setups, ensuring the game launches exactly where the user is looking.

---

## Control Scheme Expansion

### Touch → Mouse Conversion

```gdscript
# Mobile: Virtual joystick for movement
var direction: Vector2 = virtual_joystick.get_direction()

# ⬇️ Desktop: WASD + mouse aim

extends CharacterBody2D

func _physics_process(delta: float) -> void:
    # Keyboard movement (WASD)
    var input := Input.get_vector("move_left", "move_right", "move_up", "move_down")
    velocity = input.normalized() * SPEED
    
    # Mouse aiming
    var mouse_pos := get_global_mouse_position()
    look_at(mouse_pos)
    
    move_and_slide()

# Configure Project Settings → Input Map:
# move_left: A, Left Arrow
# move_right: D, Right Arrow
# move_up: W, Up Arrow
# move_down: S, Down Arrow
```

### Add Keyboard Shortcuts

```gdscript
# desktop_shortcuts.gd
extends Node

func _input(event: InputEvent) -> void:
    if event.is_action_pressed("toggle_fullscreen"):
        toggle_fullscreen()
    
    if event.is_action_pressed("quick_save"):
        save_game()
    
    if event.is_action_pressed("toggle_inventory"):
        $UI/Inventory.visible = not $UI/Inventory.visible

func toggle_fullscreen() -> void:
    if DisplayServer.window_get_mode() == DisplayServer.WINDOW_MODE_FULLSCREEN:
        DisplayServer.window_set_mode(DisplayServer.WINDOW_MODE_WINDOWED)
    else:
        DisplayServer.window_set_mode(DisplayServer.WINDOW_MODE_FULLSCREEN)

# Add to Project Settings → Input Map:
# toggle_fullscreen: F11
# quick_save: F5
# toggle_inventory: I, Tab
```

### Scroll Wheel Support

```gdscript
# Mobile: Pinch to zoom
# Desktop: Scroll wheel

func _input(event: InputEvent) -> void:
    if event is InputEventMouseButton:
        if event.button_index == MOUSE_BUTTON_WHEEL_UP:
            camera.zoom *= 1.1
        elif event.button_index == MOUSE_BUTTON_WHEEL_DOWN:
            camera.zoom *= 0.9
```

---

## Graphics Enhancement

### Resolution Scaling

```gdscript
# mobile_settings.gd (mobile)
func _ready() -> void:
    get_viewport().size = Vector2i(1280, 720)  # Mobile resolution

# ⬇️ desktop_settings.gd (desktop)

extends Node

@export var supported_resolutions: Array[Vector2i] = [
    Vector2i(1280, 720),
    Vector2i(1920, 1080),
    Vector2i(2560, 1440),
    Vector2i(3840, 2160)
]

func _ready() -> void:
    if OS.get_name() in ["Windows", "macOS", "Linux"]:
        # Start at native resolution
        var screen_size := DisplayServer.screen_get_size()
        get_window().size = screen_size
        
        # Enable higher  quality
        enable_desktop_graphics()

func enable_desktop_graphics() -> void:
    # Enable MSAA
    get_viewport().msaa_2d = Viewport.MSAA_2X
    get_viewport().msaa_3d = Viewport.MSAA_4X
    
    # Enable screen space AA
    get_viewport().screen_space_aa = Viewport.SCREEN_SPACE_AA_FXAA
    
    # Higher shadow resolution
    RenderingServer.directional_shadow_atlas_set_size(4096, true)
    
    # Enable post-processing
    var env := get_viewport().world_3d.environment
    if env:
        env.glow_enabled = true
        env.ssao_enabled = true
        env.adjustment_enabled = true
```

### Settings Menu

```gdscript
# graphics_settings.gd
extends Control

@onready var resolution_option: OptionButton = $VBoxContainer/Resolution
@onready var quality_option: OptionButton = $VBoxContainer/Quality
@onready var vsync_check: CheckBox = $VBoxContainer/VSync
@onready var fullscreen_check: CheckBox = $VBoxContainer/Fullscreen

func _ready() -> void:
    populate_settings()
    load_settings()

func populate_settings() -> void:
    # Resolution options
    resolution_option.add_item("1280x720")
    resolution_option.add_item("1920x1080")
    resolution_option.add_item("2560x1440")
    resolution_option.add_item("3840x2160")
    
    # Quality presets
    quality_option.add_item("Low")
    quality_option.add_item("Medium")
    quality_option.add_item("High")
    quality_option.add_item("Ultra")

func _on_resolution_selected(index: int) -> void:
    var resolutions := [
        Vector2i(1280, 720),
        Vector2i(1920, 1080),
        Vector2i(2560, 1440),
        Vector2i(3840, 2160)
    ]
    
    get_window().size = resolutions[index]
    save_settings()

func _on_quality_selected(index: int) -> void:
    match index:
        0:  # Low
            apply_low_quality()
        1:  # Medium
            apply_medium_quality()
        2:  # High
            apply_high_quality()
        3:  # Ultra
            apply_ultra_quality()
    
    save_settings()

func apply_ultra_quality() -> void:
    get_viewport().msaa_3d = Viewport.MSAA_8X
    get_viewport().screen_space_aa = Viewport.SCREEN_SPACE_AA_FXAA
    RenderingServer.directional_shadow_atlas_set_size(8192, true)
    
    var env := get_viewport().world_3d.environment
    if env:
        env.glow_enabled = true
        env.ssao_enabl

Related in Design