Game Development Community

Performance Issues

by Alex Poli · in Torque 2D Beginner · 05/23/2013 (7:33 pm) · 8 replies

Hello, recently I have been working on implementing a simple, fake 3D effect in t2d which works by updating the position of sprites in relation to the player and an input height value. I have it all working, but when I add more than about three of these objects the game slows down considerably. This is probably due to my inexperience with this kind of programming and the engine, so I was hoping you guys might have some pointers on improving the efficiency of what I am trying to do. What I am thinking of doing now is implementing the math-heavy functions into the engine itself as C++ should be faster than torquescript. I wanted to run my implementation by you guys first though because it might just be slow because I am doing it stupidly.

Below is my code for cuboid and cylinder objects as well as the update functions. I think the problem is with how I am performing the updates to the objects.

Cuboid:
function LightModule::createCuboid(%this, %images, %p3dHeight, %position, %scale, %heightOffset){
   
   //Bottom
   %bottom = new Sprite(){
      //custom
      Name = "Bottom";
      Origin = %position;
      Height = %heightOffset;
      Scale = %scale / 100;
      
      //standard
      Position = %position;
      class = "Cuboid";
      Image = "LightModule:"@getWord(%images, 0);
      SceneLayer = 2;
      UpdateCallback = true;
   };
   
   //Side 1
   %side1 = new Sprite(){
      //custom
      Name = "Side1";
      Parent = %bottom;
      
      //standard
      Position = %position;
      class = "Cuboid";
      Image = "LightModule:"@getWord(%images, 2);
      SceneLayer = 2;
   };
   
   //Side 2
   %side2 = new Sprite(){
      //custom
      Name = "Side2";
      Parent = %bottom;
      
      //standard
      Position = %position;
      class = "Cuboid";
      Image = "LightModule:"@getWord(%images, 3);
      SceneLayer = 2;
   };
   
   //Side 3
   %side3 = new Sprite(){
      //custom
      Name = "Side3";
      Parent = %bottom;
      
      //standard
      Position = %position;
      class = "Cuboid";
      Image = "LightModule:"@getWord(%images, 4);
      SceneLayer = 2;
   };
   
   //Side 4
   %side4 = new Sprite(){
      //custom
      Name = "Side4";
      Parent = %bottom;
      
      //standard
      Position = %position;
      class = "Cuboid";
      Image = "LightModule:"@getWord(%images, 5);
      SceneLayer = 2;
   };
   
   //Top
   %top = new Sprite(){
      //custom
      Name = "Top";
      Height = %heightOffset + %p3dHeight;
      Parent = %side1;
      
      //standard
      Position = %position;
      class = "Cuboid";
      Image = "LightModule:"@getWord(%images, 1);
      SceneLayer = 2;
   };
   
   //add objects to scene
   myScene.add(%bottom);
   myScene.add(%side1);
   myScene.add(%side2);
   myScene.add(%side3);
   myScene.add(%side4);
   myScene.add(%top);
   
}

Cylinder:
//Create a new cylinder with the designated properties
function LightModule::createCylinder(%this, %images, %p3dHeight, %position, %scale, %heightOffset){
   
   %bottom = new Sprite(){
      //custom
      Name = "Bottom";
      Origin = %position;
      Height = %heightOffset;
      Scale = %scale / 100;
      
      //standard
      Position = %position;
      class = "Cylinder";
      Image = "LightModule:"@getWord(%images, 0);
      SceneLayer = 2;
      UpdateCallback = true;
   };
   
   %middle = new Sprite(){
      //custom
      Name = "Middle";
      Parent = %bottom;
      
      //standard
      Position = %position;
      class = "Cylinder";
      Image = "LightModule:"@getWord(%images, 1);
      SceneLayer = 2;
      //UpdateCallback = true;
   };
   
   %top = new Sprite(){
      //custom
      Name = "Top";
      Height = %heightOffset + %p3dHeight;
      Parent = %middle;
      
      //standard
      Position = %position;
      class = "Cylinder";
      Image = "LightModule:"@getWord(%images, 2);
      SceneLayer = 2;
      //UpdateCallback = true;
   };
   
   myScene.add(%bottom);
   myScene.add(%middle);
   myScene.add(%top);
}

Update functions:
//==============================================================================
//update pseudo-3d effect for all sprite objects
//==============================================================================
function Sprite::onUpdate(){
   updateObjects();
}

