# Aiming from a pivot

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

## Recommended Posts

I'm having some troubles finding the correct angle to rotate a pointing object on an offset pivot point. To visualize, imagine a tank with a turret on it's right side. About 5 feet right and 3 feet up from the tank's rotating pivot origin. Here's a picture: Here's another with more angles: Tank is the blue circle, and the turret is the green blotch. The big X is the real target. The little x is what I need to calculate; it represents the target relative to the tank, instead of it's turret. I want to rotate the tank, not the turret. So I need to account for the turret's offset. I'm just not sure how. The tank's rotation always needs to be slightly less than the angle from the tank to the target. This amount is represented by the yellow shading. But this is only because the turret is on the right side; if it was on the left, the rotation would need to be slightly higher. Also, I think the distance the turret is from the origin of the tank and the distance to the target comes into account. It seems simple, but I'm not having any luck. I would really appreciate any help.

##### Share on other sites
Check this..
FLOAT distance_to_target = length(Tank - Target);VECTOR some_point(0,0,distance_to_target); // pretend pointVECTOR dir_to_turret = normalize( TurretOffset - some_point ); // dir to turret from pretendVECTOR dir_to_tank(0,0,-1) // dir to tank from pretendFLOAT magic_angle = dir_to_turret.AngleLength( dir_to_tank );

If I get the angle from the tank origin to the target and subtract "magic_angle", I get the exact angle to rotate the tank so the turret is pointing at the target. If the tank angle is negative, I have to negate magic_angle too, though, before subtracting it. The only variable that changes here is the distance.

This is a crazy hack. But maybe it points out what I'm trying to do? How do I calculate magic_angle without throwing pretend points around in pretend space? [smile]

Thanks again for any help.

edit: Here's an even more simplified version:
FLOAT td = target_distance - turret_offset.z;td /= sqrtf( (turret_offset.x * turret_offset.x) + (td * td) );FLOAT magic_angle = acosf( td );

Is this the only way to do it?

[Edited by - Jiia on March 7, 2005 4:11:31 PM]

##### Share on other sites
Quote:
 FLOAT td = target_distance - turret_offset.z;td /= sqrtf( (turret_offset.x * turret_offset.x) + (td * td) );FLOAT magic_angle = acosf( td );
Whoa...does that work? I must be missing something, 'cause I have no idea what's going on there! :-)

Anyway, I think you were on the right track initially, with translating the big x to the little x, and then basing your tank's rotation on the position of the little x. Let's say turretOffset is the offset to your turret in object space. To get the little x, you just need to subtract turretOffset from the big x. The only complication is that turretOffset will need to be in world coordinates, not object coordinates, so you have to rotate it first.

Here's some code that might do what you need. It looks like you're using 3d vectors working in the xz plane, so that's how I wrote it. Just pass the tank's angle, the target position, and the turret offset (in object coordinates) to the function, and you should get back the adjusted target. Then steer the tank toward the adjusted target in the usual way.

Vector3 AdjustTarget(float angle, const Vector3& target, const Vector3& turretOffset){//	angle = -angle; // Un-comment this if it rotates the wrong way    float s = sinf(angle);    float c = cosf(angle);        float rx = c * turretOffset.x - s * turretOffset.z;    float rz = s * turretOffset.x + c * turretOffset.z;        return target - Vector3(rx, 0.0f, rz);}

I haven't tested that or anything, so I may or may not have gotten it right. But you might give it a try and see what happens.

##### Share on other sites
Ahh, jyk. You're on my credits list as a math advisor, did ya know that? I can change it to your real name at some point if you want. Don't worry, if my game sucks, I won't mention who helped me.

Quote:
Original post by jyk
Quote:
 FLOAT td = target_distance - turret_offset.z;td /= sqrtf( (turret_offset.x * turret_offset.x) + (td * td) );FLOAT magic_angle = acosf( td );
Whoa...does that work? I must be missing something, 'cause I have no idea what's going on there! :-)

Yeah. It's the same as the unsimplified code, just that so many values were known, it reduced down to that. The angle to rotate the turret to the tank, with the target as a pivot, is the same angle I need to adjust the tank's rotation by. I noticed the black space next to the yellow shading was the exact same shape as the yellow shading, within the confines of the two target lines, heh. It started as a hacked guess but ended up almost reasonable. I'm still not so sure it's the easiest or best way.

