Game Development Community

Engine Mods (Part 1 of 2)

by William Lee Sims · in Torque Game Builder · 07/18/2012 (7:02 pm) · 2 replies

Hi all!

It's been a while since I've posted. I've been incredibly busy with my new multi-touch table project. We released a pack of 5 games and now a board-game conversion of Hansa Teutonica.

Working on Hansa Teutonica was great! We got the original art and N.R. managed to get everything to fit nicely on the screen. The game play is quick and fun. N.R.'s son, Braethun Bharathae-Lane (bbharath at unca dot edu if you're interested in contacting him), wrote an awesome music track that plays while you're in the main menu.


(Yes, I know I made a 'ts' sound a lot in this video... I was nervous.)

In the process of making these games, we've made several modifications to the engine, the most important being the integration of Windows multi-touch events and TUIO events in as Torque inputs.

I'd like to share some of the more useful modifications we've made to T2D. I'll eventually get these into the wiki, but until then, here are some changes and why you might want them for your project.

Conversions Between t2dSceneWindow Layers

As a lot of people have learned, it's really easy to build a GUI in one scene window and to place your game in another. Sometimes you need to take a item from the game and move it to a position in your GUI. Imagine collecting a gem and floating it over to the score or moving a piece from the player's controls into the game world. If your scene windows don't have the exact same camera zoom and position, it's rather difficult to get this to work.

function convertScale( %scale, %fromWindow, %toWindow )
{
  %scaleA = %fromWindow.getWindowPoint( "0 0" );
  %scaleB = %fromWindow.getWindowPoint( %scale );
  %scaleFrom = t2dVectorSub( %scaleB, %scaleA );
  
  %scaleC = %toWindow.getWorldPoint( "0 0" );
  %scaleD = %toWindow.getWorldPoint( %scaleFrom );
  return t2dVectorSub( %scaleD, %scaleC );
}

function convertPoint( %point, %fromWindow, %toWindow )
{
  %point = %fromWindow.getWindowPoint( %point );
  %point = %toWindow.getWorldPoint( %point );
  return %point;
}

Let's pretend I have a handle to a gem (%gem) that's in the GameScene shown on GameWindow that I want to move into the GUIScene (shown on GuiWindow).
%gem.setSize( convertScale( %gem.getSize(), GameWindow, GuiWindow ) );
%gem.setPosition( convertPoint( %gem.getPosition(), GameWindow, GuiWindow ) );
%gem.removeFromScene();
%gem.addToScene( GUIScene );

Displaying Numbers

I'm always needing to show numbers, and I've implemented this a million different ways, but I'm happy enough with this solution.

machinecodegames.com/showoff/ModNumbers.jpg
function displayNum( %name, %num, %qReset )
{
  if( $DisplayNumHelper[%name] $= %num && !%qReset ) return;
  $DisplayNumHelper[%name] = %num;
  
  %len = strlen( %num );
  for( %pos = 0; %pos < %len; %pos++ )
  {
    if( !isObject( %name @ %pos ) )
    {
      error( "Overflow in displayNum( " @ %name @ ", " @ %num @ " ) at position " @ %pos @ "." );
      return;
    }
    
    (%name @ %pos).setFrame( getSubStr( %num, %pos, 1 ) );
    (%name @ %pos).setVisible( true );
  }
  
  for( %pos = %len; %pos < 20; %pos ++ ) // 12 is probably enough, but whatever...
  {
    if( !isObject( %name @ %pos ) ) return;
    
    (%name @ %pos).setVisible( false );
  }
}

//////////////////////////////
// EXAMPLE
//////////////////////////////
displayNum( "Score", 0, true );
displayNum( "Time", 12 );

The string ("Score" or "Time") is the base name of the number objects. The last parameter (%qReset) forces the function to redraw the number even if it's the same as the last time it was rendered. (You typically only need to do this on the first call, like when you reset the score or time.) NOTE! This assumes that your numbers are in a 10-celled image with "0" as frame 0 and "9" as frame 9.

Hue-Saturation-Value to Red-Green-Blue

I'm always wanting to blend sprites using colors around the color wheel. In a current game, I want the colors from 0 degrees (red) to 240 degrees (blue).

function hsvToRgb( %hsv )
{
  %h = getWord( %hsv, 0 );
  %s = getWord( %hsv, 1 );
  %v = getWord( %hsv, 2 );
  
  if( %s == 0 ) return %v SPC %v SPC %v;
  
  while( %h < 0 ) %h += 360;
  while( %h >= 360 ) %h -= 360; // I hate fmod...
  %h /= 60;
  %i = mFloor( %h );
  %f = %h - %i;
  %p = %v * (1 - %s);
  %q = %v * (1 - %s * %f);
  %t = %v * (1 - %s * (1 - %f));
  if( %i == 0 ) return %v SPC %t SPC %p;
  if( %i == 1 ) return %q SPC %v SPC %p;
  if( %i == 2 ) return %p SPC %v SPC %t;
  if( %i == 3 ) return %p SPC %q SPC %v;
  if( %i == 4 ) return %t SPC %p SPC %v;
  if( %i == 5 ) return %v SPC %p SPC %q;
}