//==============================================================================
//handle movement and scale of pseudo-3d shapes
//==============================================================================
function updateObjects(){
   
   //counters
   %numberOfCylinders = 0;
   %numBottomCylinder = 0;
   %numTopCylinder = 0;
   %numMiddleCylinder = 0;
   
   %numberOfCuboids = 0;
   %numBottomCuboid = 0;
   %numTopCuboid = 0;
   %numSide1 = 0;
   %numSide2 = 0;
   %numSide3 = 0;
   %numSide4 = 0;
   
   //Get a list of all primitives
   for(%i = 0; %i < myScene.getCount(); %i++){
      %sceneObject = getWord(myScene.getSceneObjectList(), %i);
      
      //-------------Cylinders---------------
      if(strcmp(%sceneObject.class, "Cylinder") == 0 && strcmp(%sceneObject.Name, "Bottom") == 0){
         %bottom[%numBottomCylinder] = %sceneObject;
         %numBottomCylinder++;
         
         %numberOfCylinders++;
      }else if(strcmp(%sceneObject.class, "Cylinder") == 0 && strcmp(%sceneObject.Name, "Top") == 0){
         %top[%numTopCylinder] = %sceneObject;
         %numTopCylinder++;
      }else if(strcmp(%sceneObject.class, "Cylinder") == 0 && strcmp(%sceneObject.Name, "Middle") == 0){
         %middle[%numMiddleCylinder] = %sceneObject;
         %numMiddleCylinder++;
      }
      
      //-------------Cuboids---------------
      else if(strcmp(%sceneObject.class, "Cuboid") == 0 && strcmp(%sceneObject.Name, "Bottom") == 0){
         %bottomCuboid[%numBottomCuboid] = %sceneObject;
         %numBottomCuboid++;
         
         %numberOfCuboids++;
      }else if(strcmp(%sceneObject.class, "Cuboid") == 0 && strcmp(%sceneObject.Name, "Top") == 0){
         %topCuboid[%numTopCuboid] = %sceneObject;
         %numTopCuboid++;
      }else if(strcmp(%sceneObject.class, "Cuboid") == 0 && strcmp(%sceneObject.Name, "Side1") == 0){
         %side1[%numSide1] = %sceneObject;
         %numSide1++;
      }else if(strcmp(%sceneObject.class, "Cuboid") == 0 && strcmp(%sceneObject.Name, "Side2") == 0){
         %side2[%numSide2] = %sceneObject;
         %numSide2++;
      }else if(strcmp(%sceneObject.class, "Cuboid") == 0 && strcmp(%sceneObject.Name, "Side3") == 0){
         %side3[%numSide3] = %sceneObject;
         %numSide3++;
      }else if(strcmp(%sceneObject.class, "Cuboid") == 0 && strcmp(%sceneObject.Name, "Side4") == 0){
         %side4[%numSide4] = %sceneObject;
         %numSide4++;
      }
   }
   
   
   //------------------------------
   //------ Cylinder Operations ---
   //------------------------------
   for(%i = 0; %i < %numberOfCylinders; %i++){
      
      //---------Position---------
      //set the position of the base      
      %magnitudeBottom = (mLog(Vector2Distance(Player.Position, %bottom[%i].Origin)) / (%bottom[%i].Height) ) / %bottom[%i].Scale;
      %magnitudeTop = (mLog(Vector2Distance(Player.Position, %bottom[%i].Origin)) / ( (%top[%i].Height) ) ) / %bottom[%i].Scale;
      
      %baseX = getWord(%bottom[%i].Origin, 0);
      %baseY = getWord(%bottom[%i].Origin, 1);
      %playerX = getWord(Player.Position, 0);
      %playerY = getWord(Player.Position, 1);
      
      %bottomX = %baseX + ((%baseX - %playerX)/ %magnitudeBottom);
      %bottomY = %baseY + ((%baseY - %playerY)/ %magnitudeBottom);
      
      %bottom[%i].Position = %bottomX SPC %bottomY;
                                         
      //set the position of the top
      %topX = %bottomX + ((%bottomX - %playerX)/ %magnitudeTop);
      %topY = %bottomY + ((%bottomY - %playerY)/ %magnitudeTop);
      
      %top[%i].Position = %topX SPC %topY;
      
      
      //-----Scaling---------
      %width = getWord(mySceneWindow.getWindowExtents(), 2);
      %cameraHeight = 10;
      
      //set scale of base
      %bottom[%i].Size = %bottom[%i].Scale * ( %width / (%cameraHeight - %bottom[%i].Height ));
      
      //set scale of top
      %top[%i].Size = %bottom[%i].Scale * (%width / (%cameraHeight - %top[%i].Height));
      
      %angle = -mRadToDeg( mAtan( getWord(Player.Position, 0) - getWord(%bottom[%i].Origin, 0), getWord(Player.Position, 1) - getWord(%bottom[%i].Origin, 1) ) ) + 180;

      
      
      //------Stretching/deformation----------
      %connectonPoints = getCylinderPoints(%top[%i], %bottom[%i], %angle);
      %middle[%i].setSpritePolyCustom(%connectonPoints);
      //%top[%i].setBlendAlpha(0.5);
   }
   
   //------------------------------
   //------ Cuboid Operations -----
   //------------------------------
   for(%i = 0; %i < %numberOfCuboids; %i++){
      //---------Position---------
      //set the position of the base      
      %magnitudeBottom = (mLog(Vector2Distance(Player.Position, %bottomCuboid[%i].Origin)) / (%bottomCuboid[%i].Height) ) / %bottomCuboid[%i].Scale;
      %magnitudeTop = (mLog(Vector2Distance(Player.Position, %bottomCuboid[%i].Origin)) / ( (%topCuboid[%i].Height) ) ) / %bottomCuboid[%i].Scale;
      
      %cBaseX = getWord(%bottomCuboid[%i].Origin, 0);
      %cBaseY = getWord(%bottomCuboid[%i].Origin, 1);
      %playerX = getWord(Player.Position, 0);
      %playerY = getWord(Player.Position, 1);
      
      %cBottomX = %cBaseX + ((%cBaseX - %playerX)/ %magnitudeBottom);
      %cBottomY = %cBaseY + ((%cBaseY - %playerY)/ %magnitudeBottom);
      
      %bottomCuboid[%i].Position = %cBottomX SPC %cBottomY;
                                         
      //set the position of the top
      %cTopX = %cBottomX + ((%cBottomX - %playerX)/ %magnitudeTop);
      %cTopY = %cBottomY + ((%cBottomY - %playerY)/ %magnitudeTop);
      
      %topCuboid[%i].Position = %cTopX SPC %cTopY;
      
      
      //---------Scaling----------
      %width = getWord(mySceneWindow.getWindowExtents(), 2);
      %cameraHeight = 10;
      
      //set scale of base
      %bottomCuboid[%i].Size = %bottomCuboid[%i].Scale * ( %width / (%cameraHeight - %bottomCuboid[%i].Height ));
      
      //set scale of top
      %topCuboid[%i].Size = %bottomCuboid[%i].Scale * (%width / (%cameraHeight - %topCuboid[%i].Height));
      
      
      //---------Stretching/deformation----------
      %connectonPoints = getCuboidPoints(%topCuboid[%i], %bottomCuboid[%i]);
      
      %side1Connection =         getWord(%connectonPoints, 0) SPC getWord(%connectonPoints, 1) 
                           SPC   getWord(%connectonPoints, 2) SPC getWord(%connectonPoints, 3)
                           SPC   getWord(%connectonPoints, 10) SPC getWord(%connectonPoints, 11)
                           SPC   getWord(%connectonPoints, 8) SPC getWord(%connectonPoints, 9);
                           
      %side2Connection =         getWord(%connectonPoints, 2) SPC getWord(%connectonPoints, 3) 
                           SPC   getWord(%connectonPoints, 4) SPC getWord(%connectonPoints, 5)
                           SPC   getWord(%connectonPoints, 12) SPC getWord(%connectonPoints, 13)
                           SPC   getWord(%connectonPoints, 10) SPC getWord(%connectonPoints, 11);
                           
      %side3Connection =         getWord(%connectonPoints, 4) SPC getWord(%connectonPoints, 5) 
                           SPC   getWord(%connectonPoints, 6) SPC getWord(%connectonPoints, 7)
                           SPC   getWord(%connectonPoints, 14) SPC getWord(%connectonPoints, 15)
                           SPC   getWord(%connectonPoints, 12) SPC getWord(%connectonPoints, 13);
                           
      %side4Connection =         getWord(%connectonPoints, 6) SPC getWord(%connectonPoints, 7) 
                           SPC   getWord(%connectonPoints, 0) SPC getWord(%connectonPoints, 1)
                           SPC   getWord(%connectonPoints, 8) SPC getWord(%connectonPoints, 9)
                           SPC   getWord(%connectonPoints, 14) SPC getWord(%connectonPoints, 15);
      
      
      %side1[%i].setSpritePolyCustom(%side1Connection);
      %side2[%i].setSpritePolyCustom(%side2Connection);
      %side3[%i].setSpritePolyCustom(%side3Connection);
      %side4[%i].setSpritePolyCustom(%side4Connection);
   }
   
}

