Book Image

Torque 3D Game Development Cookbook

By : DAVID WYAND
Book Image

Torque 3D Game Development Cookbook

By: DAVID WYAND

Overview of this book

Torque 3D is a popular game engine that supports you in every step along the way to making your game a reality. Even with all of the power and tools that Torque 3D provides, finishing a high quality 3D game requires time and knowledge."Torque 3D Game Development Cookbook" is a practical guide that takes you through each of the major steps on the journey to creating your game, while learning a few tricks along the way.The recipes in this book start off with learning some of the finer points about TorqueScript. The book then moves on to each of Torque 3D's subsystems and ends with a variety of game play recipes.The various topics covered include activating level-specific game code and scheduling game events, dragging and dropping items between windows to work with an in-game inventory system, and covering the seams between objects with well placed decals. Some of the advanced topics include writing custom shaders and postFX, using zones to improve rendering performance, and enhancing your game's ambience through sound.Once you are done with Torque 3D Game Development Cookbook you'll be on your way to creating amazing 3D games and gain expert knowledge of Torque 3D.
Table of Contents (17 chapters)
Torque 3D Game Development Cookbook
Credits
About the Author
About the Reviewers
www.PacktPub.com
Preface
Index

Extending a SimObject instance using the class property


TorqueScript allows us to extend the script methods of a SimObject derived class through the use of the class property. This is a very powerful feature as we can modify the behavior of a SimObject instance without the need to change its source code. In this recipe, we will learn how to make use of the class property to extend the methods available to a SimObject instance.

Getting ready

We will be adding a new TorqueScript function to a project based on the Torque 3D Full template and try it out using the Empty Terrain level. If you haven't already, use the Torque Project Manager (Project Manager.exe) to create a new project from the Full template. It will be found under the My Projects directory. Then start up your favorite script editor, such as Torsion, and let's get going!

How to do it...

We are going to write a TorqueScript function that will demonstrate how to extend a SimObject instance using its class property as follows:

  1. Open the game/scripts/server/game.cs script file and add the following code to the bottom:

    function useClassProperty1()
    {
       // Create a ScriptObject and define its class property.
       // The ScriptObject class is just a generic SimObject that
       // we can create in TorqueScript.
       new ScriptObject(MyScriptObj)
          {
             class = MyExtensionClass1;
          };
       
       // Call the first method defined by the new class
       %result = MyScriptObj.addValues(2, 3);
       
       // Output the result to the console
       echo("addValues(2, 3) returned: " @ %result);
       
       // Call the second method defined by the new class
       MyScriptObj.newMethod();
       
       // Clean up our object
       MyScriptObj.delete();
    }
    
    // First method defined by our new class
    function MyExtensionClass1::addValues(%this, %param1, %param2)
    {
       return %param1 + %param2;
    }
    
    // Second method defined by our new class
    function MyExtensionClass1::newMethod(%this)
    {
       // Get the top level C++ class this object derives from.
       %objClass = %this.getClassName();
       
       // Output to the console
       echo(%objClass SPC %this.getId() 
            @ " is using the MyExtensionClass1 class");
    }
  2. Start up our game under the My Projects directory and load the Empty Terrain level. Open the console using the tilde (~) key and enter the following at the bottom of the screen:

    useClassProperty1();
    

    In the console we will see the following output:

    ==>useClassProperty1();
    addValues(2, 3) returned: 5
    ScriptObject 4152 is using the MyExtensionClass1 class
    

How it works...

In the example code we create a ScriptObject instance and set its class property to MyExtensionClass1. This extends the namespace of the ScriptObject instances to include all the methods that are defined by this new class. We then create two methods for this new class: addValues() and newMethod(). The first method takes two parameters, adds them together, and returns the result. The second method takes no parameters and just outputs some information to the console.

Making use of these new methods is straight forward. We just call them on the object like any other method. The Torque 3D engine takes care of everything for us.

There's more...

There is a lot more to know about working with the class property and extending the namespace of a SimObject instance. Let's take a look at these points.

Working with a class namespace after object creation

Object creation is not the only time we can extend the methods of a SimObject instance. We can either set the class property of the object directly or use the setClassNamespace() method to extend a SimObject instance at any time. The setClassNamespace() method has the following form:

SimObject.setClassNamespace( name );

Here the name parameter is the equivalent value passed-in to the class property. This also allows us to modify the class namespace hierarchy of a SimObject instance after having already set the class property to a different value. We can even clear the class namespace of a SimObject instance by passing-in an empty string.

If we want to know whether a SimObject instance is making use of a particular class namespace, we can use the isInNamespaceHierarchy() method. This method searches through the entire namespace tree of the object and has the following form:

result = SimObject.isInNamespaceHierarchy( name );

