This topic is 3916 days old which is more than the 365 day threshold we allow for new replies. Please post a new topic.

## Recommended Posts

ok, im going round the bend,ive tried to solve this problem from so many different angles. can someone please tell me the best way to solve this problem: I want an AI character to go to a destination, but i also want him to face that destination. And i dont want him to turn around instantly i would like him to do it over time, this includes situations where i sudenly change its direction. -turn to face a destination i.e. another character or object -move toward that destination at a speed set by me. it should be simple, and im sure ive done it before using trig. However i couldnt get my old logic to work again and then someone recomended that i should use vector math and so far, using vector math i seem to only get one or the other, either the object will go to the destination but not face it or it will face it and go in the wrong direction. im really confused now, here is a sample of some of my now messy screwed around with code. key: an object has all these atributes stored as vectors: pos = posistion dir = direction dest = destination an object also has a angle varible stored as a float. diff = difference and is always a created there and then variable. here is some code to go to the destination, which has been screwed around with alot but it still works
	RVector diff = dir - pos;
int xFactor = 1;
int zFactor = 1;

if(diff.x < 0)
{
xFactor = -1;
}
if(diff.z < 0)
{
zFactor = -1;
}
if((dir - pos).Length() >5)
{
diff.Normalize(); //turn diff into diffs unit vector
//diff.x *= xFactor;
//diff.z *= zFactor;
//this->pos.setXYZ(this->pos.x + (this->dir.x),1,this->pos.z + (this->dir.z));
this->pos.setXYZ(this->pos.x + (diff.x)*this->getSpeed(),1,this->pos.z + (diff.z)*this->getSpeed());
//	cout << "\n("<<this->pos.x <<","<<this->pos.y << ")";
}


here is the bit thats not working at the minute, where i use the direction vector to give me the angle that the object should be rotated by:
void RObj::setAngleByDir()
{
//	float x = dir.x - pos.x;
int xFactor = 1;
RVector diff = dir - pos;
if(diff.x < 0)
{
xFactor = -1;
}
diff.Normalize();

float z = diff.z;// * xFactor;
float x = diff.x * xFactor;
float rawAngle = asin(x); //get raw angle from asin
AngleDir = rawAngle * (180/PI);
AngleDir = AngleDir;

rawAngle = acos((z)); //get raw angle from asin
float AngleDirZ = rawAngle * (180/PI);

if(AngleDir - AngleDirZ > 10)
{
//AngleDir = (AngleDir+AngleDirZ)/2;
}
AngleDir = (AngleDir+AngleDirZ)/2;
//	AngleDir -= AngleDirZ;
}


something im using to try and turn the object round over time: this doesnt work either. but for now im using an alternate to see the results of the above code.
 	int reqAngle = this->getNeededAngle();
int theAngle = this->getAngle();
int speed = this->getSpeed()*10;

if(!(reqAngle - theAngle > -speed && reqAngle - theAngle < speed)) //atempt to change direction until facing that of the destination.
{
if(AngleDir - 180 < reqAngle-speed)
{
this->AngleDir-=(speed);
if(AngleDir > 360)
{
AngleDir = 0;
}

setDirByAngle(AngleDir);
}
else if(AngleDir - 180 > reqAngle+speed)
{
this->AngleDir+=(speed);
if(AngleDir < 0)
{
AngleDir = 360;
}

setDirByAngle(AngleDir);
}
}
//with the get required angle code looking like this:
float RObj::getNeededAngle()
{

float x = dest.x; //dest standing for destination vector
float y = dest.y;
float num = x* (PI/180);

float rawAngle = asin(num);
float angle = rawAngle * (180/PI);
if(angle < 0){angle = -angle;}
return angle;
}
//and the set direction by angle:
void RObj::setDirByAngle(float angleS)
{
AngleDir = angleS;
float x = dir.x;
float z = dir.z;
float sinAngle = sin((AngleDir)*(PI/180));
float cosAngle = cos((AngleDir)*(PI/180));

x = pos.x + sinAngle*1;
z = pos.z + cosAngle*1;

x = sinAngle*1;
z = cosAngle*1;

dir.setXYZ(x,1,z);

}


alternative (turns instantly)

this->dir.setXYZ(dest.x,1,dest.z); //dir standing for direction, dest meaning destination
this->setAngleByDir();


but its all got quite complex, more complex then my original logic used to work, so im starting to think that vector math is the wrong thing to use. Please help, can you please point me to good resources or show me some logic or even code that would help! i would be grateful for any help given, thank you for taking the time to read my post and thank you in advance for any replies! :-) -Chris

-me

##### Share on other sites
Assuming this is for 2d, look at the help for the atan2 function. That will give you the bearing in radians from the player position to the target position.

After that it's just a case of smoothly stepping the angle from the current player heading to the required heading, and smoothly stepping the speed and moving in the direction of the current heading.

