Capítulo 4: GDScript Básico - Fundamentos del Lenguaje
Capítulo 4: GDScript Básico - Fundamentos del Lenguaje
GDScript es el corazón de Godot. En este capítulo aprenderás desde cero todos los fundamentos necesarios para empezar a programar tus juegos. No necesitas experiencia previa en programación - empezaremos desde lo más básico.
¿Qué es GDScript?
GDScript es un lenguaje de programación creado específicamente para Godot. Si has programado en Python, te sentirás como en casa. Si nunca has programado, no te preocupes - GDScript fue diseñado para ser fácil de aprender.
Características de GDScript
- Sintaxis simple: Sin llaves {} ni punto y coma ;
- Indentación significativa: Los espacios/tabs importan (como Python)
- Tipado dinámico: No necesitas declarar tipos (pero puedes hacerlo)
- Integrado con Godot: Acceso directo a todas las funciones del motor
- Hot reload: Cambios en tiempo real sin reiniciar
Tu Primer Script
Crea un nuevo script y verás esto:
extends Node
# Called when the node enters the scene tree for the first time.
func _ready():
pass # Replace with function body.
# Called every frame. 'delta' is the elapsed time since the previous frame.
func _process(delta):
pass
Vamos a entender cada línea:
extends Node # Este script hereda de la clase Node
# Esto es un comentario - no se ejecuta
# Los comentarios explican el código
func _ready(): # Define una función llamada _ready
pass # pass significa "no hacer nada aún"
Comentarios y Documentación
Los comentarios son notas para ti y otros programadores:
# Comentario de una línea
# Puedes tener
# múltiples líneas
# de comentarios
"""
Este es un comentario
de múltiples líneas
usando triple comillas
"""
## Comentarios de documentación (para generar docs)
## @tutorial: https://docs.godotengine.org
## @deprecated: Esta función será eliminada
func mi_funcion():
# Los comentarios dentro de funciones explican la lógica
var vida = 100 # También puedes comentar al final de líneas
Variables: Almacenando Información
Las variables son como cajas donde guardas datos:
Declaración Básica
# Usar 'var' para crear variables
var mi_numero = 42
var mi_texto = "Hola Godot"
var mi_decimal = 3.14
var es_verdad = true
# Puedes cambiar el valor
mi_numero = 100
mi_texto = "Adiós"
# Variables sin valor inicial
var sin_valor # Vale null por defecto
var otra_variable = null # Explícitamente null
Tipos de Datos Básicos
# Enteros (int) - Números sin decimales
var edad = 25
var puntos = 0
var vidas = 3
var numero_negativo = -10
# Decimales (float) - Números con punto decimal
var velocidad = 5.5
var gravedad = 9.81
var pi = 3.14159
var porcentaje = 0.75
# Texto (String) - Cadenas de caracteres
var nombre = "Mario"
var mensaje = "Bienvenido al juego"
var vacio = "" # String vacío
var con_comillas = 'También con comillas simples'
# Booleanos (bool) - Verdadero o Falso
var esta_vivo = true
var juego_pausado = false
var puede_saltar = true
Constantes
Las constantes no pueden cambiar después de definirse:
# Constantes con MAYÚSCULAS (convención)
const MAX_VIDAS = 3
const VELOCIDAD_JUGADOR = 200.0
const NOMBRE_JUEGO = "Mi Súper Juego"
const GRAVEDAD = 9.8
# Esto daría error:
# MAX_VIDAS = 5 # ¡No puedes cambiar una constante!
# Úsalas para valores que nunca cambian
const ANCHO_PANTALLA = 1280
const ALTO_PANTALLA = 720
const VERSION = "1.0.0"
Conversión de Tipos
A veces necesitas convertir entre tipos:
# String a número
var texto_numero = "42"
var numero = int(texto_numero) # Convierte a 42
var decimal = float("3.14") # Convierte a 3.14
# Número a String
var puntos = 100
var texto_puntos = str(puntos) # "100"
var mensaje = "Tienes " + str(puntos) + " puntos"
# Boolean
var verdadero = bool(1) # true (cualquier número != 0)
var falso = bool(0) # false
var texto_bool = bool("") # false (string vacío)
var texto_lleno = bool("algo") # true
Operadores Matemáticos
Operaciones Básicas
# Suma
var resultado = 5 + 3 # 8
var vida = vida + 10 # Aumenta vida en 10
vida += 10 # Forma corta de lo anterior
# Resta
var diferencia = 10 - 3 # 7
var vida = vida - 20 # Reduce vida en 20
vida -= 20 # Forma corta
# Multiplicación
var producto = 4 * 5 # 20
var doble = puntos * 2
puntos *= 2 # Duplica puntos
# División
var division = 10 / 2 # 5.0 (siempre retorna float)
var mitad = vida / 2.0
vida /= 2 # Divide vida a la mitad
# División entera
var division_entera = 10 // 3 # 3 (sin decimales)
# Módulo (resto de división)
var resto = 10 % 3 # 1
var es_par = numero % 2 == 0 # true si es par
# Potencia
var cuadrado = 3 ** 2 # 9
var cubo = 2 ** 3 # 8
Operaciones con Strings
# Concatenación (unir textos)
var nombre = "Juan"
var apellido = "Pérez"
var completo = nombre + " " + apellido # "Juan Pérez"
# Repetición
var linea = "-" * 10 # "----------"
var espacios = " " * 5 # " "
# Interpolación (formato)
var vidas = 3
var puntos = 100
var mensaje = "Vidas: %d, Puntos: %d" % [vidas, puntos]
# Resultado: "Vidas: 3, Puntos: 100"
# Diferentes formatos
var pi = 3.14159
var formato = "Pi es %.2f" % pi # "Pi es 3.14"
var nombre = "Mario"
var saludo = "Hola %s" % nombre # "Hola Mario"
Operadores de Comparación
Para comparar valores:
var a = 5
var b = 10
# Igual
var son_iguales = (a == b) # false
var mismo_numero = (5 == 5) # true
# Diferente
var son_diferentes = (a != b) # true
var no_es_cero = (puntos != 0) # depende de puntos
# Mayor que
var es_mayor = (b > a) # true
var tiene_vida = (vida > 0) # para verificar si está vivo
# Menor que
var es_menor = (a < b) # true
var sin_municion = (balas < 1) # true si no hay balas
# Mayor o igual
var mayor_igual = (10 >= 10) # true
var nivel_maximo = (nivel >= 99) # verificar nivel máximo
# Menor o igual
var menor_igual = (5 <= 10) # true
var vida_critica = (vida <= 25) # verificar vida baja
Operadores Lógicos
Para combinar condiciones:
# AND (y) - Ambas deben ser verdaderas
var puede_atacar = esta_vivo and tiene_municion
var puede_comprar = tiene_dinero and tienda_abierta
# OR (o) - Al menos una debe ser verdadera
var game_over = sin_vidas or tiempo_agotado
var puede_abrir = tiene_llave or es_admin
# NOT (no) - Invierte el valor
var esta_muerto = not esta_vivo
var no_puede_saltar = not en_el_suelo
# Combinaciones
var puede_super_salto = (
esta_vivo and
en_el_suelo and
(tiene_power_up or modo_debug)
)
Estructuras de Control: if/else
El if toma decisiones en tu código:
if Básico
var vida = 75
if vida > 0:
print("Estás vivo")
# Con múltiples líneas
if vida > 0:
print("Estás vivo")
puede_mover = true
puede_atacar = true
if/else
if vida > 0:
print("Estás vivo")
else:
print("Estás muerto")
reiniciar_nivel()
# Operador ternario (if en una línea)
var estado = "vivo" if vida > 0 else "muerto"
if/elif/else
if vida >= 100:
print("Vida completa")
color_barra = Color.GREEN
elif vida >= 50:
print("Vida buena")
color_barra = Color.YELLOW
elif vida >= 25:
print("Vida baja")
color_barra = Color.ORANGE
else:
print("¡Crítico!")
color_barra = Color.RED
reproducir_alerta()
# Condiciones múltiples
if esta_vivo and tiene_llave:
abrir_puerta()
elif esta_vivo and not tiene_llave:
mostrar_mensaje("Necesitas una llave")
else:
mostrar_mensaje("Game Over")
Bucles: Repitiendo Código
Bucle for
Para repetir un número específico de veces:
# Repetir 5 veces
for i in 5:
print("Repetición ", i) # 0, 1, 2, 3, 4
# Con range para más control
for i in range(5):
print(i) # 0, 1, 2, 3, 4
for i in range(1, 6):
print(i) # 1, 2, 3, 4, 5
for i in range(10, 0, -1):
print(i) # Cuenta regresiva: 10, 9, 8...1
# Recorrer arrays
var enemigos = ["Goblin", "Orco", "Dragón"]
for enemigo in enemigos:
print("Luchando contra ", enemigo)
# Con índice
for i in range(enemigos.size()):
print("Enemigo ", i, ": ", enemigos[i])
Bucle while
Para repetir mientras una condición sea verdadera:
var contador = 0
while contador < 5:
print("Contador: ", contador)
contador += 1
# Bucle de juego típico
var jugando = true
while jugando:
actualizar_juego()
if vida <= 0:
jugando = false
# Cuidado con bucles infinitos
var x = 0
while x < 10:
print(x)
x += 1 # Sin esto, sería infinito!
Control de Bucles
# break - Sale del bucle
for i in range(10):
if i == 5:
break # Sale cuando i es 5
print(i) # Imprime 0, 1, 2, 3, 4
# continue - Salta a la siguiente iteración
for i in range(10):
if i % 2 == 0:
continue # Salta números pares
print(i) # Imprime 1, 3, 5, 7, 9
# Ejemplo práctico
var items = ["Poción", "", "Espada", "", "Escudo"]
for item in items:
if item == "":
continue # Ignora espacios vacíos
print("Tienes: ", item)
Funciones: Código Reutilizable
Las funciones son bloques de código que puedes usar múltiples veces:
Funciones Básicas
# Función simple
func saludar():
print("¡Hola!")
print("¿Cómo estás?")
# Llamar la función
saludar() # Ejecuta el código de la función
# Función con parámetros
func saludar_a(nombre):
print("¡Hola, ", nombre, "!")
# Llamar con argumentos
saludar_a("Mario") # ¡Hola, Mario!
saludar_a("Luigi") # ¡Hola, Luigi!
Funciones con Return
# Función que devuelve un valor
func sumar(a, b):
return a + b
# Usar el valor retornado
var resultado = sumar(5, 3) # resultado = 8
print("5 + 3 = ", resultado)
# Función con múltiples returns
func obtener_estado_vida(vida):
if vida >= 100:
return "Perfecto"
elif vida >= 50:
return "Bueno"
elif vida > 0:
return "Crítico"
else:
return "Muerto"
var estado = obtener_estado_vida(75) # "Bueno"
Parámetros Opcionales
# Parámetros con valores por defecto
func crear_enemigo(tipo = "Goblin", vida = 100):
print("Creando ", tipo, " con ", vida, " de vida")
# Diferentes formas de llamarla
crear_enemigo() # Goblin con 100
crear_enemigo("Orco") # Orco con 100
crear_enemigo("Dragón", 500) # Dragón con 500
# Función más compleja
func aplicar_dano(cantidad, es_critico = false, elemento = "normal"):
var dano_total = cantidad
if es_critico:
dano_total *= 2
if elemento == "fuego":
dano_total *= 1.5
return dano_total
var dano = aplicar_dano(10) # 10 de daño normal
var dano_critico = aplicar_dano(10, true) # 20 de daño
var dano_fuego = aplicar_dano(10, false, "fuego") # 15 de daño
Arrays: Listas de Elementos
Los arrays almacenan múltiples valores:
Arrays Básicos
# Crear arrays
var vacio = []
var numeros = [1, 2, 3, 4, 5]
var nombres = ["Ana", "Luis", "María"]
var mixto = [1, "texto", 3.14, true] # Diferentes tipos
# Acceder a elementos (índice empieza en 0)
var primero = numeros[0] # 1
var segundo = numeros[1] # 2
var ultimo = numeros[4] # 5
# Modificar elementos
numeros[0] = 10 # Ahora es [10, 2, 3, 4, 5]
nombres[1] = "Carlos" # ["Ana", "Carlos", "María"]
# Tamaño del array
var cantidad = numeros.size() # 5
var hay_elementos = nombres.size() > 0 # true
Operaciones con Arrays
var inventario = []
# Añadir elementos
inventario.append("Espada") # Añade al final
inventario.push_back("Escudo") # Igual que append
inventario.push_front("Poción") # Añade al inicio
# inventario = ["Poción", "Espada", "Escudo"]
# Insertar en posición específica
inventario.insert(1, "Arco")
# inventario = ["Poción", "Arco", "Espada", "Escudo"]
# Eliminar elementos
inventario.pop_back() # Quita el último
inventario.pop_front() # Quita el primero
inventario.remove_at(0) # Quita en posición específica
inventario.erase("Espada") # Quita primera ocurrencia
# Buscar elementos
var tiene_espada = "Espada" in inventario
var posicion = inventario.find("Escudo") # Índice o -1
# Limpiar array
inventario.clear() # Vacía el array
Arrays Útiles
# Array de enemigos en el nivel
var enemigos = []
func agregar_enemigo(enemigo):
enemigos.append(enemigo)
func eliminar_enemigo(enemigo):
enemigos.erase(enemigo)
# Array de puntuaciones
var puntuaciones = [100, 250, 50, 300, 150]
puntuaciones.sort() # Ordena: [50, 100, 150, 250, 300]
var mejor = puntuaciones[-1] # Último elemento: 300
# Array para histórico
var historico_vida = []
func actualizar_vida(nueva_vida):
historico_vida.append(nueva_vida)
if historico_vida.size() > 10:
historico_vida.pop_front() # Mantén solo últimos 10
Diccionarios: Pares Clave-Valor
Los diccionarios almacenan datos con etiquetas:
Diccionarios Básicos
# Crear diccionarios
var jugador = {
"nombre": "Mario",
"nivel": 5,
"vida": 100,
"mana": 50
}
# Acceder a valores
var nombre = jugador["nombre"] # "Mario"
var nivel = jugador.nivel # 5 (notación de punto)
# Modificar valores
jugador["nivel"] = 6
jugador.vida = 80
# Añadir nuevas claves
jugador["experiencia"] = 1500
jugador["clase"] = "Guerrero"
# Verificar si existe una clave
if "mana" in jugador:
print("El jugador tiene mana")
if jugador.has("inventario"):
print("Tiene inventario")
Operaciones con Diccionarios
var stats = {
"fuerza": 10,
"agilidad": 15,
"inteligencia": 8
}
# Obtener con valor por defecto
var carisma = stats.get("carisma", 5) # 5 si no existe
# Obtener todas las claves
var atributos = stats.keys() # ["fuerza", "agilidad", "inteligencia"]
# Obtener todos los valores
var valores = stats.values() # [10, 15, 8]
# Eliminar claves
stats.erase("inteligencia")
# Limpiar diccionario
stats.clear()
# Combinar diccionarios
var base_stats = {"fuerza": 10, "vida": 100}
var bonus_stats = {"fuerza": 5, "mana": 50}
base_stats.merge(bonus_stats)
# Resultado: {"fuerza": 5, "vida": 100, "mana": 50}
Usos Prácticos
# Sistema de inventario
var inventario = {
"pociones": 5,
"llaves": 2,
"oro": 100
}
func usar_item(item):
if item in inventario and inventario[item] > 0:
inventario[item] -= 1
return true
return false
# Configuración del juego
var config = {
"volumen_musica": 0.8,
"volumen_fx": 1.0,
"dificultad": "normal",
"pantalla_completa": false
}
# Base de datos de enemigos
var enemigos_db = {
"goblin": {"vida": 30, "ataque": 5, "exp": 10},
"orco": {"vida": 50, "ataque": 10, "exp": 25},
"dragon": {"vida": 200, "ataque": 30, "exp": 100}
}
func crear_enemigo(tipo):
if tipo in enemigos_db:
return enemigos_db[tipo].duplicate()
return null
Tipos Especiales de Godot
Vector2: Posiciones y Direcciones 2D
# Crear vectores
var posicion = Vector2(100, 200) # x=100, y=200
var direccion = Vector2(1, 0) # Derecha
var cero = Vector2.ZERO # (0, 0)
var arriba = Vector2.UP # (0, -1)
var abajo = Vector2.DOWN # (0, 1)
var izquierda = Vector2.LEFT # (-1, 0)
var derecha = Vector2.RIGHT # (1, 0)
# Operaciones
var suma = posicion + Vector2(50, 50)
var velocidad = direccion * 100 # Escalar
var distancia = posicion.distance_to(Vector2.ZERO)
var normalizado = direccion.normalized() # Longitud 1
# Uso común
position += velocity * delta # Mover personaje
Color: Manejo de Colores
# Crear colores
var rojo = Color.RED
var azul = Color(0, 0, 1) # RGB
var transparente = Color(1, 1, 1, 0.5) # RGBA
var desde_hex = Color("#FF5733")
# Colores predefinidos
var verde = Color.GREEN
var blanco = Color.WHITE
var negro = Color.BLACK
# Modificar sprite
$Sprite2D.modulate = Color.RED # Tinte rojo
NodePath: Referencias a Nodos
# Rutas de nodos
var ruta = NodePath("Player/Sprite2D")
var ruta_absoluta = NodePath("/root/Main/Player")
# Obtener nodos
var sprite = get_node("Sprite2D") # Hijo directo
var player = get_node("/root/Main/Player") # Ruta absoluta
var padre = get_node("..") # Nodo padre
# Notación $
var animation = $AnimationPlayer # Igual que get_node("AnimationPlayer")
var nested = $Container/Label # Nodo anidado
Sistema de Nodos y Señales
Ciclo de Vida del Nodo
extends Node
# Se llama cuando el nodo entra al árbol
func _ready():
print("Nodo listo")
configurar_inicial()
# Se llama cada frame
func _process(delta):
# delta es el tiempo desde el último frame
actualizar_logica(delta)
# Se llama para física (60 fps fijo)
func _physics_process(delta):
mover_personaje(delta)
# Se llama al salir del árbol
func _exit_tree():
guardar_datos()
print("Adiós")
Señales Básicas
# Definir señal
signal vida_cambio(nueva_vida)
signal muerto()
signal item_recogido(nombre_item)
# Emitir señal
func recibir_dano(cantidad):
vida -= cantidad
vida_cambio.emit(vida)
if vida <= 0:
muerto.emit()
# Conectar señal
func _ready():
# Conectar a función
player.muerto.connect(_on_player_muerto)
# Conectar con parámetros extra
button.pressed.connect(_on_button_pressed.bind("extra_data"))
func _on_player_muerto():
print("Game Over")
get_tree().reload_current_scene()
Input: Detectando Controles
Input Básico
func _process(delta):
# Teclas
if Input.is_action_pressed("ui_right"):
position.x += speed * delta
if Input.is_action_just_pressed("jump"):
if is_on_floor():
velocity.y = -jump_force
# Mouse
if Input.is_mouse_button_pressed(MOUSE_BUTTON_LEFT):
disparar()
var mouse_pos = get_global_mouse_position()
look_at(mouse_pos)
func _input(event):
# Eventos específicos
if event is InputEventKey:
if event.pressed and event.keycode == KEY_SPACE:
print("Espacio presionado")
if event is InputEventMouseButton:
if event.button_index == MOUSE_BUTTON_LEFT:
if event.pressed:
print("Click en: ", event.position)
Gestión de Recursos de Godot
Godot Asset Library
Godot tiene su propia “tienda” de assets y plugins gratuitos:
- En el Editor: AssetLib (pestaña junto a 2D, 3D, Script)
- Online: godotengine.org/asset-library
Tipos de Assets Disponibles
# Plugins populares en Asset Library:
# - Dialogic (sistema de diálogos)
# - Godot-Firebase (integración Firebase)
# - ProtonScatter (herramienta de scatter)
# - SmartShape2D (formas 2D inteligentes)
# Para instalar desde AssetLib:
# 1. Buscar el asset
# 2. Click en Download
# 3. Install
# 4. Activar en Project Settings > Plugins
Gestión de Dependencias
# project.godot - archivo de configuración
# No hay npm, pero puedes:
# 1. Usar git submodules para librerías
# 2. Copiar addons/ en tu proyecto
# 3. Usar el Asset Library integrado
# Estructura típica:
# res://
# ├── addons/ # Plugins y librerías
# │ ├── dialogic/
# │ └── firebase/
# ├── scenes/
# └── scripts/
Recursos y Cargas
# Cargar recursos
var textura = load("res://icon.png") # Carga inmediata
var escena = preload("res://player.tscn") # Carga en compilación
# Instanciar escenas
var player_instance = escena.instantiate()
add_child(player_instance)
# Recursos dinámicos
var ruta = "res://sprites/" + nombre_sprite + ".png"
var sprite_texture = load(ruta)
Debugging y Errores Comunes
Print Debugging
# Diferentes tipos de print
print("Mensaje normal")
print_debug("Solo en modo debug")
printerr("Mensaje de error")
push_error("Error que aparece en el debugger")
push_warning("Advertencia en el debugger")
# Print con múltiples valores
var vida = 100
var nombre = "Mario"
print("Jugador: ", nombre, " - Vida: ", vida)
# Print formateado
print("Vida: %d/%d" % [vida_actual, vida_maxima])
Errores Comunes y Soluciones
# Error: Invalid get index 'x' (on base: 'null')
# Solución: Verificar que el nodo existe
if player != null:
player.position.x = 100
# Error: Invalid call. Nonexistent function
# Solución: Verificar el nombre de la función
if target.has_method("recibir_dano"):
target.recibir_dano(10)
# Error: Division by zero
# Solución: Verificar antes de dividir
if divisor != 0:
resultado = dividendo / divisor
else:
resultado = 0
# Error: Index out of bounds
# Solución: Verificar tamaño del array
if index < array.size():
var elemento = array[index]
Ejercicios Prácticos
Ejercicio 1: Calculadora Simple
# Crea funciones para operaciones básicas
func sumar(a, b):
return a + b
func restar(a, b):
return a - b
func multiplicar(a, b):
return a * b
func dividir(a, b):
if b != 0:
return a / b
else:
push_error("División por cero")
return 0
# Prueba
func _ready():
print("2 + 3 = ", sumar(2, 3))
print("10 - 4 = ", restar(10, 4))
print("5 * 6 = ", multiplicar(5, 6))
print("20 / 4 = ", dividir(20, 4))
Ejercicio 2: Sistema de Inventario
var inventario = []
var capacidad_maxima = 10
func agregar_item(item):
if inventario.size() < capacidad_maxima:
inventario.append(item)
print("Añadido: ", item)
return true
else:
print("Inventario lleno")
return false
func usar_item(indice):
if indice < inventario.size():
var item = inventario[indice]
inventario.remove_at(indice)
print("Usado: ", item)
return item
return null
func listar_inventario():
print("=== Inventario ===")
for i in range(inventario.size()):
print(i, ": ", inventario[i])
Ejercicio 3: Control de Personaje
extends CharacterBody2D
const SPEED = 300.0
var vida = 100
var puede_moverse = true
func _ready():
print("Personaje listo")
func _physics_process(delta):
if not puede_moverse:
return
var direccion = Vector2.ZERO
if Input.is_action_pressed("ui_right"):
direccion.x += 1
if Input.is_action_pressed("ui_left"):
direccion.x -= 1
if Input.is_action_pressed("ui_down"):
direccion.y += 1
if Input.is_action_pressed("ui_up"):
direccion.y -= 1
if direccion != Vector2.ZERO:
direccion = direccion.normalized()
velocity = direccion * SPEED
move_and_slide()
func recibir_dano(cantidad):
vida -= cantidad
print("Vida: ", vida)
if vida <= 0:
morir()
func morir():
print("Game Over")
puede_moverse = false
hide()
Resumen del Capítulo
Has aprendido los fundamentos de GDScript:
- ✅ Variables y tipos de datos
- ✅ Operadores matemáticos y lógicos
- ✅ Estructuras de control (if, for, while)
- ✅ Funciones básicas
- ✅ Arrays y diccionarios
- ✅ Tipos especiales de Godot
- ✅ Sistema de nodos y señales
- ✅ Input básico
- ✅ Debugging
Próximo Capítulo
En el siguiente capítulo exploraremos conceptos intermedios de GDScript:
- Programación orientada a objetos
- Herencia y polimorfismo
- Señales avanzadas
- Coroutines y await
- Manejo de archivos
→ Capítulo 5: GDScript Intermedio
Reto: Crea un mini-juego donde el jugador debe recolectar items antes de que se acabe el tiempo. Usa todo lo aprendido: variables, arrays, funciones, input y señales.