Game Development Community

T3D torquescript performance

by Justin Knight · in Torque 3D Professional · 01/18/2010 (7:46 am) · 9 replies

I have a simple ai to fly a flock of birds around and I'm getting very jerky performance. I get 30 fps, but the bird's positions are not being updated smoothly. You can see a short video clip here:
http://www.youtube.com/watch?v=75U3zWJoJF8

My system is currently Vista 64, Intel Quad Core Q6600, GeForce GTX 260, 1GB RAM.

I initially tried using setVelocity rather than manually changing the position information but that didn't seem to have any effect. I guess setVelocity doesn't work on StaticShape?

I've made the update interval as long as I think I can make it (100ms).

Is torquescript just inefficient and I should rewrite in C++, or is there a way I can make this more efficient?

Are there any profiling tools available which can help find performance bottlenecks?

Here's my code:-
// Bird AI
// @TODO prevent going out of bounds
$state = 0; // 1=fly 2=circle
$updateIntervalMS = 100; // How often to update position in ms
$flapping = true;
$flyDelta = 0.4;
$angleDelta = 0.0;
$flockSpacing = 2.5;
$flockSize = 9;

function startAI( %bird) {
   $flock[0] = %bird;
   spawnFlock();
   updateFlock();
   for( %i=0; %i<$flockSize; %i++) 
      $flock[%i].playThread( 0, "ambient");   
   decideState();  // Decide initial state.
   scheduleFlap();
}

function spawnFlock() {
   for( %i=1; %i<$flockSize; %i++) { 
      $flock[%i] = new StaticShape() {
         dataBlock = "BirdDB";
         position = "0 0 0";
         rotation = "0 0 1 0";
         scale = "1 1 1";
      };   
      MissionCleanup.add( $flock[%i]);      
   }  
}

// Decide what to do next
function decideState() {
   //$state = getRandom( 1, 2);
   // Just fly forwards for now
   $state = 1;
   
   
   // Initialise chosen state.
   switch( $state) {
      case 1: // Fly
         // nothing to initialise
      case 2: // Circle
         $angleDelta = getRandom( 1, 7) / 1000.0;        
   }

   // Schedule first update
   scheduleNext();   
   
   // Schedule next decision
   %decisionInterval = getRandom( 5000, 20000);
   schedule( %decisionInterval,0, decideState);      
}

function scheduleFlap() {
   if( isObject($flock[0])) {   
      $flapping = ! $flapping;
      if( $flapping) {
         for( %i=0; %i<$flockSize; %i++) 
            $flock[%i].playThread( 0, "ambient");   
         %flapInterval = getRandom( 5000, 20000); 
      } else {
         for( %i=0; %i<$flockSize; %i++) 
            $flock[%i].stopThread( 0);   
         %flapInterval = getRandom( 1000, 2000); 
      }
         
           
      schedule( %flapInterval, 0, scheduleFlap);
   }
}

function scheduleNext() {
   if( isObject($flock[0])) {   
      switch( $state) {
         case 1:
            schedule( $updateIntervalMS,0, updateBirdFly);                        
         case 2:
            schedule( $updateIntervalMS,0, updateBirdCircle);      
      }  
   }
}

function isOdd( %i) {
  return( (%i % 2)!=0);  
}

// Update flock position relative to lead bird
function updateFlock() {
  %forward = $flock[0].getForwardVector();
  %forward = VectorScale( %forward, $flockSpacing);
  
  %left = (-%forward.Y) SPC %forward.X SPC 0;
  %right = %forward.Y SPC (-%forward.X) SPC 0;
  %pbird = $flock[0].getPosition();
  for( %i=1; %i<$flockSize; %i++) {
    if( isOdd(%i)) {
      %scale = $flockSpacing * (%i+1.0) / 2.0;
      %sideOffset = VectorScale( %left, %scale);      
      %backOffset = VectorScale( %forward, %scale);
    } else {
      %scale = $flockSpacing * %i / 2.0;      
      %sideOffset = VectorScale( %right, %scale);      
      %backOffset = VectorScale( %forward, %scale);
    }
    
    %position = VectorSub( %pbird, VectorAdd( %backOffset, %sideOffset));
    %tBird = $flock[0].getTransform();
    %transform = %position SPC getWord( %tBird, 3) SPC getWord( %tBird, 4) SPC getWord( %tBird, 5) SPC getWord( %tBird, 6);
    $flock[%i].setTransform( %transform);
  }
}

// Flying
function updateBirdFly() {
  if( isObject($flock[0]) && $state==1) {   
    // Move forward by $flyDelta      
    for( %i=0; %i<$flockSize; %i++) {
      %forward = $flock[%i].getForwardVector();
      %tbird = $flock[%i].getTransform();
      %tbird.X += %forward.X * $flyDelta;
      %tbird.Y += %forward.Y * $flyDelta;
      $flock[%i].setTransform( %tBird);         
    }
    scheduleNext();      
   }
}

#1
01/18/2010 (7:58 am)
TomB provided some pointers on irc

