273 lines
10 KiB
Text
273 lines
10 KiB
Text
== Lesson 1: Hello
|
|
|
|
image::learn/01-hello.png[The finished lesson, 480]
|
|
|
|
That is what you will have at the end of this lesson. It is not much to look
|
|
at. It is also a program you wrote, running on a real game engine, and
|
|
everything else in this book is built on the five lines that made it.
|
|
|
|
=== Set Up Your Work Folder
|
|
|
|
Make one folder to keep everything in, called `singe` or whatever you like,
|
|
and put your copy of Singe in it. This is your *work folder*, and every
|
|
lesson in this book puts a new folder inside it. You will make it once and
|
|
never think about it again.
|
|
|
|
Inside the work folder, make a folder called `hello`, and inside that a file
|
|
called `hello.singe`.
|
|
|
|
[literal]
|
|
----
|
|
singe/ <- your work folder, with Singe in it
|
|
hello/
|
|
hello.singe
|
|
----
|
|
|
|
That nesting is not fussiness. Singe treats the folder you run it from as the
|
|
place where games live, one folder per game, and it keeps its own files
|
|
there too. Your game goes in its own folder, one level down. From lesson two
|
|
onward each lesson is another folder beside `hello`.
|
|
|
|
Two things about the names. The part after the dot, `.singe`, is how Singe
|
|
knows this is a script. And on most computers that are not Windows, capital
|
|
letters count: `Hello.singe` and `hello.singe` are two different files. Get
|
|
into the habit now of typing names exactly.
|
|
|
|
Giving the folder and the script the same name matters as well, and you will
|
|
see why in a moment.
|
|
|
|
Open `hello.singe` in your text editor. It is empty. That is correct.
|
|
|
|
=== Type This In
|
|
|
|
[source,lua]
|
|
----
|
|
function onOverlayUpdate()
|
|
overlayClear()
|
|
overlayPrint(2, 2, "Hello!")
|
|
return OVERLAY_UPDATED
|
|
end
|
|
----
|
|
|
|
Save it.
|
|
|
|
Do not worry yet about what any of it means. Two things are worth noticing
|
|
before you run it, because they will be true of every program you ever write.
|
|
|
|
The first is that the spaces at the start of the middle three lines are there
|
|
on purpose. They do nothing at all to the program -- Singe would run it
|
|
exactly the same without them -- but they show you at a glance that those
|
|
three lines belong *inside* the thing that starts with `function` and ends
|
|
with `end`. Every programmer does this. Do it from the first day and you will
|
|
never have to learn it later.
|
|
|
|
The second is the order of the lines. They happen top to bottom, one after
|
|
another, like the steps of a recipe. That is the single most important thing
|
|
about a program, and almost everything that confuses a beginner comes from
|
|
forgetting it.
|
|
|
|
=== Run It
|
|
|
|
Open a terminal, go to your *work folder* -- not the `hello` folder inside it
|
|
-- and name the folder you want to run:
|
|
|
|
----
|
|
Singe hello
|
|
----
|
|
|
|
On Windows that is `Singe.exe hello`. If your copy of Singe lives somewhere
|
|
else, type the path to it, such as `../Singe hello`.
|
|
|
|
That is why the folder and the script share a name. Given a folder, Singe
|
|
looks inside it for a script called the same thing. Name them differently and
|
|
you get `Error: Unable to locate the game.`, which is the engine saying it
|
|
looked and found nothing it recognised. You can always be explicit instead and
|
|
write `Singe hello/hello.singe`, which does the same job.
|
|
|
|
A black window opens with the word `Hello!` near the top left corner. Press
|
|
*Escape* or *Q* to quit.
|
|
|
|
Two things will have happened that you did not ask for, and both are normal.
|
|
Folders called `Singe` and `data` appeared in your work folder, beside
|
|
`hello`: the first is the engine unpacking the files it needs, the second is
|
|
where anything your games save will go. Leave both alone. And the window is
|
|
mostly black, because you have not drawn anything except six letters.
|
|
|
|
=== What Just Happened
|
|
|
|
Now the five lines, one at a time.
|
|
|
|
[source,lua]
|
|
----
|
|
function onOverlayUpdate()
|
|
----
|
|
|
|
`function` starts a group of instructions and gives it a name. Everything from
|
|
here down to the matching `end` belongs to it. This one is called
|
|
`onOverlayUpdate`, and the empty parentheses after the name will make sense in
|
|
lesson five.
|
|
|
|
That name is not yours to choose. Singe looks for a function with exactly that
|
|
name and calls it about sixty times a second, forever, so that you have a
|
|
chance to draw. If you spell it `onoverlayupdate` or `onOverlayUpdated`, Singe
|
|
will not find it, your window will stay black, and nothing will tell you you
|
|
made a mistake. That is the first real lesson: the computer does what you
|
|
wrote, not what you meant.
|
|
|
|
A function that you write and the engine calls is a *callback*. You do not
|
|
call it yourself, and you cannot say when it runs; you write it, and Singe
|
|
calls you back when there is something to do. Every name in this book that
|
|
begins with `on` is a callback, and there is one for nearly everything that
|
|
can happen: a button pressed, a key typed, a sound finishing, the disc
|
|
reaching a frame. `onOverlayUpdate` is the one for drawing, and it is the only
|
|
place drawing is allowed.
|
|
|
|
[source,lua]
|
|
----
|
|
overlayClear()
|
|
----
|
|
|
|
This is a function too, but not one you have to write: Singe provides it,
|
|
along with several hundred others, and you are *calling* it. Calling a
|
|
function means saying its name followed by parentheses. This one wipes the
|
|
screen clean.
|
|
|
|
It comes first because drawing does not replace what was there before, it
|
|
draws on top of it. Clear, then draw, every time.
|
|
|
|
[source,lua]
|
|
----
|
|
overlayPrint(2, 2, "Hello!")
|
|
----
|
|
|
|
Another call, and this one needs to be told three things, which go inside the
|
|
parentheses separated by commas. Things you hand to a function like this are
|
|
called *arguments*.
|
|
|
|
The first two are numbers: how far across and how far down. The third is the
|
|
text to print, and it is in double quotes because that is how you tell the
|
|
computer that `Hello!` is a piece of text rather than the name of something.
|
|
Text in quotes is called a *string*, and you will use strings constantly.
|
|
|
|
Sooner or later you will want a double quote inside a string, and the obvious
|
|
way does not work: the second quote ends the string early, and you get the
|
|
same `unfinished string` error you will meet at the end of this lesson. There
|
|
are two ways round it, and both are worth knowing.
|
|
|
|
[source,lua]
|
|
----
|
|
overlayPrint(2, 2, "She said \"hello\" to me.")
|
|
overlayPrint(2, 4, 'She said "hello" to me.')
|
|
----
|
|
|
|
The first puts a backslash in front of each quote that belongs to the text.
|
|
The backslash says "the next character is part of the string, not the end of
|
|
it", and the pair together is called an *escape*. The second uses single
|
|
quotes to mark the ends of the string, which leaves double quotes free to be
|
|
ordinary text. Lua accepts either kind of quote, as long as the string ends
|
|
with the same kind it started with.
|
|
|
|
Both of those lines print exactly the same thing. Use whichever is easier to
|
|
read, which is usually the single quotes when the text is full of double
|
|
ones.
|
|
|
|
The two numbers are not pixels. `overlayPrint` is the engine's own plain
|
|
text, and it counts in character cells: `(0, 0)` is the very top left corner,
|
|
`(1, 0)` is one letter to the right, and `(0, 1)` is one line down. So `(2, 2)`
|
|
is two letters in and two lines down. Other ways of drawing text do count in
|
|
pixels, and you will meet them in lesson nineteen.
|
|
|
|
[source,lua]
|
|
----
|
|
return OVERLAY_UPDATED
|
|
----
|
|
|
|
`return` hands an answer back to whoever called the function. Singe asks your
|
|
`onOverlayUpdate` one question every time it calls it: did you change
|
|
anything? `OVERLAY_UPDATED` means yes, so put it on screen.
|
|
|
|
`OVERLAY_UPDATED` has no quotes around it because it is not text. It is a name
|
|
that Singe has already given to a value, so that you can write something you
|
|
can read instead of a number you would have to remember.
|
|
|
|
[source,lua]
|
|
----
|
|
end
|
|
----
|
|
|
|
The matching end of the `function`. Every `function` has exactly one, and
|
|
forgetting it is the single most common mistake there is.
|
|
|
|
=== Try It
|
|
|
|
Change one thing at a time, save, and run it again. That loop -- change, run,
|
|
look -- is the whole job.
|
|
|
|
. *Move the text.* Change `overlayPrint(2, 2, "Hello!")` to
|
|
`overlayPrint(10, 5, "Hello!")`. Then try `(0, 0)`. Then try `(200, 2)`, and
|
|
work out why it vanished.
|
|
. *Say something else.* Put your own name in the quotes.
|
|
. *Say two things.* Add a second `overlayPrint` line under the first one, with
|
|
a different row number and different text. Nothing stops you having as many
|
|
as you like.
|
|
. *Take the clear out.* Delete the `overlayClear()` line and run it. Nothing
|
|
changes, because nothing on your screen moves yet. Put it back anyway. In
|
|
lesson two it will matter enormously, and this is a good time to see that
|
|
some mistakes wait before they bite.
|
|
|
|
=== Break It on Purpose
|
|
|
|
Beginners spend more time reading error messages than reading books about
|
|
programming, so let us get the first one over with now.
|
|
|
|
Delete the closing quote after `Hello!`, so the line reads
|
|
`overlayPrint(2, 2, "Hello!)`, and run it. Singe refuses to start and says:
|
|
|
|
----
|
|
Error running script: hello.singe:3: unfinished string near '"Hello!)'
|
|
----
|
|
|
|
Read it from the left. `hello.singe` is which file. `3` is which line. The
|
|
rest is what went wrong, in this case a piece of text that was opened and
|
|
never closed. Go to line three, put the quote back, and it runs.
|
|
|
|
Almost every error you will ever see has that shape: a file, a line, and a
|
|
complaint. The complaint is sometimes written for someone who already knows
|
|
Lua, but the file and the line are always right, and nine times in ten the
|
|
line is enough.
|
|
|
|
=== Stop Restarting It
|
|
|
|
You have now started Singe by hand five or six times. Stop doing that. Run it
|
|
like this instead:
|
|
|
|
----
|
|
Singe -R hello
|
|
----
|
|
|
|
`-R` tells Singe to watch your script. Save a change in your editor, and the
|
|
game restarts itself a moment later with your change in it, without closing
|
|
the window. If you save a mistake, the error is printed and the window sits
|
|
empty until you save a fix.
|
|
|
|
Use `-R` for the rest of this book.
|
|
|
|
=== What You Learned
|
|
|
|
* A program is a file of instructions that happen in order, top to bottom.
|
|
* A `function` groups instructions under a name and stops at its `end`.
|
|
* `onOverlayUpdate` is a name Singe looks for and calls many times a second,
|
|
and it is where all your drawing goes.
|
|
* Calling a function means writing its name and parentheses. Anything inside
|
|
the parentheses is an argument.
|
|
* A function the engine calls for you, rather than one you call yourself, is a
|
|
callback. `onOverlayUpdate` is one.
|
|
* Text in double quotes is a string.
|
|
* Clear the screen before you draw on it.
|
|
* An error tells you the file and the line. Start there.
|
|
|
|
=== Next Time
|
|
|
|
Your `Hello!` sits where you put it and never moves. To make it move, you need
|
|
somewhere to keep a number that changes -- a *variable* -- and that is the
|
|
whole of lesson two.
|