Game Development Community

dev|Pro Game Development Curriculum

Object selection in Torque

by Dave Myers · 02/11/2002 (8:14 am) · 84 comments

Object Selection
Implemented and tested with Torque 1.1.1

Overview
This tutorial provides one method to enable object selection in your TGE-based game. This implementation will show how to implement a bounding box cursor that will be displayed when an object is selected with the mouse. It could easily be enhanced to allow you to implement a wide variety of features.

Using this Tutorial
Bold font is used to show new code in the different code snippets throughout the tutorial. Also, code that is not important has been left out and is denoted by a comment in italics reading "irrelevant code not shown for brevity".

Step 1 Capture the mouseDown event
First, let's quickly create a new bind and client function that will toggle the cursor. Add the following code to the bottom of the script file.

fps/client/scripts/default.bind.cs
[b]moveMap.bind(keyboard, "m",           "toggleMouseLook");

function toggleMouseLook(%val)
{
   if(%val)
   {
      if(Canvas.isCursorOn())
         CursorOff();
      else
         CursorOn();
   }
}[/b]

Second, let's capture any left button mouse click events on the client. To do that, we need to add in a method to the GameTSCtrl. This control is our canvas in the game so clicking somewhere on this canvas (i.e. our game screen) will generate a mouseDown event, and we can capture that event and deal with it in GameTSCtrl.

I chose to add new members and "getter" methods for the point and vector to the control rather than pass the information back to the client scripts as parameters. Either way seems fine, I just chose the former.

engine/game/gameTSCtrl.h
class GameTSCtrl : public GuiTSCtrl

{

   [i]// irrelevant code not shown for brevity[/i]

public: 

[b]   void onMouseDown(const GuiEvent &evt); //left-mouse click
   Point3F getMouse3DVec() {return mMouse3DVec;};
   Point3F getMouse3DPos() {return mMouse3DPos;};[/b]

private:
[b]
   Point3F mMouse3DVec;
   Point3F mMouse3DPos;   [/b]

   [i]// irrelevant code not shown for brevity[/i]

};

Now let's add the implementation of the onMouseDown handler method. What we need to do is take the 2D screen coordinates of the mouse and create 3D screen coordinates. We then will create a vector that has the camera world coordinates as the first point and the 3D screen coordinates as the second point. Once we have calculated these two elements, we then call a client script function, which I also named onMouseDown, and let the scripts control it from there. The idea here is that later we will take the 3D screen coordinates and the vector and shoot a picking ray into our 3D world. We'll then check to see if that ray collides with an object in the world.

I believe the code is commented pretty well, so it should be easy to follow along.

engine/game/gameTSCtrl.cc
[b]void GameTSCtrl::onMouseDown(const GuiEvent &evt)
{
   MatrixF mat;
   Point3F vel;
   if ( GameGetCameraTransform(&mat, &vel) ) 
   {
      //get the camera position
      Point3F pos;
      mat.getColumn(3,&pos);

      //take our mouse coordinates and create (x,y,z) screen coordinates
      Point3F screenPoint(evt.mousePoint.x, evt.mousePoint.y, -1);

      //take our screen coordinates and get the corresponding 
      //world coordinates (this is what unproject does for us)
      Point3F worldPoint;
      if (unproject(screenPoint, &worldPoint)) 
      {
         mMouse3DPos = pos;
         //create a vector that points from our starting point (the 
         //camera position) and heads towards our point we have chosen
         //in the world
         mMouse3DVec = worldPoint - pos;
         mMouse3DVec.normalizeSafe();

         //call client script handler
         Con::executef(this, 1, "onMouseDown");
      }
   }
}[/b]

I then exposed the "getter" methods to the scripting engine, also in engine/game/gameTSCtrl.cc
[b]static const char* cGetMouse3DVec(SimObject *ptr, S32 argc, const char **argv)
{
   GameTSCtrl* obj = static_cast<GameTSCtrl*>(ptr);

   char* retBuffer = Con::getReturnBuffer(256);
   const Point3F &vec = obj->getMouse3DVec();
   dSprintf(retBuffer, 256, "%g %g %g", vec.x, vec.y, vec.z);
   return retBuffer;
}

static const char* cGetMouse3DPos(SimObject *ptr, S32 argc, const char **argv)
{
   GameTSCtrl* obj = static_cast<GameTSCtrl*>(ptr);

   char* retBuffer = Con::getReturnBuffer(256);
   const Point3F &pos = obj->getMouse3DPos();
   dSprintf(retBuffer, 256, "%g %g %g", pos.x, pos.y, pos.z);
   return retBuffer;
}[/b]

