Fundamentos de GDScript para la lógica de juego en Godot

Capítulo 4

Tiempo estimado de lectura: 8 minutos

+ Ejercicio

GDScript en el día a día: cómo se conecta con tus nodos

GDScript es el lenguaje que usarás para darle comportamiento a los nodos de tu escena. En la práctica, casi todo se resume a: leer datos (variables), reaccionar a eventos (señales e input) y actualizar el estado del juego en el tiempo (funciones de ciclo de vida como _ready(), _process() y _physics_process()).

Crear y adjuntar un script (recordatorio práctico)

  • Selecciona un nodo en tu escena (por ejemplo, Node2D o CharacterBody2D).
  • Haz clic en “Attach Script”.
  • Abre el script y prueba los ejemplos de este capítulo.

Variables y tipos: datos que controlan tu lógica

Una variable guarda un valor que tu juego usa para decidir qué hacer: velocidad, vida, puntuación, etc. En Godot 4, es buena práctica tipar variables para detectar errores antes.

Variables sin tipo vs variables tipadas

extends Node2D

var sin_tipo = 10
var velocidad: float = 250.0
var nombre: String = "Player"
var vivo: bool = true

Tipos comunes en 2D:

  • int, float, bool, String
  • Vector2 (posiciones, direcciones, velocidades)
  • Color, Rect2
  • Referencias a nodos: Sprite2D, Label, CharacterBody2D, etc.

Ejemplo verificable: imprimir variables en la consola

Pega esto en un script de cualquier nodo y ejecuta la escena:

extends Node

var puntos: int = 0
var posicion_inicial: Vector2 = Vector2(100, 50)

func _ready() -> void:
	print("Puntos:", puntos)
	print("Posición inicial:", posicion_inicial)
	puntos += 10
	print("Puntos tras sumar:", puntos)

Verás las salidas en la pestaña “Output”.

Continúa en nuestra aplicación.
  • Escuche el audio con la pantalla apagada.
  • Obtenga un certificado al finalizar.
  • ¡Más de 5000 cursos para que explores!
O continúa leyendo más abajo...
Download App

Descargar la aplicación

Funciones: encapsular comportamiento

Una función agrupa instrucciones con un propósito claro. Puedes recibir parámetros y devolver valores.

Ejemplo: función que calcula daño

extends Node

func calcular_danio(base: int, critico: bool) -> int:
	if critico:
		return base * 2
	return base

func _ready() -> void:
	print(calcular_danio(10, false)) # 10
	print(calcular_danio(10, true))  # 20

Buenas prácticas rápidas

  • Nombra funciones con verbos: mover(), disparar(), recibir_danio().
  • Tipa parámetros y retorno cuando puedas.
  • Evita funciones gigantes: divide en partes.

Ciclo de vida: _ready(), _process() y _physics_process()

Estas funciones se llaman automáticamente en momentos específicos. Entenderlas evita bugs típicos (por ejemplo, mover física en _process o leer nodos antes de que existan).

FunciónCuándo se ejecutaUso típico
_ready()Una vez, cuando el nodo y sus hijos están listosInicializar, obtener referencias, conectar señales
_process(delta)Cada frame (variable)Animaciones por frame, UI, timers simples
_physics_process(delta)En pasos fijos de físicaMovimiento con colisiones, gravedad, move_and_slide()

Ejemplo verificable: contador de tiempo y movimiento simple

Adjunta este script a un Node2D y ejecuta. Verás impresiones y el nodo se moverá en X:

extends Node2D

var tiempo: float = 0.0

func _ready() -> void:
	print("Listo. Posición inicial:", position)

func _process(delta: float) -> void:
	tiempo += delta
	if int(tiempo) % 1 == 0:
		# Ojo: esto se cumple muchas veces; es solo demostración
		pass

func _physics_process(delta: float) -> void:
	position.x += 60.0 * delta

Nota práctica: si quieres imprimir “cada 1 segundo” de verdad, usa un acumulador y compara con un umbral (lo verás más abajo en el laboratorio).

Input: leer teclas y acciones

En Godot, lo más cómodo es usar acciones del InputMap (por ejemplo: ui_left, ui_right, ui_accept). Así tu juego no depende de una tecla específica.

Ejemplo verificable: detectar pulsación

extends Node

func _process(delta: float) -> void:
	if Input.is_action_just_pressed("ui_accept"):
		print("Aceptar presionado")

Ejemplo verificable: eje horizontal con get_axis

extends Node

func _process(delta: float) -> void:
	var x := Input.get_axis("ui_left", "ui_right")
	if x != 0:
		print("Eje horizontal:", x)

Acceso al árbol de nodos: obtener y modificar otros nodos

Tu script vive en un nodo, pero normalmente necesita hablar con otros: un Sprite2D para cambiar su color, un Label para mostrar vida, etc. Para eso se usa el árbol de nodos.

Rutas y get_node()

Si tu escena tiene una estructura como:

Player (Node2D)
├─ Sprite2D
└─ Label

Desde el script en Player puedes hacer:

extends Node2D

func _ready() -> void:
	var sprite := get_node("Sprite2D") as Sprite2D
	var label := get_node("Label") as Label
	label.text = "Hola"
	sprite.modulate = Color(1, 0.8, 0.8)

Atajo recomendado: @onready

@onready evalúa la referencia cuando el nodo ya está listo (evita nulos por orden de carga).

extends Node2D

@onready var label: Label = $Label
@onready var sprite: Sprite2D = $Sprite2D

func _ready() -> void:
	label.text = "Listo"
	sprite.rotation = 0.2

Señales: eventos que conectan nodos sin acoplarlos

Una señal es un evento. Por ejemplo: un botón emite pressed, un área emite body_entered. Puedes conectar señales desde el editor o por código.

