Apuntes DAM
    Tema 5 de 6PMDM · 2º DAM

    Programación Multimedia y Dispositivos Móviles

    Desarrollo de Juegos 2D y 3D

    Fases y documento de diseño, un juego 2D completo en Godot (movimiento, monedas, enemigos con máquina de estados, marcador y récord), juegos 3D, sonido e interfaz, exportación y publicación, con minijuego y retos autocorregidos.

    70 min lecturaAvanzado

    1. Fases del desarrollo de un juego

    Hacer un juego es como rodar una película 🎥

    📝
    Guion (preproducción)
    Documento de diseño y prototipo: se decide qué juego se hace y si es divertido.
    🎬
    Rodaje (producción)
    Se construyen niveles, personajes, arte, sonido y código.
    ✂️
    Montaje (pospro.)
    Pruebas, pulido, optimización y publicación.
    1. Concepto: género, público, plataforma y la mecánica principal (lo que el jugador hace una y otra vez).
    2. Documento de diseño (GDD): historia, mecánicas, controles, niveles, personajes, estilo gráfico, sonido, interfaz y monetización.
    3. Prototipo: la mecánica principal con formas simples (cajas y círculos) para comprobar que es divertida antes de invertir en arte.
    4. Producción: arte, animaciones, niveles, sonido, interfaz, guardado.
    5. Pruebas: errores (QA), equilibrio de dificultad, pruebas con jugadores, rendimiento en dispositivos reales.
    6. Publicación y mantenimiento: tiendas, marketing, actualizaciones y corrección de errores.
    Cómo hacer un game design document (GDD) — Alva Majo

    2. Un juego 2D paso a paso con Godot

    Construimos un juego de recoger monedas y esquivar enemigos. En Godot cada elemento es una escena que se instancia dentro de otras.

    EscenaNodosFunción
    Jugador.tscnCharacterBody2D → AnimatedSprite2D, CollisionShape2D, Camera2DSe mueve con el teclado o la pantalla táctil
    Moneda.tscnArea2D → AnimatedSprite2D, CollisionShape2D, AudioStreamPlayer2DSuma puntos al tocarla y desaparece
    Enemigo.tscnCharacterBody2D → Sprite2D, CollisionShape2D, Area2D (zona de visión)Patrulla y persigue al jugador
    Nivel.tscnNode2D → TileMapLayer, instancias de Jugador, Monedas y EnemigosEl escenario
    HUD.tscnCanvasLayer → Label (puntos), TextureRect (vidas)Interfaz por encima del juego
    Main.tscnNode → Nivel, HUD, AudioStreamPlayer (música)Controla el estado del juego

    1. Entrada y movimiento

    En Proyecto → Configuración → Mapa de entrada se crean las acciones izquierda, derecha, arriba, abajo asignando teclas, botones del mando o toques.

    jugador.gd
    1extends CharacterBody2D
    2
    3@export var velocidad := 200.0
    4signal golpeado
    5
    6func _physics_process(_delta):
    7    var direccion := Input.get_vector("izquierda", "derecha", "arriba", "abajo")
    8    velocity = direccion * velocidad
    9    move_and_slide()
    10    # Animación según el movimiento
    11    if direccion == Vector2.ZERO:
    12        $AnimatedSprite2D.play("quieto")
    13    else:
    14        $AnimatedSprite2D.play("andar")
    15        $AnimatedSprite2D.flip_h = direccion.x < 0

    2. Monedas con señales

    moneda.gd
    1extends Area2D
    2
    3signal recogida(valor)
    4@export var valor := 10
    5
    6func _on_body_entered(body):          # señal body_entered conectada en el editor
    7    if body.is_in_group("jugador"):
    8        recogida.emit(valor)
    9        $AudioStreamPlayer2D.play()
    10        $CollisionShape2D.set_deferred("disabled", true)
    11        hide()
    12        await $AudioStreamPlayer2D.finished
    13        queue_free()                    # eliminar el nodo de la escena

    3. Enemigo con máquina de estados

    enemigo.gd
    1extends CharacterBody2D
    2
    3enum Estado { PATRULLAR, PERSEGUIR }
    4var estado := Estado.PATRULLAR
    5var jugador: Node2D
    6@export var puntos_ruta: Array[Vector2] = []
    7var indice := 0
    8
    9func _physics_process(_delta):
    10    match estado:
    11        Estado.PATRULLAR:
    12            var destino = puntos_ruta[indice]
    13            velocity = global_position.direction_to(destino) * 80
    14            if global_position.distance_to(destino) < 5:
    15                indice = (indice + 1) % puntos_ruta.size()
    16        Estado.PERSEGUIR:
    17            velocity = global_position.direction_to(jugador.global_position) * 140
    18    move_and_slide()
    19
    20func _on_vision_body_entered(body):
    21    if body.is_in_group("jugador"):
    22        jugador = body
    23        estado = Estado.PERSEGUIR
    24
    25func _on_vision_body_exited(body):
    26    if body == jugador:
    27        estado = Estado.PATRULLAR

    4. Estados de la partida, marcador y récord

    main.gd
    1extends Node
    2
    3var puntos := 0
    4var record := 0
    5const FICHERO := "user://record.save"
    6
    7func _ready():
    8    if FileAccess.file_exists(FICHERO):
    9        record = FileAccess.open(FICHERO, FileAccess.READ).get_32()
    10    for moneda in get_tree().get_nodes_in_group("monedas"):
    11        moneda.recogida.connect(_on_moneda_recogida)
    12    $Nivel/Jugador.golpeado.connect(fin_partida)
    13
    14func _on_moneda_recogida(valor):
    15    puntos += valor
    16    $HUD/Puntos.text = "Puntos: %d" % puntos
    17
    18func fin_partida():
    19    get_tree().paused = true
    20    if puntos > record:
    21        record = puntos
    22        FileAccess.open(FICHERO, FileAccess.WRITE).store_32(record)
    23    $HUD/GameOver.show()                 # botón «Otra vez» → get_tree().reload_current_scene()
    Minijuego jugable: recoge las monedas y evita al enemigo (flechas o WASD)
    Consola
    (sin mensajes)
    🧩JS · DOMColisiones y recogida de monedasMedio

    Completa la lógica de un juego 2D (el dibujado ya está hecho): • colisionan(a, b): true si los rectángulos {x, y, ancho, alto} se solapan (AABB). Si solo se tocan por el borde, no colisionan. • recoger(jugador, monedas): elimina del array monedas las que colisionan con el jugador y devuelve cuántas ha recogido. • Cada vez que se recoge alguna, suma 10 puntos por moneda a la variable global puntos y escribe «Puntos: N» en #marcador.

    index.htmlsolo lectura
    script.js
    Vista previa
    🧩JS · DOMMáquina de estados de un enemigoDifícil

    Implementa siguienteEstado(estado, distancia, vida), que decide qué hace un enemigo en cada fotograma. Los estados son «patrullar», «perseguir», «atacar» y «huir». Reglas, por orden de prioridad: 1. Con vida menor que 20 siempre «huir». 2. Si la distancia al jugador es menor o igual que 30, «atacar». 3. Si está persiguiendo, sigue persiguiendo mientras la distancia sea menor o igual que 250 (así no cambia de estado continuamente en el límite); si no, vuelve a «patrullar». 4. En cualquier otro estado, empieza a «perseguir» si la distancia es menor o igual que 150; si no, «patrullar». 5. Si estaba huyendo y ya tiene 20 o más de vida, aplica las reglas 2 a 4 como si estuviera patrullando.

    index.htmlsolo lectura
    script.js
    Vista previa
    Tutorial COMPLETO de GODOT 2D desde CERO | ¡Crea tu propio juego! — LuisCanary
    Qué es una Máquina de estados y Cómo crearlas en Godot 4 — findemor

    3. Un juego 3D sencillo

    En 3D las ideas son las mismas, con una dimensión más y algunos elementos nuevos:

    • Mallas (MeshInstance3D) con materiales PBR (color, rugosidad, metalicidad, texturas). Los modelos se importan en glTF o FBX desde Blender.
    • Cámara en perspectiva (Camera3D) que sigue al jugador, a menudo sobre un brazo (SpringArm3D) para no atravesar paredes.
    • Luces: direccional (el sol), puntual (una lámpara) y focal (una linterna), con sombras; y un WorldEnvironment con cielo, niebla y efectos.
    • Física 3D: CharacterBody3D, RigidBody3D, colisionadores de caja, esfera o cápsula.
    • Navegación: NavigationRegion3D y NavigationAgent3D para que los enemigos encuentren el camino (A*).
    jugador_3d.gd
    1extends CharacterBody3D
    2
    3const VELOCIDAD := 6.0
    4const SALTO := 5.0
    5var gravedad: float = ProjectSettings.get_setting("physics/3d/default_gravity")
    6
    7func _physics_process(delta):
    8    if not is_on_floor():
    9        velocity.y -= gravedad * delta
    10    if Input.is_action_just_pressed("saltar") and is_on_floor():
    11        velocity.y = SALTO
    12    var entrada := Input.get_vector("izquierda", "derecha", "adelante", "atras")
    13    # La dirección se calcula respecto a hacia dónde mira el personaje
    14    var direccion := (transform.basis * Vector3(entrada.x, 0, entrada.y)).normalized()
    15    velocity.x = direccion.x * VELOCIDAD
    16    velocity.z = direccion.z * VELOCIDAD
    17    move_and_slide()
    18
    19func _unhandled_input(evento):
    20    if evento is InputEventMouseMotion:                 # girar con el ratón
    21        rotate_y(-evento.relative.x * 0.003)
    22        $Camara.rotate_x(-evento.relative.y * 0.003)

    Rendimiento en móviles

    En móvil cada llamada de dibujo cuenta: usa pocos materiales, texturas comprimidas y del tamaño justo, modelos con pocos polígonos (y niveles de detalle, LOD), pocas luces dinámicas y sombras «horneadas» (lightmaps). Mide con el perfilador en el dispositivo más modesto que quieras soportar.

    4. Sonido, interfaz y pulido

    ElementoTécnica
    MúsicaAudioStreamPlayer en bucle, con volumen ajustable en un bus de audio «Música»
    EfectosAudioStreamPlayer2D/3D: el volumen depende de la distancia a la cámara
    Interfaz (HUD)CanvasLayer por encima del juego; anclajes para adaptarse a cualquier pantalla
    MenúsEscenas propias: principal, pausa (get_tree().paused), opciones, game over
    Controles táctilesTouchScreenButton o joystick virtual en móvil
    Sensación («game feel»)Partículas, vibración de cámara, pequeñas pausas al golpear, animaciones con Tween
    GuardadoFicheros en user:// (JSON o ConfigFile) con récords, opciones y progreso
    LocalizaciónTraducciones en CSV/PO con tr("CLAVE")

    5. Exportar, probar y publicar

    1. En Godot: Editor → Administrar plantillas de exportación (se descargan una vez).
    2. Android: instalar el SDK de Android y un JDK, crear un keystore y configurarlos en Configuración del editor; en Proyecto → Exportar, añadir Android, indicar el paquete (com.estudio.monedas), iconos, orientación y permisos, y exportar el AAB firmado.
    3. Web (HTML5): se exporta a una carpeta con el index.html y el .wasm; se puede subir a itch.io para que cualquiera lo juegue en el navegador.
    4. Escritorio: Windows, macOS y Linux desde el mismo proyecto.
    5. Probar en dispositivos reales (varios tamaños y gamas), con el depurador remoto y el perfilador.
    6. Publicar: Google Play (AAB, ficha, clasificación por edades PEGI/IARC), itch.io, Steam; política de privacidad si hay anuncios o analítica.
    Curso GODOT 4 desde CERO [16] Exportar a Android (APK, AAB y Keystore) — Leedeo Studio

    6. Ejercicios

    Ejercicio Práctico
    Fácil

    Documento de diseño

    Escribe un documento de diseño (GDD) de una página para un juego móvil 2D sencillo de tu invención: concepto en una frase, público, mecánica principal, controles, objetivo y condiciones de victoria y derrota, progresión (niveles o dificultad), estilo gráfico y sonoro, interfaz (pantallas) y monetización.

    Ejercicio Práctico
    Difícil

    Tu primer juego en Godot

    Siguiendo el tema, crea en Godot el juego de recoger monedas: jugador con animación de andar, al menos 10 monedas repartidas en un nivel hecho con TileMap, un enemigo que patrulla y persigue al jugador al verlo, marcador de puntos, pantalla de fin de partida con botón para reintentar y récord guardado. Exporta a web y súbelo a itch.io (o a Android).

    ¿Has terminado este tema?

    Crea una cuenta gratis para guardar qué temas has terminado, subir de nivel y ganar medallas.

    Guardar mi progreso