godot-adapt-mobile-to-desktop
Compare original and translation side by side
🇺🇸
Original
English🇨🇳
Translation
ChineseAdapt: 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
undefinedgdscript
undefinedMobile: 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, 下箭头
undefinedundefinedAdd Keyboard Shortcuts
添加键盘快捷键
gdscript
undefinedgdscript
undefineddesktop_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.visiblefunc 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.visiblefunc 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
undefinedundefinedScroll Wheel Support
滚轮支持
gdscript
undefinedgdscript
undefinedMobile: 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
undefinedgdscript
undefinedmobile_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 = trueundefinedextends 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 = trueundefinedSettings Menu
设置菜单
gdscript
undefinedgdscript
undefinedgraphics_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 = truefunc _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 = truefunc _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
undefinedgdscript
undefinedMobile: 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
undefinedgdscript
undefinedwindow_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)undefinedextends 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)undefinedBorderless 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
undefinedgdscript
undefinedRequires 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()
undefinedextends 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()
undefinedDiscord Rich Presence
Discord Rich Presence
gdscript
undefinedgdscript
undefinedRequires 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
undefinedgdscript
undefinedMobile: 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)undefinedfunc _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)undefinedIncreased Draw Distance
提升绘制距离
gdscript
undefinedgdscript
undefinedMobile: 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