##### Share on other sites
Before I even jump in to attempt to help you, it looks like you're using a 3D engine to render your 2D game. This is a great idea, however, my recommendation is that you position the camera so that you are only operating along the x and y axis in either the 1st or 4th quadrant (if you've done over 2D games, the 4th quadrant might feel more familiar).

This is just a recommendation that might prevent you from getting confused during those late night sessions where you realize "Aw crap, I didn't mean to put the "z" in the "y"" or something of that sort... enough speculation, on with the code!

	RVector diff = dir - pos;	int xFactor = 1;	int zFactor = 1;	if(diff.x < 0)	{		xFactor = -1;	}	if(diff.z < 0)	{		zFactor = -1;	}	if((dir - pos).Length() >5)	{		diff.Normalize(); //turn diff into diffs unit vector		this->pos.setXYZ(this->pos.x + (diff.x)*this->getSpeed(),1,this->pos.z + (diff.z)*this->getSpeed()); 	}

^ This is an interesting way to approach this - it should work fine (as you have already reported).

void RObj::setAngleByDir(){//	float x = dir.x - pos.x;	int xFactor = 1;	RVector diff = dir - pos;	if(diff.x < 0)	{		xFactor = -1;	}	diff.Normalize();		float z = diff.z;// * xFactor;	float x = diff.x * xFactor;	float rawAngle = asin(x); //get raw angle from asin	AngleDir = rawAngle * (180/PI);	AngleDir = AngleDir;		  rawAngle = acos((z)); //get raw angle from asin	float AngleDirZ = rawAngle * (180/PI);		if(AngleDir - AngleDirZ > 10)	{		//AngleDir = (AngleDir+AngleDirZ)/2;	}	AngleDir = (AngleDir+AngleDirZ)/2;//	AngleDir -= AngleDirZ;}

I wish I could go more in depth for you here, draw you a picture of a triangle and demonstrate a bit of right triangle trig - however, I am not at home and don't really have a means of doing that. I recommend you google around and understand what it means to call something like acos(z) and how truly incorrect it is.

From now on: y = z;

It IS possible to solve the angle from an acos call - for example, consider the following statement:

Thus, in solving for theta:

Where do you think you made your error? How would you correct this?

As tidy said, look up atan2 (its a standard function from the c-lib). Its usage is recommended more because grabbing the "length" of a vector is a more costly task (sqrt is involved) then using your, already stored, y variable and x variable.

Consider that:
tan theta = opposite_length/adjacent_length = dir.y / dir.x;

Thus, solving for theta:
atan (opposite_length/adjacent_length) = atan(dir.y / dir.x) = theta;

An image I added in after thought- in case you don't understand "opposite length" and "hypotenuse," etc...

So from now on, in order to "grab" your angle:
atan(dir.y/dir.x) will give you the angle in radians.

Also, as a side note, I noticed you made a call "AngleDir = AngleDir" - this either shows that you don't know what you're doing (I doubt this) or that you're getting so desperate in attempting to get this to work that you're going to botch up a LOT. Just stop in situations like this, and make sure you can explain everything to yourself, and to someone else. It's a humiliating experience asking for any kind of help from the people in #gamedev, so I don't know if I recommend that, but posting on the forums (as you did, but SOONER) is recommended. But, please, only do so after you have put some serious thought in to it, and attempt to explain your understanding of the mathematics before you do so - so it doesn't look like you're just coming to the forums to solve problems at the first sign of trouble.

It looks evident that the rest of your problems derive from the same "unfamiliarness" with Trigonometry. If you want to adjust your angle - simply get the current angle, adjust by "speed", then set the angle again.

So:

Note: Is it just me or is the + symbol not showing up? Just in case its not just a problem in the "preview" box - angle = angle + speed; or you could use the shorthand version angle += speed;

float angle = atan2(dir.y, dir.x);angle = angle + speed; // speed can be negative too, in that case, it'll spin the other way.setNewDirection(dir.angle);

And I'm sorry to say, but I've gotta cut this off right now, my girlfriend has to be somewhere, and I need to leave :P

When I get home I'll try to correct a few misunderstandings I might see arising - feel free to leave more questions as well. I might have over-complicated it, but I think the understanding is more important.

##### Share on other sites
Thanks for the quick replies everyone! just to clarify a few things it is actually a program in 3D but the problem is 2D as i dont want the objects y position to change in anyway. which is why im using x and z. Also i have realised that i was being really stupid, thanks AfroFire and tidy i should have being trying to use tan for my reverse back. i was just thinking than i got there with sin, the best thing i could do was simple reverse the function and just did a stupid thing to use sin, i had actually looked at some results in excel and i could see the inaccuracy!

so now im using tan, and it nearly works however....im now using:
void RObj::setAngleByDir(){	RVector diff = dir - pos;	float tanAngle = atan(diff.x/diff.z);	AngleDir = tanAngle * (180/PI);}

which very nearly works, however, i will reach certain points and the object will completely rotate 180 and then be facing the wrong direction but will turn correctly, if that makes sense. so its back face will end up facing me and then halfway through switch 180 again!

***********EDIT******************************
WHOA WHOA WHOA
atan2 is magical, i used atan2 instead of atan and magically everthing works! why is atan2 better then atan? wierd! but yey!!!! everything seems to be working how i origninally designed it! the charcter sometimes likes to turn around in a full circle sometimes which i dont understarnd, but with a bit of tweaking it should work! thank you everyone that atan2 function was my life saver aswel as the fresh eye to the problem it was a good slap in my face! because i know my trig okish i just wasnt implenting it properly!

p.s. the Angle = Angle thing was actually a rudimentry way of giving myself a break point to see what Angle was at that point. i know it was a bad thing to do but it worked well. :-) mystery solved. lol

- sincerly thanks everyone ill see if i can get this going properly (good few weeks yet, if i dont give up on it) and then post up a demo or something! it should be a war type game where you can control troops.
- Chris

***********/Edit*****************************

Ok after basically just kicking myself and just re-writing all the code it now looks more like this:
//the bit that uses the functions:void RAICharacter::OnUpdate(float deltaTime){	switch(this->CurrentState)	{	case(FOLLOW):		this->setDestination(this->GetTarget()->getPos()); // destination for object == the position of the target		break;	};}void RAICharacter::OnAnimate(float deltaTime){	faceDestOverTime(0);//	goToDestTest();}

the new face dest over time function which works:
void RObj::faceDestOverTime(float deltaTime){		int reqAngle = this->getNeededAngle();		if(reqAngle < 0) //sometimes reqAngle is a negative	{		reqAngle = reqAngle + 360;	}	if(AngleDir > reqAngle)	{		AngleDir--;		}	if(AngleDir < reqAngle)	{		AngleDir++;		}	if(AngleDir > 360)	{		AngleDir = 0;	}	if(AngleDir < 0)	{		AngleDir = 360;	}	//AngleDir= -AngleDir;	this->setDirByAngle(AngleDir);	}

the getNeededAngle which doesnt work 100%
float RObj::getNeededAngle(){	RVector diff = dest - pos;	float tanAngle = atan(diff.x/diff.z);	float Angle = tanAngle * (180/PI);	return Angle;}

the setDirByAngle function which works:
void RObj::setDirByAngle(float angleS){	AngleDir = angleS;	float x = dir.x;	float z = dir.z;	float sinAngle = sin((AngleDir)*(PI/180));	float cosAngle = cos((AngleDir)*(PI/180));	//x = pos.x + sinAngle*1;	//z = pos.z + cosAngle*1;		x = sinAngle*1;	z = cosAngle*1;	dir.setXYZ(x,1,z);}

but it still does that flippy thing which is irratating!

##### Share on other sites
When you want to rotate about a single axis to face a particular direction you can choose the short or long way around - it sounds like this is the problem you are seeing, where sometimes your player will take the long way around when you always want to take the short way. You need a function that takes a current and desired angle and returns the smallest angular difference between them which will indicate if you need to increase or decrease your current angle to reach the desired angle.

Edit: I forgot to add the reason atan2 gives you better results than your original approach is that atan2 corrects the angle returned by the atan function based on the quadrant of the circle that the line between the source and target lies in (imagine a circle with origin at the source position, radius = distance to target, the target lies on the perimeter of the circle, the line goes from the origin to the target).

[Edited by - tidy on October 26, 2007 4:35:31 PM]

##### Share on other sites
Quote:
 Original post by tidyYou need a function that takes a current and desired angle and returns the smallest angular difference between them which will indicate if you need to increase or decrease your current angle to reach the desired angle.

yer this was the function that i have had great difficulty achieving, it would always nearly work and the logic would always make sense! i have now found a way to get it to work, originally the problem was if Ai was at 10 degrees and his destination angle was 340 obviously it would be beter to go backward, and catering for this situation and he opposite and so on never seemed to work untill at last i now have a solution!
Original post by tidy
Edit: I forgot to add the reason atan2 gives you better results than your original approach is that atan2 corrects the angle returned by the atan function based on the quadrant of the circle that the line between the source and target lies in (imagine a circle with origin at the source position, radius = distance to target, the target lies on the perimeter of the circle, the line goes from the origin to the target).[/
ah thanks, that makes sense it almost sounds like what i was doing with the turning function, correcting the angle difference in an abstrac sense. if im honest it makes sense but it also goes a bit over my head! lol!

Thanks again to everyone who posted, my crowd system is starting to take shape now, im having a bit of trouble with collision reponse which i may post soon as thats been a slight annoyance for a while, the basic collision response is relatively easy but actually writing one where the AI makes a sensible change in direction with out looking like a brain dead moron is actually proving to be a challenge!

-Chris

1. 1
2. 2
3. 3
Rutin
19
4. 4
5. 5

• 10
• 14
• 30
• 13
• 11
• ### Forum Statistics

• Total Topics
631782
• Total Posts
3002332
×