Game Development Community

Help scaling rotation speed to object size

by RollerJesus · in Torque Game Builder · 02/22/2010 (10:49 am) · 5 replies

Hello all...

I'm working on this bit of code that marks an object selected by spawning a group of t2dAnimatedSprites and setting them to orbit the object using this example. The radius and number of sprites are determined by the object width or height (which ever is greater) but I'm struggling to get the speed normalized since I don't ever have the upper bounds of an object's width/height. Ideas here would be much appreciated.

function selectionMarker::onLevelLoaded( %this, %scene )
{
   echo("selectionMarker loaded.");   
}

function selectionMarker::onUpdate( %this )
{   
   %this.currentAngle += %this.angleDelta;
   %center = %this.center;
   %vector = mCos( %this.currentAngle ) SPC mSin( %this.currentAngle );
   %vector = t2dVectorScale( %vector, %this.radius );
   %vector = t2dVectorAdd( %vector, %center );
   %this.setPosition( %vector );
}

function clickableObject::onMouseUp(%this, %modifier, %worldPosition, %clicks)
{
   echo("Width of clicked object:" SPC %this.getWidth());
   
   if(!%this.isSelected)
   {     
      %this.isSelected = true;
      
      //get the radius for the selected item
      if(%this.getWidth() >= %this.getHeight())
      {
         %this.targetRadius = %this.getWidth() / 2;  
      }
      else
      {
         %this.targetRadius = %this.getHeight() / 2; 
      }
      
      //figure our how many marker symbols should be created (cast radius as an int)
      %this.numMarkers =  getSubStr(%this.targetRadius,0,strpos(%this.targetRadius, ".")); 
      
      //create the marker symbols
      for(%i = 0; %i < %this.numMarkers; %i++)
      {
         //store the animated sprites in an array so we can delete them later
         %this.markers[%i] = new t2dAnimatedSprite()
         {
            class = "selectionMarker";
            animationName = "particles1Animation";
            size = "5.000 5.000";
            currentAngle = 0 + (%i / %this.numMarkers) * (2 * 3.14);
            radius = %this.targetRadius;
            center = %this.getPosition();     
            //angleDelta needs to be scaled somehow...
            angleDelta = 0.04;
            scenegraph = %this.getSceneGraph();
         };
         
         %this.markers[%i].enableUpdateCallback();
         
         //make the markers "march"
         %this.markers[%i].setAnimationFrame(%i);
         
         //%numFrames = getWordCount(%this.markers[%i].getAnimationName().animationFrames);
      }
   }
   else
   {
      %this.isSelected = false;
   
      //delete the markers attached to this object
      for(%j = 0; %j < %this.numMarkers; %j++)
      {
         %this.markers[%j].safeDelete();
      }
   }
}

function clickableObject::onLevelLoaded(%this, %sceneGraph)
{
   echo("clickableObject loaded.");
   %this.isSelected = false;
}

Usage:
Copy the above code into a script file and make sure it's executed.
Create an object and class it as 'clickableObject'
Create an animation named particles1Animation and class it as 'selectionMarker'

#1
02/22/2010 (5:54 pm)
If I'm understanding correctly, you just want to make the speed the same, regardless of the radius?

You should just have to divide a fixed number (say, $radialSpeed) by your radius and set that as your "angleDelta".

Don't quote me on this, but I think "onUpdate" is called every 0.032 seconds. So if you set $radialSpeed to pixels/(0.032)seconds, you'll be good. Like, if you want 100 pixels/second, set $radialSpeed to 3125.

I'm going to try out your code right now to make sure that I'm right.
#2
02/22/2010 (6:20 pm)
Excellent code! I might have to borrow this for my Touch Table project. I put two different sized objects in (with the changes below) and had them touch at edges. It looked like gears meshing together. Very cool!

I think it's safe to say (after looking at the code) that onUpdate will be called for every 0.032 seconds that have passed.

Ignore my dividing by 0.032!!! (Yes, even I can make mistakes! *HEHE*)

I'd make a global variable called $markerRadialSpeed and set it to, say, 15 for 15 pixels/second.
$markerRadialSpeed = 15; // pixels per second

Then, change line 8 above to read:
// This function is called every 0.032 seconds
%this.currentAngle += %this.angleDelta * 0.032;

