Smooth turning

If you are a new Irrlicht Engine user, and have a newbie-question, this is the forum for you. You may also post general programming questions here.
Post Reply
JP
Posts: 4526
Joined: Tue Sep 13, 2005 2:56 pm
Location: UK
Contact:

Smooth turning

Post by JP »

I'm getting my agents to move around smoothly at the moment, so that they don't jump to their destination in one move, but actually look like they're walking there.

Anyway, i've got the code which moves then forward smoothly, but i'm having a bit of trouble getting them to turn smoothly, more precisely when to stop turning them.

I'm using a time based method, so each time through the game loop it checks if any agents are currently wishing to turn, and then turns them a certain amount based on the elapsed time if they do need turning.

Here's my code which checks whether they've reached their desired direction (if they have then they'll stop wanting to turn):

(apologies for it being java, should be simple enough to understand tho!)

Code: Select all

private boolean notAtDestinationDirection() {
        
        if (destinationDirection.getZ() == -1 && turningRight && forward.getZ() > -1)
            return true;
        else
            if (destinationDirection.getZ() == -1 && !turningRight && forward.getZ() < -1)
                return true;
            else
                if (destinationDirection.getX() == 1 && turningRight && forward.getX() < 1)
                    return true;
                else
                    if (destinationDirection.getX() == 1 && !turningRight && forward.getX() > 1)
                        return true;
                    else
                        if (destinationDirection.getZ() == 1 && turningRight && forward.getZ() < 1)
                            return true;
                        else
                            if (destinationDirection.getZ() == 1 && !turningRight && forward.getZ() > 1)
                                return true;
                            else
                                if (destinationDirection.getX() == -1 && turningRight && forward.getX() < -1)
                                    return true;
                                else
                                    if (destinationDirection.getX() == -1 && !turningRight && forward.getX() > -1)
                                        return true;
                                    else
                                        return false;
        
    }
Basically that sometimes works... Sometimes if i tell the agent to turn right from its starting direction it turns full circle and then stops at 90 degrees to the right of its original direction (which is what i want, but it should stop the first time it reaches its desired direction), but then sometimes it doesn't stop at any point and just keeps turning for as long as i can be bothered to watch it.

So there's something wrong with my code above i guess, as it's not stopping at the right point, so if anyone else has any experience with this (there should be lots of people!) then maybe you can see what's wrong or suggest a better method, just say if you need anymore code or explanation!
Image Image Image
dhenton9000
Posts: 395
Joined: Fri Apr 08, 2005 8:46 pm

Post by dhenton9000 »

is getZ() a float?

Does this mean that you have to hit exactly -1 or 1 to indicate I'm done turning? Maybe use a tolerance value?
gigolo

Post by gigolo »

lets say you store your y-rotation (the direction your agents are facing) in degrees (0-360). now you have your current direction in "old" and your new direction in "new" (so when your current direction is 90° and you wish to turn 45° to the right, then "new" is 135°).

Code: Select all

	// diff is the amount of degree to change
	f32 diff = new-old;
	f32 temp = 0;

	// make positive
	if(diff<0) 
		temp = -diff;
	else
		temp = diff;

	// find shortest direction, turn left or right?
	if(temp > 180)
		temp = -(360-temp);

	// if it was negative before we made it positive, do it again
	if(diff<0)
		temp = -temp;

	// save
	final_angle = temp;
the code above is not necessary, but it gives you the shortest way to turn (turning left is maybe shorter than turning right). Next is the code you call every frame (and pass the delta time to it)

Code: Select all

#define PLAYER_TURN_SPEED 2.5f
void	Update(u32 pTime)
{
	// degree this frame
	f32 thisFrame = pTime * PLAYER_TURN_SPEED;

	// check "direction"
	if( final_angle < 0 )
	{
		thisFrame = -thisFrame;
		if( thisFrame > final_angle)
		{
			mPlayer->SetYRotation( mPlayer->GetYRotation() + thisFrame );
			final_angle -= thisFrame;
		}
		else
		{
			mPlayer->SetYRotation( mPlayer->GetYRotation() + final_angle);
			final_angle = 0;
		}
	}
	else if( final_angle> 0 )
	{
		if( thisFrame < final_angle)
		{
			mPlayer->SetYRotation( mPlayer->GetYRotation() + thisFrame );
			mAngle -= thisFrame;
		}
		else
		{
			mPlayer->SetYRotation( mPlayer->GetYRotation() + final_angle);
			final_angle= 0;
		}
	}

	// check if turn is finished
	if(final_angle== 0)
	{ 
        // count down	
		f32 angle = mPlayer->GetYRotation();
		while(angle<0)
			angle+=360;
		while(angle>360)
			angle-=360;			

		// save and ready
		mPlayer->SetYRotation( angle );
		mFinished = true;
	}
}
The SetYRotation is just a little wrapper from myself, in fact it just calls setRotation on a scene-node (with the given y-rotation filled in), mPlayer is an agent in your case.
JP
Posts: 4526
Joined: Tue Sep 13, 2005 2:56 pm
Location: UK
Contact:

Post by JP »

Cheers gigolo, havn't got time to read or try your code right now but will do later!

dhenton: The destinationDirection is the desired direction the agent wishes to face, and then i was just checking to see if the forward vector of the agent (which way it is facing) has passed that desired direction or not, the < and > bits i think should have caused it to stop when it was anywhere past the desired direction, but i think you may be right that for some reason it is having to hit the value of 1 or -1 exactly, hence why it only stops sometimes, as it's having a varying amount of degrees added each frame so won't necessarily hit 1 or -1 each time.

Cheers for answering guys, hopefully it's solved now!
Image Image Image
JP
Posts: 4526
Joined: Tue Sep 13, 2005 2:56 pm
Location: UK
Contact:

Post by JP »

Gigolo, i tried out your code and it seems better than mine, but it turns about 90 degrees for the first turn, but then it does less than 90 for the next turn, and it actually takes 5 turns to get back to roughly the starting direction....

Any ideas?
Image Image Image
JP
Posts: 4526
Joined: Tue Sep 13, 2005 2:56 pm
Location: UK
Contact:

Post by JP »

Also i don't understand what most of this code is for:

Code: Select all

// check if turn is finished
   if(final_angle== 0)
   {
        // count down   
      f32 angle = mPlayer->GetYRotation();
      while(angle<0)
         angle+=360;
      while(angle>360)
         angle-=360;         

      // save and ready
      mPlayer->SetYRotation( angle );
      mFinished = true;
   }
I've replaced it with:

Code: Select all

   if(final_angle== 0){
      turning = false;
   }
where turning is a boolean which says whether the agent is currently turning or not, so when the final angle is zero the turn is complete so don't turn anymore.

What's the counting down and resetting the agents rotation for? :?
Image Image Image
JP
Posts: 4526
Joined: Tue Sep 13, 2005 2:56 pm
Location: UK
Contact:

Post by JP »

Nearly fixed the problem of the rotation not being quite 90 degrees, it was something to do with my final angle variable being an int, it's now a float and works better, but it doesn't always do exactly 90 degrees which is what i need, sometimes it does and sometimes it doesn't... seemingly atleast!
Image Image Image
pfo
Posts: 370
Joined: Mon Aug 29, 2005 10:54 pm
Location: http://web.utk.edu/~pfox1

Post by pfo »

Floating point innaccuraccies during rotations will make it nearly impossible to get something to face 'exactly' towards something else. You'd be best off defining some tolerance ammount and test if your new rotation is within that range. If I have a node and I click a button to apply a 360 rotation to it using setRotationDegrees, over time a 'fudge factor' will emerge and my object will not be facing forward anymore, even though 360 degree rotations should leave an object facing the same direction.
JP
Posts: 4526
Joined: Tue Sep 13, 2005 2:56 pm
Location: UK
Contact:

Post by JP »

Yeah good point, i can probably just check the rough direction it's facing when it thinks its facing the right way and then set it to the correct direction. This is fine at the moment as i only want it to move in a grid based fashion, but could be harder if i want to make it non-grid based movement...
Image Image Image
gigolo

Post by gigolo »

JP wrote:Also i don't understand what most of this code is for:

Code: Select all

// check if turn is finished
   if(final_angle== 0)
   {
        // count down   
      f32 angle = mPlayer->GetYRotation();
      while(angle<0)
         angle+=360;
      while(angle>360)
         angle-=360;         

      // save and ready
      mPlayer->SetYRotation( angle );
      mFinished = true;
   }
I've replaced it with:

Code: Select all

   if(final_angle== 0){
      turning = false;
   }
where turning is a boolean which says whether the agent is currently turning or not, so when the final angle is zero the turn is complete so don't turn anymore.

What's the counting down and resetting the agents rotation for? :?
I dont know if you need the counting down, but in some cases the result is under 0° or above 360° (in my case my players turned twice then). after that the result is again applied to the player - the mFinished is also a private variable from my project, nearly the same as your bool turning.
JP
Posts: 4526
Joined: Tue Sep 13, 2005 2:56 pm
Location: UK
Contact:

Post by JP »

Ok, well thanks to both of you it's now working perfectly i think, so thank you very much!! :D
Image Image Image
Post Reply