Now I call %sprite.setBlendColor( hsvToRgb( %hue SPC 1 SPC 1 ) ).

Progress

Sometimes I need to know the percent progress I've made between two numbers. Let's say you have a game where the player started with $100 and has to get to $1000. You can show the progress (including negative progress) with this little method.
// I added this to math/mConsoleFunctions.cc inside the GemeralMath group.
// Finds where <value> fits between <low> and <high>. -WLS
ConsoleFunction( percentBetween, F32, 4, 4, "( <low>, <high>, <value> )" )
{
  F32 low = dAtof( argv[1] );
  F32 high = dAtof( argv[2] );
  F32 value = dAtof( argv[3] );

  if( low == high ) return 0;

  return (value - low) / (high - low);
}

// TorqueScript example
%percentComplete = percentBetween( 100, 1000, $playersCurrentMoney );

Vector Helpers

I am always manipulating vectors in my games.

One common example is that I need to place a new sprite offset from another sprite, but at an angle. For this, I wrote t2dVectorFromAngle.

Other times, I need to get a position some percentage between two other points; this is great for status bars, but has a million uses. For this, I wrote t2dVectorLerp.

// I added these to t2dVector.cc inside the t2dVectorMath group.

//-----------------------------------------------------------------------------
// Gets a unit (or scaled) vector from an angle. - WLS
//-----------------------------------------------------------------------------
ConsoleFunction( t2dVectorFromAngle, const char *, 2, 3, "( <angDegrees> [,scale] )" )
{
  F32 ang = mDegToRad( dAtof( argv[1] ) );
  F32 len = 1;
  if( argc == 3 ) len = dAtof( argv[2] );
  char *pBuffer = Con::getReturnBuffer( 64 );
  dSprintf( pBuffer, 64, "%f %f", len * mCos( ang ), len * mSin( ang ) );
  return pBuffer;
}

//-----------------------------------------------------------------------------
// Linearlly interpolates between two points in size. - WLS
//-----------------------------------------------------------------------------
ConsoleFunction( t2dVectorLerp, const char *, 4, 4, "( <pointA>, <pointB>, <pctBetween> )" )
{
  t2dVector pointA, pointB, pointMid;
  F32 pctBetween = dAtof( argv[3] );
  dSscanf( argv[1], "%f %f", &pointA.mX, &pointA.mY );
  dSscanf( argv[2], "%f %f", &pointB.mX, &pointB.mY );
  pointMid = pointA + pctBetween * (pointB - pointA);
  char *pBuffer = Con::getReturnBuffer( 64 );
  dSprintf( pBuffer, 64, "%f %f", pointMid.mX, pointMid.mY );
  return pBuffer;
}

// TorqueScript t2dVectorFromAngle Example
%playerPos = $player.getPosition();
%icon = new t2dStaticSprite() { ... }; // An icon to show below and to the right of the player
%offset = t2dVectorFromAngle( 45, 100 ); // 45 degrees, 100 pixels away
%icon.setPosition( t2dVectorAdd( %playerPos, %offset ) );

// TorqueScript t2dVectorLerp Example
%playerPos = $player.getPosition();
%enemyPos = $enemy.getPosition();
%target = new t2dStaticSprite() { ... }; // A target to show one-quarter of the way between the player and the enemy
%target.setPosition( t2dVectorLerp( %playerPos, %enemyPos, 0.25 ) );

I'm always needing the mid-point between two points, too. Now it's as simple as calling t2dVectorLerp with "0.5" as the percent.

Script Loading

I don't keep extra scripts in my directories. If I do, I tend to rename them with a ".deleteMe" extension. Because of this, I can load all of the script files with one easy call:

function loadAllScripts()
{
  // Exec game scripts.
  %gameSpec = "./gameScripts/*.cs";
  for (%file = findFirstFile(%gameSpec); %file !$= ""; %file = findNextFile(%gameSpec))
    exec(%file);
}

In initializeProject(), I just call "loadAllScripts();".

I'll post a second list of mods next week. If you have any questions, comments, or snide remarks, please feel free to leave them below!

#1
07/20/2012 (12:20 pm)
Now that is some awesome stuff, looking forward to the rest of it!
#2
07/20/2012 (12:51 pm)
I'm jealous of that game/rig/awesome on so many levels... The fly-outs from the game to the GUI look amazing and it seems you shared it?

Beautiful!