Game Development Community

Problem with RigidShape and setHidden()

by Rubes · in Torque Game Engine · 06/30/2006 (10:09 am) · 18 replies

Here's one that has me puzzled...

I just implemented the RigidShape resource for my project, but I'm having an issue with it. In my game, I need to use the setHidden() method to hide objects at times, and this would apply to rigidShape objects. However, when I call the setHidden(true) method on a rigidShape, the Torque engine crashes hard.

I've tried my best to track down where in the code this might be happening, but I've only gotten so far. It looks like the call to setHidden goes from there to SceneObject::removeFromScene(). From there, it makes two calls, first to SceneGraph::removeObjectFromScene(), and then to its Container::removeObject() method.

It seems to get through all of this fine right up to the removeObject() method. The engine makes a call to getContainer(), and then to the Container::removeObject() method, but I can't quite figure out where exactly in that area the engine trips up. I have been adding my rigidShapes to my world in a SimGroup, and using the setHidden() method with other object types (like Items) works fine.

I've also noticed that the startFade() method does nothing on rigidShapes. No fading, but no crashing either. My Item objects will fade just fine.

If anyone with a better knowledge of this code could help me out, I'd be grateful. Thanks...

#1
06/30/2006 (10:23 am)
Is RigidShape a subclass of shapeBase? I was under the impression that setHidden() and startFade() was exclusive to shapeBase, but if they were - you wouldn't get a crash, right?
#2
06/30/2006 (10:27 am)
Yes, rigidShape is a subclass of ShapeBase:

class RigidShape: public ShapeBase

And if you take a rigidShape and do a dump() command, it does show all of the methods common to the ShapeBase class.
#3
07/05/2006 (9:45 am)
Just a bump to see if anyone can help out with this...if anyone is currently using the RigidShape resource, could you try the setHidden(true) method on one of the rigidShape objects to see if it crashes?

Thanks...
#4
07/06/2006 (7:58 am)
I dont use sethidden , but for some info, it crashes !
#5
07/06/2006 (8:05 am)
@Billy: For some info like what? You can try using the setHidden command from the console. Just get the ID of one of your rigidShapes, then type this into the console:

<objectID>.setHidden(true);

Thanks...
#6
07/07/2006 (8:22 am)
Ye i know how to sethidden .
I told you it crash, maybe i wasn't clear enough:)
If you find a solution then I'm happy to hear about it.
#7
07/07/2006 (8:28 am)
Ah, gotcha. Thanks. I'm still hoping for some guidance.
#8
07/08/2006 (11:34 am)
Here's what I think I've found so far:

It looks like the engine gets all the way through the setHidden method for the RigidShape object without crashing. I'm pretty sure the crash is occurring in the Container::findObjects() method. Problem is that most of this material is beyond my knowledge right now, so I can't quite work out what's happening in it.

If anyone is more familiar with this stuff and could postulate why running setHidden on a RigidShape might be causing this method to bail, I'd be appreciative.
#9
07/09/2006 (1:40 am)
I think you may be correct to suspect Container::findObjects(), but I don't think it's actually the Container class that's at fault. After a bit of digging, I suspect this may be what's happening:

setHidden() removes the object from its container, yet it leaves the object in the process lists. So, processTick() comes around on the hidden RigidShape, which results in a call to ShapeBase::updateContainer() (line 685 in rigidShape.cc). Well, ShapeBase::updateContainer() then tries to call findObjects() on mContainer (line 1288 in shapeBase.cc), except mContainer is now NULL because the RigidShape object has been removed from the container. Calling functions on null pointers makes bad things happen. :P

RigidShape::processTick() should check that either mHidden is false or that mContainer isn't NULL before calling updateContainer().

Furthermore, there are a number of other places where mContainer is used (either directly or as a result of a getContainer() call) where it isn't checked. Mostly for ray casts ("getContainer()->castRay(...)"). These too would fail if they are called when the object is hidden. In RigidShape, the only ray casts I see are done client side. I think the client ghost is actually removed when the server object is hidden, so if that is true, client side ray casts using mContainer should be safe. I can't say for certain, but regardless, start with the call to updateContainer(), and if you're still experiencing crashes then I would suggest checking the mContainer->castRay() or getContainer()->castRay() calls to see if they're being executed on a NULL container pointer.

Also, I'm curious.. has anyone tried calling setHidden() on a Vehicle or Player object? I suspect those classes may suffer from the same problem.

---

Unrelated side note: "updateContainer" seems to me to be a misnamed function, as it doesn't actually update the container. Rather, it updates several ShapeBase physics variables depending on the shape's position in relation to PhysicalZones or WaterBlocks. The only relation updateContainer has to the shape's container is where it uses the container's findObjects() function to search for WaterBlocks or PhysicalZones. Why it's called "updateContainer" is a mystery to me. The function really should be called something like "updatePhysicsVariables", I think.
#10
07/09/2006 (8:32 am)
@Scott: Thanks, that explanation is really helpful. And you're right, calling setHidden(true) on the player object also causes a crash.

