Aiming from a pivot

Started by
21 comments, last by Jiia 19 years, 1 month ago
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: Image Hosted by ImageShack.us Here's another with more angles: Image Hosted by ImageShack.us 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.
Advertisement
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]
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.
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 jyk
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.

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 jyk
Here'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.

Free Image Hosting at www.ImageShack.us

I really appreciate your help
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.
Quote:Original post by jyk
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.

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 jyk
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.

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 jyk
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?

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 jyk
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.

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]
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 point
VECTOR dir_to_turret = normalize( TurretOffset - some_point ); // dir to turret from pretend
VECTOR dir_to_tank(0,0,-1) // dir to tank from pretend
FLOAT 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).
Quote:Original post by jyk
---- my code ------
offset = turretoffset rotated by current orientation
newtarget = 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:
Image Hosted by ImageShack.us
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 jyk
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.

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 jyk
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?

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 jyk
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).

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.

Thanks for your help dude
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 :-|
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

This topic is closed to new replies.

Advertisement