Conectar una señal por código (ejemplo verificable)

Crea un nodo Button (en una escena UI) y adjunta este script al nodo padre (por ejemplo, Control). Asegúrate de que el botón se llame Button.

extends Control

@onready var button: Button = $Button

func _ready() -> void:
	button.pressed.connect(_on_button_pressed)

func _on_button_pressed() -> void:
	print("Botón presionado")

Al ejecutar y hacer clic, verás el mensaje en consola.

Crear tus propias señales

También puedes definir señales en tus scripts para avisar a otros sistemas (por ejemplo, “murió el jugador” o “cambió la vida”).

extends Node

signal vida_cambiada(nueva_vida: int)

var vida: int = 100

func recibir_danio(cantidad: int) -> void:
	vida = max(vida - cantidad, 0)
	vida_cambiada.emit(vida)

@export: parámetros configurables desde el Inspector

@export expone variables en el Inspector para ajustar valores sin tocar código. Esto acelera el ajuste fino (tuning) de velocidad, gravedad, saltos, etc.

Ejemplo: exportar velocidad y color

extends Node2D

@export var velocidad: float = 200.0
@export var color: Color = Color(1, 1, 1)

@onready var sprite: Sprite2D = $Sprite2D

func _ready() -> void:
	sprite.modulate = color
	print("Velocidad configurada:", velocidad)

Ejecuta la escena, cambia velocidad y color desde el Inspector y vuelve a ejecutar para validar.

Export con rangos (útil para tuning)

@export_range(0.0, 1000.0, 10.0) var velocidad: float = 250.0

Mini-laboratorio: script de Jugador configurable y validación en ejecución

Objetivo: crear un Jugador con movimiento horizontal, gravedad y salto, donde velocidad y gravedad se ajusten desde el Inspector. Validarás que los cambios afectan el comportamiento al ejecutar.

Paso 1: Preparar el nodo Jugador

  • Crea un nodo CharacterBody2D llamado Jugador.
  • Como hijos, agrega: CollisionShape2D (con una forma) y opcionalmente Sprite2D para verlo.
  • Adjunta un script nuevo a Jugador.

Paso 2: Escribir el script con @export y lógica de movimiento

Pega este código en el script de Jugador:

extends CharacterBody2D

@export_range(0.0, 800.0, 10.0) var velocidad: float = 220.0
@export_range(0.0, 3000.0, 50.0) var gravedad: float = 1200.0
@export_range(0.0, 1200.0, 10.0) var fuerza_salto: float = 420.0

var _acum_log: float = 0.0

func _ready() -> void:
	print("Jugador listo")
	print("velocidad=", velocidad, " gravedad=", gravedad, " salto=", fuerza_salto)

func _physics_process(delta: float) -> void:
	# 1) Gravedad
	if not is_on_floor():
		velocity.y += gravedad * delta
	else:
		# Evita acumular velocidad hacia abajo al tocar el piso
		if velocity.y > 0:
			velocity.y = 0

	# 2) Movimiento horizontal
	var dir_x := Input.get_axis("ui_left", "ui_right")
	velocity.x = dir_x * velocidad

	# 3) Salto
	if Input.is_action_just_pressed("ui_accept") and is_on_floor():
		velocity.y = -fuerza_salto

	# 4) Aplicar movimiento con colisiones
	move_and_slide()

	# 5) Validación simple en consola (cada 0.5s)
	_acum_log += delta
	if _acum_log >= 0.5:
		_acum_log = 0.0
		print("pos=", global_position, " vel=", velocity)

Paso 3: Validar en ejecución (pruebas rápidas)

  • Ejecuta la escena y mueve con ui_left/ui_right (flechas o A/D según tu InputMap).
  • Salta con ui_accept (por defecto Enter/Espacio según configuración).
  • Observa en consola el log de posición y velocidad cada 0.5s.

Paso 4: Ajuste desde el Inspector (comprobación de que @export funciona)

  • Con el nodo Jugador seleccionado, cambia velocidad (por ejemplo, 220 → 400) y ejecuta: deberías moverte más rápido.
  • Cambia gravedad (1200 → 2000) y ejecuta: caerás más rápido y el salto se sentirá más “pesado”.
  • Cambia fuerza_salto (420 → 600) y ejecuta: el salto alcanzará mayor altura.

Paso 5 (opcional): cambiar una propiedad visual al moverte

Si tu Jugador tiene un Sprite2D hijo, añade esto para verificar acceso al árbol de nodos y cambios de propiedades:

@onready var sprite: Sprite2D = $Sprite2D

func _physics_process(delta: float) -> void:
	# ... tu código anterior ...
	if velocity.x != 0:
		sprite.modulate = Color(0.8, 1.0, 0.8)
	else:
		sprite.modulate = Color(1.0, 1.0, 1.0)

Ahora responde el ejercicio sobre el contenido:

¿Cuál es la razón principal para usar @onready al guardar referencias a nodos hijos (por ejemplo, $Label o $Sprite2D) en un script?

¡Tienes razón! Felicitaciones, ahora pasa a la página siguiente.

¡Tú error! Inténtalo de nuevo.

@onready retrasa la asignación hasta que el nodo y sus hijos están listos, reduciendo errores por intentar acceder a nodos que aún no existen.

Siguiente capítulo

Movimiento 2D y control del jugador con CharacterBody2D

Arrow Right Icon
Portada de libro electrónico gratuitaGodot desde Cero: Crea tu Primer Juego 2D con GDScript
33%

Godot desde Cero: Crea tu Primer Juego 2D con GDScript

Nuevo curso

12 páginas

Descarga la aplicación para obtener una certificación gratuita y escuchar cursos en segundo plano, incluso con la pantalla apagada.