416 lines
18 KiB
Text
416 lines
18 KiB
Text
[#_lesson_13_a_game_folder]
|
|
== Lección 13: Una carpeta de juego
|
|
|
|
image::learn/13-game-folder.png[La lección terminada, 480]
|
|
|
|
Tu juego es un script en una carpeta cuyo nombre recuerdas, que se arranca
|
|
escribiendo su nombre en una terminal. Eso te vale a ti y no le sirve a nadie
|
|
más. Un jugador debería poder copiar una carpeta junto a Singe, arrancar el
|
|
menú, ver tu juego en la lista con su título y su arte, y pulsar un botón. La
|
|
imagen de arriba es ese menú, con tu juego dentro.
|
|
|
|
Para eso hace falta un archivo nuevo, de unas seis líneas, y dos imágenes que
|
|
puedes sacar con una tecla. Tu script no cambia en absoluto.
|
|
|
|
[#_give_the_game_a_name]
|
|
=== Ponle nombre al juego
|
|
|
|
Un juego tiene dos nombres, uno para el ordenador y otro para las personas.
|
|
|
|
El del ordenador es la carpeta, y lo elegiste en la lección nueve: `rocks`, con
|
|
`rocks.singe` dentro. La carpeta y el script comparten nombre, como en todas
|
|
las lecciones desde la lección uno, y por eso `Singe rocks` encuentra el script
|
|
solo con la carpeta. Mantén ese nombre sencillo. Sin espacios, que una terminal
|
|
lee como el hueco entre dos nombres. Y con las mismas mayúsculas en todos los
|
|
sitios donde lo escribas: en la mayoría de los ordenadores que no son Windows,
|
|
`rocks` y `Rocks` son dos carpetas distintas, y un juego que solo funciona en
|
|
tu ordenador no es un juego que pueda tener nadie más.
|
|
|
|
El nombre para las personas puede ser el que quieras, con mayúsculas, espacios
|
|
y todo, y no vive en el nombre de la carpeta. Vive en el archivo del que trata
|
|
esta lección, para que cambiarle el nombre al juego en el menú nunca signifique
|
|
cambiarle el nombre a una carpeta.
|
|
|
|
[#_what_goes_in_the_folder]
|
|
=== Qué va en la carpeta
|
|
|
|
Todo lo que necesita tu juego, y nada más. Al final de esta lección tiene este
|
|
aspecto:
|
|
|
|
----
|
|
rocks/
|
|
games.dat Lo que lee el menú
|
|
rocks.singe El juego
|
|
art/ El kit de arte de la lección nueve
|
|
spaceship.png
|
|
asteroid.png
|
|
bullet.png
|
|
star.png
|
|
shoot.wav
|
|
boom.wav
|
|
...
|
|
cabinet.png Una imagen que el menú muestra junto a la lista
|
|
marquee.png Una imagen que el menú muestra encima de ella
|
|
----
|
|
|
|
Cualquier otra cosa que haya acabado ahí mientras aprendías -- `walk.singe` de
|
|
la lección nueve, la pequeña prueba de sonido de la lección diez -- no es parte
|
|
del juego. Muévelas a una carpeta propia junto a `rocks`. El kit de arte
|
|
también tiene unos cuantos archivos que este juego nunca carga, y de momento no
|
|
hacen ningún daño. La lección treinta es donde repasas una carpeta de juego y
|
|
quitas todo lo que no es el juego.
|
|
|
|
La regla que hay detrás de esa organización es que un juego nunca sale de su
|
|
propia carpeta. Ni para el arte, ni para un sonido, ni para una biblioteca que
|
|
también usas en otro juego -- si compartes código entre dos juegos, cada uno
|
|
guarda su propia copia. Hay exactamente un sitio fuera de la carpeta desde el
|
|
que un juego puede cargar, y ya estás cargando desde él: la propia carpeta
|
|
`Singe` del motor, donde vive `Singe/Framework.singe`, y desde la lección diez
|
|
también el clic y la música.
|
|
|
|
La carpeta del juego está en tu carpeta de trabajo, junto al motor:
|
|
|
|
----
|
|
singe/ Tu carpeta de trabajo
|
|
Singe-v3.00-... El propio motor, se llame como se llame el tuyo
|
|
Menu.sh Arranca el menú. Menu.bat en Windows
|
|
Singe/ Archivos de soporte. No son tuyos; déjalos en paz
|
|
data/ Todo lo que escribe cualquier cosa, tu partida guardada incluida
|
|
rocks/ Tu juego
|
|
----
|
|
|
|
El ordenador de un jugador tiene esa misma forma. El motor escribió `Singe`,
|
|
`data` y `Menu.sh` (`Menu.bat` en Windows) la primera vez que ejecutaste algo,
|
|
allá en la lección uno. La lección uno mencionó los dos primeros. El tercero es
|
|
nuevo para ti, y esta lección lo pone a trabajar.
|
|
|
|
[#_paths_count_from_where_singe_started]
|
|
=== Las rutas cuentan desde donde arrancó Singe
|
|
|
|
Llevas haciendo esto desde la lección nueve, así que esto es un recordatorio,
|
|
no un cambio.
|
|
|
|
Cada nombre que carga tu juego -- `art/spaceship.png`, `art/boom.wav` -- se
|
|
busca empezando por la carpeta en la que *se arrancó* Singe, no por la carpeta
|
|
en la que está tu script. Arrancas Singe en tu carpeta de trabajo, así que un
|
|
`art/spaceship.png` a secas se buscaría en `singe/art`, que no existe. `DIR` es
|
|
la propia carpeta de tu script con la barra ya puesta al final, así que un
|
|
nombre pegado a ella apunta a tu archivo desde dondequiera que se haya
|
|
arrancado Singe:
|
|
|
|
[source,lua]
|
|
----
|
|
local shipSprite = spriteLoad(DIR .. "art/spaceship.png")
|
|
----
|
|
|
|
El menú arranca Singe en la carpeta en la que está `Menu.sh`, que vuelve a ser
|
|
tu carpeta de trabajo, así que `DIR` hace ahí exactamente lo mismo que hace
|
|
para ti. Lo sigue haciendo cuando el juego se empaqueta en un solo archivo en
|
|
la lección treinta, cuando la carpeta de trabajo de otra persona se llama de
|
|
una forma completamente distinta, y en un ordenador que no verás nunca. Un
|
|
nombre que empieza por `Singe/` es la carpeta del motor y nunca necesita `DIR`;
|
|
todos los nombres que son tuyos lo necesitan.
|
|
|
|
[#_games_dat]
|
|
=== games.dat
|
|
|
|
`games.dat` es como se presenta un juego. El menú lee el `games.dat` de cada
|
|
carpeta de juego que tiene al lado y enumera lo que encuentra. Sin `games.dat`
|
|
no hay entrada en la lista: tu carpeta es invisible, por bueno que sea el juego
|
|
que hay dentro.
|
|
|
|
Crea un archivo nuevo llamado `games.dat` en tu carpeta `rocks`. Este es el más
|
|
pequeño que funciona:
|
|
|
|
[source,lua]
|
|
----
|
|
GAMES = {
|
|
{
|
|
TITLE = "Rocks",
|
|
SCRIPT = "rocks/rocks.singe"
|
|
}
|
|
}
|
|
----
|
|
|
|
Eso es Lua, y es una tabla del tipo que conociste en la lección seis: `GAMES`
|
|
es una lista, y cada cosa de la lista es un registro que describe un juego. Una
|
|
carpeta puede contener varios juegos -- por eso es una lista -- pero la tuya
|
|
contiene uno.
|
|
|
|
`SCRIPT` es el campo que el motor necesita para ejecutar el juego, y `TITLE` el
|
|
que necesita el menú para mostrarlo en la lista. La ruta del script se cuenta
|
|
desde la carpeta *de encima* del `games.dat`, no desde la carpeta en la que
|
|
está el `games.dat`. Así que un `games.dat` dentro de `rocks` nombra su propio
|
|
script como `rocks/rocks.singe`. Parece que dice `rocks` dos veces, y está
|
|
bien. Todas las demás rutas del archivo funcionan igual. Si te equivocas, nada
|
|
se queja cuando escribes el nombre: la entrada simplemente nunca coincide con
|
|
el script que has ejecutado.
|
|
|
|
`TITLE` es el nombre para las personas del principio de esta lección. Es lo que
|
|
muestra el menú, y los juegos se listan por orden de título, así que el título
|
|
también decide en qué lugar de la lista queda tu juego.
|
|
|
|
[#_telling_the_menu_more]
|
|
=== Contarle más al menú
|
|
|
|
El menú tiene todo un panel que llenar junto a la lista, y lo llena con el
|
|
resto de la entrada:
|
|
|
|
[source,lua]
|
|
----
|
|
GAMES = {
|
|
{
|
|
TITLE = "Rocks",
|
|
SCRIPT = "rocks/rocks.singe",
|
|
CANVAS_X = 720,
|
|
CANVAS_Y = 480,
|
|
YEAR = 2026,
|
|
GENRE = "Shooter",
|
|
DEVELOPER = "Your Name Here",
|
|
PUBLISHER = "Your Name Here",
|
|
DESCRIPTION = "Shoot the rocks before they reach your ship.",
|
|
CABINET = "rocks/cabinet.png",
|
|
MARQUEE = "rocks/marquee.png"
|
|
}
|
|
}
|
|
----
|
|
|
|
`CABINET` y `MARQUEE` son archivos de imagen normales en la carpeta de tu
|
|
juego, en cualquier formato que Singe sepa leer. El arte de la recreativa es la
|
|
imagen grande junto a la lista y la marquesina es la tira de encima. Hazlas del
|
|
tamaño que quieras; el menú las encaja en sus paneles.
|
|
|
|
No necesitas un programa de dibujo para hacerlas, porque tu juego puede sacarse
|
|
su propia foto. Ejecútalo y, cuando la pantalla tenga el aspecto con el que
|
|
quieres que la vea el mundo, pulsa F12 (o F11, porque algunos escritorios se
|
|
quedan F12 para ellos). Singe guarda una imagen de la ventana en la carpeta de
|
|
datos de tu juego, `data/rocks`, con el nombre `singe000.png`, luego
|
|
`singe001.png`, y así sucesivamente. Saca una de la pantalla de título y otra
|
|
en mitad de una pelea. Cópialas a tu carpeta `rocks` y cámbiales el nombre: la
|
|
de la pelea es `cabinet.png` y la del título es `marquee.png`.
|
|
|
|
Una marquesina de verdad es una tira ancha con el nombre del juego en letras
|
|
grandes, y una pantalla entera apretujada ahí dentro parece un sello de
|
|
correos. Servirá hasta que tengas algo mejor. Si dejas fuera `MARQUEE`, el menú
|
|
pone ahí la imagen que usa el propio motor cuando falta una, un tablero de
|
|
ajedrez rosa que dice MISSING, y eso es peor.
|
|
|
|
`YEAR`, `GENRE`, `DEVELOPER`, `PUBLISHER` y `DESCRIPTION` son el texto de
|
|
debajo, y el motor nunca los mira. Son para la persona que lee el menú, que es
|
|
de lo que se trata.
|
|
|
|
`CANVAS_X` y `CANVAS_Y` son el tamaño del mundo de tu juego, y 720 por 480 es
|
|
el valor por defecto -- el mismo mundo en el que llevas dibujando desde la
|
|
lección uno. Ponlos solo si tu juego quiere otra forma. Un juego sin campo
|
|
`VIDEO` es un juego sin vídeo, que es lo que es el tuyo; la lección catorce es
|
|
donde ese campo empieza a importar.
|
|
|
|
La sección `games.dat` de la referencia enumera todos los campos que existen,
|
|
incluidos varios para hardware que todavía no tienes. Vale la pena conocer ya
|
|
el nombre de unos cuantos más.
|
|
|
|
`REQUIRES` dice lo que un juego necesita de la máquina más allá del 2D
|
|
sencillo: `"GUI"` para los menús y páginas de la lección veinte, `"SCENE"` para
|
|
el 3D que empieza en la lección veintidós, y unos cuantos más. El menú lo lee
|
|
para poder decirle a un jugador «esta máquina no puede ejecutar eso» en lugar
|
|
de dejar que el juego falle a medias. Rocks es 2D sencillo, y dejar fuera
|
|
`REQUIRES` significa exactamente eso, así que déjalo fuera. Rara vez tendrás
|
|
que averiguar la lista tú mismo, porque Singe se da cuenta de lo que usa un
|
|
juego mientras se ejecuta y lo dice cuando la entrada no lo incluye. A partir
|
|
de la lección veinte verás líneas como esta, tenga o no tenga el juego un
|
|
`games.dat`:
|
|
|
|
----
|
|
Warning: this game uses GUI, which its games.dat does not list in REQUIRES.
|
|
----
|
|
|
|
Es un consejo, no un error; el juego funciona de las dos maneras. El arreglo es
|
|
una línea en la entrada, `REQUIRES = { "GUI" },`, y al empaquetar un juego en
|
|
la lección treinta se escribe la lista por ti si la entrada no tiene ninguna.
|
|
Su compañero `TIER`, un número del 0 al 3, dice cuánta máquina quiere un juego,
|
|
y puede esperar hasta que escribas un juego lo bastante pesado como para que
|
|
importe.
|
|
|
|
`GAME_ID` es una identidad única para tu juego, que necesitan las tablas de
|
|
récords en línea de la lección veintiocho y nada más. Y `RESOLUTION_X` y
|
|
`RESOLUTION_Y` piden un tamaño de ventana concreto. Olvídate del resto hasta
|
|
que algo te mande a buscarlo.
|
|
|
|
[#_running_it_from_the_menu]
|
|
=== Ejecutarlo desde el menú
|
|
|
|
Ejecuta `Menu.sh` -- `Menu.bat` en Windows -- desde tu carpeta de trabajo.
|
|
Encuentra el Singe más nuevo que tiene al lado y arranca el menú.
|
|
|
|
Tu juego está en la lista, con su arte al lado y su descripción debajo. Es lo
|
|
único que hay en la lista, porque `rocks` es la única carpeta a la que le has
|
|
dado un `games.dat`; `hello`, `dodge` y las demás no son juegos, por lo que el
|
|
menú puede saber. Arriba y abajo mueven un juego, izquierda y derecha mueven
|
|
una página, y start o un botón de disparo juega el que esté seleccionado. Sal
|
|
del juego con Esc y vuelves al menú, en el mismo juego, listo para otra vez. El
|
|
menú también recuerda en qué juego estabas de una ejecución a otra.
|
|
|
|
Eso es todo lo que hace falta para publicar un juego, en lo que al motor
|
|
respecta. Un jugador copia tu carpeta junto a Singe y aparece.
|
|
|
|
Escribir el nombre sigue funcionando, y funciona igual:
|
|
|
|
----
|
|
Singe rocks
|
|
----
|
|
|
|
Cuando lanzas un script a mano así, Singe busca un `games.dat` a su lado -- y
|
|
hasta en tres carpetas por encima -- y si una entrada nombra el script que has
|
|
lanzado, los ajustes de esa entrada se le aplican. Así que un juego funciona
|
|
igual desde una terminal que desde el menú, lo que significa que puedes seguir
|
|
trabajando como hasta ahora, con `-R` y un editor de texto, y aun así fiarte de
|
|
lo que le llegará a un jugador. También escribe en el mismo sitio: lo arranques
|
|
como lo arranques, la partida guardada de tu juego es la de `data/rocks`.
|
|
|
|
[#_later_one_file]
|
|
=== Más adelante: un solo archivo
|
|
|
|
Una carpeta es cómoda para trabajar y algo incómoda para regalar: una docena de
|
|
archivos, una carpeta dentro de otra y un jugador que la descomprime en el
|
|
sitio equivocado. Un juego de Singe terminado se puede empaquetar en un único
|
|
archivo `.game` que el motor ejecuta directamente, con arte, sonidos y todo, y
|
|
que un jugador copia junto a Singe y listo.
|
|
|
|
Para eso no tiene que cambiar nada de tu juego. Es la lección treinta, y vale
|
|
la pena esperar: empaqueta un juego que no está terminado y mañana lo volverás
|
|
a empaquetar.
|
|
|
|
[#_what_just_happened_9]
|
|
=== Qué acaba de pasar
|
|
|
|
[source,lua]
|
|
----
|
|
GAMES = {
|
|
{
|
|
----
|
|
|
|
Dos llaves, porque es una lista de registros. La de fuera es la lista. La de
|
|
dentro es lo primero de la lista, que es el registro de un juego. Equivocarse
|
|
con una de ellas es el error habitual, y Lua te dirá la línea.
|
|
|
|
[source,lua]
|
|
----
|
|
SCRIPT = "rocks/rocks.singe",
|
|
----
|
|
|
|
Se cuenta desde la carpeta de encima del `games.dat`, que es tu carpeta de
|
|
trabajo. Es el mismo nombre que escribirías después de `Singe` para ejecutar el
|
|
script a mano, y esa es la forma más fácil de recordarlo.
|
|
|
|
La coma al final de la línea separa un campo del siguiente, exactamente como en
|
|
la lección seis. El último campo de un registro no lleva coma detrás -- a Lua
|
|
no le importa si la lleva, pero si no la pones tu archivo se parecerá al de
|
|
todos los demás. Una coma que falta en medio es otra cosa, y es el error de
|
|
esta lección.
|
|
|
|
[source,lua]
|
|
----
|
|
CABINET = "rocks/cabinet.png",
|
|
----
|
|
|
|
Es una imagen que carga el menú, no tu juego, así que se nombra como `SCRIPT`,
|
|
desde la carpeta de encima, y no con `DIR`. `DIR` es de tu script mientras se
|
|
ejecuta, y el menú y el motor leen `games.dat` antes de que tu script haya
|
|
empezado.
|
|
|
|
[#_try_it_13]
|
|
=== Pruébalo
|
|
|
|
. *Haz un arte mejor.* Si tienes un programa de dibujo, haz un `marquee.png`
|
|
como es debido: una tira ancha con ROCKS en letras grandes. Ponlo en lugar de
|
|
la captura de pantalla y vuelve a mirar el menú.
|
|
. *Dale un games.dat a Dodge.* Escribe un `games.dat` en tu carpeta `dodge` de
|
|
la lección siete y arranca el menú. Ahora hay dos juegos, por orden de
|
|
título. Luego cambia el `TITLE` de Rocks a `Asteroid Field` y mira cómo sube
|
|
por encima de Dodge: la lista se ordena por título, no por carpeta.
|
|
. *Rompe games.dat para el menú.* Borra una de las llaves de cierre del
|
|
`games.dat` de `rocks` y arranca el menú. Tu juego ha desaparecido de la
|
|
lista, y la terminal dice por qué. Vuelve a ponerla.
|
|
. *Equivócate con SCRIPT.* Pon `CANVAS_X` a `1280` y `CANVAS_Y` a `720`, y
|
|
ejecuta `Singe rocks`. La zona de juego es más ancha. Ahora cambia `SCRIPT` a
|
|
`rocks.singe` y vuelve a ejecutarlo. El juego ha vuelto a su tamaño de antes,
|
|
y nada te lo ha dicho: la entrada ya no nombra el script que has ejecutado,
|
|
así que no se aplica. Deja los dos como estaban.
|
|
. *Encuentra tu partida guardada.* Arranca el juego desde el menú, marca un
|
|
récord, sal al menú y sal del menú. Ejecuta `Singe rocks` y mira el título.
|
|
Luego busca en `data/rocks` el archivo que lo ha recordado.
|
|
|
|
[#_break_it_on_purpose_12]
|
|
=== Rómpelo a propósito
|
|
|
|
Quita la coma del final de la línea de `TITLE`, para que el principio del
|
|
archivo quede así:
|
|
|
|
[source,lua]
|
|
----
|
|
GAMES = {
|
|
{
|
|
TITLE = "Rocks"
|
|
SCRIPT = "rocks/rocks.singe",
|
|
----
|
|
|
|
Ejecuta `Singe rocks`. El juego arranca, y la terminal dice esto:
|
|
|
|
----
|
|
Note: rocks/games.dat ignored: [string "rocks/games.dat"]:4: '}' expected (to close '{' at line 2) near 'SCRIPT'
|
|
----
|
|
|
|
Tu juego sigue funcionando, porque un juego arrancado por su nombre no necesita
|
|
su entrada. Simplemente funciona sin ella. Pero arranca el menú y tu juego ha
|
|
desaparecido. Si era el único, la lista dice `No games yet`, y la terminal dice
|
|
lo mismo de otra forma:
|
|
|
|
----
|
|
rocks/games.dat: rocks/games.dat:4: '}' expected (to close '{' at line 2) near 'SCRIPT' Skipped.
|
|
----
|
|
|
|
Léelo como cualquier otro error de Lua. El archivo es `rocks/games.dat` y la
|
|
línea es la 4. Pero la línea 4 es la de `SCRIPT`, y no tiene nada de malo. El
|
|
error está al final de la línea 3. Después de `TITLE = "Rocks"`, Lua esperaba o
|
|
una coma y otro campo, o la `}` que cierra el registro que empezó en la línea
|
|
2. En su lugar encontró `SCRIPT`, así que la línea 4 es donde se dio cuenta. Lo
|
|
que propone para arreglarlo -- una `}` -- está mal; la línea que da va una por
|
|
detrás. Cuando un error señala el principio de una línea, mira el final de la
|
|
línea anterior.
|
|
|
|
Vuelve a poner la coma y arranca otra vez el menú.
|
|
|
|
[#_what_you_learned_13]
|
|
=== Lo que has aprendido
|
|
|
|
* Un juego es una carpeta: el script, su arte y un `games.dat`, y nada de fuera
|
|
de ella salvo la propia carpeta `Singe` del motor.
|
|
* El nombre de la carpeta es para el ordenador, así que mantenlo sencillo. El
|
|
nombre para las personas es `TITLE`.
|
|
* Los nombres que carga tu script se cuentan desde donde se arrancó Singe, y
|
|
por eso cada uno de ellos se construye a partir de `DIR`.
|
|
* `games.dat` es un archivo de Lua que contiene una lista `GAMES`, y cada
|
|
entrada es un juego.
|
|
* Una entrada necesita `TITLE` y `SCRIPT` y nada más; sus rutas cuentan desde
|
|
la carpeta de encima del `games.dat`.
|
|
* `CABINET`, `MARQUEE`, `YEAR`, `GENRE`, `DEVELOPER`, `PUBLISHER` y
|
|
`DESCRIPTION` son lo que el menú le muestra al jugador, y F12 saca una imagen
|
|
que puedes usar para las dos primeras.
|
|
* `REQUIRES` le dice al menú lo que un juego necesita más allá del 2D, y Singe
|
|
te avisa cuando un juego usa algo que su entrada no incluye.
|
|
* `Menu.sh` o `Menu.bat` arranca el menú incluido, que enumera cada carpeta de
|
|
juego que tiene al lado, ordenadas por título.
|
|
* Un juego lanzado por su nombre también recibe su entrada de `games.dat`, así
|
|
que se comporta igual de las dos formas, y un `games.dat` que no se puede
|
|
cargar se avisa y se ignora.
|
|
* Un juego terminado se puede empaquetar más adelante en un solo archivo, en la
|
|
lección treinta.
|
|
|
|
[#_next_time_13]
|
|
=== La próxima vez
|
|
|
|
Con esto termina la segunda parte: tienes un juego, con arte, sonido,
|
|
colisiones, puntuación y una forma de que otra persona lo ejecute. La tercera
|
|
parte trata de lo que casi ningún otro motor hace -- reproducir imágenes
|
|
filmadas como el mundo en el que ocurre el juego -- y la lección catorce
|
|
empieza con el disco, el fotograma y la espera.
|