Platform: Mobile
Touch-first input, safe area handling, and battery optimization define mobile development.
Available Scripts
mobile_safe_area_handler.gd
Expert safe area handling for notched screens using anchored margins.
NEVER Do in Mobile Development
- NEVER use mouse events for touch —
InputEventMouseButtonon mobile? Unreliable. UseInputEventScreenTouch+InputEventScreenDragfor touch. - NEVER ignore safe areas — UI behind iPhone notch = unusable. Call
DisplayServer.get_display_safe_area(), offset UI by top/bottom insets. - NEVER keep 60 FPS when backgrounded — App in background @ 60 FPS = battery drain + store rejection. Set
Engine.max_fps = 0onNOTIFICATION_APPLICATION_FOCUS_OUT. - NEVER use desktop UI sizes — 12pt font on phone = unreadable. Minimum 16-18pt for mobile. Use
get_viewport().sizeto scale dynamically. - NEVER forget VRAM compression — Uncompressed textures on mobile = out of memory crash. Enable
rendering/textures/vram_compression/import_etc2_astc=truein project.godot. - NEVER block main thread for saves — Saving large file on touch = freeze = ANR (Application Not Responding). Use
FileAccesson worker thread. - NEVER ignore orientation changes — Game locked to portrait but device rotates? Jarring. Handle
size_changedsignal or specifywindow/handheld/orientationin project.godot.
# Replace mouse/keyboard with touch
func _input(event: InputEvent) -> void:
if event is InputEventScreenTouch:
if event.pressed:
on_touch_start(event.position)
else:
on_touch_end(event.position)
elif event is InputEventScreenDrag:
on_touch_drag(event.position, event.relative)
Virtual Joystick
# virtual_joystick.gd
extends Control
signal joystick_moved(direction: Vector2)
var is_pressed := false
var center: Vector2
var touch_index := -1
func _gui_input(event: InputEvent) -> void:
if event is InputEventScreenTouch:
if event.pressed:
is_pressed = true
center = event.position
touch_index = event.index
elif event.index == touch_index:
is_pressed = false
joystick_moved.emit(Vector2.ZERO)
elif event is InputEventScreenDrag and event.index == touch_index:
var direction := (event.position - center).normalized()
joystick_moved.emit(direction)
Responsive UI
# Adapt to screen size
func _ready() -> void:
get_viewport().size_changed.connect(_on_viewport_resized)
_on_viewport_resized()
func _on_viewport_resized() -> void:
var viewport_size := get_viewport().get_visible_rect().size
var aspect := viewport_size.x / viewport_size.y
if aspect < 1.5: # Tall screen
$UI.layout_mode = VBoxContainer.LAYOUT_MODE_VERTICAL
else: # Wide screen
$UI.layout_mode = HBoxContainer.LAYOUT_MODE_HORIZONTAL
Battery Optimization
# Lower frame rate when inactive
func _notification(what: int) -> void:
match what:
NOTIFICATION_APPLICATION_FOCUS_OUT:
Engine.max_fps = 30
NOTIFICATION_APPLICATION_FOCUS_IN:
Engine.max_fps = 60
Safe Areas (Notches)
func apply_safe_area() -> void:
var safe_area := DisplayServer.get_display_safe_area()
# Adjust UI margins
$UI.offset_top = safe_area.position.y
$UI.offset_left = safe_area.position.x
Performance Settings
# project.godot mobile settings
[rendering]
renderer/rendering_method="mobile"
textures/vram_compression/import_etc2_astc=true
[display]
window/handheld/orientation="landscape"
App Store Metadata
- Icons: 512x512 (Android), 1024x1024 (iOS)
- Screenshots: Multiple resolutions
- Privacy policy required
- Age rating
Best Practices
- Touch-First - Design for fingers, not mouse
- Performance - Target 60 FPS on mid-range
- Battery - Reduce FPS when backgrounded
- Permissions - Request only what you need
Reference
- Related:
godot-export-builds,godot-ui-containers
Related
- Master Skill: godot-master