Quote:
 Original post by jykAnyway, I think you were on the right track initially, with translating the big x to the little x, and then basing your tank's rotation on the position of the little x. Let's say turretOffset is the offset to your turret in object space. To get the little x, you just need to subtract turretOffset from the big x. The only complication is that turretOffset will need to be in world coordinates, not object coordinates, so you have to rotate it first.

I was trying like hell to do this, but how is it possible? The only way to know the offset of the turret from the tank in world space is to know the angle to rotate the tank. But the goal of the whole operation is to know the the angle to rotate the tank. If you use the angle of the tank to the big X, the offset of the turret is incorrect. So no way to get the turret location without rotating to the correct angle first.

Quote:
 Original post by jykHere's some code that might do what you need. It looks like you're using 3d vectors working in the xz plane.

Yep, 3D vectors in ground space. Do I pass the current angle of the tank, before it tries to rotate at all toward any targets? Or do I pass the angle from the tank's facing to the target? I might be way off, but it looks like the function just moves the target with the turret offset based on the current rotation of the tank? Or by the angle to the target from the tank origin?
Doesn't this make the method progressive? Like the tank needs to rotate step by step, using the turret's changing location to get the angle? What if I needed to rotate the tank in a single step?

Sorry if I'm way off. I've been at this for a while, my brain is dust.

By the way, my tank isn't really a tank, it's a human. His current weapon is the "turret". They use their upper body to aim vertically and their legs to twist. The twisting is the "tank rotation", and I don't really have an angle for it. Twist rotates the character on his pivot point but while animating the legs as bent. So the only angle I really have is the character's forward direction. He won't have any twist until I perform the entire calculation. This means no progressive turret positions :(

The way your function works is exactly how I get him to spin-turn to aim while moving. But it doesn't work for the twist, since I have no idea where the gun will be until he twists.

##### Share on other sites
Ok, I was curious so I wrote a little demo of two tanks running around. One has a turret off to the side, and targets the other with it. I drew a targeting line from the turret to see exactly where it was aiming, and used my method for targeting. The targeting works perfectly - even with the turret off to the side, the targeting line crosses the center of the other tank at all times. Well, I say *my* method, but again, you hinted at it in your original post.

If that other code you wrote is correct, then you're too smart for me 'cause I still can't figure it out :-) In any case, though, I think the other approach is probably better. It's just a matter of figuring out an implementation that works for you.

The basic problem is to figure out whether to turn left or right based on the position of a target. I take it you already have this solved? If so, how are you doing it?

The only complication here is offsetting the target by the turret position.

In the code I gave you, 'angle' is not the angle by which to turn, but the angle that represents the current orientation of the tank or character. You mentioned you didn't have an angle for the character. What do you have? A matrix? Direction vector? Quaternion? Whatever it is, it can be used to figure out the offset for the target. If you can give me that info I'll be glad to fill in the details.

I'm pretty sure that this is the optimal solution for your problem. It's just a matter of fitting the implementation to your particular circumstances.

##### Share on other sites
Quote:
 Original post by jykOk, I was curious so I wrote a little demo of two tanks running around. One has a turret off to the side, and targets the other with it. I drew a targeting line from the turret to see exactly where it was aiming, and used my method for targeting. The targeting works perfectly - even with the turret off to the side, the targeting line crosses the center of the other tank at all times. Well, I say *my* method, but again, you hinted at it in your original post.

It works for me too, when the character is moving and spinning his whole body to change horizontal aiming. Are you sure your tank can rotate in a single step from any orientation to the target? The only reason this method works for my character is because each frame, I orientate the turret to his current facing and get the angle to the target from it. This angle is added to his current angle and set as his goal to spin more. But it won't work in one single rotate procedure. Again, I might be confused as usual and we may be doing two completely different things. But I think they are the same, except that yours is much more efficient, not having to multiply a vector with a matrix. Here's my code for it:
VECTOR turret_offset = RealTurretOffset;turret_offset.OrientateXZ( GetTransform() ); // Rotates the turret offset by the tank orientationVECTOR relative_target = RealTarget - turret_offset;character_try_direction = normalize( relative_target - char_pos );// heres the vector orientate codeVOID VECTOR::OrientateXZ(const D3DXMATRIX &mat){	FLOAT tx = x;	FLOAT tz = z;	x = (tx * mat._11) + (tz * mat._31);	z = (tx * mat._13) + (tz * mat._33);}

