TorqueScript/Datablocks

From TDN

Contents

Introduction to Datablocks

Overview

Conceptually, a datablock is a construct that provides a central place to store information, such that other objects can refer to the datablock and share that information. A typical example is that a datablock will hold the filename for an art asset (let's say, a 3D model of a plane), then when we want to create a new plane in the game world, we instantiate the new plane object with a reference to the datablock. If we create multiple planes, we can have them all share the same datablock, and therefore all share the same 3D model.

Datablocks provide a convenient mechanism for sharing information amongst objects of the same type, and also provide performance benefits because the datablock only needs to be loaded once for all objects sharing that datablock.

Concept

When learning about datablocks, it is good to distinguish the four constructs at work:

- Datablock Class
- Object Class
- Datablock Instance
- Object Instance

(To confuse matters, a Datablock is actually a special type of Object, but for the remainder of this discussion they will be treated as independent constructs.)

Typically, when a type of thing is made available in the game engine (let's say, a flying vehicle), there will be a Datablock Class and an Object Class exposed to the scripting engine (eg. "FlyingVehicleData" and "FlyingVehicle", respectively). The Datablock Class represents our generic type of datablock, from which we can create one or more specific Datablock Instances. Similarly, the Object Class represents our generic type of object, from which we can create one or more Object Instances. Datablock Classes and Object Classes are provided by the game engine, but we create Datablock Instances and Object Instances in our game's scripting code.

(Note, it is common practice for the name of a Datablock Class to share the same name as the corresponding Object Class, but with the suffix "Data".)

In our simple flying vehicle example, the important distinction to remember between Datablock Instance and Object Instance is that only the Object Instance will create an actual flying vehicle in the game world. Creating a Datablock Instance provides just a central repositry of information for a specific type of flying vehicle.

So, let's say we are creating a flight simulator. We want three types of aircraft: An F14 Tomcat, a F117 Nighthawk, and a Mig29 Fulcrum. The player flies an F117 Nighthawk, an escort is provided in the form of two F14 Tomcats , while four Mig29 Fulcrums will attempt to intercept the player. Here we have three types of aircraft, and so we would create three Datablock Instances, one for each. In our mission we have seven individual aircraft (one F117, two F14's, and four Mig29's) and so will create seven Object Instances, one for each. For each Object Instance, we would reference back to a single Datablock Instance, where Object Instances of the same type of craft would each share the same Datablock Instance:

OBJECT INSTANCE (OBJECT CLASS) DATABLOCK INSTANCE (DATABLOCK CLASS)
F117 Nighthawk #1 (FlyingVehicle) F117 Nighthawk Datablock (FlyingVehicleData)
F14 Tomcat #1 (FlyingVehicle) F14 Tomcat Datablock (FlyingVehicleData)
F14 Tomcat #2 (FlyingVehicle) F14 Tomcat Datablock (FlyingVehicleData)
Mig29 Fulcrum #1 (FlyingVehicle) Mig29Fulcrum Datablock (FlyingVehicleData)
Mig29 Fulcrum #2 (FlyingVehicle) Mig29Fulcrum Datablock (FlyingVehicleData)
Mig29 Fulcrum #3 (FlyingVehicle) Mig29Fulcrum Datablock (FlyingVehicleData)
Mig29 Fulcrum #4 (FlyingVehicle) Mig29Fulcrum Datablock (FlyingVehicleData)

Hopefully, you now understand the concept of datablocks and how they relate to objects. Next we look at how datablocks are used in scripts.

Usage

In it's simplest form, this is how to create a datablock, and then create a world object that uses that datablock:

  // create the datablock
  %myNighthawkDatablock = datablock FlyingVehicleData() {
     category = "Vehicles";
     shapeFile = "~/data/shapes/vehicles/aircraft_f117/aircraft_f117.dts";
     mass = 60;
     
     //... more fields describing this type of aircraft.
  };
  
  // create the object
     %obj = new FlyingVehicle() {
     datablock = %myNighthawkDatablock;
  };

In this code there are two steps. First, we instantiate a new FlyingVehicleData datablock by using the special keyword "datablock". This creates a Datablock Instance, and assigns it to the %myNighthawkDatablock variable. Inside the curly braces we specify values for various fields available within the FlyingVehicleData datablock, such as what model file to use, and the mass of this type of aircraft.

Then we instantiate a FlyingVehicle object using the special keyword "new". This creates an Object Instance, and assigns it to the %obj variable. Inside the curly braces we specify which datablock this object should be associated with, so we set this to be the %myNighthawkDatablock variable.

Note that in both cases, after the closing curly brace we place a semi-colon.

The above example is not based on a real world example, and is not how you would see datablocks used in typical game scripts. The example is presented to make it clear what's happening when datablocks are created, and how to associate them with an object.

In the real world, datablocks are used more like this:

  // create a Datablock Instance, assign it the object name F117NighthawkBlock
  datablock FlyingVehicleData(F117NighthawkBlock)
  {
     category = "Vehicles";
     shapeFile = "~/data/shapes/vehicles/aircraft_f117/aircraft_f117.dts";
     mass = 60;
  
     //... more fields describing this type of aircraft.
  };
  
  // create a Datablock Instance, assign it the object name F14TomcatBlock
  datablock FlyingVehicleData(F14TomcatBlock)
  {
     category = "Vehicles";
     shapeFile = "~/data/shapes/vehicles/aircraft_f14/aircraft_f14.dts";
     mass = 50;
  
     //... more fields describing this type of aircraft.
  };
  
  // provide a generic function for creating FlyingVehicle's of any datablock type
  function FlyingVehicleData::create(%block)
  {
     %obj = new FlyingVehicle() {
        datablock = %block;
     };
     return %obj;
  }
  
  // create our mission objects
  function CreateMissionObjects(%f117SpawnPoint, %f14SpawnPoint1, %f14SpawnPoint2)
  {
     %obj = FlyingVehicleData::create(F117NighthawkBlock);
     MissionCleanup.add(%obj);
     %obj.setTransform(%f117SpawnPoint);
  
     %obj = FlyingVehicleData::create(F14TomcatBlock);
     MissionCleanup.add(%obj);
     %obj.setTransform(%f14SpawnPoint1);
  
     %obj = FlyingVehicleData::create(F14TomcatBlock);
     MissionCleanup.add(%obj);
     %obj.setTransform(%f14SpawnPoint2);
  }

In this example, we create two Datablock Instances and assign them each a unique Object Name. The Object Name gives us an easy way to refer to a Datablock Instance from anywhere in the script code, which is very useful for datablocks. You can name it anything you like, but it is useful to attach the word "Block", or "DB", or something else to clearly identify it as a Datablock Instance. [TODO: provide a useful link describing how name identifiers work]

Then we provide a function that creates FlyingVehicle Object Instances with any datablock we like. Note that the create() function is not a member function of the FlyingVehicleData datablock. It is what is often called a Static Function, we provide the FlyingVehicleData:: namespace qualifier for convenience only. [TODO: provide a useful link to namespaces and member functions]

Then we are free to create our mission objects at will. Typically this happens at the start of a new mission. In the CreateMissionObjects() function we create one Nighthawk, and two Tomcats. For each new object, we add it to the MissionCleanup list, so that the objects will be removed when the current mission finishes. Then we set the starting position for each object by specifying spawn points. [TODO: provide useful link to creating game world objects]

Datablock Inheritence

[TODO: provide explanation of how inheritence works with datablocks, and also how the inherency function hierarchy is formed when creating objects]

Datablocks and the Mission Editor

To see and use Datablocks, you must be in the World Inspector mode.

[TODO: provide description of how datablocks appear in the Mission Editor, and how to use them]

Why Datablocks?

The primary reason for storing large amounts of static data in datablocks is actually part of the network design and strategy for overcoming some of the challenges of games over the Internet (see Torque Networking). The primary concept is that we group all of the static data that won't be changing over the duration of the game into a block that can be referenced very simply, and transmit that data to the clients at the beginning of the game before action has started. This way, the client won't get game interruptions while the network layer handles a lot of data being transmitted all at once.

Examples

Check out the datablocks related to DTS shapes such as the player, vehicles and weapons.
DTS Scripting Guide