Aiming from a pivot

Started by
21 comments, last by Jiia 19 years, 1 month ago
actually read your post closer and realized that I didn't actually answer your question =(. working on another solution now...

-me
Advertisement
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;}
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
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 :-|
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]
Quote:Original post by jyk
Vector3 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 jyk
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..

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]
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...
Quote:Original post by jyk
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.

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.
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.
Quote:Original post by jyk
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.

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

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

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]

This topic is closed to new replies.

Advertisement