Game Development Community

Updated Object Selection (with TLK bonus)

by Rubes · 07/03/2006 (11:32 am) · 75 comments

Overview

This tutorial is based on the two excellent Object Selection tutorials written by Dave Myers (seen here and here). The reason for updating it is to bring it up to TGE version 1.4, and to try and incorporate some of the modifications that I have added to alter its functionality, including the addition of object highlighting with TLK. This tutorial should work with a stock installation of TGE/TLK 1.4, using the "starter.fps" mod as a starting point.

What this tutorial accomplishes

- Ability to select objects using the mouse crosshairs (and mouse click)
- Indicate an object is selected by making it brighter using TLK
- Enable selection of objects inside (or near) other objects by exempting an already selected object from further selection

This tutorial is directed at FPS games played in first-person mode, mostly because it uses the crosshair for selection, but it can of course be adapted to other styles. The original tutorials, for instance, show you how to select objects by toggling on the mouse arrow cursor and using that to choose the desired object, which would be ideal for third-person play. Refer to the earlier versions of this resource for ways to do that.

This code will only allow you to select DTS objects only, and only those with collision boxes. Although it could potentially be adapted for TSStatic objects, it right now functions only with ShapeBase-derived objects.

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 mouse button click


We will use the mouse button for object selection; that is, the player will position the crosshairs directly over the object they want to select, and then click the mouse button to select it. I will show you how to specify either the left or right mouse button for this purpose.

In the stock "starter.fps" mod, a click of the left mouse button alters a global variable, which is interpreted by the C++ engine as a trigger to fire the main weapon. Here, we will change things so that the left mouse button triggers the script function which handles object selection.

File: /starter.fps/client/scripts/default.bind.cs

Find the following code and make the changes noted in bold:

//-------------------------------------------------------------------
// Mouse Trigger
//-------------------------------------------------------------------

function mouseFire(%val)
{
   $mvTriggerCount0++;
}

function altTrigger(%val)
{
   $mvTriggerCount1++;
}

[b]
// ** Delete or Comment Out the following two lines:

//moveMap.bind( mouse, button0, mouseFire );
//moveMap.bind( mouse, button1, altTrigger );

// ** Add the following lines:

function processClick(%val)
{
   if (%val && $firstperson)
      commandToServer('SelectObject');
}

moveMap.bind( mouse, button0, processClick);
[/b]

Here, the left mouse button (button0) was originally bound to the mouseFire() function, while the right mouse button was bound to the altTrigger() function. We now instead bind the left mouse button to our new processClick function, which sends a command to the server to run the SelectObject function, which is introduced below.

If you would prefer to bind the right mouse button to this function instead of the left mouse button, then use button1 in the moveMap.bind command instead of button0.


NOTE: Make sure and delete the current config.cs and config.cs.dso files found in /starter.fps/client. This file (config.cs) is created automatically each time you shutdown the game and is executed when starting up the game.



Step 2 - Set up our connection to handle our selected object


This next section is basically code taken verbatim from Dave Myers's previous object selection resource.

Before we can actually select an object, we need to develop a system that will (1) store which object is selected on each individual client, and (2) provide a mechanism for the server to tell this to the client so that each client can appropriately handle (eg, render) its selected object. We will do this by modifying the GameConnection class.

File: engine/game/gameConnection.h

Find the following code and make the changes noted in bold:

class GameConnection : public NetConnection
{

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

private:
   /// @name Move Packets
   /// Write/read move data to the packet.
   /// @{

   void moveWritePacket(BitStream *bstream);
   void moveReadPacket(BitStream *bstream);
   /// @}
[b]
   ///object selection additions
   SimObjectPtr<ShapeBase> mSelectedObj;//pointer to our selected object
   bool mChangedSelectedObj; //flag used to determine if we have changed our selected object[/b]

public:
[b]
   ///object selection additions
   void setSelectedObject(ShapeBase *so) { mSelectedObj = so; mChangedSelectedObj = true; }
   ShapeBase* getSelectedObject()  { return  mSelectedObj; }
   void clearSelectedObject() { mSelectedObj = NULL; mChangedSelectedObj = true; }
   bool hasChangedSelectedObject() { return mChangedSelectedObj; }[/b]
     
