godot-adapt-mobile-to-desktop

Compare original and translation side by side

🇺🇸

Original

English
🇨🇳

Translation

Chinese

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.

  • 绝对不要保留纯触控控制 —— 添加鼠标/键盘替代方案。桌面端使用触控控制会显得笨拙且限制操作精度。
  • 绝对不要锁定移动端分辨率 —— 桌面端可支持1920x1080及更高帧率。放大UI、增加渲染距离。
  • 绝对不要隐藏图形设置 —— 桌面玩家期望拥有画质选项(分辨率、垂直同步、阴影、抗锯齿)。
  • 绝对不要使用移动端尺寸的UI —— 触控目标(44pt)对鼠标来说太大。将按钮/文字尺寸缩小30-50%。
  • 绝对不要忘记窗口管理 —— 玩家期望支持全屏、无边框、最大化以及多显示器适配。

Available Scripts

可用脚本

MANDATORY: Read the appropriate script before implementing the corresponding pattern.
必填:在实现对应方案前,请先阅读相应脚本。

desktop_input_adapter.gd

desktop_input_adapter.gd

Bridges virtual joystick logic to keyboard WASD. Maps keys to input vectors and handles high-DPI UI scaling.
将虚拟摇杆逻辑与键盘WASD按键桥接。将按键映射为输入向量并处理高DPI UI缩放。

hover_bridge.gd

hover_bridge.gd

Re-enables desktop hover features in mobile-first codebases. Adds tooltips, mouse enter/exit signals, and hover visual feedback.

在移动端优先的代码库中重新启用桌面端悬停功能。添加工具提示、鼠标进入/退出信号以及悬停视觉反馈。

Control Scheme Expansion

控制方案扩展

Touch → Mouse Conversion

触控 → 鼠标转换

gdscript
undefined
gdscript
undefined

Mobile: Virtual joystick for movement

移动端:使用虚拟摇杆移动

var direction: Vector2 = virtual_joystick.get_direction()
var direction: Vector2 = virtual_joystick.get_direction()

⬇️ Desktop: WASD + mouse aim

⬇️ 桌面端:WASD + 鼠标瞄准

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()
extends CharacterBody2D
func _physics_process(delta: float) -> void: # 键盘移动(WASD) var input := Input.get_vector("move_left", "move_right", "move_up", "move_down") velocity = input.normalized() * SPEED
# 鼠标瞄准
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_left: A, 左箭头

move_right: D, Right Arrow

move_right: D, 右箭头

move_up: W, Up Arrow

move_up: W, 上箭头

move_down: S, Down Arrow

move_down: S, 下箭头

undefined
undefined

Add Keyboard Shortcuts

添加键盘快捷键

gdscript
undefined
gdscript
undefined

desktop_shortcuts.gd

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)
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

toggle_fullscreen: F11

quick_save: F5

quick_save: F5

toggle_inventory: I, Tab

toggle_inventory: I, Tab

undefined
undefined

Scroll Wheel Support

滚轮支持

gdscript
undefined
gdscript
undefined

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

---
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
undefined
gdscript
undefined

mobile_settings.gd (mobile)

mobile_settings.gd(移动端)

func _ready() -> void: get_viewport().size = Vector2i(1280, 720) # Mobile resolution
func _ready() -> void: get_viewport().size = Vector2i(1280, 720) # 移动端分辨率

⬇️ desktop_settings.gd (desktop)

⬇️ desktop_settings.gd(桌面端)

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
undefined
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"]: # 以原生分辨率启动 var screen_size := DisplayServer.screen_get_size() get_window().size = screen_size
    # 启用更高画质
    enable_desktop_graphics()
func enable_desktop_graphics() -> void: # 启用MSAA get_viewport().msaa_2d = Viewport.MSAA_2X get_viewport().msaa_3d = Viewport.MSAA_4X
# 启用屏幕空间抗锯齿
get_viewport().screen_space_aa = Viewport.SCREEN_SPACE_AA_FXAA

# 提升阴影分辨率
RenderingServer.directional_shadow_atlas_set_size(4096, true)

# 启用后处理
var env := get_viewport().world_3d.environment
if env:
    env.glow_enabled = true
    env.ssao_enabled = true
    env.adjustment_enabled = true
undefined

Settings Menu

设置菜单

gdscript
undefined
gdscript
undefined