void GameTSCtrl::consoleInit()
{
   [b]Con::addCommand("GameTSCtrl", "getMouse3DVec", cGetMouse3DVec, "GameTSCtrl.getMouse3DVec();", 2, 2);
   Con::addCommand("GameTSCtrl", "getMouse3DPos", cGetMouse3DPos, "GameTSCtrl.getMouse3DPos();", 2, 2);[/b]
}

Currently, the Torque demo has a GUI element called GuiShapeNameHud that is used to display player names and damage over the heads of the models. This control is actually almost the same size as the canvas itself, which means that any mouse events are actually handled by this control, not our GameTSCtrl. I preferred to keep the control, but modify it so that it will call to my new mouse down event handler in GameTSCtrl. To do that I inserted the following onMouseDown handler, which simply calls up to its parent in this case, our GameTSCtrl.

engine/game/fps/guiShapeNameHud.cc
class GuiShapeNameHud : public GuiControl {

[i] // irrelevant code not shown for brevity[/i]

public:

[b]   virtual void onMouseDown(const GuiEvent &evt);[/b]

[i] // irrelevant code not shown for brevity[/i]

};

[i] // irrelevant code not shown for brevity[/i]

void GuiShapeNameHud::initPersistFields()
{
   Parent::initPersistFields();
   addField( "fillColor", TypeColorF, Offset( mFillColor, GuiShapeNameHud ) );
   addField( "frameColor", TypeColorF, Offset( mFrameColor, GuiShapeNameHud ) );
   addField( "textColor", TypeColorF, Offset( mTextColor, GuiShapeNameHud ) );

   addField( "showFill", TypeBool, Offset( mShowFill, GuiShapeNameHud ) );
   addField( "showFrame", TypeBool, Offset( mShowFrame, GuiShapeNameHud ) );
   addField( "verticalOffset", TypeF32, Offset( mVerticalOffset, GuiShapeNameHud ) );
   addField( "distanceFade", TypeF32, Offset( mDistanceFade, GuiShapeNameHud ) );
}


[b]void GuiShapeNameHud::onMouseDown(const GuiEvent &evt)
{
   // Let's let the parent execute its event handling (if any)
   GuiTSCtrl *parent = dynamic_cast<GuiTSCtrl*>(getParent());
   if (parent)
	   parent->onMouseDown(evt);
}[/b]

