315 lines
12 KiB
Text
315 lines
12 KiB
Text
== Lesson 13: A Game Folder
|
|
|
|
image::learn/13-game-folder.png[The finished lesson, 480]
|
|
|
|
Your game is a script in a folder you remember the name of, started by typing
|
|
its name at a terminal. That is fine for you and useless for anybody else. A
|
|
player should be able to copy a folder next to Singe, start the menu, see your
|
|
game listed with its title and its artwork, and press a button.
|
|
|
|
That takes one new file, about six lines long, and one change to your script.
|
|
Nothing else about the game changes at all.
|
|
|
|
=== Give the Game a Name
|
|
|
|
A game lives in a folder of its own, and the folder, the script inside it, and
|
|
the game's name should all match. Rename your folder `Rocks` and the script
|
|
inside it `Rocks.singe`.
|
|
|
|
Do not put a space in the folder name. Match the capitals exactly wherever you
|
|
type it: on most computers that are not Windows, `rocks` and `Rocks` are two
|
|
different folders, and a game that only runs on your machine is not a game
|
|
anybody else can have.
|
|
|
|
When the folder and the script share a name, Singe will find the script from
|
|
the folder alone, so both of these run your game:
|
|
|
|
----
|
|
Singe Rocks/Rocks.singe
|
|
Singe Rocks
|
|
----
|
|
|
|
=== What Goes in the Folder
|
|
|
|
Everything your game needs, and nothing else:
|
|
|
|
----
|
|
Rocks/
|
|
games.dat What the menu reads
|
|
Rocks.singe The game
|
|
art/
|
|
ship.png
|
|
rock.png
|
|
shot.png
|
|
star.png
|
|
shoot.wav
|
|
boom.wav
|
|
cabinet.png Artwork the menu shows beside the list
|
|
marquee.png Artwork the menu shows above it
|
|
----
|
|
|
|
The rule behind that layout is that a game never reaches outside its own
|
|
folder. Not for artwork, not for a sound, and not for a library you also use in
|
|
another game -- if you share code between two games, each one keeps its own
|
|
copy. There is exactly one file outside the folder a game may load, and you are
|
|
already loading it: `Singe/Framework.singe`.
|
|
|
|
The folder itself sits beside the Singe program:
|
|
|
|
----
|
|
Singe-v3.00-Linux-x86_64 The engine itself, whatever yours is called
|
|
Menu.sh Starts the menu. Menu.bat on Windows
|
|
Singe/ Support files. Not yours; leave them alone
|
|
Rocks/ Your game
|
|
data/ Everything anything writes, your save among it
|
|
----
|
|
|
|
The engine wrote `Menu.sh`, the `Singe` folder, and `data` for you, the first
|
|
time you ever ran it. Those are what appeared in your work folder in lesson
|
|
one.
|
|
|
|
=== Paths Count from Where Singe Started
|
|
|
|
Here is the change your script needs, and the reason it needs it.
|
|
|
|
Every name your game loads -- `art/ship.png`, `art/boom.wav` -- is looked for
|
|
starting at the folder Singe was *started in*, not the folder your script is
|
|
in. All through this book those have been the same folder, because you stood
|
|
inside the game's folder and typed `Singe rocks`. Start it from the
|
|
folder above, or start it from the menu, and they are not the same folder at
|
|
all, and not one of your files is found.
|
|
|
|
`Framework.singe` sets a variable for the answer. `DIR` is your script's own
|
|
folder, with the separator already on the end, so a name glued to it points at
|
|
your file from wherever Singe was started:
|
|
|
|
[source,lua]
|
|
----
|
|
local shipSprite = spriteLoad(DIR .. "art/ship.png")
|
|
local rockSprite = spriteLoad(DIR .. "art/rock.png")
|
|
local shotSprite = spriteLoad(DIR .. "art/shot.png")
|
|
local shootSound = soundLoad(DIR .. "art/shoot.wav")
|
|
local boomSound = soundLoad(DIR .. "art/boom.wav")
|
|
----
|
|
|
|
`..` is the same glue you used for `"SCORE " .. score` in lesson twelve, doing
|
|
the same job on a file name.
|
|
|
|
Do that to every name your game loads, every time, from now on. It costs seven
|
|
characters and it is the difference between a game that runs on your machine
|
|
and a game that runs.
|
|
|
|
=== games.dat
|
|
|
|
`games.dat` is how a game introduces itself. The menu reads the `games.dat` of
|
|
every game folder beside it, and lists what it finds. No `games.dat`, no
|
|
listing: your folder is invisible, however good the game inside it is.
|
|
|
|
Make a new file called `games.dat` in your `Rocks` folder. This is the smallest
|
|
one that works:
|
|
|
|
[source,lua]
|
|
----
|
|
GAMES = {
|
|
{
|
|
TITLE = "Rocks",
|
|
SCRIPT = "Rocks/Rocks.singe"
|
|
}
|
|
}
|
|
----
|
|
|
|
That is Lua, and it is a table of the kind you met in lesson six: `GAMES` is a
|
|
list, and each thing in the list is a record describing one game. One folder
|
|
can hold several games -- that is why it is a list -- but yours holds one.
|
|
|
|
`SCRIPT` is the only field the engine truly needs, and its path is counted from
|
|
the folder *above* the `games.dat`, not from the folder the `games.dat` is in.
|
|
So a `games.dat` inside `Rocks` names its own script as `Rocks/Rocks.singe`.
|
|
That looks like it says `Rocks` twice and it is correct. Every other path in
|
|
the file works the same way.
|
|
|
|
`TITLE` is what the menu shows. Games are listed in title order, so a title is
|
|
also where your game sits in the list.
|
|
|
|
=== Telling the Menu More
|
|
|
|
The menu has a whole panel to fill beside the list, and it fills it from the
|
|
rest of the entry:
|
|
|
|
[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` and `MARQUEE` are ordinary image files in your game folder, in any
|
|
format Singe can read. The cabinet art is the big picture beside the list and
|
|
the marquee is the strip above it. Make them whatever size you like; the menu
|
|
fits them into their panels. Leave either out and the menu manages without it.
|
|
|
|
`YEAR`, `GENRE`, `DEVELOPER`, `PUBLISHER`, and `DESCRIPTION` are the text
|
|
underneath, and the engine never looks at them. They are for the person reading
|
|
the menu, which is the point.
|
|
|
|
`CANVAS_X` and `CANVAS_Y` are the size of your game's world, and 720 by 480 is
|
|
the default -- the same world you have been drawing in since lesson one. Set
|
|
them only if your game wants a different shape. A game with no `VIDEO` field is
|
|
a game with no video, which is what yours is; lesson fourteen is where that
|
|
field starts to matter.
|
|
|
|
The manual's `games.dat` section lists every field there is, including several
|
|
for hardware you do not have yet. Two are worth knowing the names of now.
|
|
`GAME_ID` is a unique identity for your game, which the online high score
|
|
tables in lesson twenty-eight need and nothing else does. `RESOLUTION_X` and
|
|
`RESOLUTION_Y` ask for a particular window size. Ignore the rest until
|
|
something sends you looking.
|
|
|
|
=== Running It from the Menu
|
|
|
|
Run `Menu.sh` -- `Menu.bat` on Windows -- from the folder Singe lives in.
|
|
|
|
Your game is in the list, sorted among anything else that is there, with its
|
|
artwork beside it and its description underneath. Up and down move one game,
|
|
left and right move a page, and start or a fire button plays the one that is
|
|
selected. Quit the game with Escape and you are back at the menu, on the same
|
|
game, ready to go again. The menu remembers which game you were on between
|
|
runs.
|
|
|
|
That is the whole of shipping a game, as far as the engine is concerned. A
|
|
player copies your folder next to Singe and it appears.
|
|
|
|
Typing the name still works, and works the same way:
|
|
|
|
----
|
|
Singe Rocks
|
|
----
|
|
|
|
When you launch a script by hand like that, Singe looks for a `games.dat`
|
|
beside it -- and in up to three folders above it -- and if an entry names the
|
|
script you launched, that entry's settings apply to it. So a game plays the
|
|
same from a terminal as from the menu, which means you can go on working the
|
|
way you have been, with `-R` and a text editor, and still trust what a player
|
|
will get.
|
|
|
|
=== Later: One File
|
|
|
|
A folder is easy to work in and slightly awkward to give away: a dozen files, a
|
|
folder inside a folder, and a player who unzips it into the wrong place. A
|
|
finished Singe game can be packed into a single `.game` file that the engine
|
|
runs directly, artwork and sounds and all, which a player copies next to Singe
|
|
and is done.
|
|
|
|
Nothing in your game has to change for that. It is lesson twenty-nine, and it
|
|
is worth waiting for: pack a game that is not finished and you will pack it
|
|
again tomorrow.
|
|
|
|
=== What Just Happened
|
|
|
|
[source,lua]
|
|
----
|
|
local shipSprite = spriteLoad(DIR .. "art/ship.png")
|
|
----
|
|
|
|
`DIR` is a variable `Singe/Framework.singe` sets before your script runs, and
|
|
holds your script's own folder with a trailing `/` on it. It exists only
|
|
because your script loads the framework, so if the first line of your script is
|
|
not `dofile("Singe/Framework.singe")`, put it there now. This is the first time
|
|
the framework has given you something you could not manage without.
|
|
|
|
[source,lua]
|
|
----
|
|
GAMES = {
|
|
{
|
|
----
|
|
|
|
Two braces, because it is a list of records. The outer one is the list. The
|
|
inner one is the first thing in the list, which is the record for one game.
|
|
Getting one of them wrong is the usual mistake, and Lua will tell you the line.
|
|
|
|
[source,lua]
|
|
----
|
|
SCRIPT = "Rocks/Rocks.singe",
|
|
----
|
|
|
|
The comma at the end of each line separates one field from the next, exactly as
|
|
in lesson six. The last field in a record has no comma after it -- Lua does not
|
|
mind if it does, but leave it off and your file will look like everybody
|
|
else's.
|
|
|
|
=== Try It
|
|
|
|
. *Make the artwork.* Draw a `cabinet.png` and a `marquee.png`, put them in
|
|
your game folder, name them in `games.dat`, and look at the menu.
|
|
. *Move your game in the list.* Change `TITLE` to `Asteroid Field` and start
|
|
the menu again. The list is sorted by title, not by folder.
|
|
. *Break `games.dat`.* Delete one of the closing braces and start the menu.
|
|
Your game is gone from the list; the others are still there. Put it back.
|
|
. *Two entries, one folder.* Add a second record to `GAMES` pointing at your
|
|
lesson seven script, with a title of its own. One folder, two games in the
|
|
menu.
|
|
. *Find your save.* You changed how the game is started, so ask yourself where
|
|
its high score went. Print `singeGetDataPath()` and look. It is not the same
|
|
folder it was last lesson, and now you know why it is worth asking rather
|
|
than assuming.
|
|
|
|
=== Break It on Purpose
|
|
|
|
Take the `DIR ..` back off one line, so it reads `spriteLoad("art/ship.png")`
|
|
again, and start the game from the folder above -- `Singe Rocks`, or from the
|
|
menu. The game dies before it draws anything, and the terminal says:
|
|
|
|
----
|
|
24:spriteLoad: Couldn't open art/ship.png: No such file or directory
|
|
----
|
|
|
|
The exact wording after the colon comes from your operating system and differs
|
|
a little between them, but the shape is the same everywhere: the line, the
|
|
engine function that gave up, and the name it could not find.
|
|
|
|
It could not find `art/ship.png` because it looked in the folder you started
|
|
Singe in, which is the folder holding `Rocks`, and there is no `art` there.
|
|
The file it wanted is one folder further down, which is exactly what `DIR`
|
|
knows and a bare name does not.
|
|
|
|
This is the error you will meet the first time you hand your game to somebody
|
|
else, if you meet it at all, because it is invisible from inside the game's own
|
|
folder. Put the `DIR ..` back and start it from both places.
|
|
|
|
=== What You Learned
|
|
|
|
* A game is a folder: the script, its artwork, and a `games.dat`, and nothing
|
|
from outside it except `Singe/Framework.singe`.
|
|
* Name the folder and the script the same, with no spaces, and `Singe Rocks`
|
|
will find it.
|
|
* Names your script loads are counted from where Singe was started, so build
|
|
every one of them from `DIR`.
|
|
* `games.dat` is a Lua file holding a `GAMES` list, and each entry is one game.
|
|
* An entry needs `TITLE` and `SCRIPT` and nothing else; paths in it count from
|
|
the folder above the `games.dat`.
|
|
* `CABINET`, `MARQUEE`, `YEAR`, `GENRE`, `DEVELOPER`, `PUBLISHER`, and
|
|
`DESCRIPTION` are what the menu shows the player.
|
|
* `Menu.sh` or `Menu.bat` starts the bundled menu, which lists every game
|
|
folder beside it, sorted by title.
|
|
* A game launched by name gets its `games.dat` entry too, so it behaves the
|
|
same either way.
|
|
* A finished game can be packed into one file later, in lesson twenty-nine.
|
|
|
|
=== Next Time
|
|
|
|
That is part two finished: you have a game, with art, sound, collision, score,
|
|
and a way for somebody else to run it. Part three is about the thing almost no
|
|
other engine does -- playing film as the world the game happens in -- and
|
|
lesson fourteen starts with the disc, the frame, and the wait.
|