graphics_settings.gd

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_enabled = true
    env.ssil_enabled = true
    env.sdfgi_enabled = true
func _on_vsync_toggled(enabled: bool) -> void: if enabled: DisplayServer.window_set_vsync_mode(DisplayServer.VSYNC_ENABLED) else: DisplayServer.window_set_vsync_mode(DisplayServer.VSYNC_DISABLED)
save_settings()
func _on_fullscreen_toggled(enabled: bool) -> void: if enabled: DisplayServer.window_set_mode(DisplayServer.WINDOW_MODE_FULLSCREEN) else: DisplayServer.window_set_mode(DisplayServer.WINDOW_MODE_WINDOWED)
save_settings()
func save_settings() -> void: var config := ConfigFile.new() config.set_value("graphics", "resolution_index", resolution_option.selected) config.set_value("graphics", "quality", quality_option.selected) config.set_value("graphics", "vsync", vsync_check.button_pressed) config.set_value("graphics", "fullscreen", fullscreen_check.button_pressed) config.save("user://settings.cfg")
func load_settings() -> void: var config := ConfigFile.new() if config.load("user://settings.cfg") == OK: resolution_option.selected = config.get_value("graphics", "resolution_index", 1) quality_option.selected = config.get_value("graphics", "quality", 2) vsync_check.button_pressed = config.get_value("graphics", "vsync", true) fullscreen_check.button_pressed = config.get_value("graphics", "fullscreen", false)
    # Apply settings
    _on_resolution_selected(resolution_option.selected)
    _on_quality_selected(quality_option.selected)
    _on_vsync_toggled(vsync_check.button_pressed)
    _on_fullscreen_toggled(fullscreen_check.button_pressed)

---
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_option.add_item("1280x720") resolution_option.add_item("1920x1080") resolution_option.add_item("2560x1440") resolution_option.add_item("3840x2160")
# 画质预设
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_enabled = true
    env.ssil_enabled = true
    env.sdfgi_enabled = true
func _on_vsync_toggled(enabled: bool) -> void: if enabled: DisplayServer.window_set_vsync_mode(DisplayServer.VSYNC_ENABLED) else: DisplayServer.window_set_vsync_mode(DisplayServer.VSYNC_DISABLED)
save_settings()
func _on_fullscreen_toggled(enabled: bool) -> void: if enabled: DisplayServer.window_set_mode(DisplayServer.WINDOW_MODE_FULLSCREEN) else: DisplayServer.window_set_mode(DisplayServer.WINDOW_MODE_WINDOWED)
save_settings()
func save_settings() -> void: var config := ConfigFile.new() config.set_value("graphics", "resolution_index", resolution_option.selected) config.set_value("graphics", "quality", quality_option.selected) config.set_value("graphics", "vsync", vsync_check.button_pressed) config.set_value("graphics", "fullscreen", fullscreen_check.button_pressed) config.save("user://settings.cfg")
func load_settings() -> void: var config := ConfigFile.new() if config.load("user://settings.cfg") == OK: resolution_option.selected = config.get_value("graphics", "resolution_index", 1) quality_option.selected = config.get_value("graphics", "quality", 2) vsync_check.button_pressed = config.get_value("graphics", "vsync", true) fullscreen_check.button_pressed = config.get_value("graphics", "fullscreen", false)
    # 应用设置
    _on_resolution_selected(resolution_option.selected)
    _on_quality_selected(quality_option.selected)
    _on_vsync_toggled(vsync_check.button_pressed)
    _on_fullscreen_toggled(fullscreen_check.button_pressed)

---

UI Layout Expansion

UI布局扩展

Mobile UI → Desktop UI

移动端UI → 桌面端UI

gdscript
undefined
gdscript
undefined

Mobile: Compact HUD, large touch buttons

移动端:紧凑HUD,大触控按钮

Scene: MobileHUD.tscn

场景:MobileHUD.tscn

- Virtual joystick (bottom-left)

- 虚拟摇杆(左下角)

- Action buttons (bottom-right, 80x80px)

- 操作按钮(右下角,80x80px)

⬇️ Desktop: Spread UI, smaller elements

⬇️ 桌面端:分散式UI,更小元素

Scene: DesktopHUD.tscn

场景:DesktopHUD.tscn

- Health/Mana bars (top-left, 40px tall)

- 生命值/魔法值栏(左上角,40px高)