//==============================================================================
//Handle the placement of the vertices of the middle piece of cylinders
//==============================================================================
function getCylinderPoints(%top, %bottom, %angle){
   %tPoints = %top.getPosition();
   %bPoints = %bottom.getPosition();
   
   %bWidth = %bottom.getWidth();
   %bHeight = 0;//change offset from center
   %tWidth = %top.getWidth();
   %tHeight = 0;//change offset from center
   
   %bRad = 0.5 * mSqrt( (%bWidth * %bWidth) + (%bHeight * %bHeight) );
   %tRad = 0.5 * mSqrt( (%tWidth * %tWidth) + (%tHeight * %tHeight) );
   
   %bTheta0 = mAtan(%bHeight,%bWidth);
   %bTheta1 = 3.141592 - %bTheta0;
   %tTheta0 = mAtan(%tHeight,%tWidth);
   %tTheta1 = 3.141592 - %tTheta0;
   
   
   %bA = %bRad * mCos(mDegToRad(%angle) - %bTheta0) SPC %bRad * mSin(mDegToRad(%angle) - %bTheta0);
   %bB = %bRad * mCos(mDegToRad(%angle) - %bTheta1) SPC %bRad * mSin(mDegToRad(%angle) - %bTheta1);
   %tA = %tRad * mCos(mDegToRad(%angle) - %tTheta0) SPC %tRad * mSin(mDegToRad(%angle) - %tTheta0);
   %tB = %tRad * mCos(mDegToRad(%angle) - %tTheta1) SPC %tRad * mSin(mDegToRad(%angle) - %tTheta1);
   
   %bottomConnection0 = getWord(%bA, 0) + getWord(%bPoints, 0) SPC getWord(%bA, 1) + getWord(%bPoints, 1);
   %bottomConnection1 = getWord(%bB, 0) + getWord(%bPoints, 0) SPC getWord(%bB, 1) + getWord(%bPoints, 1);
   %topConnection2 = getWord(%tB, 0) + getWord(%tPoints, 0) SPC getWord(%tB, 1) + getWord(%tPoints, 1);
   %topConnection3 = getWord(%tA, 0) + getWord(%tPoints, 0) SPC getWord(%tA, 1) + getWord(%tPoints, 1);
   
   return %bottomConnection0 SPC %bottomConnection1 SPC %topConnection2 SPC %topConnection3;
}

