Game Development Community

dev|Pro Game Development Curriculum

Disabling physics on out of scope bots/mobs

by Peter Simard · 10/18/2007 (2:07 pm) · 9 comments

Installation

Place inside the Player public block in player.h:
void			setSleeping(bool flag);
	bool			isSleeping();
	void			updateSleepState();
	bool			mIsSleeping;

Modify Player::ProcessTick() in player.cc:
[b]From:[/b]
      if (!isGhost())
         updateAnimation(TickSec);

      PROFILE_START(Player_PhysicsSection);
      if(isServerObject() || (didRenderLastRender() || getControllingClient()))

[b]To:[/b]
      if (!isGhost())
      {
                 updateAnimation(TickSec);
	 	 updateSleepState();
      }

      PROFILE_START(Player_PhysicsSection);
      if ((isServerObject() || (didRenderLastRender() || getControllingClient())) && !isSleeping())

Add these functions to the end of player.cc:
void Player::updateSleepState()
{
      if(getControllingClient())
      {
           setSleeping(false);
           return;
      }

	Point3F localPosition = getPosition();
	SimGroup* pClientGroup = Sim::getClientGroup();
	F32	wakeRange = 1000;
	Sky * sky = gServerSceneGraph->getCurrentSky();
	if(sky)
                 wakeRange = sky->getVisibleDistance() * 2;

	for (SimGroup::iterator itr = pClientGroup->begin(); itr != pClientGroup->end(); itr++)
	{
		GameConnection* nc = static_cast<GameConnection*>(*itr);
		Player* plr = dynamic_cast<Player*>(nc->getControlObject());
		if (plr)
		{
			// This is a player that a client controls.
			// If it is within Scope * 2, the mob will not sleep
			if ((localPosition - plr->getPosition()).len() < wakeRange)
			{
				// It is within the wake range, so don't sleep
				setSleeping(false);
				return;
			}
		}
	}

	setSleeping(true);
}

void Player::setSleeping(bool flag)
{
	// Ignore it if its the same
	if(flag == mIsSleeping)
		return;

	mIsSleeping = flag;
	
	if(mIsSleeping)
	{
		Con::executef(this, 1, "onSleep");
	}
	else
	{
		Con::executef(this, 1, "onWake");
	}
}

bool Player::isSleeping()
{
	return mIsSleeping;
}

ConsoleMethod( Player, isSleeping, bool, 2, 2, "()")
{
    return object->isSleeping();
}

Note: You can use isSleeping() from script inside your AI calls to to further optimize performance.

#1
10/01/2007 (5:22 am)
I was thinking of something like this not just last night. This resource looks good. Quite useful for most of us I would think.

I would however optimize updateSleepState() by checking to see if this player is a control object itself. If it is you don't have to run the loop that checks every client. Right now on real player objects (as this works on both real players and ai players) it would get to the current player and eventually run the check on itself which of course would be 0 distance. Unless there was another player nearby that could trigger it before it got to itself.
#2
10/18/2007 (2:43 pm)
Very interesting.
I'll try to play with it soon.. But then I'll need to re-design AI logic in our project.. Huh...
#3
10/18/2007 (3:46 pm)
@Brian - If the object is being controlled by a client, it will early out of updateSleepState:

if(getControllingClient())
		return;
#4
10/19/2007 (4:51 am)
Nice job Peter, handy for certain things definitely!!
#5
10/19/2007 (11:23 am)
hey, this is great resource! Now I can run more zone servers on single computer, nice!

Btw, only one suggestion, change
if(getControllingClient())
   {
      setSleeping(false);
      return;
   }
This is needed in case the player object created without being controlled by client, and later the client attached to it. Or if you use camera and "drop player to camera". Perfect!
#6
10/19/2007 (2:59 pm)
Thanks for the fix bank. I have updated the resource.
#7
12/27/2007 (10:34 pm)
im not sure where the Player public block is in player.h, im not that experienced in the c++ aspect of torque thanks
#8
09/07/2009 (1:18 pm)
Minor update.
replace "updateSleepState()" with this:
void Player::updateSleepState()
{
   if(getControllingClient())
   {
      setSleeping(false);
      return;
   }
   Point3F localPosition = getPosition();
   SimGroup* pClientGroup = Sim::getClientGroup();
   F32	wakeRange = 1000;
   Sky * sky = gServerSceneGraph->getCurrentSky();
   if(sky)
      wakeRange = sky->getVisibleDistance() * 2;
   const F32 wakeRangeSq = wakeRange * wakeRange;
   for (SimGroup::iterator itr = pClientGroup->begin(); itr != pClientGroup->end(); itr++)
   {
      GameConnection* nc = static_cast<GameConnection*>(*itr);
      GameBase* ctrlobj = dynamic_cast<GameBase*>(nc->getControlObject());
      if (ctrlobj && ctrlobj->getTypeMask() & (PlayerObjectType | VehicleObjectType | CameraObjectType))
      {
         // This is a control object that a client controls.
         // If it is within Scope * 2, the mob will not sleep
         if ((localPosition - ctrlobj->getPosition()).lenSquared() < wakeRangeSq)
         {
            // It is within the wake range, so don't sleep
            setSleeping(false);
            return;
         }
      }
   }
   setSleeping(true);
}
What's new:
It's now easier to configure when AI will sleep.
My code checks if the control object is one of: Player, Vehicle, Camera.
Before, it was only allowing Player's as control object, so if you "dead", control object switches to the Camera and AI stopped "thinking". So now you can easily configure it to your needs.

Second change - we now comparing lenSquared() to save processor ticks on calculating real distance.
#9
09/23/2009 (9:21 am)
LOL, judging by my previous statement and the date it was made, I had no idea what I was doing back then... LMAO, good stuff.

I should probably look into this again since I obviously didnt know how to make it work before.