|
Hi !. In this tutorial we will see something almost
indispensable in a CRPG: A " script " language. We will see in that consists,
why it's so important, and how to manage it in our executable.
An advice for understanding this tutorial: you must
have knowledge of programming languages (after all, we are programming
a language). So catch air, breathe deep, and prepare you to a very theoretical
and huge tutorial ^_-.
Without more waits (that you have already had enough
^_-), we begin:
From design to the screen
Prelude
Script language
The compiler and the interpreter
Associating a tile with an event.
Instruction files and index files
Compilation of instructions of our "script" language
. How the compiler works
Compilation of an instruction. Syntax of a language
Visual compiler
Format of the instructions of the interpreter's pseudocode
How the interpreter's instructions are executed
FROM DESIGN TO THE SCREEN (Languages, compilers and Interpreters)
Prelude
When a game is designed, a very important part (besides the design of characters, of combats, graphic design,...) is the design of the script. This consists on having written what the characters of our game should make in each moment (What they say, what they should make in every moment, what should happen them according to what you/they have made before...)
([...]
(Situation: The prison of the Imperial Star Destroyer
" Sovereign ". The characters are without money neither equip)
[Eric Mathesar enters in a
cell. Inside it is the main character, alone. Eric looks at the main character.
He is crestfallen, sat down in the bed]
Eric: Mister <Name_1>.
Finally we know each other in person.
<IF MAIN_KNOWS_MATHESAR>
[The
main character shows a surprise expression, at the same time that she lifts
the head]
<Name_1>:
Mathesar!!
[Mathesar
shows a surprise expression, and then a smile]
Eric:
Hey. It has been all a surprise. I didn't know that I was so well-known
among the scum like you.
<IN ANOTHER CASE>
[The main character looks
at a moment to Mathesar]
<Name_1>: And ?. I should
care about this?
Eric: You should..., but I
am being rude presenting me this way... I'm known as Eric Mathesar.
<ENDIF>
[...])
But having the script, the characters of your game will not do what is written on it (It's a pity, by the way ^_-). We need to indicate to the game's engine what things must do, and in what moments should make them. For that we need a " script " language.
Script Language
A " script " language is a programming language (as
Pascal, C) invented by ourselves (although we can take the syntax of other
languages) whose function is to indicate to the game what should make in
each moment.
This " script " language is saved in a text file
or in a binary one, and the procedures and functions that it has must access
to the engine's capacities (Give_Money, Move_NPC, Teleport, Talk, Change_Tile,
Night_Or_Day, Effect,...).
Like example we will translate to a script language the upper script:
Fade(@NormalToBlack);
QuitBelongings(@All);
Variable(@Money)=Money();
Money()=0;
% The party has no money and no belongings
Teleport(@SDPrisonMap, 30,70);
% Teleporting the camera to the tile X=30, Y=70
TeleportNPC(@Mathesar,30,60);
% Teleport the two characters
TeleportNPC(@Principal,30,76);
Expression(@Main,@Sit);
Fade(@BlackToNormal);
% And I show the map after loading the things
MoveNPC(@Mathesar,@Down,13);
Talk(@MathesarFace, 'Eric:
Mister <Nombre_1>. Finally we know each other in person.');
If
(Flag(@Main_Knows_Mathesar)=TRUE)
Expression(@Main,@LookUp);
Expression(@Main,@Surprise);
Talk(@MainFace, '<Name_1>:
Mathesar!!');
Expression(@Mathesar,@Surprise);
Expression(@Mathesar,@Smile);
Talk(@MathesarFace, 'Eric:
Hey. It has been all a surprise. I didn't know that I was so well-known
among the scum like you.');
Else
Expression(@Main,@LookUp);
Talk(@MainFace, '<Name_1>:
And ?. I should care about this?');
Talk(@MathesarFace, 'Eric:
You should..., but I am being rude presenting me this way... I'm known
as Eric Mathesar.');
End;
The elements that our script language should have are:
Talk(0, ' Hello, <D001>,
it has been a long time'); // 1º instruction that
is executed
MoveNPC(1, ' M2G014A203
') ;
// 2º instruction that is executed
Talk(1, ' <D000>!!.
You're alive!! ') ;
// 3º instruction that is executed
Music(#HappyMoment);
// 4º instruction that is executed
The compiler and the interpreter
Here we should make an stop and say something very
important: the "script" language is used because it is a language for humans,
simple of understanding for us (The step of translating the script to the
language is relatively simple). If we read a "script" program, we know
perfectly what will happen in a certain moment of the game.
But for a machine (our game), it is not so simple.
We must pass the "script" language to another language that can be understood
more easily (and in less time), and be executed by our source code.
Is like speaking of a Java program compiled for the
Java Virtual Machine and of a Assembler program. Java is easily understandable
for human, but it needs to translate its instructions in run-time to assembler
=> slow.
Ensamblador is very difficult to understand at
first sight (and at second sight, and at third sight,...), but a program
compiled in assembler will go very quickly in comparison with the other
one.
So, we should create one mechanism to pass the instructions
from the "script" to a more "machine" language, and other mechanism for
executing those "machine" instructions. Those tasks are made by the
compiler and the interpreter.
An "script" instruction is translated to one or
more "pseudocode" instructions (we will say "pseudocode" when talking about
"machine" instructions). This is made this way 'cause if the pseudocode
has lots of instructions, the interpreter will be more complex, and therefore
slower.
An example:
You directly could execute the " script " language (without going previously by a compiler) during the game, but it would be slower, and therefore it would not be good.
(NOTE: Technically the function of an Interpreter it is not the indicated (to execute the instructions compiled previously by a compiler ), but we believe that it is better "interpreter" that "executioner" (of instructions ^_-). For interested ones, the real function of an interpreter is to pick up the instructions of a high-level language and execute them [VisualBasic has interpreted instructions, the p-code]).
Associating a tile with an event. Instruction files and index files
Well, we already know how to pass our script to a
"script" language, and we have seen that this language is translated to
other ("pseudocode"), quicker of understanding for the computer.
But what the script says happens when stepping certain
tiles (p. e.g. in the previous example of the fountain) or when looking
towards them. What solution can we take?.
Like we comment in previous lessons, the solution is the following one: Each tile has associated an event, and that event is some lines of "script" code. When one steps a tile, we check if that tile has an associated event, and execute the code associated to that event.
_COMMENT_
There's a curiosity about the game Ultima 9,
of Origin. In an article, their developers commented that, in certain areas
of the game, using a trick to fly means that there will be events that
would not be executed, and therefore it would be impossible to advance
correctly in the game.
What happens? In Ultima 9 (We suppose) the events
are associated with certain sections of the 3D map(i.e. a square that occupies
the entrance to a town). If we don't pass through those sections, the events
are not activated (imagine that there is an event in the drawbridge of
a town, which shows the murder of somebody. If we enter flying [so we don't
pass trough the drawbridge], we won't be able to see that murder, the flags
of that event won't be activated, and possibly an error will happen).
_END COMMENT ^.^_
The question that arises is: What instructions are
associated to an event?.
The answer is the following one: When we associate
"script" code to an event (Step in (45,3)=Talk
), those lines of
code stay (compiled) in a binary file, in the position X. And that tile
(45,3) has an event number that points to a file that indicates the first
instruction of that event.
Maybe sounds difficults (it "is" ^_-), but with an example all is solved:
Instructions [_Pa 0 _Di 1]. _Pa is in the position
540 of the instructions' file of its map.
That event should happen when stepping the tile
(45,3).
That tile has an event nº, 5.
And in a file (index file), the position 5 contains
the value 540.
Therefore, when stepping the tile (45,3), we
pick up their event number [5], we look in an index file where the first
instruction of this event is [540] and we pick up from the instructions'
file of instructions the 1º instruction (_Pa).
Then, for the execution of the instructions we need 2 files for each map (or for the whole game):
Ok. We already know how to give to the interpreter a series of instructions so it could executes them (Nº Event = Index = 1º Instruction). And those instructions stay in a binary file. But now there is a detail: How we obtain those files (index and instructions) starting from the "script" language ?. That means: How the compiler works (not entering in details)?.
Usually, each map has an associated file where our mini-programs stay. And each event of the map has associate one of those mini-programs:
File of Map Number 1
[0] <= event number
#Talk(0, ' Hullo');
[1] <= event Nº
#Talk(0, ' Ey, I said
hullo!');
#Talk(1, ' Yes, I know.');
The task that the compiler has is the following one:
Compilación of an instruction. Syntax
of a language
And how we compile a single instruction?. For this,
we need to know the format of all the instructions that exist in the language
(SYNTAX), to fit our code in one of them.
For being able to compile a language, we need to
know the syntax of this language. That means to know the format of their
instruciones. For example:
Operand: (Operand = Element that is used to be
assigned to a variable)
[Variable | FunctionVariable | Number | ({Operand}
Operators {Operand}) | NOT (Operand)]
That means that and Operand can be a Variable, a FunctionVariable (a function that acts as variable), a number, or a group of operands with an operator ((#Variable(1)+4), 5, (NOT(4=4)),...)
Therefore, for compiling a line of code, we have to look the type of that instruction (operand, function,...this's made using the syntax), look if that instruction fulfills the syntax, and translate that instruction to interpreter's pseudocode.
Visual Compiler
You will have been able to observe that making a compiler is a "peñazo" (it's a boring, time-eater task). But there is better solution that making the compiler in a manual way.
We're sure that all the "official" (commercial) games has something very similar to what we're going to explain, although the idea was thanks to RPG Maker 2000, of ASCII Corp (we will speak from him to the end of this lesson).
The solution consists in that the instructions of the language are not introduced using lines of text (that will be compiled), but using menus in a visual enviroment. When the "line" of code is made, the program will take charge of keeping the code line (already compiled) in a list of instructions (that will be an array, a list of pointers,...) that will be saved into the instruction files and index files.
Like always, with an example we solve everything ^.^:
BEFORE
#Talk(0, ' Hello, World! '); => Compiler (Syntactical Analysis, a "peñazo"
of programming) => | _Pa 0 | _Pa 142 | _Di |
NOW
When pressing OK => | _Pa 0 | _Pa 142 | _Di | (LINE)
That LINE stays in a list of instructions.
But with this method we need to make a "decompiler". Its task is to "decompile" a compiled line, so shows the instruction in the "script" language. ( | _Pa 0 | _Pa 142 | _Di | => #Talk(0, 'Hello, World!')). This is made 'cause without this the designer (us ^_^) will not know what happend with an specific event.
Imagines the script exposed some pages ago. If
we introduce the instructions this way but then we could not see them in
"script" language, we would not know what is happening in that event.
Format of the instructions of the interpreter's pseudocode
The instructions of the pseudocode must be
stored in some format in the files that before have been commented. We're
going to talk about this now.
Each instruction (i. e. _Pa 0, or _Di ) is composed
of their ID (_Pa) and their data (0). The ID takes charge of indicating
to the interpreter what this instruction means (_Pa => This's the parameter
of a function, _Di => shows a dialogue with the parameters that before
have been passed) and the data indicate... well it's easy ^_-, one or more
data associated to the ID (i.e. the parameter is a 0).
But questions arises: Where are stored the strings
that instructions uses?. The length of the instructions are fixed or variable?.
In what format we save the instructions' ID?. Well: Now we will comment
these questions:
How the interpreter's instructions are executed
After this "parrafada" (=lot of text), we already know that the interpreter begins to execute a series of instructions when an event is activated. What the interpreter receives are pseudocode instructions (p. e.g. _Pa 0).
And how is executed a pseudocode instruction in our
interpreter (->executable)?
Using a CASE structure. It looks the type of that
instruction, and according to what instruction is, it reads one or more
data and certain tasks are made.
Case IDInstructionRead Of:
IDTalk:...
// Shows a dialogue. It uses two parameters.
IDMovement:... //
Move one NPC. Uses two parameters.
IDParameter:... //
Saves a parameter in the stack of parameters
End;
But this is a little more complex. This is because
our "script" language doesn't only support procedures, but also functions,
expressions, control sentences...
We will talk of those elements in the next part
of this tutorial
END OF PART 1