-
Notifications
You must be signed in to change notification settings - Fork 1.5k
Scripting Tutorial
This guide will take you through the process of creating a basic script for Torque 2D.
The following guide(s) are recommended before you begin this tutorial:
The following guide(s) are useful to have available while completing this tutorial:
What you need
- A local checkout of of Torque 2D
- A text editor. Generally, any text editor will work fine as long as it does not introduce whitespace symbols. Avoid any advanced word processor, such as Microsoft Word. Recommended editors include, but are not limited to:
- Windows: Torsion, Notepad++, Visual Studio
- OS X: Xcode, TextWrangler, TextEdit (in "no formatting" mode)
When talking about creating or editing a script for Torque 2D, this always translates to a TorqueScript file. TorqueScript (TS) is a proprietary scripting language developed specifically for Torque technology. The language itself is derived from the scripting used for Tribes 2, which was the base tech Torque evolved from.
Scripts are written and stored in .cs files, which are compiled and executed by a binary compiled via the C++ engine (.exe for Windows or .app OS X). The CS extension stands for "C Script," meaning the language resembles C programming. Though there is a connection, TorqueScript is a much higher level language which is easier to learn than standard C or C++.
This file format should not be confused with Microsoft's C#, which also uses a .cs extension. The two languages are completely unrelated. If you have C# editing software installed, such as Visual Studio, there will be a conflict when opening a TorqueScript file. It's up to you to manage what programs should open a .cs file.
Using Windows file system to create a TorqueScript file.
Using Torsion to create a TorqueScript file.
Using OS X file system to create a TorqueScript file.
Now that we have a script, the first step you should always take is executing the file. Sometimes you will see this mentioned as "exec'ing".
Without executing, Torque will not use any code that will be written in the new script. All you need to know is the function call and path to the script. The example in this tutorial has created myScript.cs in the modules/SpriteToy/1 directory. The following code is called at the very top of the SpriteToy::create function:
function SpriteToy::create( %this )
{
// Execute the script. In this example, the file is in the same
// as the directory as this script.
exec("./myScript.cs");
// ...rest of function is unchanged
}
Now, all the code you write in your script will be loaded in Torque. Let's add some code now.
Much of your TorqueScript experience will come down to calling existing functions and writing your own. Functions are a blocks of code that only execute when you call them by name.
There are essentially two types of functions: global and object method. The simplest function
is a stand-alone global. Add the following to your script (myScript.cs in this example):
// Global function used to print the message "Hello World"
function helloWorld()
{
// Call the echo function with the phrase "Hello World"
echo("Hello World!");
}
The above code demonstrates the following important concepts:
- Proper TorqueScript syntax
- A global
function
structure - Commenting
- Calling a ConsoleFunction that was written in C++ previously
The function
keyword, like other TorqueScript keywords, is case sensitive. If you were to use Function
, your code would not work. Comments start with the //
syntax. Any text on the same line as the comment will be ignored by the TorqueScript processor. These are useful for providing information about your code, so you should comment as often as possible.
A ConsoleFunction
is a global function written in C++, then exposed to TorqueScript. You should never try to create a script version, such as function echo()
. You will have bad results.
Your function is now ready to use. You can call it at any point after your script has been executed. For example, the following code in myScript.cs shows the call:
function SpriteToy::create( %this )
{
// Execute the script. In this example, the file is in the same
// as the directory as this script.
exec("./myScript.cs");
// Call the new function
helloWorld();
// ...rest of function is unchanged
}
Your console should output "Hello World"
after the function is called. Now, let's say you want to change the text that is printed to the console. You could change the line of code that has "Hello World", or you can pass in a parameter. Change helloWorld
to the following:
// Global function used to print text to the console
// %message - The text you wish to print
function helloWorld(%message)
{
// Call the echo function, passing %message as the argument
echo(%message);
}
Now that the function signature has changed, you need to update your call. Change that code to the following:
function SpriteToy::create( %this )
{
// Execute the script. In this example, the file is in the same
// as the directory as this script.
exec("./myScript.cs");
// Call the new function
helloWorld("Hello World");
// ...rest of function is unchanged
}
The above is just an example of passing a parameter. You can pass whatever text you want, making your helloWorld
function more flexible. Now that variables and arguments are being introduced, we should cover scope.
Next, let's make some more changes to the helloWorld
function. Change the code to the following:
// Global function used to print text to the console
// %message - The text you wish to print
function helloWorld(%message)
{
// Store the text in a local variable
%myVariable = %message;
// Call the echo function, passing %myVariable as the value
echo(%myVariable);
}
The %myVariable = %message;
demonstrates storing a value in a local variable. That variable is then used by the echo
function to print a message. As soon as the function finishes executing, %myVariable and its value will be lost. It will no longer consume memory or be usable. If you want your variable to exist outside of the helloWorld
function, use a global variable:
// Global function used to print text to the console
// %message - The text you wish to print
function helloWorld(%message)
{
// Store the text in a global variable
$MyVariable = %message;
// Call the echo function, passing $MyVariable as the value
echo($MyVariable);
}
The above code stores the value in $MyVariable
, which is a global variable. Even after the function finishes executing, you can access and manipulate $MyVariable
. The a global variables impact on memory and performance is next to nothing.
Proper scoping is much more important when dealing with objects. For example, change the helloWorld
function body to do the following:
// Global function used to print text to the console
// %message - The text you wish to print
function helloWorld(%message)
{
// Create a new ScriptObject
%myObject = new ScriptObject();
}
The %myObject
local variable will store the ID of the new ScriptObject. Once the function finishes, the local variable goes away. You can no longer access it by name. However, the ScriptObject will continue to exist outside of the function. If you know its ID or name, you can access it after the helloWorld
function is called. This is extremely important to remember.
If you do not want an object to exist outside of a function, you will have to delete it before the function finishes or you will essentially have a "memory leak." You can always delete the object outside of the function, if you know the name or ID. However, unless you are tracking that some how, you will be hard pressed to do so.
In addition to the creation of stand-alone functions, TorqueScript allows you to create and call methods attached to objects. The first step is to create a namespace for your object. The two most common ways of doing achieving this is by giving an object a name or assigning a class.
Naming an object
// Global function used to print text to the console
// %message - The text you wish to print
function helloWorld(%message)
{
// Create a new ScriptObject named MyScriptObject
%myObject = new ScriptObject(MyScriptObject);
}
The above code named the new object "MyScriptObject". You can access and extend that specific object in a way beyond how a global variable works. The following is just example code, so need to copy it:
// Called in some other function
%id = MyScriptObject.getId();
Naming an object is supposed to make it unique. You should never create multiple objects using the same name. Naming a second object MyScriptObject will result in losing track of the original. It will still exist and have a unique ID, but you can not refer to it by name.
Giving an object a class
// Global function used to print text to the console
// %message - The text you wish to print
function helloWorld(%message)
{
// Create a new ScriptObject named MyScriptObject
%myObject = new ScriptObject()
{
class = "Messenger";
};
}
The above code drops the name and constructs the object using the {};
syntax. You can fill out the properties of an object by declaring and assigning them inside of the object body. This allows you to create multiple objects with similar attributes, such as the class
field, without namespace stomping.
Speaking of namespaces, stomping, and extending, let's talk about object methods.
Some of the more important ConsoleMethods are already written in C++, then exposed to script. When you want functionality not provided out of the box, you can add script based functions to objects. As shown in the previous section, you will need to start by creating a namespace for the object. Naming an object applies limitations, so we're going to use the class instead.
For this example, we are going to add a function to the Messenger
class. All this function will do is take in an argument, which will then be printed to the console using echo
. Add the following code to your script:
// Global function used to print text to the console
// %message - The text you wish to print
function helloWorld(%message)
{
// Create a new ScriptObject named MyScriptObject
%myObject = new ScriptObject()
{
class = "Messenger";
};
%myObject.printMessage(%message);
}
Currently, that last line of code will produce a warning in the console. The warning will state that the method could not be found. It needs to be implemented first. Right after the helloWorld
function, add the following:
// Object method called from a ScriptObject with a class of "Messenger"
function Messenger::printMessage(%this, %message)
{
echo(%message);
}
The above code implements the printMessage
object method for the Messenger
namespace. If you remember, the class field for the new object was set to Messenger
. Let's recap everything so far.
- You created a script
- You loaded it into Torque using
exec("./myScript.cs");
- You added the global
helloWorld
function, which takes in a single argument - In that function, you created a new
ScriptObject
with a class ofMessenger
- You added an object method to the
Messenger
namespace, calledprintMessage
- When you call
helloWorld("Hello World");
, that text is sent to the global function, which is then passed into%myObject.printMessage(%message);
.
That's a very simple chain that takes longer to explain than it does to code and execute. There is another type of function that can also be utilized when extending an object. That would be a callback function.
Callbacks are functions that will be called as soon as a certain condition is met. Basically, a trigger occurs that will result in a function being called on an object. The C++ code contains many callback executions. One of the most common is the onAdd
callback. If this function is written for the object in TorqueScript, it will be executed when the object has been successfully created.
What happens in that function is up to you. It's completely your call on what the callback should do. Here is an example of a callback being implemented on the Messenger
class:
// Callback invoked when a "Messenger" has been added to the simulation
function Messenger::onAdd(%this)
{
// Get this object's unique ID
%id = %this.getId();
// Concatenate the ID with a string
%onAddMessage = %id SPC "was added";
// Print the onAdd message
echo(%onAddMessage);
}
You can add the above code to your myScript.cs script. When your object is created in helloWorld
, that ::onAdd function will be called. The body of the function simply gets the ID of the created object, concatenates it with the string "was added", and prints the message to the console.
Isolating code to packages.
Finally...