Here the name parameter is the namespace to search for, and the result is either true or false depending on whether the SimObject instance is making use of the given namespace or not. This is different from the isMemberOfClass() method, which only tests if the object is an instance of the given C++ class.

Extending even further with the superClass property

We can add a second layer of new methods through setting the superClass property of a SimObject instance. By using this property, we insert another set of methods between the C++ class of the object and the class namespace set with the class property.

It is not very common to make use of the superClass property, and sometimes it can be confusing understanding where each method call is routed to. But this extra namespace layer is available to those advanced users if they need it.

As with the class property, we can set the superClass instance either at the time of object creation or any time afterwards. To set the superClass namespace after object creation, we can set the superClass property directly or use the setSuperClassNamespace() method. The setSuperClassNamespace() method has the following form:

SimObject.setSuperClassNamespace( name );

Here the name parameter is the equivalent value passed-in to the superClass property.

Understanding the namespace hierarchy

The class and superClass properties of the SimObject instance are powerful features that allow us to extend the functionality of Torque 3D through script. However, it can be confusing to figure out where a method call will be handled. Let's walk through how this works behind the scene.

Unfortunately, the class and superClass properties have misleading names. They are not used to extend the class of a SimObject instance in a C++ sense. What they do is allow us to add new namespaces to an object when it comes to method lookup. We can insert new functionality into the namespace hierarchy of an object and intercept a method call before it is passed on to the C++ engine layer.

The namespace hierarchy of an object derived from a SimObject instance looks like the following list, with the numbers exhibiting the actual order:

  1. Optional globally unique name is used as a namespace

  2. Optional class property namespace

  3. Optional superClass property namespace

  4. Direct C++ class

  5. Parent C++ class

  6. Grandparent C++ class and so on…

So when we call a method on a SimObject instance, it is first sent to the globally unique name of the object as a namespace, if any. This allows us to write the methods that are specific to that object instance. If it is not handled at this level, the method call is passed on to the class property namespace, if any. If the method call is not handled there, it is passed on to the superClass property namespace if it has been defined. If the method call has still not been handled at this point, it then moves on to the C++ class hierarchy, where it is passed along until it reaches the SimObject class.

When a method call is handled at the previous hierarchy levels 1, 2, or 3, we can decide to continue to pass the method call along the call chain. This allows us to intercept a method call but still allows for the original functionality. To do this we use the special Parent namespace to call the method again.

It's time for an example. Add the following code to the end of the game/scripts/server/game.cs script file:

function useClassProperty2()
{
   // Create a ScriptObject and define its class property.
   // The ScriptObject class is just a generic SimObject that
   // we can create in TorqueScript.
   new ScriptObject(MyScriptObj)
      {
         class = MyExtensionClass2;
      };
   
   // Get the name of our ScriptObject.  Normally this
   // would just return our globally unique name, but
   // our class namespace will change how this method
   // works.
   %result = MyScriptObj.getName();
   
   // Output the result the console
   echo("getName() returned: '" @ %result @ "'");
   
   // Clean up our object
   MyScriptObj.delete();
}

function MyExtensionClass2::getName(%this)
{
   // Call the parent method and obtain its result
   %result = Parent::getName(%this);
   
   // Return our modified result
   return "Our name is: " @ %result;
}

Now start up our game under the My Projects directory and load the Empty Terrain level. Open the console using the tilde (~) key and enter the following at the bottom of the screen:

useClassProperty2();

In the console we will see the following output:

==>useClassProperty2();
getName() returned: 'Our name is: MyScriptObj'

The MyExtensionClass2 namespace defines the getName() method and the ScriptObject instance is making use of this namespace by setting its class property. When we call the getName() method of the object, as expected the MyExtensionClass2 intercepts it.

Within MyExtensionClass2::getName() the original getName() method is called with the static Parent namespace. This is done with the following code:

   // Call the parent method and obtain its result
   %result = Parent::getName(%this);

As this is a static call we need to manually include our object with the method call. Here this is done by passing in the %this variable as the first parameter to getName(). This Parent call eventually makes its way to the C++ SimObject class, where the globally unique name of the object is retrieved and passed into the %result variable. MyExtensionClass2::getName() uses this result for its own work and passes everything back to the caller.

Limitations of the class property

A limitation of using the class property is that once a class namespace has been assigned to a particular C++ class, it may only be used by that C++ class from then on.

For example, we had assigned the MyExtensionClass1 class namespace to the class property of a ScriptObject instance at the beginning of this recipe. From that point onwards, we can only use the MyExtensionClass1 class namespace with another ScriptObject instance. If we were to try to assign the same class namespace to a Player class instance, we would get an error in the console and the assignment would not occur.

The superClass property does not have this limitation. You may reuse a namespace between different C++ classes, so long as you limit its use to the superClass property.

See also

  • Creating a new SimObject instance

  • Creating a new internal name only SimObject instance