Quote:
 Original post by jykIf that other code you wrote is correct, then you're too smart for me 'cause I still can't figure it out :-) In any case, though, I think the other approach is probably better. It's just a matter of figuring out an implementation that works for you.

Nah, it's not complicated, but it works perfectly. I wouldn't get it either. Just that disecting the vector math until it came to the result makes it simple. The concept I had was that a target straight in front of the tank can cast a direction toward the tank and then toward the turret. The angle between these two directions is the exact same angle we need to offset the tank rotation. But the farther the target is in front of the tank, the more the angle changes. That's why the only input of the calculation is the distance to the target from the tank. So in the code, I assume the target is in front of the tank, so no rotations or directions are needed. If you break down this version of the code, you come up with what I have:
VECTOR some_point(0,0,distance_to_target); // pretend pointVECTOR dir_to_turret = normalize( TurretOffset - some_point ); // dir to turret from pretendVECTOR dir_to_tank(0,0,-1) // dir to tank from pretendFLOAT magic_angle = dir_to_turret.AngleLength( dir_to_tank );

All of the values are hard coded except the turret offset and distance to the target. td = dir_to_turret, and the angle was pretty simple to calculate, since only one coordinate was left unzeroed with hard coded values plugged in.

So get the angle from the tank to the real target, subtract magic_angle, and that's it.

Quote:
 Original post by jykThe basic problem is to figure out whether to turn left or right based on the position of a target. I take it you already have this solved? If so, how are you doing it?

Well, in my code, way up there where I calculate "character_try_direction", I end up with a new direction to face. So I just do a Y-Cross (TankForward.z*ToTarget.x - TankForward.x*ToTarget.z), and if it's negative, turn left, heheh.

Quote:
 Original post by jykYou mentioned you didn't have an angle for the character. What do you have? A matrix? Direction vector? Quaternion? Whatever it is, it can be used to figure out the offset for the target. If you can give me that info I'll be glad to fill in the details.

No, I can get an angle for the character. But this aiming rotation is used to add twist to his legs, which turns his whole upper body, but his feet stay planted, and his forward direction and angle don't change, even after he rotates to aim. But his hip also stays planted (well, it's x/z position does anyways), so his upper body is basically pivoting on his hip. But I don't have any directions, except for forward, until he twists. So why is the tank's current direction needed to offset the target? The turret-offset based on the tank's current rotation may be way off from the offset it will be once it aims at the target. For example, if your tank is facing forward, and the target is exactly to the right, puting the target at it's offset relative to tank-forward won't be anything close to what the target needs offset to, which is almost tank-right. The almost being the killer of all of my previous attempts.

I might be wrong. Your method may be doing something that I'm not understanding. Let me know if your method can really rotate to the correct angle in a single rotate. I would really like to understand how it can do that by offseting the target.

Thanks again

[Edited by - Jiia on March 8, 2005 12:17:39 AM]

##### Share on other sites
Quote:
 Nah, it's not complicated, but it works perfectly. I wouldn't get it either. Just that disecting the vector math until it came to the result makes it simple. The concept I had was that a target straight in front of the tank can cast a direction toward the tank and then toward the turret. The angle between these two directions is the exact same angle we need to offset the tank rotation. But the farther the target is in front of the tank, the more the angle changes. That's why the only input of the calculation is the distance to the target from the tank. So in the code, I assume the target is in front of the tank, so no rotations or directions are needed. If you break down this version of the code, you come up with what I have:VECTOR some_point(0,0,distance_to_target); // pretend pointVECTOR dir_to_turret = normalize( TurretOffset - some_point ); // dir to turret from pretendVECTOR dir_to_tank(0,0,-1) // dir to tank from pretendFLOAT magic_angle = dir_to_turret.AngleLength( dir_to_tank );
