Skip to content

Scripting Tutorial

Syrup84 edited this page Mar 11, 2014 · 32 revisions

This guide will take you through the process of creating a basic script for Torque 2D. It will not cover all facets of TorqueScript capabilities or syntax. This doc mainly shows you the first steps to creating a new script, adding code, and getting it to run in 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)

Table of Contents

Chapter 1. Script Definition

Chapter 2. Creating a script

Chapter 3. Executing a script

Chapter 4. Global functions

Chapter 5. Script scope

Chapter 6. Extending objects

Chapter 7. Object method

Chapter 8. Callbacks

Chapter 9. Packages

Chapter 10. Conclusion

Chapter 1. Script Definition

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.

Chapter 2. Creating a script

- Windows: Explorer -

Creating a new TorqueScript file on Windows, without a tool, is as simple as creating an new text document:

Step 1: Pick the directory you wish to create the file. In this tutorial, I chose Torque2D/modules/SpriteToy/1/.

Step 1

Step 2: Create a new text document (.txt). I just right click->New->Text Document.

Step 2

Step 3: When given the option, or after it has been created, change the name of the script file. What's important is that the extension is changed from .txt to .cs. In this example, I named it myScript.cs

Step 3

- Windows: Torsion -

If you are using Torsion, you can use its interface to create a script file. This can be done in three simple steps:

Step 1: In the project tab on the left, choose the folder where you want the new script. Here, I chose Torque2D/modules/SpriteToy/1/

Step 1

Step 2: Right click on the folder. When context menu pops up, select New Script

Step 2

Step 3: Name your script. In this tutorial, I chose newScript.cs

Step 3

- OS X -

One of the fastest ways to create a new script on OS X is to just use the Finder to copy and edit an existing script. If you are using something like AppCode or Xcode, you can later add templates that make this process easier. For now, you can just copy a script we already provided:

Step 1: Find a script to copy. You will delete all the contents later, so just pick something easily accessible. In this tutorial, I picked the root main.cs

Step 1

Step 2: Duplicate the main.cs file, which should give you a file called main copy.cs

Step 2

Step 3: Rename the duplicate file to what you want your script to be called. In this tutorial, I use myScript.cs

Step 3

*** Step 4: Choose the new location where you will be copying your script. In this tutorial, I used Torque2D/modules/SpriteToy/1/.***

Step 4

*** Step 5: Copy or move your new script file to the location you chose. It should look like this when you are finished.***

Step 5

Chapter 3. Executing a script

For the rest of this tutorial, I will be using Torsion on Windows. What you choose to edit with is up to you. There is a list of suggestions at the top of this document.

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.

Chapter 4. Global functions

Much of your TorqueScript experience will come down to calling existing functions and writing your own. Functions are 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:

  1. Proper TorqueScript syntax
  2. A global function structure
  3. Commenting
  4. 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.

Chapter 5. Script 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. A global variable's 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.

Chapter 6. Extending Objects

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, no 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.

Chapter 7. Object method

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.

  1. You created a script
  2. You loaded it into Torque using exec("./myScript.cs");
  3. You added the global helloWorld function, which takes in a single argument
  4. In that function, you created a new ScriptObject with a class of Messenger
  5. You added an object method to the Messenger namespace, called printMessage
  6. 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.

Chapter 8. Callbacks

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.

There are many other callbacks written in the source code. If you perform a search using in an IDE (like Visual Studio or AppCode), look for Con::executef. The search results should give you a full list of callbacks and what they are attached to.

Chapter 9. Packages

One final topic we will cover is the concept of script packages. You can create one using the keyword package. The purpose of this encapsulate functions inside of a structure that must be activated before use. The following example moves the Messenger::onAdd function into a package:

package PackageOne
{
    // 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);
    }
};

The Messenger::onAdd function will no longer be called when the object is created. This is because the package must be activated. If you switch back to the script where you execute the script file, you can make the following change:

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");

    // Activate the first package
    activatePackage( PackageOne );

    // Call the new function
    helloWorld("Hello World");

    // ...rest of function is unchanged
}

Notice that activatePackage must be called before the helloWorld function. Otherwise, the Messenger::onAdd function technically does not exist and will not be called. The true power of this system comes to light when you have multiple packages that can be toggled at will. Add the following PackageTwo:

package PackageOne
{
    // 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);
    }
};

package PackageTwo
{
    // Callback invoked when a "Messenger" has been added to the simulation
    function Messenger::onAdd(%this)
    {
        // Do nothing
    }
};

Notice the Messenger::onAdd function in PackageTwo does nothing at all. You will never want multiple packages activated if they are overriding the same functions. Instead, use activatePackage and deactivatePackage to toggle them. Add the following code to the script where you activated the first package:

function SpriteToy::create( %this )
{
    // Load the script
    exec("./myScript.cs");

    // Enable PackageOne
    activatePackage( PackageOne );
    
    // Call the new function
    helloWorld("Hello World");
    
    // Disable PackageOne
    deactivatePackage( PackageOne );
    
    // Enable PackageTwo
    activatePackage( PackageTwo );
    
    // Call the function again
    helloWorld("Hello World");

    // ...rest of function is unchanged
}

Now, when you call helloWorld the first time, you will get a message letting you know that a Messenger object has been added. When that is deactivated, then PackageTwo is activated, you will not get the onAdd message when helloWorld is called again.

Chapter 10. Conclusion

As with all code, you should always try to plan out what you are trying to achieve ahead of time. Try to figure out what object methods you will need, when its appropriate to use a global function, whether or not you need packages, and so on. This tutorial was a very brief peek at TorqueScript, but hopefully it was enough to get you started on writing your own functions. Remember to refer back to the TorqueScript Overview and TorqueScript Syntax guides when you need in depth coverage of the language.