/**
   Core render method wich does all the work.
   This method scans through all the current client ShapeBase objects
   and if they are named, displays their name and damage value. If the
   shape is a PlayerObjectType then values are displayed offset from it's
   eye point, otherwise the bounding box center is used.
*/
void GuiShapeNameHud::onRender( Point2I, const RectI &updateRect)
{

[i] // irrelevant code not shown for brevity[/i]

Step 2 Client handler for mouse event
We now need to add a handler to the scripts for PlayGui, which is the script name of our canvas GameTSCtrl. This new method will grab the vector and point and call to a server script function SelectObject.

example/fps/client/scripts/playGui.cs
[b]function PlayGui::onMouseDown(%this)
{
   // mouseVec = vector from camera point to 3d mouse coords (normalized)
   %mouseVec = %this.getMouse3DVec();
   // cameraPoint = the world position of the camera
   %cameraPoint = %this.getMouse3DPos();

   commandToServer('SelectObject', %mouseVec, %cameraPoint);
}[/b]


Step 3 Set up our connection to handle our selected object

Before we can actually select an object, we need to set aside a place to save this information, and provide a way for the server to update the client so that we can render (at least in this example) a bounding box cursor around our selected object. I have chosen to save this information as part of the GameConnection class.

engine/game/gameConnection.h
class GameConnection : public NetConnection
{

[i]// irrelevant code not shown for brevity[/i]

private:
[b]   SimObjectPtr<ShapeBase> mSelectedObj;//pointer to our selected object
   bool mChangedSelectedObj; //flag used to determine if we have changed our selected object[/b]

public:

[b]   void setSelectedObj(ShapeBase *so) { mSelectedObj = so; mChangedSelectedObj = true; }
   ShapeBase* getSelectedObj()  { return  mSelectedObj; }
   bool hasChangedSelectedObj() { return mChangedSelectedObj; }[/b]

We'll need to pass the selected object around between the server and client, so in order to do so we need to modify the readPacket and writePacket methods of our GameConnection class. For a brief description of why, take a look at Justin Mette's tutorial on node hiding, starting with Step 2 Updating ghosts. Basically, we will be saving off the selected object on the server, but our client needs to know what object is selected so that it can render the cursor around the ghost object.

We also need to create the "getter/setter" functions so we can get and set the selected object from the server scripts.

engine/game/gameConnection.cc
GameConnection::GameConnection(bool ghostFrom, bool ghostTo, bool sendEvents) 
   : NetConnection(ghostFrom, ghostTo, sendEvents)
{
[b]   mSelectedObj = NULL;
   mChangedSelectedObj = false;[/b]

[i]// irrelevant code not shown for brevity[/i]

}

[i]// irrelevant code not shown for brevity[/i]

static int cGetControlObject(SimObject *obj, S32, const char **argv)
{
   argv;
   GameConnection *cptr = (GameConnection *) obj;
   SimObject* cp = cptr->getControlObject();
   return cp? cp->getId(): 0;
}

[b]static bool cSetSelectedObj(SimObject *obj, S32, const char **argv)
{
   GameConnection *cptr = (GameConnection *) obj;
   ShapeBase *gb;
   if(!Sim::findObject(argv[2], gb))
      return false;

   cptr->setSelectedObj(gb);
   return true;
}

static int cGetSelectedObj(SimObject *obj, S32, const char **argv)
{
   GameConnection *cptr = (GameConnection *) obj;
   SimObject* cp = cptr->getSelectedObj();
   return cp? cp->getId(): 0;
}
[/b]

void GameConnection::consoleInit()
{
[b]   Con::addCommand("GameConnection", "setSelectedObj", cSetSelectedObj, "conn.setSelectedObj(%obj)", 3, 3);
   Con::addCommand("GameConnection", "getSelectedObj", cGetSelectedObj, "conn.getSelectedObj()", 2, 2);[/b]
}

void GameConnection::readPacket(BitStream *bstream)
{
   char stringBuf[256];
   stringBuf[0] = 0;
   bstream->setStringBuffer(stringBuf);

   clearCompression();
   if (isServerConnection())
   {
      mLastMoveAck = bstream->readInt(32);
      if (mLastMoveAck < mFirstMoveIndex)
         mLastMoveAck = mFirstMoveIndex;
      if(mLastMoveAck > mLastClientMove)
         mLastClientMove = mLastMoveAck;
      while(mFirstMoveIndex < mLastMoveAck)
      {
         AssertFatal(mMoveList.size(), "Popping off too many moves!");
         mMoveList.pop_front();
         mFirstMoveIndex++;
      }

[b]      // selected object - do we have a change in status?
      if (bstream->readFlag())
      {  
         // do we have a selected object now?
         bool hasSelectedObj = bstream->readFlag();

         if ((hasSelectedObj) && (bstream->readFlag()))
         {
            S32 gIndex = bstream->readInt(10);
            ShapeBase* obj = static_cast<ShapeBase*>(resolveGhost(gIndex));
            if (mSelectedObj != obj)
               setSelectedObj(obj);
         }
         else
            mSelectedObj = NULL;
      }[/b]

      mDamageFlash = 0;
      mWhiteOut = 0;
      if(bstream->readFlag())
      {
         if(bstream->readFlag())
            mDamageFlash = bstream->readFloat(7);
         if(bstream->readFlag())
            mWhiteOut = bstream->readFloat(7) * 1.5;
      }

[i]// irrelevant code not shown for brevity[/i]

}

void GameConnection::writePacket(BitStream *bstream, PacketNotify *note)
{

[i]   // irrelevant code not shown for brevity[/i]

   else
   {
      // The only time mMoveList will not be empty at this
      // point is during a change in control object.

      bstream->writeInt(mLastMoveAck - mMoveList.size(),32);

      S32 gIndex = -1;

[b]      // selected object - have we changed the status of the selected object?
      if (bstream->writeFlag(hasChangedSelectedObj()))
      {
         // do we have a selected object?
         if ((bstream->writeFlag(mSelectedObj != NULL)) && (!mSelectedObj.isNull()))
         {
            gIndex = getGhostIndex(mSelectedObj);
            if (bstream->writeFlag(gIndex != -1))
               bstream->writeInt(gIndex,10);
         }
            
         // reset the status of our object
         mChangedSelectedObj = false;
      }

      gIndex = -1;[/b]

      // get the ghost index of the control object, and write out
      // all the damage flash & white out 

      if (!mControlObject.isNull())
      {
         gIndex = getGhostIndex(mControlObject);
      
         F32 flash = mControlObject->getDamageFlash();
         F32 whiteOut = mControlObject->getWhiteOut();
         if(bstream->writeFlag(flash != 0 || whiteOut != 0))
         {
            if(bstream->writeFlag(flash != 0))
               bstream->writeFloat(flash, 7);
            if(bstream->writeFlag(whiteOut != 0))
               bstream->writeFloat(whiteOut/1.5, 7);
         }
      }
      else
         bstream->writeFlag(false);

[i]      // irrelevant code not shown for brevity[/i]

}

Step 4 Check if we have selected something

The server is responsible for determining if we have selected something, and if so it will save off the selected object for use later. Let's keep this simple and add our new server command SelectObject to an existing script file (I personally put this and other related server commands in a separate file). The code below is pretty well-commented, so it should be pretty easy to follow the logic there.

fps/server/scripts/commands.cs
[b]function serverCmdSelectObject(%client, %mouseVec, %cameraPoint)
{
   //Determine how far should the picking ray extend into the world?
   %selectRange = 200; 
   // scale mouseVec to the range the player is able to select with mouse
   %mouseScaled = VectorScale(%mouseVec, %selectRange);
   // cameraPoint = the world position of the camera
   // rangeEnd = camera point + length of selectable range
   %rangeEnd = VectorAdd(%cameraPoint, %mouseScaled);

   // Search for anything that is selectable  below are some examples
   %searchMasks = $TypeMasks::PlayerObjectType | $TypeMasks::CorpseObjectType |
      				$TypeMasks::ItemObjectType | $TypeMasks::TriggerObjectType;

   // Search for objects within the range that fit the masks above
   // If we are in first person mode, we make sure player is not selectable by setting fourth parameter (exempt 
   // from collisions) when calling ContainerRayCast
   %player = %client.player;
   if ($firstPerson)
   {
	  %scanTarg = ContainerRayCast (%cameraPoint, %rangeEnd, %searchMasks, %player);
   }
   else //3rd person - player is selectable in this case
   {
	  %scanTarg = ContainerRayCast (%cameraPoint, %rangeEnd, %searchMasks);
   }

   // a target in range was found so select it
   if (%scanTarg)
   {
      %targetObject = firstWord(%scanTarg);

      %client.setSelectedObj(%targetObject);
   }
}[/b]

Step 5 Example implementation
Your use of object selection will be specific to your game needs. However, let's go ahead and test it by making a small addition to the ShapeBase::renderObject method. There is already code there to show a bounding box if in debug mode, so let's just reuse it in the case we have something selected as a 3D cursor of sorts. First, we need to get the server connection, which is where we stored our selected object earlier. We then simply get the object id from the object and compare it to object id of the shapebase we are rendering. If they match, we go ahead and show the bounding box:

engine/game/shapeBase.cc
void ShapeBase::renderObject(SceneState* state, SceneRenderImage* image)
{
[i]      // irrelevant code not shown for brevity[/i]

   dglNPatchEnd();
   
   glMatrixMode(GL_PROJECTION);
   glPopMatrix();
   glMatrixMode(GL_MODELVIEW);
   dglSetViewport(viewport);

   uninstallLights();

[b]   // if we have been selected, then render a cursor around us
   // for now this is simply a bounding box
   GameConnection* conn = GameConnection::getServerConnection();
   ShapeBase* selectedObj = NULL;
   if (conn)
      selectedObj = conn->getSelectedObj();

   S32 selectedId = -1;

   if (selectedObj != NULL)
      selectedId = selectedObj->getId();[/b]

        // Debugging Bounding Box  use it as a temporary cursor of sorts
        if (!mShapeInstance || gShowBoundingBox[b] || (getId() == selectedId)[/b]) 
       {
           glDisable(GL_DEPTH_TEST);
           Point3F box;
           glPushMatrix();
           dglMultMatrix(&getRenderTransform());
           box = (mObjBox.min + mObjBox.max) * 0.5;
           glTranslatef(box.x,box.y,box.z);
           box = (mObjBox.max - mObjBox.min) * 0.5;
          glScalef(box.x,box.y,box.z);
          glColor3f(1, 0, 1);
          wireCube(Point3F(1,1,1),Point3F(0,0,0));
          glPopMatrix();
 }

[i]      // irrelevant code not shown for brevity[/i]

Conclusion
I have tested this code with Torque 1.1.1 right from the CVS repository. If you have any problems or suggestions with the tutorial, let's discuss them in the feedback.

Enjoy!
Dave Myers
21-6 Productions

About the author

Considerable experience developing with Torque-based technologies and produced the first third-party game using any Torque technology (Orbz). Game designer, programmer, and producer, and credits include the innovative title Orbz and the colorful BuggOut.

Page «Previous 1 2 3 4 5 Last »
#1
02/11/2002 (9:01 am)
wow thats a lot of code,,,what does it do? in plain english, (i for one dont really understand it
#2
02/11/2002 (9:35 am)
The tutorial is for object selection - which means the ability to select an object in the game, such as a player or item. In this case I wanted to show a cursor around the selected object, which is why I added code to the engine itself.

If there is something in particular you do not understand, feel free to ask and I'll try to help out.
#3
02/11/2002 (12:56 pm)
David,

Not quite seeing why you need to alter anything server side for picking?

Surely this could all be done client side?

Nice work though, its going to be useful for a lot of people.

Phil.
#4
02/11/2002 (2:06 pm)
Thanks, Phil.

It's been awhile since I've worked on this bit of code, and my initial direction was to do everything client-side. I need to think about it a bit more to see how you'd do it all on the client.
#5
02/11/2002 (6:37 pm)
Well, I had a bit of a problem with it... everything worked perfectly up to the call to %client.setSelectedObj(%targetObject); in server/scripts/commnads.cs - I could verify that it did detect the appropriate object using the containerRayCast, but when it calls setSelectedObject, it only highlights (selects) nothing in 1st ppov and only selects the player in the 3rd ppov. I used the following search mask:

%searchMasks =
$TypeMasks::StaticObjectType | $TypeMasks::EnvironmentObjectType | $TypeMasks::TerrainObjectType |
$TypeMasks::InteriorObjectType | $TypeMasks::WaterObjectType | $TypeMasks::TriggerObjectType |
$TypeMasks::MarkerObjectType | $TypeMasks::ForceFieldObjectType | $TypeMasks::GameBaseObjectType |
$TypeMasks::ShapeBaseObjectType | $TypeMasks::CameraObjectType | $TypeMasks::StaticShapeObjectType |
$TypeMasks::PlayerObjectType | $TypeMasks::ItemObjectType | $TypeMasks::VehicleObjectType |
$TypeMasks::VehicleBlockerObjectType | $TypeMasks::ProjectileObjectType | $TypeMasks::ExplosionObjectType |
$TypeMasks::CorpseObjectType | $TypeMasks::DebrisObjectType | $TypeMasks::PhysicalZoneObjectType |
$TypeMasks::StaticTSObjectType | $TypeMasks::GuiControlObjectType | $TypeMasks::StaticRenderedObjectType |
$TypeMasks::DamagableItemObjectType ;


which is pretty well everything :-)

I think the problem may be in renderObjects - I'll have a closer look later tonight. Shouldn't take too long to figure out what's going on. BTW, I'm testing in multiplayer mode, as the host.


Thanks for this - it will be tres useful !
#6
02/11/2002 (10:58 pm)
Everything works fine for me.
Must be a problem on your end Cerdip.
#7
02/12/2002 (6:06 am)
Phil, the initial implementation of this was indeed client-only. After learning more about the engine, it was determined that the code needed to have server components for two reasons.

First, cheating - if the selection code was client-only, it might be possible for a cheat to be written where any object (regardless of distance, for example) could be selected and viewed which would be an unfair advantage for the client with the cheat. We wanted to make sure that the server controlled whether an object could be selected or not - based on the rules of the game.

Second, to act on the selected object - we needed a server side reference to the object. For example, to pick up the object a call needed to be made to the server. By storing the selected object in the server for the particular client, we were able to easily write an array of fuctions for acting on the selected object.

Hope that makes sense, it's still early :)

Justin
#8
02/12/2002 (6:24 am)
Yeah, my memory isn't what it used to be, cuz I knew that the first attempt was purely client-side and it worked to a degree. Fuzzy me. ;)
#9
02/12/2002 (5:35 pm)
OK, thanks Matt. I still haven't got the bounding box to show up on anything other than the player avatar, but that's ok, since it's the on-screen selection part that's the point here anyway ... and that's working fine for me too.
#10
02/13/2002 (8:40 pm)
If anyone is interested, I wrote a function that deselects all objects when you click "blank" space.

I'm currently working on integrating shift-clicking (select more than one object) as well as possibly a drag and select (like seen in most RTS games)

Anyway, thanks for this David. It's a great feature.
#11
02/13/2002 (11:20 pm)
Nice to hear people finding it useful. We actually use different behavior for double-clicking and single-clicking, so one addition to the tut could be the following:

Step 1 - Check for double click

Check to see if we have clicked more than once in the canvas mouseDown event handler. Using the code from above and altering it a little:

engine/game/gameTSCtrl.cc
void GameTSCtrl::onMouseDown(const GuiEvent &evt)
{
   MatrixF mat;
   Point3F vel;
   if ( GameGetCameraTransform(&mat, &vel) ) 
   {
      //get the camera position
      Point3F pos;
      mat.getColumn(3,&pos);

      //take our mouse coordinates and create (x,y,z) screen coordinates
      Point3F screenPoint(evt.mousePoint.x, evt.mousePoint.y, -1);

      //take our screen coordinates and get the corresponding 
      //world coordinates (this is what unproject does for us)
      Point3F worldPoint;
      if (unproject(screenPoint, &worldPoint)) 
      {
         mMouse3DPos = pos;
         //create a vector that points from our starting point (the 
         //camera position) and heads towards our point we have chosen
         //in the world
         mMouse3DVec = worldPoint - pos;
         mMouse3DVec.normalizeSafe();

[b]         //did we click more than once?
         S32 doubleClick = (evt.mouseClickCount > 1)?1:0;

         //call client script handler
         Con::executef(this, 2, "onMouseDown", Con::getIntArg(doubleClick));[/b]
      }
   }
}

Step 2 - Change client script handler to determine what to do on double-click

I chose to call the same function on the client script for single and double-click, and then decide from there what behavior I wanted. An example below picks up an object on double-click:

example/fps/client/scripts/playGui.cs
function PlayGui::onMouseDown(%this[b], %doubleClick[/b])
{
   // mouseVec = vector from camera point to 3d mouse coords (normalized)
   %mouseVec = %this.getMouse3DVec();
   // cameraPoint = the world position of the camera
   %cameraPoint = %this.getMouse3DPos();

[b]   if (%doubleClick)
      commandToServer('PickupObject', %mouseVec, %cameraPoint);
   else[/b]
      commandToServer('SelectObject', %mouseVec, %cameraPoint);
}
#12
03/19/2002 (10:09 pm)
As there are changes to the server code, will this only work if I'm connected to a server? What about if I'm running it standalone?
#13
03/19/2002 (10:25 pm)
Hmmmmmm, nothing happens. I've double checked the code. I've added printf statements in the onMouseDown functions, but they don't get called.
#14
06/02/2002 (5:44 pm)
Excellant would have taken me ages to figure this all out saved me a lot of effort, and has increased my understanding of how the engine works immensely.

Owen
#15
08/18/2002 (11:50 pm)
This is some great stuff here.. however, I've been looking for a way to check which mouse button was pressed down. The GuiEvent structure doesn't have a field to determine mouse buttons, and I don't have a clue as to how to get it from direct input.. any ideas?
#16
08/18/2002 (11:51 pm)
darn back button =|
#17
09/08/2002 (11:57 pm)
Great Tutorial!

One thing, i've added all of the modifications and everything is compiling with out errors. However when i connect to a server the client crashes at the "loading mission" stage..

The last two lines in the console log is

Sent connect request
Connection accepted - id 1094 protocol 3

Does anyone have any ideas what this could be?

(EDIT)
When i replace the modified executable with the old executable everything returns to normal and no errors occur
#18
10/11/2002 (2:57 pm)
This tutorial is excellent. It works right out of the box. I want to figure out how to change it slightly so that clicking on an object once selects it and if you click it again it gets deselected. If you click on emtpy space everything is deselected.

Great job David Myers
#19
11/12/2002 (5:50 pm)
Any object that overrides the renderObject method and doesn't call Parent::renderObject will need to have the same code put in place for checking to see if the current object is selected and draw the bounding box.
#20
11/18/2002 (9:18 pm)
Anyone home? I know this was released a while back but, I've ran into a snag, similar to Stuart's.

The last thing I see is 'sending heartbeat to master server' and the app dies. So, I started digging...

Unluckly, my Torque-fu isn't that great. So I narrowed it down considerably, and discovered it's exactly where I figured it was: something has changed in the gameConnection section - the area that's the largest mystery to me. Oh Yay! ;-)

Any clues? Or am I missing something blatantly obvious that's setting right in front o' me?
Page «Previous 1 2 3 4 5 Last »