Ah, I finally got it! I thought magic_angle was the *total* angle to rotate in order to aim at the target, and I couldn't figure out how you were getting that using only the *distance* to the target. But now I see that magic_angle is just the angle to adjust by. It all makes sense now :-)

Anyway, I adjusted my method so as to rotate in one step from the current orientation to the target, and that worked fine. I also tried your method, which worked as well.

The only reason I might still recommend the 'adjust the target position' approach is that it's more generic. That is, if you have functionality available to turn toward a given target (a reasonable thing for a player or entity class), you can just feed it the adjusted target position and everything will work as it should - no adjustments or special cases necessary.

Now:
Quote:
 VECTOR turret_offset = RealTurretOffset;turret_offset.OrientateXZ( GetTransform() ); VECTOR relative_target = RealTarget - turret_offset;character_try_direction = normalize( relative_target - char_pos );VOID VECTOR::OrientateXZ(const D3DXMATRIX &mat){ FLOAT tx = x; FLOAT tz = z; x = (tx * mat._11) + (tz * mat._31); z = (tx * mat._13) + (tz * mat._33);}
I tried it with code almost exactly like what you have here, and it worked fine. You end up with character_try_direction, which is the direction you would like to achieve. If you have your current direction vector, you should be able to get the exact angle to rotate by to reach the new orientation.

I wasn't sure from your post whether this was working for you or not. So are you not sure how to get the exact angle from the current direction vector and the goal direction vector?