Quote:
[11:40] <TomB> scripts are always compiled regardless so compiling to dso just cuts out the time it takes to compile, which will only have an effect on performance when you exec the script
[11:41] <nev7n> thanks TomB - guess I need to find another way to improve my script's performance
[11:41] <TomB> redo it in C++
[11:41] <TomB> updating the position in scirpt using schedule will only result in jerky updates
[11:42] <nev7n> is there a C++ equivalent of schedule, or is there some kind of gameloop you hook into?
[11:45] <TomB> well part of the problem is the way you're going about implementing it
[11:45] <TomB> redoing it in C++ in exactly the same way you've done it in script will always result in jerkyness
[11:45] <nev7n> I guessed that might be the case
[11:46] <nev7n> it's pretty much my first torque code so it's as much a learning exercise as anything else
[11:47] <TomB> i dont know how to explain how to do it in a way you'd understand
[11:47] <TomB> without having to also explain how torque networking and game objects work
[11:48] <TomB> id suggest looking into that, particularly processTick() and interpolateTick()
[11:48] <TomB> and implement flocking in a netcode friendly manner
[11:48] <nev7n> OK thanks - I'll look into those
[11:49] <TomB> the issue with setting the position manually is that the object's interpolated position is a lot different to the one you're setting and so the game warps the object to the new position
[11:50] <TomB> so for it to not be jerky you have to implement it properly as part of a game object so the positions are interpolated correctly
[11:50] <nev7n> I initially tried using setVelocity but it didn't seem to have any effect
[11:50] <nev7n> I guess setVelocity isn't implemented on staticShape?
[11:50] <TomB> this is not something you can do in script and get a good result
[11:51] <TomB> staticshape is by it's very definition static
[11:51] <TomB> it doesnt move at all
[11:51] <TomB> so if you're using static shape you're probably using the wrong thing anwyay
[11:51] <nev7n> hehe probably - I used that because I need to animate it
[11:52] <TomB> everything that uses a DTS shape supports animation
[11:52] <TomB> except maybe TSStatic
[11:52] <TomB> i forget
[11:53] <nev7n> I don't think TSStatic does
[11:56] <nev7n> maybe I should be using rigidshape
[11:56] <TomB> if i were you i'd make a new object for it
[11:56] <TomB> nothing existing really fits
[11:57] <TomB> that way you can also handle the whoel flock with one object
[11:57] <TomB> with each individual bird treated almost like a DTS particle
[11:57] <nev7n> yes that would make sense
[11:57] <TomB> which will mean you can interpolate the whoel flock in one go based on the lead bird
[11:58] <TomB> it is a good project for learning how torque works
[11:58] <nev7n> thanks TomB - that's been very helpful
#2
01/18/2010 (8:54 am)
TorqueScript is indeed really slow. Been working some with the VM lately and from the viewpoint of performance, it is horribly written. Example: stack frames are full hashtables instead of arrays pre-layouted by the compiler.

I have tackled a few glaring things for 1.1 Beta (like the tremendous amount of dynamic memory allocation the script evaluator used to perform) but given that TorqueScript's lifetime is probably slowly coming to a close, more significant overhauls will probably not be worth the investment.

//Offtopic:

I've also added two new statements to TorqueScript:

foreach( %obj in $MySimSet ) doSomethingWith( %obj );

and

foreach$( %str in "my string vector" ) doSomethingWith( %str );

Aside from being convenient, they also perform better. Especially the string version is significantly faster in extracting strings than using getWord(%str,i).
#3
01/18/2010 (10:54 am)
Quote: ...given that TorqueScript's lifetime is probably slowly coming to a close ...

??? Can you explain this a bit Rene?

#4
01/18/2010 (10:59 am)

Just general observation. Everyone these days seems to be moving towards graphical scripting and just generally away from proprietary scripting languages and custom bytecode VMs.
#5
01/18/2010 (11:56 am)
Ok so never planned yet... :-)

I agree and, beside the fact that what you learn in a language generally can be usefull when you start to learn another language, the possibility to reuse knowledge of a standard scripting language in many context is awesome... For example using knoledge of js achieved in web-dev for game-dev...
#6
01/18/2010 (12:02 pm)
I'd expect that most scripting languages have a fair bit in common (C style syntax). I actually chose to work with TGE because TorqueScript seemed like a less user-friendly version of Game Script Code that I'd become familiar with whilst modding CoD.
#7
01/18/2010 (12:21 pm)

Yep, they do. And personally, I like TorqueScript quite a bit. Mostly, it's pretty fun and easy to work with and architecture of Torque that goes with it is a great thing rather.

However, as with many engine components today, it often just doesn't make sense to not move over to dedicated middleware and utilize third-party tech. Specialized packages usually see a level of investment and development that is really hard to outmatch with custom built components. Plus justifying the development effort is difficult when there are other, often higher priority parts calling for attention.

But ok, enough of my dangerously close to off-topic musings here.
#8
01/18/2010 (2:19 pm)
Justin-

I've been where you are now. You can achieve what you wan by using the item class. They are perfect objects to use if you want a simple object(non-player,non-vehicle) which moves using setvelocity. Item class also has a cool property called "gravitymod" which if set to zero will disable gravity for the object creating a weightless, frictionless object.

IMO, the item class is the best most flexible object class in torque.
#9
01/19/2010 (7:50 am)
@Sean: Thanks the item class did indeed do the job.

Anything more complicated and I'd probably create my own Object Class to handle it.

@Rene: I've grown to like foreach from doing lots of php, that will be a nice addition.