- Minimap (top-right, 200x200px)

- 小地图(右上角,200x200px)

- Hotbar (bottom-center, 50x50px slots)

- 快捷栏(底部中央,50x50px插槽)

- Chat (bottom-left, resizable)

- 聊天框(左下角,可调整大小)

extends Control
func _ready() -> void: if OS.has_feature("mobile"): _setup_mobile_ui() else: _setup_desktop_ui()
func _setup_mobile_ui() -> void: # Large buttons, bottom corners $VirtualJoystick.visible = true $ActionButtons.scale = Vector2(1.5, 1.5) $Minimap.visible = false # Too cluttered
func _setup_desktop_ui() -> void: # Compact, corners and edges $VirtualJoystick.visible = false $ActionButtons.scale = Vector2(0.8, 0.8) $Minimap.visible = true $ChatBox.visible = true

---
extends Control
func _ready() -> void: if OS.has_feature("mobile"): _setup_mobile_ui() else: _setup_desktop_ui()
func _setup_mobile_ui() -> void: # 大按钮,位于底部角落 $VirtualJoystick.visible = true $ActionButtons.scale = Vector2(1.5, 1.5) $Minimap.visible = false # 过于拥挤
func _setup_desktop_ui() -> void: # 紧凑布局,分布在角落和边缘 $VirtualJoystick.visible = false $ActionButtons.scale = Vector2(0.8, 0.8) $Minimap.visible = true $ChatBox.visible = true

---

Window Management

窗口管理

Multi-Monitor Support

多显示器支持

gdscript
undefined
gdscript
undefined

window_manager.gd

window_manager.gd

extends Node
func _ready() -> void: # Detect monitors var screen_count := DisplayServer.get_screen_count() print("Detected %d monitors" % screen_count)
# Allow window dragging between monitors
DisplayServer.window_set_flag(DisplayServer.WINDOW_FLAG_BORDERLESS, false)
func move_to_monitor(monitor_index: int) -> void: var screen_pos := DisplayServer.screen_get_position(monitor_index) var screen_size := DisplayServer.screen_get_size(monitor_index)
# Center window on target monitor
var window_size := get_window().size
var centered_pos := screen_pos + (screen_size - window_size) / 2

DisplayServer.window_set_position(centered_pos)
undefined
extends Node
func _ready() -> void: # 检测显示器数量 var screen_count := DisplayServer.get_screen_count() print("检测到 %d 台显示器" % screen_count)
# 允许在显示器间拖动窗口
DisplayServer.window_set_flag(DisplayServer.WINDOW_FLAG_BORDERLESS, false)
func move_to_monitor(monitor_index: int) -> void: var screen_pos := DisplayServer.screen_get_position(monitor_index) var screen_size := DisplayServer.screen_get_size(monitor_index)
# 将窗口居中到目标显示器
var window_size := get_window().size
var centered_pos := screen_pos + (screen_size - window_size) / 2

DisplayServer.window_set_position(centered_pos)
undefined

Borderless Fullscreen

无边框全屏

gdscript
func set_borderless_fullscreen(enabled: bool) -> void:
    if enabled:
        # Get screen size
        var screen_size := DisplayServer.screen_get_size()
        
        # Set window to screen size
        get_window().size = screen_size
        get_window().position = Vector2i.ZERO
        
        # Remove border
        DisplayServer.window_set_flag(DisplayServer.WINDOW_FLAG_BORDERLESS, true)
    else:
        DisplayServer.window_set_flag(DisplayServer.WINDOW_FLAG_BORDERLESS, false)
        DisplayServer.window_set_mode(DisplayServer.WINDOW_MODE_WINDOWED)

gdscript
func set_borderless_fullscreen(enabled: bool) -> void:
    if enabled:
        # 获取屏幕尺寸
        var screen_size := DisplayServer.screen_get_size()
        
        # 将窗口设置为屏幕尺寸
        get_window().size = screen_size
        get_window().position = Vector2i.ZERO
        
        # 移除边框
        DisplayServer.window_set_flag(DisplayServer.WINDOW_FLAG_BORDERLESS, true)
    else:
        DisplayServer.window_set_flag(DisplayServer.WINDOW_FLAG_BORDERLESS, false)
        DisplayServer.window_set_mode(DisplayServer.WINDOW_MODE_WINDOWED)

Platform-Specific Features

