Spybots Smart Parts  
  The Unofficial Resource Centre for Lego Spybotic  
   
img  
   
   

TUTORIALS

TUTORIALS

Intermediate

Tasks, Sub-routines etc - NQC

Tutorial Details:
Difficulty Level: Intermediate
Topics Covered: Creating and using Tasks, Sub-routines and macros in Mindscript.
Assumed Knowledge: The Basics, My First Program, Constants, Variables & Counters,Conditional(if), Conditional (Select), Forever Yours
Written By: BILL LANE ( based on Mark Overmars tutorial available in the Brixc CC download in the Documentation folder)

Print Version

 

The following tutorial is heavily indebted to Mark Overmars tutorial (with revisions by John Hansen) that is available with the Bricx CC download. I review this material here because the topic is so important and on the off chance that someone is programming Spybot's in NQC without Bricx CC.

 

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.

 

This tutorial is protected by International Intellectual Property Rights laws and may not be reproduced or redistributed in full or part, without the prior written consent of the author. Unauthorized reproduction of this tutorial or its contents may result in prosecution.

 

 
 
DISCLAIMER: All content is provided as is, with no warranty stated or implied regarding the quality or accuracy of any content on or off this site. All trademarks, service marks, and copyrights are property of their respective owners. This site is not sponsored, authorized or sanctioned by the LEGO Group nor representative of their opinions in any way.PRIVACY POLICY