|
Up until now we've written all our code into the main task.
But if we wanted to do anything a little bit involved this
would very quickly become very messy. It would also be very
ineffficient. Because there would be certain command combinations
that we would need more than once. Rather than rewriting these
blocks of commands over and over we can place them into their
own task and call that task whenever we need to use that sequence
of commands. Let's look at a simple example:
main(){
while (true){start mySound; wait 200;}
}
task mySound(){
PlaySound(1);
}
The main task uses while
to loop continuously through a command sequence. The
first command runs the mySound task
by using the start keyword.
It then waits 2 seconds before repeating the process. We use
the task keyword to declare
the task called mySound.
It's structure is identical to the main
task. Task mySound
plays a short sound and then ends. The result is a beep sound
that repeats once every 2 seconds.
One thing to note here is that both tasks are running in
parallel. In this example the loop could have been placed
in mySound. So that the main task starts mySound and then
mySound keeps going until it's told to stop. In this way you
could have multiple tasks all running continuously doing their
own things. As such it is good that NQC provides a stop
keyword to complement the start.
To stop mySound we need only write stop
mySound. This could be done from mySound, from the
main task or from any other task. Another option is to use
StopAllTasks() which ends
all running tasks (including the main task).
Another option is to use a sub-routine.
As you can see from the example below a sub-routine looks
almost identical to a task:
sub mySound(){
PlaySound(1);
}
main(){
while (true){mySound(); wait 200;}
}
The word sub used instead
of the task keyword. Notice
you can start the sub-routine
by simply writing it's name. No need to use start.
Another thing to note is that sub-routines
are declared before the main
task and tasks
are declared after the main task.
I don't know why this is. But I do know you'll get an error
if you try to do it the other way around.
A further option is the use of inline functions. On the surface
an inline function is
nearly identical to a sub-routine.
To create them you just write void
instead of sub. But there
is one very important difference between an inline
function and a sub-routine.
If you use a sub-routine
only one copy of that sub-routine
is kept in memory and different tasks and sub-routines may
call that sub-routine when required. But an inline
function acts as a kind of shorthand for a block of
code. When the program is compiled wherever the inline function's
name appears the code is substituted for the name. So that
multiple copies of that code block will appear in the downloaded
program.
One advantage of functions
is that you are able to pass them arguments
as in the following example:
void mySound(int thisSound, int thisTime){
sound thisSound;
wait thisTime;
}
main(){
switch (gGameState)
{
case 1: mySound(1, 50); break;
case 2: mySound(2, 70); break;
case 3: mySound(3, 80); break;
default: mSound(4, 90); break;
}
}
Here we've declared two parameters
for mySound; thisSound and thisTime, by using brackets after
the name but before the curly braces. Note that you must declare
the type of argument that will be passed. In this case I have
declared that they will both be of type
int; which means I can pass a number to the function.
In the main task we have a conditional select
and dependant on the value of gGameState we send mySound different
values(inside brackets). The first value represents the first
argument; thisSound. The second value represents the second
argument; thisTime. Which means we use one function
but it can play many different sounds.
Yet another option is to use
macros. On the surface a macro
is nearly identical to a function.
To create them you just write #define
instead of void.
You can choose to use parameters
or to not use parameters. Like functions they are copied into
place during compilation. The difference to note is that the
text must all be on one line.
So there it is! We have looked at four different options
to breaking up our code into smaller useful parcels. All of
these methods serve to simplify the use, execution and debugging
of our code.
|