平台专属功能

Steam Integration (Example)

Steam集成(示例)

gdscript
undefined
gdscript
undefined

Requires GodotSteam plugin

需要GodotSteam插件

extends Node
var steam_initialized := false
func _ready() -> void: if OS.get_name() in ["Windows", "Linux", "macOS"]: initialize_steam()
func initialize_steam() -> void: var init_result := Steam.steamInit() if init_result.status == Steam.STEAM_OK: steam_initialized = true print("Steam initialized")
    # Enable achievements
    Steam.requestStats()
func unlock_achievement(achievement_id: String) -> void: if steam_initialized: Steam.setAchievement(achievement_id) Steam.storeStats()
undefined
extends Node
var steam_initialized := false
func _ready() -> void: if OS.get_name() in ["Windows", "Linux", "macOS"]: initialize_steam()
func initialize_steam() -> void: var init_result := Steam.steamInit() if init_result.status == Steam.STEAM_OK: steam_initialized = true print("Steam初始化完成")
    # 启用成就系统
    Steam.requestStats()
func unlock_achievement(achievement_id: String) -> void: if steam_initialized: Steam.setAchievement(achievement_id) Steam.storeStats()
undefined

Discord Rich Presence

Discord Rich Presence

gdscript
undefined
gdscript
undefined

Requires Discord SDK integration

需要集成Discord SDK

extends Node
func update_presence(state: String, details: String) -> void: if OS.get_name() == "Windows": # Update Discord presence # (Requires plugin) pass

---
extends Node
func update_presence(state: String, details: String) -> void: if OS.get_name() == "Windows": # 更新Discord状态 # (需要插件) pass

---

Performance Enhancements

性能优化

Unlock Frame Rate

解锁帧率

gdscript
undefined
gdscript
undefined

Mobile: Locked to 60 FPS

移动端:锁定60 FPS

Engine.max_fps = 60
Engine.max_fps = 60

Desktop: Unlock or match monitor refresh rate

桌面端:解锁或匹配显示器刷新率

func _ready() -> void: if not OS.has_feature("mobile"): Engine.max_fps = 0 # Unlimited (use VSync to cap)
    # Or match monitor:
    var refresh_rate := DisplayServer.screen_get_refresh_rate()
    Engine.max_fps = int(refresh_rate)
undefined
func _ready() -> void: if not OS.has_feature("mobile"): Engine.max_fps = 0 # 无限制(使用垂直同步来封顶)
    # 或匹配显示器刷新率:
    var refresh_rate := DisplayServer.screen_get_refresh_rate()
    Engine.max_fps = int(refresh_rate)
undefined

Increased Draw Distance

提升绘制距离

gdscript
undefined
gdscript
undefined

Mobile: Low draw distance

移动端:低绘制距离

var camera: Camera3D camera.far = 100.0
var camera: Camera3D camera.far = 100.0

Desktop: Higher

桌面端:更高绘制距离

camera.far = 500.0
camera.far = 500.0

Also increase shadow distance

同时提升阴影距离

var sun: DirectionalLight3D sun.directional_shadow_max_distance = 200.0 # Up from 50

---
var sun: DirectionalLight3D sun.directional_shadow_max_distance = 200.0 # 从50提升

---

Testing Checklist

测试清单

  • Mouse controls feel precise (no acceleration issues)
  • All mobile touch controls have keyboard/mouse equivalents
  • Graphics settings menu works correctly
  • Fullscreen, windowed, borderless modes all function
  • Multi-monitor setup works (dragging window, centering)
  • Resolution changes don't crash or distort UI
  • VSync toggle works
  • Runs at 144+ FPS on high-end hardware
  • Settings persist across sessions
  • Game scales well to ultrawide monitors (21:9, 32:9)
  • 鼠标控制精准(无加速度问题)
  • 所有移动端触控控制都有键鼠替代方案
  • 图形设置菜单功能正常
  • 全屏、窗口化、无边框模式均可正常运行
  • 多显示器设置正常(窗口拖动、居中)
  • 分辨率切换不会导致崩溃或UI变形
  • 垂直同步切换功能正常
  • 在高端硬件上可达到144+ FPS
  • 设置可在会话间保存
  • 游戏可良好适配超宽显示器(21:9、32:9)

Reference

参考

  • Master Skill: godot-master
  • 核心技能:godot-master