//==============================================================================
//Find all the vertices on the top and bottom planes and return them 
//==============================================================================
function getCuboidPoints(%top, %bottom){
   %topPts = %top.getArea();
   %bottomPts = %bottom.getArea();
   
   %bp0 = getWord(%bottomPts, 0) SPC getWord(%bottomPts, 1);
   %bp1 = getWord(%bottomPts, 2) SPC getWord(%bottomPts, 1);
   %bp2 = getWord(%bottomPts, 2) SPC getWord(%bottomPts, 3);
   %bp3 = getWord(%bottomPts, 0) SPC getWord(%bottomPts, 3);
   
   %tp0 = getWord(%topPts, 0) SPC getWord(%topPts, 1);
   %tp1 = getWord(%topPts, 2) SPC getWord(%topPts, 1);
   %tp2 = getWord(%topPts, 2) SPC getWord(%topPts, 3);
   %tp3 = getWord(%topPts, 0) SPC getWord(%topPts, 3);
   
   return %bp0 SPC %bp1 SPC %bp2 SPC %bp3 SPC %tp0 SPC %tp1 SPC %tp2 SPC %tp3;
}
Note: The setSpritePolyCustom function is one I implemented into the engine myself, and has been tested as being not responsible for the slowdown I am experiencing.

#1
05/23/2013 (7:53 pm)
Ambitious! This type of math-heavy stuff is indeed way more suited to C++ implementation if you want acceptable performance.

One thing that pops to mind though : Are you running a debug build or a release build? Debug builds are extremely slow when compared to their Release counterparts.
#2
05/23/2013 (8:11 pm)
I am using a release build. I will probably start implementing my math stuff into the engine and hopefully get better performance with more objects.
#3
05/23/2013 (9:15 pm)
Simon,

It's the math. This is like 3D lite..... I would move it to C++ to speed things up.

Ron
#4
05/23/2013 (9:19 pm)
Community superstar Paul Jan has brought many math upgrades to T2D's math.
Maybe you can take a few cues from his work

You can find his repository and changes at the link below

github.com/pchan126/Torque2D
#5
05/23/2013 (9:27 pm)
Cool, thanks for the link! I will definitely check that out!
I had a feeling it was the math, but I wanted to run it by you guys before I started implementing it into the engine just in case I was handling something in a way that was inefficient.
Thanks for the help!
#6
05/23/2013 (10:17 pm)
Another thing -
strcmp(%sceneObject.class, "Cylinder")
// is equivalent to and probably slower than
if (%sceneObject.class $= "Cylinder")
// and is more cumbersome to write to boot....
#7
05/24/2013 (1:31 am)
I would be cautious about using some of my math functions through - I'm pretty sure that there are some errors in the matrix multiplication portions. The math upgrade was just the first step in the graphics update I'm still working on.
#8
05/24/2013 (7:54 am)
T3D has tested matrix math functions that you can borrow if you'd like....