Then, change line 50 above to read:
angleDelta = $markerRadialSpeed / %this.targetRadius;

One small thing. If you want to cast a float to an int, it is better to use the floor, round, or ceiling functions. In this case, you probably want floor, so change line 35 above to:
%this.numMarkers = mFloor(%this.targetRadius);

Hope that helps!
#3
02/22/2010 (6:44 pm)
Awesome William! I'll give it a shot when I get at a machine with TGB on it. Of course, feel free to use the code as you wish.

Thanks for the tip on mFloor too... That's good stuff! I think round would be ideal here but as you can see I was scrambling for some way to cast to an int.

FYI...I also stressed this setup by taking out the part that deletes the markers out and letting it create new ones every click with a random radius. It took A LOT of clicks to get TGB to slow down and looked pretty sweet too!

Thanks for the help.
#4
02/22/2010 (7:03 pm)
It's good to hear that it performs fast, too!

I was playing with the code a tiny bit (it's some fun code to play with) and did the following 2 things. Take as you wish:

Simplified the radius calculation
//get the radius for the selected item
%this.targetRadius = 0.5 * t2dGetMax( %this.getWidth(), %this.getHeight() );

Inside the new sprite, I set "visible = 0;" right after the "class = ..." line. Then, before the enableUpdateCallback line, I added:
%this.markers[%i].schedule( %i * 1000 / %this.numMarkers, setVisible, true );
This causes the effect to appear over 1 second. You can easily modify the time by changing the "1000" in that line to the number of millis you want it to appear over.
#5
02/22/2010 (10:23 pm)
Very nice! I added Position = "5000 5000" where the markers are created because I was getting a flash in the middle of the scene when I clicked on an object.

Full code for posterity's sake.
$markerRadialSpeed = 15;

function selectionMarker::onLevelLoaded( %this, %scene )
{
   %this.isSelected = false;
}

function selectionMarker::onUpdate( %this )
{   
   //%this.currentAngle += %this.angleDelta;
   %this.currentAngle += %this.angleDelta * 0.032;
   %center = %this.center;
   %vector = mCos( %this.currentAngle ) SPC mSin( %this.currentAngle );
   %vector = t2dVectorScale( %vector, %this.radius );
   %vector = t2dVectorAdd( %vector, %center );
   %this.setPosition( %vector );
}

function clickableObject::onMouseUp(%this, %modifier, %worldPosition, %clicks)
{
   echo("Width of clicked object:" SPC %this.getWidth());
   
   if(!%this.isSelected)
   {     
      %this.isSelected = true;
      
      //get the radius for the selected item
      %this.targetRadius = 0.5 * t2dGetMax( %this.getWidth(), %this.getHeight() ); 
      
      //figure our how many marker symbols should be created (cast radius as an int)
      %this.numMarkers = mRound(%this.targetRadius);
      
      //create the marker symbols
      for(%i = 0; %i < %this.numMarkers; %i++)
      {
         //store the animated sprites in an array so we can delete them later
         %this.markers[%i] = new t2dAnimatedSprite()
         {
            class = "selectionMarker";
            Position = "5000 5000";
            visible = 0;
            animationName = "particles1Animation";
            size = "5.000 5.000";
            currentAngle = 0 + (%i / %this.numMarkers) * (2 * 3.14);
            radius = %this.targetRadius;
            center = %this.getPosition();     
            //angleDelta = 0.04;
            angleDelta = $markerRadialSpeed / %this.targetRadius;  
            scenegraph = %this.getSceneGraph();            
         };
         
         //make the markers appear sequenially
         %this.markers[%i].schedule( %i * 500 / %this.numMarkers, setVisible, true );
         
         %this.markers[%i].enableUpdateCallback();
         
         //make the markers "march"
         %this.markers[%i].setAnimationFrame(%i);
      }
   }
   else
   {
      %this.isSelected = false;
   
      //delete the markers attached to this object
      for(%j = 0; %j < %this.numMarkers; %j++)
      {
         %this.markers[%j].safeDelete();
      }
   }
}

function clickableObject::onLevelLoaded(%this, %sceneGraph)
{
   echo("clickableObject loaded.");
}

Thanks a lot for the help.