singe/docs/es/lessons/13-game-folder.adoc

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.