Parte: 2 — Desarrollo 3D: motores, escenas y transformaciones · Fuente: Documentación oficial de Godot 4 — Physics introduction (Area3D) ⏱️ Duración estimada: 55 min · Nivel: Intermedio
Aprender a usar Area3D como detector de eventos en el espacio 3D: zonas que reaccionan cuando algo entra o sale de ellas sin bloquear el movimiento como lo haría un muro sólido. Con esta herramienta construirás la lógica invisible que hace vivir un nivel: checkpoints que recuerdan por dónde ibas, zonas de daño que restan vida, puertas que se abren al acercarte y disparadores de eventos.
Al terminar habrás montado una escena jugable con un CharacterBody3D que atraviesa tres áreas distintas —un checkpoint, una zona de daño y una puerta automática— y verás por consola y en pantalla cómo cada una responde a tu presencia.
Al finalizar, el alumno podrá:
Area3D (detección) y un cuerpo sólido (StaticBody3D/CharacterBody3D) que bloquea el paso.body_entered, body_exited y area_entered de un Area3D.| # | Tema | Por qué importa |
|---|---|---|
| 1 | Area3D como disparador | Es el nodo que detecta presencia sin frenar el movimiento. |
| 2 | Señales body_entered / body_exited | Son el mecanismo por el que el área te avisa de quién entra o sale. |
| 3 | CollisionShape3D | Define la forma y el tamaño exacto de la zona detectada. |
| 4 | Capas y máscaras de colisión | Permiten que un área ignore lo que no debe detectar. |
| 5 | monitoring y monitorable | Activan o desactivan la detección de un área en tiempo real. |
| 6 | Checkpoints | Guardan progreso y evitan repetir todo el nivel al morir. |
| 7 | Zonas de daño y puertas | Son los usos clásicos que dan interactividad al nivel. |
Area3D que define su forma (caja, esfera, cápsula). Clave: sin una forma asignada, el área no detecta nada.PhysicsBody3D (como un CharacterBody3D) entra en el área. Clave: recibe como argumento el cuerpo que entró.false desactiva una puerta o trampa ya usada.Area3D.Necesitas Godot 4.x desde https://godotengine.org/download. La referencia central de esta clase es la clase Area3D en https://docs.godotengine.org/en/stable/classes/class_area3d.html y la introducción a la física en https://docs.godotengine.org/en/stable/tutorials/physics/physics_introduction.html. Para entender capas y máscaras revisa https://docs.godotengine.org/en/stable/tutorials/physics/physics_introduction.html#collision-layers-and-masks. Reutilizaremos un CharacterBody3D simple como jugador; si vienes de clases anteriores puedes emplear el que ya tengas.
Montaremos un pequeño nivel con un jugador y tres áreas que reaccionan a él.
Crea una escena con raíz Node3D llamada Nivel. Añade un suelo: un StaticBody3D con un CollisionShape3D (forma BoxShape3D ancha y plana) y un MeshInstance3D con BoxMesh para verlo. Agrega también una Camera3D y una DirectionalLight3D.
Añade el jugador. Crea un CharacterBody3D llamado Jugador con su CollisionShape3D (una CapsuleShape3D) y un MeshInstance3D con CapsuleMesh. Adjúntale este script de movimiento:
extends CharacterBody3D
@export var velocidad: float = 5.0
var vida: int = 100
var punto_reaparicion: Vector3 = Vector3.ZERO
func _physics_process(_delta: float) -> void:
var entrada := Input.get_vector("ui_left", "ui_right", "ui_up", "ui_down")
velocity.x = entrada.x * velocidad
velocity.z = entrada.y * velocidad
move_and_slide()
func aplicar_dano(cantidad: int) -> void:
vida -= cantidad
print("Vida: ", vida)
if vida <= 0:
reaparecer()
func guardar_checkpoint(posicion: Vector3) -> void:
punto_reaparicion = posicion
print("Checkpoint guardado en ", posicion)
func reaparecer() -> void:
vida = 100
global_position = punto_reaparicion
print("Reapareces en el último checkpoint")
Area3D llamado Checkpoint con un CollisionShape3D (BoxShape3D). Colócalo a un lado del suelo. Adjúntale este script y conecta su señal body_entered desde el panel Nodo → Señales hacia la función _on_body_entered:extends Area3D
func _on_body_entered(body: Node3D) -> void:
if body.has_method("guardar_checkpoint"):
body.guardar_checkpoint(global_position)
monitoring = false # ya usado, no volver a disparar
Area3D llamado ZonaDano con su forma. Este daña de forma continua mientras el jugador permanezca dentro, usando body_entered y body_exited:extends Area3D
@export var dano_por_tick: int = 10
var cuerpos_dentro: Array[Node3D] = []
func _ready() -> void:
body_entered.connect(_on_body_entered)
body_exited.connect(_on_body_exited)
var reloj := Timer.new()
reloj.wait_time = 1.0
reloj.autostart = true
reloj.timeout.connect(_aplicar_dano)
add_child(reloj)
func _on_body_entered(body: Node3D) -> void:
if body.has_method("aplicar_dano"):
cuerpos_dentro.append(body)
func _on_body_exited(body: Node3D) -> void:
cuerpos_dentro.erase(body)
func _aplicar_dano() -> void:
for cuerpo in cuerpos_dentro:
cuerpo.aplicar_dano(dano_por_tick)
Area3D llamado SensorPuerta con su forma, y como hermano un MeshInstance3D llamado HojaPuerta (un BoxMesh vertical). El área anima la puerta hacia arriba al entrar y la baja al salir con un Tween:extends Area3D
@onready var hoja: MeshInstance3D = $"../HojaPuerta"
var y_cerrada: float
var y_abierta: float
func _ready() -> void:
y_cerrada = hoja.position.y
y_abierta = y_cerrada + 3.0
body_entered.connect(func(_b): _mover_puerta(y_abierta))
body_exited.connect(func(_b): _mover_puerta(y_cerrada))
func _mover_puerta(destino_y: float) -> void:
var tween := create_tween()
tween.tween_property(hoja, "position:y", destino_y, 0.5)
ZonaDano cambie el color del MeshInstance3D del jugador mientras esté dentro (usa un StandardMaterial3D).monitoring = false tras abrirse).Area3D "meta" que al entrar imprima "Nivel completado" y detenga el movimiento del jugador.body_exited en el checkpoint para imprimir un mensaje cuando el jugador se aleja.Construye una sala con tres áreas encadenadas: un checkpoint inicial, una zona de daño que el jugador debe cruzar perdiendo vida, y una puerta final que solo se abre si el jugador llega con vida mayor que cero. Si muere en la zona de daño, debe reaparecer en el checkpoint con la vida restaurada. Criterio de aceptación: al ejecutar, cruzar el checkpoint guarda la posición; atravesar la zona resta vida cada segundo; morir reaparece al jugador en el checkpoint con vida 100; y la puerta final se abre solo cuando el jugador llega vivo, verificable por consola y en pantalla.
| Síntoma / mensaje | Causa y cómo arreglar |
|---|---|
| El área nunca dispara la señal | Falta el CollisionShape3D o no tiene forma asignada. Añade la forma al área. |
| Detecta objetos que no debería | Máscara de colisión mal configurada. Ajusta collision_mask para incluir solo la capa del jugador. |
| El jugador choca contra el área como si fuera muro | Confundiste Area3D con StaticBody3D. El Area3D no bloquea; usa un cuerpo sólido si quieres colisión. |
body_entered no llega al script del jugador |
El cuerpo no tiene el método esperado. Verifica con body.has_method("...") antes de llamarlo. |
| La zona de daño sigue dañando tras salir | Olvidaste conectar body_exited o quitar el cuerpo del array. Usa cuerpos_dentro.erase(body). |
| La puerta no encuentra la hoja | Ruta relativa incorrecta en $"../HojaPuerta". Verifica que la hoja sea hermana del área. |
❓ ¿Cuál es la diferencia real entre Area3D y un cuerpo sólido? El Area3D solo informa de entradas y salidas; nunca detiene ni empuja. Un StaticBody3D o CharacterBody3D sí impide atravesarlo. Usa áreas para lógica (triggers) y cuerpos para geometría física.
❓ ¿Por qué mi área detecta al jugador y también a las paredes? Porque su máscara incluye la capa de las paredes. Separa jugador y entorno en capas distintas y deja en la máscara del área solo la capa del jugador.
❓ ¿Puedo detectar otras áreas en vez de cuerpos? Sí, con la señal area_entered(area). Es útil, por ejemplo, para que una zona de agua detecte un proyectil que también es un Area3D.
❓ ¿Cómo desactivo un trigger que ya se usó? Pon su propiedad monitoring en false. El área dejará de emitir señales hasta que la vuelvas a activar.
Clase 062 - NavigationServer 3D: navmesh y pathfinding