In any case, it seems you have a solution that works. But if you're interested in using the other method, I think you're almost there (if you haven't got it figured out already).

##### Share on other sites
Quote:
 Original post by jyk---- my code ------offset = turretoffset rotated by current orientationnewtarget = target - offset;goaldirection = direction from position to newtarget-------------------I tried it with code almost exactly like what you have here, and it worked fine. You end up with character_try_direction, which is the direction you would like to achieve. If you have your current direction vector, you should be able to get the exact angle to rotate by to reach the new orientation.

I don't think that code will give you a direction that makes the turret aim at the target. I made another image, trying to sort it out:

The large green arrow (pointing down) shows the "tank"'s current forward vector. The blue ball is the current turret location. The white X is the target. The little yellow dot is where the target ends up if you subtract the current offset from the turret to the tank. The yellow ball is the turret's location after rotating to [face the tank toward the offset target]. You can see it isn't pointing correctly. But if you were to repeat this whole procedure again, and perhaps again, it will end up exactly correct. What am I missing to make it all happen in a single step?

Quote:
 Original post by jykThe only reason I might still recommend the 'adjust the target position' approach is that it's more generic. That is, if you have functionality available to turn toward a given target (a reasonable thing for a player or entity class), you can just feed it the adjusted target position and everything will work as it should - no adjustments or special cases necessary.

I did get the moving target method to work. I had to first get the angle from the tank origin to the target, and add that with the tank's current y angle. I rotated the turret offset by this result. Subtract the new turret offset from the target, and then finally rotate the tank to point at the new target location. Is this the same method you're using? I don't see where you're getting the angle to the target before using the turret offset to move the target. If the angle to pass to your function is just the tank's current angle before it changes because of any targeting, then the turret offset is just relative to where the base is currently facing. I don't understand how that offset is useful, as in the picture.

Quote:
 Original post by jykI wasn't sure from your post whether this was working for you or not. So are you not sure how to get the exact angle from the current direction vector and the goal direction vector?

I've been using that method while the character is walking or running. It works for this because he spins progressively over time. It won't work for my standing-still leg twist, because that needs to be calculated all at once. Or at least this is my reasoning, heh.

Quote:
 Original post by jykIn any case, it seems you have a solution that works. But if you're interested in using the other method, I think you're almost there (if you haven't got it figured out already).

Well like I said, I did get it working by moving the target. But I had to rotate the base object to the target, then get the turret offset before I could calculate the offset target.

##### Share on other sites
Hey Jiia,

Well, for all my posts it looks like I haven't helped you much so far - sorry! I wasn't thinking about the problem carefully enough. You are absolutely correct - the method I suggested doesn't reach the solution instantaneously - it takes a couple of iterations. The iterations are few enough that it *looks* instantaneous, but you're right that it's not.

Truth is, your 'magic angle' solution is probably perfectly sufficient. However I'm still interested in the ideal 'theoretical' solution that gives the exact angle to rotate without any 'hacks'. I think I've got it figured out, but I won't be able to test it until I get home from work tonight. In any case, I'll post my results later this evening.

Again, sorry for being a bit slow to catch on to what you were saying :-|

##### Share on other sites
honestly, i'm not really sure what the problem is. the tank and the turret are seperate entities. You calculate one angle of rotation for the tank itself, and an entirely different angle for the turret. Facing the tank itself is simple:

note: -z is assumed default forward direction. +y is "up"

Vector3 targetPos;Vector3 tankCenter;Vector3 lookVector;lookVector = targetPos - tankCenter;lookVector.Normalize();float angleToRotateTank = arccos( lookVector.Dot( Vector3(0,0,-1) ) );//i might have this backwardsif ( lookVector.Cross( Vector3(0,0,-1) ).y > 0 )    angleToRotateTank *= -1;

then at any given frame you would do exactly the same calculation to get the appropriate orientation of the tank turret. the only thing that will be different is the centerPos of the turret which will have to be calculated by knowing the current orientation of the tank

//whatever the offset of the turret is from the center of the tank.Vector3 turretOffsetVector(3,6,5);//rotate the offset vector to get the actual worldpos of the turretturretOffsetVector.RotateY( angleToRotateTank );Vector3 turretPos = tankCenter + turretOffsetVector;Vector3 turretLookVector;turretLookVector = targetPos - turretCenter;turretLookVector.Normalize();float angleToRotateTurret = arccos( turretLookVector.Dot( Vector3(0,0,-1) ) );//i might have this backwardsif ( turretLookVector.Cross( Vector3(0,0,-1) ).y > 0 )    angleToRotateTurret *= -1;

the more "realistic" solution for the tank behavior is to have the body just turn to face it's direction of movement, and to have the turret act independently to aim and acquire it's target. The body of the tank would only rotate to face the target if the current orientation blocks the LOS of the turret to the target. but you can wait for that later b/c it just adds more complication (you can essentially just set up "dead zones" in the turret rotation. if aiming at the target would enter the dead zone, you pass a message back to the tank body so that it will alter it's orientation appropriately).

-me

-me

##### Share on other sites
Hehe, sorry for the confusion. It's a difficult situation to explain. This is the most efficient version of the routine that I've come up with. It still needs tweaked (like it doesn't acount for situation where target == pivot_pos). Also, I have a lot of specific vector methods, sorry if it's confusing.
inline FLOAT AimTurretOnPivot( const VECTOR &pivot_pos, const VECTOR &pivot_space_turret_offset,  const VECTOR &target, const MATRIX &inverse_pivot_state ){	// Prepare	VECTOR dir_to_target;	// Get Direction to target from our position (in pivot_space)	dir_to_target.AsOrientatedXZ( target - pivot_pos, inverse_pivot_state );	FLOAT target_distance = dir_to_target.GetLengthAndNormalizeXZ();	// Calculate Angle	FLOAT y_angle = ( dir_to_target.z >= 1.0f ) ? 0.0f : ( dir_to_target.z <= -1.0f ) ? PI : acosf( dir_to_target.z );	if( dir_to_target.x < 0.0f )		y_angle = -y_angle;	// Offset angle using angle from the <target to position> to <target to weapon origin>	FLOAT td = target_distance - pivot_space_turret_offset.z;	if( td == 0.0f ) // turret z matches target z in pivot space while facing target		y_angle -= PI_HALF; // err I think this will need negated if the turret is on the left	else		y_angle -= acosf( td / sqrtf( (pivot_space_turret_offset.x * pivot_space_turret_offset.x) + (td * td) ) );	return y_angle;}

##### Share on other sites
ok got it:

this assumes that the lookAt of the weapon is always parallel with the lookAt of the tank.

T = target
C = tank center
G = gun
  ^  |      T  |     /|  |    / |  |   /  |  |  /   |  | /    G  |/_____|  C

So essentially we are solving for the forward vector of the tank when it's at the "solution" position. We know the vector from the center of the tank to the target, we know the offset vector of the gun placement (which gives us the length of the bottom of the right triangle there). So therefore we have 2 sides known of a right triangle. we then solve for the lower L angle of that triangle. 90 - that angle gives us the angle between the toTarget vector and the forward vector. thus we have solved for the forward vector of the tank. The only change to the below code would be if you had the gun placement on the other side of the tank, you would have to do the rotation with a positive angle. However you could put a programatic check in there to just test the value of the up coord on the cross between the tanks placement and the offset vector to the turret.

Vector3 targetPos;Vector3 tankCenter;Vector3 toTarget = targetPos - tankCenter;float angle = 90 - arccos( turretOffset.x / toTarget.length() );toTarget.rotateY( -angle );toTarget.normalize();float tankOrientAngle = arccos( toTarget.Dot( Vector3(0,0,-1) ) );

-me

##### Share on other sites
Palidine: Just to clarify, we weren't trying to solve the problem of how to adjust the tank's rotation to account for the offset turret. Jiia actually gave the solution to that in his second post. I think it's similar to your solution.

What I was trying to solve was the following. Say you already have a function such as AimAtTarget() which aligns your forward vector with the target. How can you adjust the input to this function (the target position) to automatically adjust for the offset turret?

The first answer I gave was wrong, and honestly I don't know what I was thinking. I'm pretty sure I've got it right now. If I get it wrong this time, I'll give up!

All you need to do is offset the target position along a normalized vector perpendicular (in the xz plane) to the vector from the player to the target. The amount to offset is however far 'off to the side' the turret is in local space. In this case I think that would be turretOffset.x.

You might have to negate the perp vector, but I think the code would look something like this:

Vector3 toTarget = target - player.position;
toTarget.Normalize();
Vector3 perp(-toTarget.z, 0.0f, toTarget.x);
target -= perp * turretOffset.x;

You should then be able to feed 'target' into an existing AimAtTarget() function, and get the correct behavior.

The two solutions basically boil down to the same thing, and either could be used. If it were my game, I'd use the 'adjust target' approach, for the reason that I think it's better to factor out as much behavior as possible. For example, it would make sense for a basic 'entity' class to have an AimAtTarget() function. This function would simply re-orient the entity so that it was facing the target exactly - no more, no less. You could use this for anything - guided missiles, flocking, etc.

Now, let's say further down the class hierarchy you have a player class, whose weapon is off to the side. The functionality for aiming at a target is already built into the class - why rewrite or modify it? Better to adjust the input and use the functionality already available, IMO.

So anyway, that's my suggestion - offset the target position as described above, and then feed it to your already-existing 'aim at' function. I should have gotten that right the first time - I just haven't been thinking very clearly this week :-|

##### Share on other sites
You know what: that's still not right. It works for relatively small turret offsets, and is probably fine for practical purposes. But if the offset is large (which it probably won't be in an actual game), error is introduced.

Obviously you've already got a practical solution; but I'm keeping after it for my own purposes because it's an interesting problem. I just *know* that there has to be a way (probably very obvious and I'm just missing it) to adjust the target position so it can be fed to a standard 'aim at' function, with perfect results. Another way of putting it is that there must be a 'one shot' solution that doesn't involve iteration or correction.

Anyway, sorry to have kind of wasted your time with my posts. It is an interesting problem though...

[Edit: Ok, I did figure it out. But it's no big deal - just an application of the same trig used in the 'magic angle' formula - so I won't post it unless someone's really interested.]

[Edited by - jyk on March 9, 2005 9:25:06 PM]

##### Share on other sites
Quote:
 Original post by jykVector3 toTarget = target - player.position;toTarget.Normalize();Vector3 perp(-toTarget.z, 0.0f, toTarget.x);target -= perp * turretOffset.x;

This is why I stay and bug people even though I have an answer [grin]

I knew there was a simple answer. Lets see, that's what, 4 ways to find this other target angle? My method allowed my character to aim even if they are tilted front-back or left-right (like if my character is swaying in a combatic jump [wink]). So that was my main concern with this new one. It works perfectly. I just have to remove the base position from the target and un-orientate it from the rotation of the character before adjusting anything.

Quote:
 Original post by jykThe two solutions basically boil down to the same thing, and either could be used. If it were my game, I'd use the 'adjust target' approach..

All of the functionality with none of the pain. Not to mention that it's visibly more accurate, having less calculations = less error. Both of the old methods also had a little jumping point where the angle would get stuck a tiny, tiny amount at certain spots around the zero point. The instant method would stick near the character-forward angle, and the progressive one near the turret-forward angle. Probably because of dot-product errors. Anyway, that doesn't happen anymore.

I really appreciate your extra help. This aim-twisting code is burning me out. Time to move on to something else [smile]

##### Share on other sites
Quote:
 This aim-twisting code is burning me out. Time to move on to something else.
Then you should probably ignore my last post about a 'more correct' method :-) If you've got it working to your satisfaction, you should go with it...

##### Share on other sites
Quote:
 Original post by jykYou know what: that's still not right. It works for relatively small turret offsets, and is probably fine for practical purposes. But if the offset is large (which it probably won't be in an actual game), error is introduced.

I don't get it, it's absolutely correct. The x offset of the turret is always exactly the amount the target needs to move in the orientation of facing it with the tank. By getting the direction to the target and perping it, you have the answer. What's the error you're mentioning?

edit: I'm pretty sure you're wrong. Err, I mean right. You're wrong about being wrong. The scale of the turret offset is only relative to the distance to the target. The only way I could imagine it messing up is if the target is within the pivoting circle. But none of the methods mentioned so far would handle that correctly.

edit2: Well, I guess the magic_angle algorithm (my trademark?) handled it. But for my situation, the pivot circle is so small, the character can't aim down or up enough to reach it anyways.

##### Share on other sites
Yeah, I double-checked it, and my 'perp-vector' idea isn't accurate. The error is only noticable if the turret offset is large compared to the distance to the target, so you could probably use that method and never notice a problem. But it's only an approximation of the correct solution.

No matter how you look at it, I think the exact solution boils down to the method that you and Palidine already posted. I tried a bunch of different things, but in the end my code ended up looking pretty much like yours and Palidine's. The only difference is that rather than using the 'magic angle' to find the tank's final orientation, I'm using it to adjust the target's position so I can feed it to an 'aim at' function that I already have.

Again, you could probably use the 'perp-vector' method and never notice any inaccuracy.

You mentioned that you had some 'sticking' or 'jumping' problems with the trig-based solution. The only thing I can think of regarding that is to make sure and always clamp any argument to acos() to [-1, 1]. Also, in the line:

FLOAT td = target_distance - turret_offset.z;

I don't think you should be subtracting turret_offset.z, as only the lateral offset (turret_offset.x) should affect the result.

##### Share on other sites
Quote:
 Original post by jykYeah, I double-checked it, and my 'perp-vector' idea isn't accurate. The error is only noticable if the turret offset is large compared to the distance to the target, so you could probably use that method and never notice a problem. But it's only an approximation of the correct solution.

I really can't get any errors to occur with it. Do you have time to explain a situation where it's off? I mean all of the crap I'm pulling off to get my character to point an animated hierarchy bone at a target has small errors anyway, so maybe I just can't see the difference.

I'm using actual animations to twist him around and tilt the weapon up or down. The weapon barrel has to stay aligned with a pivot origin when he animates to aim vertically, and extreme up or down was difficult to pose, so I had to move the weapon up or down when he aims straight in those directions. As long as the barrel is lined up with the pivot point and target angle, the aim is perfect (eg, while aiming straight forward, it's okay to move the weapon back or forward). But when interpolating between frames, the angles may be slightly off. Within game scale, the error is never more than half an inch at extreme distances, and the error that is there is constant and doesn't fluctuate. So I'm happy with the result, for now.

Quote:
 Original post by jykYou mentioned that you had some 'sticking' or 'jumping' problems with the trig-based solution. The only thing I can think of regarding that is to make sure and always clamp any argument to acos() to [-1, 1].

Yeah, I've programmed the debugging version of my math engine to punch me in the face for doing that. The float values become invalid. It's easy to detect this, because the statement ( value >= 0 || value <= 0 ) will be false. I'm not sure why it was sticking. You have to understand though, I'm zooming in really close to far off distance of the aiming ray.

Quote:
 Original post by jykAlso, in the line:FLOAT td = target_distance - turret_offset.z;I don't think you should be subtracting turret_offset.z, as only the lateral offset (turret_offset.x) should affect the result.

Well, it's calculating the direction to the turret from the fake target in front of the origin. The only reason the z value is needed is to normalize x in the direction = normalized(offset) calculation. It's basically getting the length that the target is in front of the turret.

I really wish I could understand how your method is messing up. Even drawing it out and rotating lines in a paint program, I can never mess it up outside of the pivot arc.

edit:

Okay, I've finally found a place where it it misses the target. With my character, the distance is equivalent to having the target on his foot, so that was why I was having trouble having ..trouble.

It's really hard to simulate this in a paint program. Can you tell me where it messes up? Is distance the only error factor? Does it matter if the target is in front of or behind the pivot or turret? Does the error change with different angles but keeping the distance the same?

From what I tried to test, it seems like this problem pops up once the target reaches a specific distance from the outside of the rotate-arc. I don't know for sure. I didn't test many angles. If this is true, the distance must be related to something. I have a feeling that using that something to offset the target (along with or instead of turret.x) might fix it when the distance becomes lower than this amount. I'd like to stick to the perp vector idea if possible, heh.

I've been awake too long to be efficient. But since I won't be able to drop this until I understand it, I'll have to try again after some long temporary death.

[Edited by - Jiia on March 10, 2005 1:54:26 AM]

##### Share on other sites
well, if you take my solution and stop at this line:

toTarget.rotateY( -angle );

you can then just do this:

Vector3 aimAtPosition = tankCenter + toTarget;

Aiming at that position will get you the correct orientation if you just pass it to a standard AimAt function. AFAIK this solution will only break down when the target is inside the radius of the circle in your diagrams. remember that the +/- of the angle is determined by whether the turret is on the left/right side of the tank, respectively (again i could have the signs backwards but i think it's right).

-me

##### Share on other sites
Quote:
 well, if you take my solution and stop at this line:toTarget.rotateY( -angle );you can then just do this:Vector3 aimAtPosition = tankCenter + toTarget;
@Palidine:

Yup, that's exactly what my code ended up looking like. You got it right from the start - it just took me a while to catch up :-)

@Jiia

The error in the perp-vector method is a little hard to describe without a picture, but I'll give it a try:

Imagine (or draw) a tank properly aligned with a target, that is, a line extending forward from the turret crosses the target exactly. Also, draw a line extending along the tank's forward direction vector. The 'adjusted' target position has to lie on this second line in order for the 'aim at' function to work correctly, so that's what we're trying to accomplish.

Now draw a line from the tank center to the target. Next, draw our perp-vector, which starts at the target position, is perpendicular to the tank-to-target line, and has a length of turretOffset.x. You'll see that it doesn't actually reach the 'forward direction line' of the tank. Moreover, the error increases as the turret offset increases.

I don't know if that cleared it up or not (I need to figure out how to post images). Actually, now that I'm thinking about it, it may be possible to use the perp-vector method after all - it's just that the amount of displacement isn't offset.x, but a larger value computed from that. If I figure it out, I'll edit this post with the result.

The good news: you can use the perp-vector method. Bad news: you still have to compute the magic angle! So it's really just two ways of doing the same thing.

If the magic angle is:

magicAngle = pi * 0.5f - acos(turretOffset.x / distToTarget);

Then the amount by which you need to displace along the perp-vector is not turretOffset.x, but rather:

offset = turretOffset.x / cos(magicAngle);
[/Edit]

[Edited by - jyk on March 10, 2005 8:39:38 PM]

##### Share on other sites
But this error, does it only start to go off once the target gets so close? So the angle is irrelevant? And if the target is beyound this certain close distance, there are no problems? If so, then the error distance is probably related to the turret offset and can be used to fix the target further.

Maybe not. I have no idea. Sleep didn't help my brain much.
Anyway, I have 3 different methods to choose from, I don't wanna bug you guys more than that. Thanks for both of your help.

edit: You can upload images to image shack for free. If you mean html-wise, just type in < img src="address" >, with no space after the < , into the post. If that didn't come out right, just view the source code for this page and search for "img",hehe.

##### Share on other sites

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