   /// @name Protocol Versions
   ///
   /// Protocol versions are used to indicated changes in network traffic.
   /// These could be changes in how any object transmits or processes
   /// network information. You can specify backwards compatability by
   /// specifying a MinRequireProtocolVersion.  If the client
   /// protocol is >= this min value, the connection is accepted.
   ///
   /// Torque (V12) SDK 1.0 uses protocol  =  1
   ///
   /// Torque SDK 1.1 uses protocol = 2
   /// @{
   static const U32 CurrentProtocolVersion;
   static const U32 MinRequiredProtocolVersion;
   /// @}

So now, each client's GameConnection will hold its own selected object (mSelectedObject), and will have methods of getting and setting the currently selected object. That's the header file, so now we need to enter some additional code into the .cc file as well.

In order to pass the selected object around between the server and client, we will need to modify the readPacket and writePacket methods of our GameConnection class. This way each client will know what object is selected so that it can render it the way we want.

We will also create the console functions for "getting", "setting", and "clearing" the selected object directly from script.

File: engine/game/gameConnection.cc

Find the following code and make the changes noted in bold:

GameConnection::GameConnection()
{
[i]// irrelevant code not shown for brevity[/i]
  
   //blackout vars
   mBlackOut = 0.0f;
   mBlackOutTimeMS = 0;
   mBlackOutStartTimeMS = 0;
   mFadeToBlack = false;
[b]
   //object selection additions
   mSelectedObj = NULL;
   mChangedSelectedObj = false;[/b]
}

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

ConsoleMethod( GameConnection, getControlObject, S32, 2, 2, "")
{
   argv;
   SimObject* cp = object->getControlObject();
   return cp? cp->getId(): 0;
}

[b]
//------------------------------------------------------------------
// object selection additions
//------------------------------------------------------------------
ConsoleMethod( GameConnection, setSelectedObject, bool, 3, 3, "(%object)")
{
   ShapeBase *sb;

   if(!Sim::findObject(argv[2], sb))
      return false;

   object->setSelectedObject(sb);

   return true;
}

//------------------------------------------------------------------
// object selection additions
//------------------------------------------------------------------
ConsoleMethod( GameConnection, getSelectedObject, S32, 2, 2, "()")
{
   SimObject* so = object->getSelectedObject();

   return so? so->getId(): 0;
}

//--------------------------------------------------------------------------
// object selection additions
//--------------------------------------------------------------------------
ConsoleMethod( GameConnection, clearSelectedObject, void, 2, 2, "()")
{ 
	object->clearSelectedObject(); 
}[/b]

ConsoleMethod( GameConnection, isAIControlled, bool, 2, 2, "")
{
   return object->isAIControlled();
}

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]
      // object selection additions
      // 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)
               setSelectedObject(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]
      // object selection additions
      // selected object - have we changed the status of the selected object?
      if (bstream->writeFlag(hasChangedSelectedObject()))
      {
         // 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 3 - Exempt two selected objects from RayCast collision detection


The method we will use to determine which object the player is trying to select is to cast a ray out from the player's eye, through the crosshair. The first object (of the desired type) that the ray collides with will be the selected object. The ContainerRayCast method does this for us.

However, one problem with this arises: some objects might not be "selectable" because their collision box is engulfed by a nearby object. For example, if there is a coin inside a chest, the coin could never be selected because the chest's collision box will always be detected first.

Fortunately, the ContainerRayCast method allows us to specify one object that we want exempted from collision detection. However, we usually specify that the player object be exempted, because a RayCast that starts at the player's eye will always collide with the player first. So now we need to specify two exempted objects -- the player, and an arbitrary second object. Later, in script, we will specify that this second object is any currently selected object. This way, if we try to select the coin, the chest will be selected first; the second time we try selecting the coin, the chest will be exempt from detection, and the coin will be selected. Make sense?

I've already submitted this step as a separate resource, but I'll repeat the code changes here.

File: engine/sim/sceneObject.cc

Find the console function for ContainerRayCast and make the changes noted in bold:

[b]// Object selection: Change the define below to allow for the extra exempt object.

ConsoleFunction( containerRayCast, const char*, 4, 6, "( Point3F start, Point3F end, bitset mask, SceneObject exempt1=NULL SceneObject exempt2=NULL)"[/b]
      "Cast a ray from start to end, checking for collision against items matching mask.\n\n"
      "If exempt1 (and exempt2) is specified, then it is temporarily excluded from collision checks (For "
      "instance, you might want to exclude the player if said player was firing a weapon.)\n"
      "@returns A string containing either null, if nothing was struck, or these fields:\n"
      "            - The ID of the object that was struck.\n"
      "            - The x, y, z position that it was struck.\n"
      "            - The x, y, z of the normal of the face that was struck.")
{
   char *returnBuffer = Con::getReturnBuffer(256);
   Point3F start, end;
   dSscanf(argv[1], "%g %g %g", &start.x, &start.y, &start.z);
   dSscanf(argv[2], "%g %g %g", &end.x,   &end.y,   &end.z);
   U32 mask = dAtoi(argv[3]);

   SceneObject* pExempt = NULL;
[b]   SceneObject* pExempt2 = NULL;[/b]

   if (argc > 4) {
      U32 exemptId = dAtoi(argv[4]);
      Sim::findObject(exemptId, pExempt);
   }
   if (pExempt)
      pExempt->disableCollision();

[b]
   if (argc > 5) {
      U32 exemptId = dAtoi(argv[5]);
      Sim::findObject(exemptId, pExempt2);
   }
   if (pExempt2)
      pExempt2->disableCollision();
 [/b]
   
   RayInfo rinfo;
   S32 ret = 0;
   if (gServerContainer.castRay(start, end, mask, ) == true)
      ret = rinfo.object->getId();

   if (pExempt)
      pExempt->enableCollision();
[b]
   if (pExempt2)
      pExempt2->enableCollision();[/b]

   // add the hit position and normal?
   if(ret)
   {
      dSprintf(returnBuffer, 256, "%d %g %g %g %g %g %g",
               ret, rinfo.point.x, rinfo.point.y, rinfo.point.z,
               rinfo.normal.x, rinfo.normal.y, rinfo.normal.z);
   }
   else
   {
      returnBuffer[0] = '0';
      returnBuffer[1] = '[[4fc760bac15f4]]';
   }

   return(returnBuffer);
}


Step 4 - Check to see if we have selected something


The server is responsible for determining if we have selected something following the detection of a mouse click. Way up above, we decided to call the server function "SelectObject" when the left mouse button is clicked. Here we will define this function. As part of this function we will specify a global script variable that will hold the ID of the object that we have selected: $selectedObj.

It's easiest to add this function to an existing script file, such as /server/scripts/commands.cs.

File: starter.fps/server/scripts/commands.cs

Enter this code anywhere in the script file:

[b]
//---------------------------------------------------------------------
// object selection additions
//---------------------------------------------------------------------
function serverCmdSelectObject(%client)
{
   // Specify how far the picking ray should extend into the world
   %selectRange = 50;

   %player = %client.player;
   %eye = %player.getEyeVector();
   %vec = vectorScale(%eye, %selectRange);

   // startPoint = the player's eye position
   // endPoint = startPoint + length of selectable range
   %startPoint = %player.getEyeTransform();
   %endPoint = VectorAdd(%startPoint, %vec);

   // Search for anything that is selectable; below are some examples
   %searchMasks = (
      $TypeMasks::ShapeBaseObjectType |
      $TypeMasks::ItemObjectType |
      $TypeMasks::CorpseObjectType
   );
   
   %exempt = 0;
   if ($selectedObj) %exempt = $selectedObj;

   %object = ContainerRayCast(%startPoint, %endPoint, %searchMasks, %player, %exempt);

   if(%object)
   {
      $selectedObj = firstWord(%object);
      %client.setSelectedObject($selectedObj);
      %name = $selectedObj.getDatablock().pickupName;
      ChatHud.addLine(%name @ " was selected.");
   }
   else
   {
      ChatHud.addLine("No object selected.");
      %client.clearSelectedObject();
      $selectedObj = 0;
   }
}
[/b]

This function does a number of things. First, it gets the client player's eye position, and creates a vector that extends from this point for a specified range to an endpoint. It then specifies which objects are considered "selectable" using the TypeMasks. Next, it checks to see if any objects are currently selected (using $selectedObj), and if so, specifies that object to be exempt from detection. It then calls the ContainerRayCast method. If an object is returned, it sets that as the selected object and calls the client's setSelectedObject method, which we defined above in GameConnection. If no object is returned, then we clear any currently selected object and call the client's clearSelectedObject method, also defined above.

Note that if you want the ChatHud message to display correctly, the object you select must have its pickupName defined in its datablock. For example, in starter.fps, the health kit has a pickupName defined, but the health patch does not. You can add this to the health patch datablock to make it display the message "a health kit was selected" when you click on one.

Step 5 - Render the selected object


The final step is to give some kind of visual feedback to indicate to a player that an object is actually selected. In the original Object Selection resource, Dave Myers included some code that would draw the selected object's bounding box when it was selected. If this suits your purpose, then go ahead and use that code. Here, we will instead use TLK to render the currently selected object much brighter than usual, in a manner that resembles "self-illumination".

We will do this by modifying the ShapeBase::renderObject method, thereby altering the code that is run every time a ShapeBase object is rendered to the screen.

To do this, we need to get the server connection, which is where we stored our selected object earlier. We then simply compare the object ID of the selected object with the object ID of the ShapeBase we are rendering. If they match, we go ahead and alter the TLK lighting component.

File: engine/game/shapeBase.cc

Find the following code and make the changes noted in bold:

void ShapeBase::renderObject(SceneState* state, SceneRenderImage* image)
{
   PROFILE_START(ShapeBaseRenderObject);
   AssertFatal(dglIsInCanonicalState(), "Error, GL not in canonical state on entry");

   RectI viewport;
   dglGetViewport(&viewport);
[b]
   // Object selection additions
   // if we have been selected, then render us with a brighter light.
   // Must be called just prior to the call to installLights().
   
   GameConnection* conn = GameConnection::getConnectionToServer();
   ShapeBase* selectedObj = NULL;
   
   if (conn)
	   selectedObj = conn->getSelectedObject();
   
   S32 selectedId = -1;
   
   if (selectedObj != NULL)
	   selectedId = selectedObj->getId();
   
   if (getId() == selectedId)
	   sgLightManager::sgGlobalBlendColor = ColorF(5.0, 5.0, 5.0, 5.0);
   else sgLightManager::sgGlobalBlendColor = ColorF(1.0, 1.0, 1.0, 1.0);
[/b]

   installLights();

   glMatrixMode(GL_PROJECTION);
   glPushMatrix();
   state->setupObjectProjection(this);

   // irrelevant code not shown for brevity

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

   uninstallLights();
	
[b]
   // MAR: need to reset this so that other non-selectable objects (like TSSTatics) don't get rendered brightly
   // since this doesn't get reset automatically for those objects

   sgLightManager::sgGlobalBlendColor = ColorF(1.0, 1.0, 1.0, 1.0);
[/b]
   // irrelevant code not shown for brevity

As you see, if the ID of the current object is the same as the selected object, then it sets the value of sgLightManager::sgGlobalBlendColor to ColorF(5.0, 5.0, 5.0, 5.0). This specifes the RGB colors to all be brighter by a factor of 5, which makes the object essentially self-illuminating. Higher values appear to have little effect. Resetting the values to 1.0 will make the object have normal illumination. If you like, you can set the R, G, or B values differently than the others for a different effect; using (5.0, 1.0, 1.0, 1.0) will make your selected object a bright red color, for instance.


Example In Action

Here are a couple of pics showing the selection effect with brightening using TLK. The first image shows the object before selection; the second image is after clicking on the object to select it.

homepage.mac.com/rubes/select1.jpg
homepage.mac.com/rubes/select2.jpg

Conclusion

This is a pretty hefty set of changes, but most of them have already been done and tested from the earlier resources. Nevertheless, there are bound to be some issues and questions, so feel free to discuss them below in the feedback.

Enjoy!
Page «Previous 1 2 3 4 Last »
#1
05/04/2006 (1:42 pm)
Question, does the glow show up for all players, or just the person doing the selecting?
#2
05/04/2006 (3:08 pm)
the "highlight" is showed only for the persone who DID the selection.
No one else knows who selected which object...
#3
06/12/2006 (3:58 pm)
Good question, I haven't tried it in a multi-player situation. But I imagine bank is right since rendering and object selected should all be client-side so highlighting should only show up for the client who made the selection.
#4
07/10/2006 (12:50 pm)
Instead of using the eye vector and trying to do those calcs serverside, in the MMOKit we simply select the ObjectsID clientside pass that to the server and have the server do a look up on the ghostIndex. Thats also how it's done in the RTSKit I believe. The advantage of this method would be 2 fold.
#1 it reduces server load considerably since the server doesn't have to "try and figure out" what you clicked on at each mouse click.
#2 You can change the client selection method or even have multiple client selection methods all with out having to change the serverside functions at all.

There are a few other advantages as well.
If you are interested I'ld be glad to post the bit of code that handles the "what to pick" side of things for ya.

Regards,
Dreamer
#5
07/10/2006 (2:06 pm)
@Dreamer: Absolutely, I'd be interested in seeing that. Thanks...
#6
07/10/2006 (2:24 pm)
Also you are forgetting something here, GuiShapeNameHud is going to intercept the MouseDown Event, so you should add a handler to that to pass it up the chain.
#7
07/10/2006 (3:26 pm)
That's a good point, although in practice that doesn't seem to be the case. I've found that I can select AIPlayers without any issues, unless I'm forgetting some changes that I might have made...
#8
07/11/2006 (3:10 pm)
Oh I noticed one other thing...
You are using $SelectedObject on the server side, thats a very bad idea. What will happen is each client will be updating a global $SelectedObject at each mouse click.
I would exempt %client.player.getSelectedObject(); instead
#9
07/11/2006 (3:16 pm)
True...I've been working exclusively on a single-player game, so I didn't take that into account. That's an excellent point.
#10
07/14/2006 (4:06 pm)
Hey after adapting this to work properly in multiplayer mode as well, I noticed that in true single player mode if you target a single object sometimes multiple objects would be highlighted. I tracked this issue down to the fact that in singleplayer mode the client and server may still have different IDs for the same object and therefore different objects may effectively share the same ID. Or at least thats my best guess.
By moving the game into hosted multiplayer mode the issue was instantly resolved, I believe this is because the seperation between client and server object IDs becomes distinct. So anyways if anyone sees a similar issue that may very well be the cause of it.

Regards,
Dreamer
#11
07/14/2006 (4:44 pm)
That's interesting...I've only run this in single player mode, and I haven't yet seen any problem with more than one object being highlighted. What did you do to adapt it to multiplayer play other than what you mentioned in your last post?
#12
07/15/2006 (4:58 pm)
I've finally come across a situation like this, although I've only seen it happen with two particular objects. One of them is a selectable Item object, the other is a non-selectable TSStatic. When I select the Item object, both it and the TSStatic object light up, and they both return to normal when the Item is de-selected.

But there are two weird things about it:

1. It doesn't happen every time I run the game. I have to run the game about 5 times before I see this behavior again. Otherwise, it behaves normally.

2. When it does happen, the TSStatic doesn't always stay highlighted. If I rotate the camera around (but keep the Item within the camera's view), the TSStatic will sometimes become un-highlighted -- even when it and the Item object are right in the center of the camera's view. It seems to be position related: every time the camera moves to a particular view, the object will un-highlight, then highlight again when the camera returns to the original view.

Somehow I can't see this as related to the object's ID. The highlighting occurs only during the ShapeBase::renderObject method, and it determines the currently selected object the same way each time. How would different server/client IDs affect that?
#13
07/15/2006 (5:13 pm)
Rubes,

Make sure sgLightManager::sgGlobalBlendColor is set back to ColorF(1.0, 1.0, 1.0, 1.0) in ShapeBase::renderObject after rending is complete. Although the code makes sure all shape base objects are reinitialized before rendering, the TSStatic rending doesn't and the blend color could affect a TSStatic rendered after a selected shape base derived object.

This can also affect other non-shape base dts objects, like projectiles.
#14
07/15/2006 (5:25 pm)
Great point, John. That would explain why I only saw it with TSStatic objects. I put the call sgLightManager::sgGlobalBlendColor = ColorF(1.0, 1.0, 1.0, 1.0); right after the uninstallLights(); call and I haven't seen it happen again. Problem is, it only showed its face every once in awhile, so I can't be 100% sure it's gone yet.

Once I'm convinced of it I'll edit the code above.

Thanks!
#15
07/21/2006 (6:51 am)
I have implemented the code on linux platform and it works well

Thanks

On question for my information : why the selection is done on the server side ?
#16
07/24/2006 (12:04 pm)
One major reason for doing things on the server side is security. Other reasons could be that perhaps you only want one person to select something at a time, and the server keeps score.

I've not tried this one yet, but the implementation I made prior to Rubes' resource would detect and highlight selectable objects only on the client, then do an actual selection on the sever when the mouse was pressed (think WoW).

It might be nice to use a form of ObjectSelection across clients like when a group of players wants to target the same critter or something similar (think EQ).
#17
07/24/2006 (12:34 pm)
I think that would be the best way to do it, in hindsight...each client should be able to select something different, and highlighting should probably take place client-side. I'm still a newbie with respect to the whole client-server architecture, so that's the main reason why it wasn't done "correctly" in that respect.

Feel free to make suggestions on how best to do that...
#18
07/31/2006 (1:26 pm)
I am having some issues with this resource. The build seems to work fine, yet when i start the "mission" the game will crash on the loading of any dso with a StaticShape datablock in it

below is a typical error...

CP/server/scripts/SniperTarget.cs (52): Register object failed for object (null) of class StaticShape.

this is the code that it is referencing:

line 47: function StaticShapeData::create(%block)
line 48: {
line 49: %obj = new StaticShape()
line 51: {
line 52: dataBlock = %block;
line 53: };
line 54: return(%obj);
line 55: }


I am at a loss i've commented out 3-4 other objects but it keeps crashing at the item above the previous crash item in the game.cs file...

example:


exec("./stryker.cs");
exec("./Sniper.cs");
exec("./snperTarget.cs"); // Third File to Crash
exec("./bmw.cs"); // Second File to Crash
exec("./Van.cs"); // First File to crash



If anyone has a clue... pls help!
#19
07/31/2006 (1:39 pm)
That's odd...I thought the ::create() method was generally only called from the world editor, when you add a new object that way. Just loading individual objects from a mission file shouldn't call that routine. Do you keep defining the same routine in each one of those files?
#20
07/31/2006 (1:42 pm)
Lol yes, but why would the error just start with this resource? Previous to this resource no problems...
Page «Previous 1 2 3 4 Last »