I did try a quick check to see if adding

if (getContainer()) { }

around the call to updateContainer in RigidShape would fix things, and it didn't. There are only a couple of references to getContainer() and mContainer in RigidShape, so I could try those as well.

However, one question that comes up is, why do certain shape classes work with setHidden()? The Item class, for instance, works fine. Interestingly, there are no references to mContainer in Item.cc, but there is one (unprotected) call to getContainer(), and there is also an unprotected call to updateContainer (in the Item::updatePos method). So I wonder why there would be no problems for Items but crashes with RigidShapes or Players? I've pored over the code for those classes but I can't really figure that out.
#11
07/09/2006 (8:39 am)
You know that sethidden disables collision as well right? i imagine that's the part that is causing the crashing.

Players, vehicles, and rigid shape, actively check for collision... so there's a good chance this is the issue.

if you just want to hide and object, just do a .startfade command.

%object.startfade(%delay, true);

should be able to set the delay to 0 and it should happen instantly.
#12
07/09/2006 (8:46 am)
Actually yes, the goal is to completely remove an object, not just make it invisible, and I need to do this without actually deleting the object. setHidden does exactly what I need.

That's a good point, though...
#13
07/09/2006 (1:12 pm)
@Ramen: Yes, I think you're correct. Collisions too are a problem, and I think it's for the same reason...

Vehicles, Players, and RigidShapes all call a function "updateWorkingCollisionSet()" which in turn calls Convex::updateWorkingList(), which has a call to getContainer() (see convex.cc line 460).

So, you'd want to avoid processing collisions as well when hidden, since the "working collision set" cannot be updated without a container.

Might be best to avoid running processTick() entirely while hidden. I put a brief
if (mHidden) return;
at the start of processTick() for my vehicle class, and I can now hide them without causing a crash.

BTW, Item works because updatePos() isn't called when hidden (see item.cc line 368).
#14
07/09/2006 (1:49 pm)
I see. well that's good to know. I had a thread awhile back where i asked a similiar question.

www.garagegames.com/mg/forums/result.thread.php?qt=42860
#15
07/09/2006 (2:32 pm)
@Scott--

Wow, you're absolutely right. That worked perfectly! And you're right, I didn't catch that call to isHidden() in Item.cc. Nice catch.
#16
04/17/2008 (3:53 am)
It's currently TGE version 1.5.2 and Scott's fix is still needed and valid for the rigidshapes
It was helpful, thanks :)

Is there a reason why this is not in the HEAD? Not a true solution?
#17
12/17/2008 (1:33 pm)
Not processing ticks makes sense to me since it seems like setHidden is used when you want to suspend an object from game play without actually making its removal permanent. The solution also worked for me on a similar sounding problem. In fact, after reading both of Scott's posts, I tried an experiment which actually extended upon the solution (I think... if anyone more experienced sees a problem with it let me know).

Specifically, I went into ShapeBase::setHidden, and added a line near the beginning which reads...

this->setProcessTick( ! hidden );

Essentially, switching off processing of ticks for the object when it is about to be hidden, and switching it back on again when it comes out of hiding. So far, this has solved the problem for Players, Vehicles, and RigidShapes in a single move, as well as any other, as yet uncreated ShapeBase classes that may eventually run afoul of this issue in the future.

There is, however, one really hazardous situation in which this fails: control objects. Yeah, I know. Don't ask what sprained mental process led me to test it with a control object. I just did, and it crashed hard.

Of course, I can't think of a situation where you would deliberately hide your control object, but that doesn't mean it can't happen accidentally. Consider a container search that iteratively applies some processing to all objects inside a target region, or if you inadvertently use setControlObject on an object that was previously hidden. So it seemed prudent to also add some low overhead sanity checks.

1.) In ShapeBase::setHidden, at the very beginning (before even the line I listed above), I added...

// Hiding control objects is bad for your game's health.  Just say no.
      GameConnection *cl = this->getControllingClient();
	  if (cl && cl->getControlObject()==this)
	  {
	        Con::warnf("Warn: Object %d could not be hidden: currently in use as a control object.",this->getId());
	        return;
	  }

Explicitly disallowing control objects as eligible for hiding and sending a little friendly debugging feedback to the game designer to let them know what nearly happened here.

Then, in GameConnection::setControlObject, just below the check that test to see if the object already is the control object, I added...

if (obj->isHidden())
           obj->setHidden(false);

...to implicitly kick the object out of hiding prior to allowing it to assume its new role as a control object.

Of course, I could also have taken the HAL9000 approach ("I'm afraid I can't let you do that Dave"), and just disallowed the use of the specified object as a control object, but this seems more likely to cause side effects that require actual debugging during development.
#18
12/17/2008 (3:43 pm)
Thats some helpful information, glade i saw this dated thread.
That "this->setProcessTick( ! hidden );" bit also helps on other things, what a good idea.