Desarrollo en directo de un juego de rol en BASIC y C para Amstrad CPC.
Seguidnos en Twitter: @FranGallegoBR, @Hec_Linares, #TeamBASIC, #TeamC, #AmstradGameDevChallenge
EPISODIO 5: Game Design, modelo ECS y más #AGC05
Fecha: Martes, 11 de septiembre de 2019, 21:00h
>> Código fuente <<
|| >> Plantilla Doc. Diseño << || >> Game Design Doc. Template <<
>>> Contenidos detallados <<<
- Noticias:
- Taller de #AmstradGameDevChallenge en RetroZaragoza 2019 (28/09/2019 16:00h). Inscipciones abiertas. Se intentará emitir en directo.
- Novedad en #AGC05: Cronómetro para las intervenciones del #TeamC y el #TeamBASIC. 15 minutos + 5 extra por intervención.
- Acalaración respecto a #AGC y el #CPCRetroDev
- Desarrollo:
- BASIC:
- Revisión de nuevo tipo de Memory Leak en BASIC:
- Hay un problema al realizar SYMBOL AFTER. Se reserva nueva memoria para los UDGs donde esté en ese momento HIMEM.
- Es importante realizar un SYMBOL AFTER 256 al inicio del programa para liberar la memoria reservada para UDGs antes de reservar nueva.
- Observamos problemas que pueden ocurrir con el uso de SYMBOL AFTER y otros comandos que manejen HIMEM, como por ejemplo los CALL de inicialización (&BBFF y &BB4E)
- Analizamos un problema importante de consumo de memoria en BASIC: el código que usamos para definir los UDG ocupa mucho espacio
- Vemos como utilizar la orden CHAIN MERGE para tener 2 programas separados que se encadenan: uno inicializa todo (variables, UDGs, etc) y otro sólo ejecuta, pero solo tiene los datos, y no el código de inicialización, ahorrando memoria.
- Solucionamos un problema de inicialización con DEFINT, que elimina todas las variables previamente definidas.
- Nota importante: La opción CHAIN MERGE funciona bien con programas BASIC binarios. Sin embargo, con programas BASIC en formato ASCII no funciona bien en los 464 y 664: hay un bug en el firmware.
- Game Design
- Proponemos un formato de documento para Game Design con una estructura concreta ayudar a estructurar nuestras ideas de juego.
- Revisamos el documento y sus apartados y vemos la utilidad y que significa cada uno de ellos.
- Vemos los principios iniciales de Game Design de nuestro juego, Dungeon Castles.
- Vemos bocetos y modelos a utilizar para el diseño y desarrollo del juego.
- Hablamos de lo importante que es tener modelos de referencia en los que basarse para las mecánicas.
- Vemos la historia de los castillos de Alicante, base de nuestro juego Dungeon Castles.
- Revisamos los apartados del documento en el caso de nuestro juego y cómo los hemos rellenado inicialmente.
- Entendemos cómo evitar el bloating (sobrecrecimiento) de características
- Hablamos del mapeado en nuestro juego y ponemos ejemplos de algunos tipos de mapa en su diseño inicial.
- Diseñamos unos tipos concretos de objetos que deben haber en nuestro juego y cómo funcionarán.
- Seleccionamos unas mecánicas concretas de combate y debatimos sobre ellas
- Vemos también como muy importante el diseño iterativo y cómo afrontar correctamente las iteraciones.
- Explicamos lo que es el Mínimo producto viable (MVP en inglés) y cómo definirlo y trabajar en él para empezar.
- Debatimos sobre cómo diseñar un MVP y cómo seleccionar mecánicas simples para que sea un produto completo
- Technical Design (Diagramas y ECS)
- Vemos por qué es importante realizar diagramas técnicos del funcionamiento de nuestro código
- Conforme un programa crece es imposible mantener en la cabeza todo el código, lo que hace y cómo funciona
- Explicamos que los diagramas no se elaboran como siempre se explica en ingeniería: primero el diagrama completo y luego pogramar. Esto no funciona habitualmente así.
- Entendemos que los diagramas técnicos deben ser algo vivo, que se mantiene actualizado y que se usa para reflexionar, diseñar y para entender nuestro código al completo.
- Vemos un ejemplo de diagrama de llamadas realizado con draw.io
- Con el diagrama entendemos la estructura de llamadas entre las distintas partes del código y las dependencias que eso implica
- Vemos que la estructura de nuestro juego se está complicando mucho y eso teniendo en cuenta que sólo hemos hecho unas pocas partes.
- Para evitar que la estructura siga complicándose y el código llegue pronto a ser inmanejable, vemos otras posibles estructuras mejores
- Hablamos del modelo Entidad-Componente-Sistema (ECS: Entity-Component-System).
- Explicamos desde 0 cómo se estructura una aplicación siguiendo el modelo ECS y qué siginifican los distintos componentes.
- Resolvemos dudas respecto al planteamiento del modelo y casos concretos.
- Hablamos de las diferencias entre ECS y el modelo de Componentes de Unity3D y Unreal Engine: no son el mismo modelo
- Migración a ECS
- Intentamos migrar el código a Entity-Component-System
- Vemos y analizamos las dificultades que surgen y lo que eso significa respecto a lo acoplado que estaba nuestro código anterior
- Intentamos desacoplar el movimiento del jugador del código que pinta, comprueba colisiones y realiza ataques a otros personajes
- Pese a intentarlo, se nos complica tanto que ni siquiera podemos llegar a probar lo que desarrollamos.
- Con este ejemplo entendemos lo importante que es separar las responsabilidades y que cada sistema haga exclusivamente sus funciones sin acoplarse a lo que hace el resto del código.
- Esto nos dejaver las ventajas de tener los sistemas separados a la hora de desarrollar nuestro juego.
- C:
- Explicamos el funcionamiento de los includes y su utilidad a la hora de tener cosas definidas en varios ficheros.
- Entendemos la diferencia real entre ficheros de cabecera (.H) y unidades de traducción (.C): se trata de una convención útil
- Empezamos a separar el código que tenemos en C en varios ficheros para poder tenerlo organizado.
- Esto nos permitirá en el futuro modelo ECS tener todos los sistemas bien separados y organizados.
- Vemos la diferencia entre usar comillas y mayor/menor a la hora de incluir ficheros.
- Entendemos que el proceso de compilación se ocupa de ficheros uno a uno, por separado. Ningún fichero conoce lo que hay en los demás ficheros. Para eso utilizamos las declaraciones añadidas en los ficheros de cabecera.
- Incidimos en la diferencia entre declaraciones y definiciones y su importancia.
- Vemos el uso de extern para poder declarar variables sin definirlas.
- Explicamos cómo usar include guards en C para evitar que un fichero de cabecera sea procesado más de una vez allá donde es incluído.
EPISODIO 4: Especial Gráficos UDG #AGC04
Fecha: Martes, 27 de agosto de 2019, 21:00h
>> Código fuente <<
|| >> UDG Designer <<
>>> Contenidos detallados <<<
- Noticias:
- 4ª edición de los premios de animación de la comunidad de Madrid, con premios de 2000€ y 1000€ a los mejores videojuegos culturales.
- Actualizaciones de CPCtelera
- cpct_rvm ahora soporta uso de nombres largos y modo warp en arranque.
- cpc2cdt permite importar ficheros ASCII en CDTs.
- cpct_pack permite la generación de ficheros binarios comprimidos.
- cpct_bin2sna permite crear snapshots de múltiples ficheros binarios.
- Nueva versión del emulador CPCEC de CNGSoft. Funciona en Windows y en Linux/Mac (con wine).
- Desarrollo:
- BASIC:
- Repaso del código BASIC: uso de fre("") y UNT()
- Reestructuración del código de gestión de enemigos y personaje principal
- Descarga directa y uso en línea de comando de la última versión del emulador de CNGSoft CPCEC (wget http://cngsoft.no-ip.org/cpcec-20190817.zip)
- Sustitución de mensajes por gráficos: usando subrutinas de dibujado para personaje y enemigos
- Uso de variables temporales para acortar el código y mejorar su legibilidad
- Introducción a los gráficos UDG: ¿Qué son? ¿Cómo se crean? Definición y uso de distinas bases numéricas.
- Uso de SYMBOL y SYMBOL AFTER.
- ¿Por qué es necesario SYMBOL AFTER? Uso de memoria de los UDGs. Modificación a mano de la memoria que define los UDGs.
- Presentación de hoja de cálculo para dibujado de UDGs.
- Soporte para que los enemigos puedan tener sus propios UDGs
- Más información sobre gráficos UDG en el blog de Fremos.
- Dibujado de sprites formados por múltiples caracteres y con varios colores: uso del modo transparente.
- Dibujando sprites multicolor y multicarácter usando cadenas y códigos de control: String Sprites
- Definición de String Sprites constantes en un array, accesibles por su índice: evitando copiar cadenas en tiempo de ejecución.
- Activación/Desactivación de modo transaparente usando cadenas y el código de control 22
- C:
- Problemas de SYMBOL AFTER en C:
- No hay SYMBOL AFTER en C ni en el firmware de Amstrad.
- En C se generan binarios, y al cargar un binario Amstrad desactiva los UDGs.
- El firmware almacena 3 variables en memoria para gestionar los UDG: Valor ASCII del primer UDG definible, Puntero a la matriz de UDGs y flag de activación.
- Las variables están en ubicaciones distintas en los firmwares 1.0 y 1.1.
- Implementación de SYMBOL AFTER en C.
- Detalle sobre el casting de direcciones de memoria absolutas a punteros.
- Implementación de SYMBOL en C usando el código de control 25.
- Entendiendo HIMEM, el firmware y la ROM, para saber dónde ubicar la tabla de definiciones de UDG para C.
- Problema: realizar un SYMBOL AFTER antes de la inicialización de texto y gráficos con los CALL 0xBBFF y call 0xBB4E (initMode()). Estas inicializaciones deshacen el SYMBOL AFTER.
- Detección de la versión del firmware actualmente en ejecución: Usando el final de la tabla de saltos (High jump-block) que es distinto en las 2 versiones.
- Problema en SDCC con múltiples comparaciones en una misma línea.
- Definición de múltiples caracteres en C sin usar SYMBOL: haciendo que el puntero de SYMBOL AFTER apunte a un array con los datos.
- Imprimiendo sprites de enemigos formados de varios carácteres.
- Creación manual de string sprites en arrays para dibujar enemigos multicaracter y multicolor.
- Dibujando enemigos con 2 caracteres de altura, 3 colores y 9 UDGs en total.
- Uso del modo transparencia con una función de activación/desactivación.
- Nuevos misterios y trabajo para casa:
- Nuevo problema de memory leak en BASIC: en cada nueva ejecución del juego desaparecen 48 bytes. ¿Por qué?
- Los UDG en BASIC nos ocupan doble: el código que genera los datos de los UDG, más los propios datos de los UDG.
- ¿Es posible tener en BASIC en memoria sólo los datos de los UDG, sin el código que los genera, como en C?
Fecha: Martes, 20 de agosto de 2019, 21:00h
>>> Contenidos detallados <<<
- Noticias:
- Actualización de #CPCtelera: añadido modificador -w para activar el modo Warp al inicio en RVM
- Misterio en BASIC:
- Recordatorio: al realizar varios ciclos de ejecución de nuestro juego, durante la ejecución, se pierden bytes de memoria
- Comentarios de usuarios que han investigado y se han planteado dudas
- Si la memoria se va perdiendo, ¿Se quedará nuestro programa al final sin memoria?
- Usuario comenta que parece pederse la memoria en los INPUTs, que parece un fallo del firmware del #Amstrad
- Revisión de la dirección de memoria &B08D que contiene datos del firmware: 'Final del espacio libre: byte antes de la zona de strings = HIMEM'
- Detalle importante: hay 2 firmwares de Amstrad CPC, el 1.0 y el 1.1. Las variables del firmware están en direcciones distintas según la versión del firmware
- Mapa básico de memoria del Amstrad CPC y definición de qué es HIMEM
- Funcionamiento de los bancos de memoria y las ROMs del CPC a nivel básico
- HIMEM: dirección más alta de espacio para el programa en BASIC (código y variables)
- La zona de memoria de BASIC está protegida por el intérprete: no nos permite escribir en ella desde fuera.
- La orden MEMORY sirve para cambiar HIMEM de posición: utilidades
- Vemos el valor de HIMEM desde BASIC, usamos HEX$ para convertirlo y vamos a esa zona de memoria a ver qué hay
- Cómo configurar el visor de memoria de RVM para mostrar contenido de RAM y ROM
- Ponemos a prueba la zona de strings de BASIC (en HIMEM) asignando valores a a$ y vemos cómo reacciona la memoria
- La zona de strings de BASIC almacena strings nuevos sin borrar antiguos: funciona como un Stack Allocator (un gestor de memoria tipo pila)
- Esto explica el misterio: en cada ciclo del programa metemos valores nuevos en los strings con nuestros INPUT.
- No es realmente un memory leak, sino una consecuencia del Stack Allocator, que funciona similar a lenguajes de script modernos.
- Es una estrategia óptima para la gestión dinámica de la memoria de strings.
- ¿Hace este comportamiento que se nos pueda acabar la memoria? Creamos un programa para ponerlo a prueba
- Cuando no queda memoria, BASIC libera la memoria de strings que ya no son usados, compactando los usados. Básicamente, recolecta la basura.
- Potenciales ventajas e inconvenientes del funcionamiento de la memoria de strings
- Controlar y liberar la memoria (recoger la basura) cuando nosotros queramos usando FRE("")
- Usando UNT para evitar tener que hacer PRINT de FRE("") y no causar Overflows
- añadiendo la recolección de basura a nuestro programa y poniéndolo a prueba
- Observando el comportamiento de la memoria cuando forzamos la liberación de strings
- Truquito en asignación de variables: uso de otras variables en lugar de valores inmediatos
- Resolución de dudas planteadas en el chat
EPISODIO 3 #AGC03
Fecha: Martes, 6 de agosto de 2019, 21:00h
>>> Contenidos detallados <<<
- Noticias:
- Actualización en CPCtelera 1.5 WIP: Inclusión de soporte para STDC getchar()
- #AmstradGameDevChallenge estará presente en RetroZaragoza 2019: grabaremos un episodio en directo en persona.
- Recomendación: temática para los juegos RPG sobre leyendas reales de Castillos, Monasterios, Pueblos o lugares de la geografía española.
- Preguntas y proyectos de los miembros del grupo:
- Problema con la inicialización de variables globales en SDCC: no se les asigna valor.
- Soluciones para variables globales: funciones de inicialización, valores constantes y punteros no constantes y copia de valores con
cpct_memcpy
- Sugerencia de uso de
grep
para eliminar los comentarios en BASIC. - Problema con
.gitignore
y la carpetaobj/
- ¿Cómo se puede hacer
SYMBOL AFTER
en C? - Apreciaciones sobre el uso de
cpct_setVideoMode
, el firmware y las funciones de CPCtelera. - Detalles a tener en cuenta en el uso de
OPENIN
yOPENOUT
en BASIC - Misterio en BASIC:
- Pérdidas de memoria en nuestro juego. ¿A qué se deben? Dos semanas para encontrar la respuesta.
- Desarrollo:
- Inicialización rápida y fácil de la pantalla, los colores y el módulo de texto sin tener que ajustarlo todo a mano.
- Llamadas a funciones del firmware con código ensamblador en línea: nota sobre potenciales problemas
- Creación y manejo de
structs
en C: ¿Qué son? ¿Cómo funcionan? - Introducción de espacio para múltiples enemigos usando un array de structs en C
- Importancia de los comentarios en BASIC: usarlos como referencia de variables y estructuras de memoria
- Simulación de structs en BASIC usando arrays y prefijos de nombre
- Uso de una referencia variable a un elemento para generalizar el código previo directamente
- Diseño básico de un gestor de enemigos: creación y destrucción
- Visualización de múltiples enemigos e interacción: problemas asociados
- Gestión del array de enemigos al eliminar uno de ellos: posibles alternativas
- Añadiendo color a los enemigos como forma de distinguirlos
EPISODIO 2 #AGC02
Fecha: Martes, 23 de julio de 2019, 21:00h
>>> Contenidos detallados <<<
- Noticias:
- Anuncio oficial del #CPCRetroDev 2019
- Revisión de proyectos en desarrollo de los miembros del #TeamC y el #TeamBASIC
- Desarrollo:
- Creación de un script bash para generar un DSK a partir del fichero .BAS
- Comentarios que no ocupan memoria en BASIC
- Reemplazo y uso de variables con nombre corto en BASIC
- Usando
LOCATE
desde C para dibujar personajes en una posición concreta - Uso de funciones y parámetros en C para reutilizar y simplificar el código
- Diferencias entre las funciones
printf
yputs
y uso del firmware - Análisis de estartegias más óptimas de programación en C usando el código ensamblador generado
- Uso de las funciones
CHR$
ySTRING$
en BASIC para pintar caracteres y repetirlos - Movimiento de los personajes en una dimensión y ataque por movimiento
- Uso de
GOSUB
y subrutinas en BASIC para modularizar el código - Dudas sobre paso de parámetros a funciones y subrutinas en BASIC y C
- Detalle sobre las comparaciones y asignaciones en C
- Introducción a los arrays en C y BASIC
- Funciones aleatorias simples para ataques variables en juegos de ROL
- Definición de funciones matemáticas en BASIC
- Cálculos enteros y reales y redondeos en BASIC y C
- Uso de
RANDOMIZE
para controlar las secuencias pseudoaleatorias
EPISODIO 1 #AGC01
Fecha: Martes, 9 de julio de 2019, 21:00h
>>> Contenidos detallados <<<
- Presentación de la serie
- Herramientas a utilizar y organización
- Implementado un esquema inicial muy básico de juego y bucle principal emergente, sin apenas estructurar.
- Primeros pasos con variables
- Player y enemigo con energía, ataque y defensa
- Player y enemigo atacan y defienden
- Hoja de cálculo para diseño de UDGs
- Solicitud de inscripción a la organización de Github
- CPCtelera
- CPCtelera 1.5 WIP
- CPCtelera Manual de Referencia
- Manual del usuario de Amstrad CPC 464 1987
- Manual del firmware del Amstrad CPC
Cread vuestros repositorios en AmstradGameDevChallenge y empezad vuestros propios RPG en BASIC y C. En el próximo episodio los analizaremos en directo y compartiremos las ideas de programación entre todos.