Parte: 11 — Móvil, consolas y plataformas · Fuente: Documentación de Godot 4 (Multiple resolutions, DisplayServer) ⏱️ Duración estimada: 80 min · Nivel: Intermedio
Los teléfonos no comparten pantalla: hay relaciones de aspecto de 16:9 a 20:9, densidades muy distintas y, sobre todo, notches e islas (Dynamic Island) que recortan la zona superior, además de barras de gestos abajo. Si anclas tu vida, tu botón de pausa o tu marcador a la esquina absoluta, quedarán debajo del notch o tapados por la barra del sistema en muchos dispositivos.
En esta clase adaptamos la UI a la safe area: la región garantizada libre de recortes. Godot 4 la expone con DisplayServer.get_display_safe_area(). Configuramos el modo de estirado del proyecto para soportar múltiples resoluciones y aplicamos márgenes dinámicos para que ningún elemento crítico caiga bajo el notch, en cualquier orientación.
Al finalizar, el alumno podrá:
DisplayServer.get_display_safe_area().| # | Tema | Por qué importa |
|---|---|---|
| 1 | Diversidad de pantallas | Muchos aspect ratios y densidades. |
| 2 | Stretch mode y aspect | Cómo escala tu juego a cada pantalla. |
| 3 | Notch / isla | Recortan la parte superior de la UI. |
| 4 | Safe area | Región garantizada sin recortes. |
| 5 | get_display_safe_area() |
API para leer la zona segura. |
| 6 | Anclas y contenedores | Colocar UI relativa a bordes seguros. |
| 7 | Orientación | Portrait vs landscape cambian la safe area. |
| 8 | Escalado de UI | Legibilidad en densidades distintas. |
disabled, canvas_items, viewport). Clave: canvas_items mantiene la UI nítida al escalar.keep, expand, etc.). Clave: expand muestra más área en pantallas anchas.Rect2i con la región usable sin recortes ni barras del sistema. Clave: base para posicionar la UI.DisplayServer.get_display_safe_area(): devuelve la safe area en píxeles físicos. Clave: fuente de verdad para los márgenes.Control. Clave: layout que se adapta al tamaño del viewport.Trabaja con un CanvasLayer de HUD y controles basados en Control. En Project → Project Settings → Display → Window → Stretch define Mode: canvas_items y Aspect: expand, y una resolución base (por ejemplo 1080×1920 portrait o 1920×1080 landscape). Para probar el notch necesitas un dispositivo con recorte o el simulador; en el editor puedes simular la safe area aplicando un margen manual mientras desarrollas.
Consulta la guía de múltiples resoluciones en https://docs.godotengine.org/en/stable/tutorials/rendering/multiple_resolutions.html y DisplayServer.get_display_safe_area() en https://docs.godotengine.org/en/stable/classes/class_displayserver.html. La orientación se ajusta en Project Settings → Display → Window → Handheld → Orientation.
Adaptaremos la UI a la safe area para que no quede bajo el notch.
Configura el estirado: en Project Settings → Display → Window → Stretch, pon Mode = canvas_items y Aspect = expand. Fija la resolución base a tu orientación objetivo (por ejemplo 720×1280 portrait).
Crea un CanvasLayer HUD con un Control raíz llamado SafeArea que ocupe toda la pantalla (anclas Full Rect). Dentro coloca tu UI: MarcadorTop arriba, BotonPausa arriba a la derecha, Vidas arriba a la izquierda.
Añade a SafeArea un script que consulte la zona segura y aplique márgenes para que sus hijos no invadan el notch ni las barras:
extends Control
func _ready() -> void:
_aplicar_safe_area()
# Reaplica al rotar o cambiar tamaño de ventana.
get_tree().root.size_changed.connect(_aplicar_safe_area)
func _aplicar_safe_area() -> void:
var segura: Rect2i = DisplayServer.get_display_safe_area()
var pantalla: Vector2i = DisplayServer.screen_get_size()
# Márgenes en píxeles físicos (parte recortada en cada lado).
var izq: int = segura.position.x
var arriba: int = segura.position.y
var der: int = pantalla.x - (segura.position.x + segura.size.x)
var abajo: int = pantalla.y - (segura.position.y + segura.size.y)
# Escala de físico -> unidades del viewport (por el stretch).
var escala: Vector2 = get_viewport_rect().size / Vector2(pantalla)
# Empuja los bordes de este Control hacia dentro de la zona segura.
offset_left = izq * escala.x
offset_top = arriba * escala.y
offset_right = -der * escala.x
offset_bottom = -abajo * escala.y
Como este SafeArea tiene anclas Full Rect, al mover sus offset_* hacia dentro toda su UI hija queda automáticamente dentro de la zona segura. Ancla Vidas arriba-izquierda, BotonPausa arriba-derecha y MarcadorTop centrado arriba dentro de SafeArea.
Prueba en el editor simulando un notch: temporalmente fuerza offset_top = 80 para ver cómo la UI baja y deja hueco arriba. Quita el override cuando confirmes el efecto; en dispositivo real lo hará get_display_safe_area().
Soporta orientación: si tu juego admite portrait y landscape, la señal size_changed vuelve a llamar _aplicar_safe_area() al rotar, recalculando márgenes para la nueva forma del notch/barras.
Exporta a Android (clase 201) y prueba en un teléfono con notch: verifica que ni el marcador ni el botón de pausa quedan bajo el recorte, y que abajo respetas la barra de gestos.
Con esto tu HUD se mantiene visible y tappable en cualquier pantalla, notch incluido.
canvas_items.Adapta el HUD de tu juego a la safe area usando DisplayServer.get_display_safe_area(), de modo que ningún elemento interactivo o de información quede bajo el notch/isla ni bajo las barras del sistema, y que el layout se recalcule al cambiar de orientación.
Criterio de aceptación: el proyecto usa stretch canvas_items/expand; el HUD lee la safe area y aplica márgenes dinámicos; en un dispositivo (o simulando el recorte) el marcador y los botones quedan completamente dentro de la zona segura y siguen siendo tappables; y al rotar el dispositivo la UI se reajusta sin solaparse con el notch.
| Síntoma / mensaje | Causa y cómo arreglar |
|---|---|
| El marcador queda bajo el notch | No aplicaste la safe area. Usa get_display_safe_area() y empuja los offsets hacia dentro. |
| La UI se ve borrosa al escalar | Stretch en viewport o disabled. Usa canvas_items para UI nítida. |
| Botón inferior tapado por la barra de gestos | Ignoraste el margen inferior. Resta también abajo de la safe area. |
| Al rotar, la UI no se reacomoda | No reconectaste size_changed. Reaplica la safe area en esa señal. |
| Márgenes desproporcionados | Mezclaste píxeles físicos con unidades del viewport. Aplica la escala viewport/screen. |
En el editor get_display_safe_area da toda la pantalla |
En escritorio no hay notch. Prueba en dispositivo o simula el margen a mano. |
❓ ¿get_display_safe_area() funciona en el editor de escritorio? Devuelve la ventana completa porque no hay notch. La zona segura real solo aparece en dispositivos móviles con recorte, así que valida en hardware o simula el margen mientras desarrollas.
❓ ¿Debo meter TODO dentro de la safe area? No: fondos y elementos decorativos pueden cubrir toda la pantalla (se ven mejor a sangre). Solo la UI crítica e interactiva debe quedar dentro de la zona segura.
❓ ¿Qué stretch mode conviene en móvil? canvas_items con aspect expand es lo habitual: mantiene la UI nítida y aprovecha pantallas más anchas mostrando algo más de área en lugar de barras negras.
❓ ¿Cómo manejo portrait y landscape a la vez? Recalcula la safe area en la señal size_changed y usa anclas/contenedores en vez de posiciones fijas, para que el layout se adapte a ambas formas.
Clase 204 - Rendimiento y batería en móvil