Hey guys,
I have a homing missile which I want to direct towards the target with a limited rotation speed.
The rotation should be limited by an angle in Degrees per second.
How do I accomplish this?
Hey guys,
I have a homing missile which I want to direct towards the target with a limited rotation speed.
The rotation should be limited by an angle in Degrees per second.
How do I accomplish this?
2D or 3D?
It's 3D
vec curDir = turret.orientation.FrontDirection();
vec targetDir = (target - turret.Position).Unit();
// calculate axis and angle rotation between directions
vec axis = (curDir.Cross(targetDir)).Unit(); // eventually reverse operands - not sure about it
float angle = acos( curDir.Dot(targetDir) ); // Notice this is always positive (the direction of the axis defines if we rotate left or right)
// limit angle if necessary
const float maxAngularVelocity = 1.0f / 180.0f * 3.14f; // e.g. 1 degree per second
const float timestep = 1.0f / 60.0f;
float maxAnglePerUpdate = maxAngularVelocity / timestep;
if (angle > maxAnglePerUpdate) angle = maxAnglePerUpdate;
// finally rotate turret
turret.orientation.RotateByAxisAndAngle (axis, angle);
I hope this is correct - written just out of my head and not tested.
Be sure to try the correct order for the cross product.
EDIT: bugfix: maxAnglePerUpdate = maxAngularVelovity * timestep
Thank you for your reply,
sadly it isn't working for me. The projectile either keeps spinning wildly or is going in a different direction
31 minutes ago, Twaxx said:The projectile either keeps spinning wildly or is going in a different direction
Oops, used division instead multiplication, see Edit of above post.
I'v tested it now with this code:
static bool visTurret = 1; ImGui::Checkbox("visTurret", &visTurret);
if (visTurret)
{
struct Turret
{
quat orientation;
vec position;
Turret ()
{
orientation.Identity();
position = vec(0,0,0);
}
};
static Turret turret;
static vec target (10, 3, 0);
ImGui::DragFloat3 ("target pos", (float*)⌖, 0.01f);
vec curDir = turret.orientation.Rotate(vec(1,0,0));
vec targetDir = vec(target - turret.position).Unit();
// calculate axis and angle rotation between directions
float angle = acos( curDir.Dot(targetDir) ); // Notice this is always positive (the direction of the axis defines if we rotate left or right)
if (angle > 1.0e-5f) // otherwise axis would be result of division by zero
{
vec axis = (curDir.Cross(targetDir)).Unit(); // eventually reverse operands - not sure about it
// limit angle if necessary
const float maxAngularVelocity = 10.0f / 180.0f * 3.14f; // e.g. 10 degree per second
const float timestep = 1.0f / 500.0f; // i have 500 fps
float maxAnglePerUpdate = maxAngularVelocity * timestep;
if (angle > maxAnglePerUpdate) angle = maxAnglePerUpdate;
// finally rotate turret
quat rot; rot.FromAxisAndAngle (axis, angle);
turret.orientation *= rot;
turret.orientation.Normalize();
}
// visualize
RenderLine (target, turret.position, 0.5,0.5,0.5);
RenderQuat (turret.position, turret.orientation);
RenderPoint (target, 1,1,1);
} There is still one problem: If curDir and TargetDir point to opposite halfspace, the turret aligns to the back side
But i doubt it is worth to fix this, because a real turret has some joint limits, and can not roll. It would be better to start a new approach that builds two rotations from two Euler angles (like a FPS camera - no roll there as well).
I suggest you try it out anyways and tell me what you think...
Thank you very much, I got it working
I just wanted to mention that Slerp https://en.wikipedia.org/wiki/Slerp also does this and solves some simple problems you get from this kind of rotation.
You would calculate the offset to find the rotation to the target. Then use speed as the time value, updating each tic. This is a real easy way if you have a math library already hooked up.For example the Unity version would look like this:
Unity:
void HomeOnTarget()
{
//Get offset and make it a rotation
Vector3 offset = Target.transform.position - this.transform.position;
Vector3 TargetDirection = offset.normalized;
//Slerp between current rotation and target rotation based on speed
this.transform.rotation = Quaternion.Slerp(this.transform.rotation,
Quaternion.LookRotation(TargetDirection, this.transform.up), Time.deltaTime/ Speed);
//Move in rotation direction
this.transform.position += this.transform.forward * Speed * Time.fixedDeltaTime;
} Slerp makes a lot of rotation math simple.
3 hours ago, Scouting Ninja said:Quaternion.LookRotation(TargetDirection, this.transform.up)
That's nice. But it would be better to use global up direction instead the turrets local, then the rolling is already prevented.
My backwards bug still bothered me and while fixing it i tried this and it works nicely, also added a damping so motion is smooth (constant speed always looks so cheap).
Other than that our approaches should be equivalent, assuming Unity already chooses the shorter arc for the slerp internally.
struct Turret
{
quat orientation;
vec position;
Turret ()
{
orientation.Identity();
position = vec(0,0,0);
}
};
static Turret turret;
static vec target (10, 3, 0);
ImGui::DragFloat3 ("target pos", (float*)⌖, 0.01f);
vec curDir = turret.orientation.Rotate(vec(1,0,0));
vec targetDir = vec(target - turret.position).Unit();
float dot = curDir.Dot(targetDir);
if (fabs(dot) < 1.0f - 1.0e-5f) // otherwise axis would be result of division by zero
{
// calculate axis and angle rotation between directions
float angle = acos(dot); // Notice this is always positive (the direction of the axis defines if we rotate left or right)
vec axis = (curDir.Cross(targetDir)).Unit();
// optional: some damping to make it smooth. This simple approach assumes constant timestep and needs to be adjusted to it.
const float damp = 0.03f;
angle *= damp;
// limit angle if necessary
const float maxAngularVelocity = 30.0f / 180.0f * 3.14f; // e.g. 30 degrees per second
const float timestep = 1.0f / 500.0f; // i have 500 fps
float maxAnglePerUpdate = maxAngularVelocity * timestep;
if (angle > maxAnglePerUpdate) angle = maxAnglePerUpdate;
// finally rotate turret
quat rot; rot.FromAxisAndAngle (axis, angle);
if (0) // no upvector - turret will roll and go out of place after some time
{
//turret.orientation *= rot; // depending on conventions, multiplication order decides to do the rotation in either local or global space, which i never remember. So this line caused the backwards bug for me i've mentioned before.
turret.orientation = rot * turret.orientation;
turret.orientation.Normalize();
}
else // using upvector, turret Y axis remains on the ground plane
{
const vec globalUp (0,1,0);
vec newTurretDir = rot.Rotate(curDir);
if (fabs( globalUp.Dot(newTurretDir) ) < 1.0f - 1.0e-5f) // turret would be clueless uf pointing exactly up / downwards
{
vec side = vec(globalUp.Cross(newTurretDir)).Unit();
vec localUp = newTurretDir.Cross(side);
matrix temp;
temp[0] = newTurretDir;
temp[1] = side;
temp[2] = localUp;
temp.Rotation()->ToQuat(turret.orientation);
}
}
}
// visualize
RenderLine (target, turret.position, 0.5,0.5,0.5);
RenderQuat (turret.position, turret.orientation);
RenderPoint (target, 1,1,1);
RenderCircle (1, turret.position, vec(0,1,0), 0,1,0); The way I would do it is:
You can find code to compute the "most natural rotation" in this thread.
Depending on the genre, a more realistic Physics simulation of the missile's dynamics and a control mechanism (like a PID loop) to guide the missile could give you better results.
1 hour ago, alvaro said:The way I would do it is:
- Compute the quaternion that would produce the "most natural rotation" that would align your missile with the target.
- Look at its real coordinate to see if the rotation is larger than the limit. The real part should be at least cos(limit_angle/2). If needed, modify the quaternion so its real part is exactly cos(limit_angle/2), scaling the imaginary parts to keep the quaternion having unit length.
- Apply the quaternion to rotate the missile.
You can find code to compute the "most natural rotation" in this thread.
... which is exactly what i've done in my first reply. Your 'most natural rotation' is just the shortest rotation between two directions. You calculate it by converting axis and angle to quaternion, while i kept working with axis and angle because i think it's more intuitive for somebody asking a question like this.
Only mentioning this to avoid confusion or wrong expections, correct me if i'm wrong.
The problem is, a turret mounted on ground or a vehicle can not take any most natural rotation because it is usually constructed by two hinges. The up vector fixes this in a simple way, but this does not help with joint limits. So if this is necessary we either need to model the two hinges, or (more easily) convert the target direction to spherical coords in the turret reference orientation and clip them.
I
8 minutes ago, JoeJ said:... which is exactly what i've done in my first reply. Your 'most natural rotation' is just the shortest rotation between two directions. You calculate it by converting axis and angle to quaternion, while i kept working with axis and angle because i think it's more intuitive for somebody asking a question like this.
Only mentioning this to avoid confusion or wrong expections, correct me if i'm wrong.
No, you are probably right. But you mentioned you were getting the problem that it could align to the opposite of the desired direction, so I tried to think about it from scratch.
QuoteThe problem is, a turret mounted on ground or a vehicle can not take any most natural rotation because it is usually constructed by two hinges. The up vector fixes this in a simple way, but this does not help with joint limits. So if this is necessary we either need to model the two hinges, or (more easily) convert the target direction to spherical coords in the turret reference orientation and clip them.
I though we were talking about a homing missile, not a turret.
1 hour ago, alvaro said:you were getting the problem that it could align to the opposite of the desired direction
That was just a bug caused by wrong quat multiplication order.
I could not resist to implement angle limits based on spherical coords - expected 10 mins of work but underestimated trig trial and error fun, haha. Works pretty well however
static bool visTurret = 1; ImGui::Checkbox("visTurret", &visTurret);
if (visTurret)
{
struct Turret
{
// orientaton convention: X axis: front (turret shooting direction), Y axis: up, Z axis: side
quat orientation;
vec position;
quat mountOrientation; // e.g. the ground or a vehicle frame, we use it to define up vector and angle limits
float angleLimitLeft;
float angleLimitRight;
float angleLimitUp;
float angleLimitDown;
Turret ()
{
orientation.Identity();
mountOrientation.Identity();
position = vec(0,0,0);
angleLimitLeft = - 45.0f / 180.0f * 3.14f;
angleLimitRight = + 45.0f / 180.0f * 3.14f;
angleLimitUp = - 30.0f / 180.0f * 3.14f;
angleLimitDown = 0;
}
};
static Turret turret;
static vec target (10, 3, 0);
ImGui::DragFloat3 ("target pos", (float*)⌖, 0.01f);
vec curDir = turret.orientation.Rotate(vec(1,0,0));
vec targetDir = vec(target - turret.position).Unit();
if (1) // angle limits
{
vec dir = turret.mountOrientation.Unrotate(targetDir); // move to local mount space
const int a=0, b=2, c=1;
// cartesian unit vector to spherical coordinates
float theta, phi;
{
float denom = sqrt(dir[a]*dir[a] + dir[b]*dir[b]);
float t = -dir[a] / (denom + 1.0e-16f);
theta = acos( max(-1,min(1,dir[c])) );
phi = acos( max(-1,min(1,t)) );
if (dir[b] > 0) phi = 2.0f*PI - phi;
}
// clip spherical coordinates
phi -= PI;
if (phi < turret.angleLimitLeft) phi = turret.angleLimitLeft;
if (phi > turret.angleLimitRight) phi = turret.angleLimitRight;
phi += PI;
theta -= PI/2;
if (theta < turret.angleLimitUp) theta = turret.angleLimitUp;
if (theta > turret.angleLimitDown) theta = turret.angleLimitDown;
theta += PI/2;
ImGui::Text("t %f p %f", theta/PI*180, phi/PI*180);
// spherical coordinates to cartesian unit vector
{
dir[a] = -sin(theta) * cos(phi);
dir[b] = -sin(theta) * sin(phi);
dir[c] = cos(theta);
}
targetDir = turret.mountOrientation.Rotate(dir); // back to global space
}
float dot = curDir.Dot(targetDir);
if (fabs(dot) < 1.0f - 1.0e-5f) // otherwise axis would be result of division by zero
{
// calculate axis and angle rotation between directions
float angle = acos(dot); // Notice this is always positive (the direction of the axis defines if we rotate left or right)
vec axis = (curDir.Cross(targetDir)).Unit();
// optional: some damping to make it smooth. This simple approach assumes constant timestep and needs to be adjusted to this value. (for 60 fps try 0.3)
const float damp = 0.03f;
angle *= damp;
// limit angle if necessary
const float maxAngularVelocity = 30.0f / 180.0f * 3.14f; // e.g. 30 degrees per second
const float timestep = 1.0f / 500.0f; // i have 500 fps
float maxAnglePerUpdate = maxAngularVelocity * timestep;
if (angle > maxAnglePerUpdate) angle = maxAnglePerUpdate;
// finally rotate turret
quat rot; rot.FromAxisAndAngle (axis, angle);
if (0) // no upvector, ok for a spaceship
{
turret.orientation = rot * turret.orientation;
turret.orientation.Normalize();
}
else // using upvector, turret side axis remains on the up plane defined by mountOrientation
{
const vec globalUp = turret.mountOrientation.Rotate(vec(0,1,0));
vec newTurretDir = rot.Rotate(curDir);
if (fabs( globalUp.Dot(newTurretDir) ) < 1.0f - 1.0e-5f) // turret would be clueless uf pointing exactly up / downwards
{
vec side = vec(globalUp.Cross(newTurretDir)).Unit();
vec localUp = newTurretDir.Cross(side);
matrix temp;
temp[0] = newTurretDir;
temp[1] = side;
temp[2] = localUp;
temp.Rotation()->ToQuat(turret.orientation);
}
}
}
// visualize
RenderLine (target, turret.position, 0.5f,0.5f,0.5f);
RenderQuat (turret.position, turret.orientation);
RenderQuat (turret.position, turret.mountOrientation, 0.2f);
RenderPoint (target, 1,1,1);
RenderCircle (1, turret.position, vec(0,1,0), 0,1,0);
} Edit: Doh - it would be faster and easier to use 4 plane constraints to limit target dir instead using spherical coords.
On 7/7/2018 at 3:01 PM, Twaxx said:How do I accomplish this?
I bet the topic starter is uninstalling his compiler by now,
You surely are some professors here, i try to understand your code : i gave up, tomorrow i read again.
12 minutes ago, Fulcrum.013 said:On 7/7/2018 at 2:01 PM, Twaxx said:How do I accomplish this?
I would like to combine this with a projectile moving at parabolic trajectory under gravity. Would look really cool if such a turret can still hit a target moving at constant velocity. And i've never seen this in a game yet (but i never play RTS games).
7 minutes ago, the incredible smoker said:You surely are some professors here
Not really - just made the same mistake i've mentioned you did recently: Using trig when it's not necessary
(Although the plane based approach for limits might have disadvantages on ranges > 180 dgrees)
41 minutes ago, JoeJ said:I would like to combine this with a projectile moving at parabolic trajectory under gravity. Would look really cool if such a turret can still hit a target moving at constant velocity. And i've never seen this in a game yet (but i never play RTS games).
What's the scenario you're describing here? A turret on a moving object trying to hit another moving object?
1 hour ago, JoeJ said:Would look really cool if such a turret can still hit a target moving at constant velocity.
It just keept velocity vector directed to interception point while missile have enought energy to change its velocity vector. And it works with changes of sight line, that is result of movements, so automatically take care about any forces applied to both missile and target regardless of its nature. It able to hit ever maneuring targets while missile have enought motor enerjy to make maneurs with G that exids target's maneurs G.
1 hour ago, JoeJ said:And i've never seen this in a game yet (but i never play RTS games).
I guess it just becouse automation control theory exids college course of CS. Locally even not all universities have it course. Also i dont know how it been at overseas, but locally prior middle of 80th it has been teached only into "closed" universities where KGB keept watch that studients and instructors can not get their texbooks and records out of campus. It technique firstly seen into AAA+++ RTS with elements of FPS released at 1947 AKA Coold War. So it enougt roubst to implement even on CPU that slover then controllers that drive LED lamps. Also it widely used into air combat simulations games.
44 minutes ago, ferrous said:A turret on a moving object trying to hit another moving object?
Topic starter question related to homing missiles aiming, that is really something like flying singly charged shotgun.
2 hours ago, JoeJ said:I would like to combine this with a projectile moving at parabolic trajectory under gravity. Would look really cool if such a turret can still hit a target moving at constant velocity. And i've never seen this in a game yet (but i never play RTS games).
I seem to recall that Age of Empires had something like this. The archers would fire at a target, but the arrows would follow a ballistic trajectory and could miss. I don't think they really took tracking into account, though.
Other RTS games (StarCraft comes to mind) intentionally don't have this for anything other than visual effects. Actually simulating a homing missile would mean you need to simulate more stuff and make combat less deterministic, which would result in networking and tweaking game balance becoming harder.
1 hour ago, Fulcrum.013 said:I guess it just becouse automation control theory exids college course of CS.
I would say that automation control theory is more of an engineering topic than computer science, since it's really just applied calculus. At least where I went to school, you might come across it in a robotics course or a course about numerical integration, but I wouldn't expect any particular computer science graduate to know anything about it. Many computer science graduates can barely do calculus as it is, since most CS (at that level) is discrete and abstract mathematics. I can think of exactly one computer science class where we used calculus and that was during maybe one lecture. ![]()
1 hour ago, ferrous said:What's the scenario you're describing here? A turret on a moving object trying to hit another moving object?
Think of throwing a stone at a hole in the ground. By using the equation describing its motion, you can calculate the parabolic trajectory, necessary initial velocity and direction of the stone, and the time duration it requires to hit the target. (Can be done all analytically.)
Now if the target would be a toy car driving at constant speed and direction, we could do as above to calculate the duration, then we integrate the duration to car position and repeat the process for a few iterations to get a good enough approximization. My question is, can we do this analytically as well without a need for iterations? I guess so but i've never tried, and often problems that appear easy to me turn out to have no analytic solution and vice versa. (So if you know the answer is no please tell me to save time - 2 or 3 iterations should be good enough anyways.)
If the guy who throws the stone is moving as well is of no concern, because it is only the relative velocity between turret and target that matters, which we can calculate easily in any case.
But what makes it interesting is, there are always 2 solutions to this (it's a quadratic equation), so we can choose a higher or a lower angle. In realty one always chooses the lower angle: less energy, shorter hit duraton, better chance to hit. But if we pick the higher angle, the projectile is much longer in the air, and then it looks really cool if it still hits the target, like clever AI or something. I think this is very useful for games.
1 hour ago, Fulcrum.013 said:Topic starter question related to homing missiles aiming
Oh, missed the 'homing' missle part, but the code to control a turret towards a direction is useful in any case. For advanced stuff now being discussed we would just calculate a better target vector.
34 minutes ago, Oberon_Command said:Many computer science graduates can barely do calculus as it is
Makes me feel better
I've never learned Calculus at all, not even proper linear algebra. (CS neither)
I wonder how you can do anything without calculus in CS. I mean, learning trees, graphs or sorting algorithms isn't a hurdle, but knowing nothing about calculus is. I think i've wasted more years in reinventing wheels than proper education would have taken. I can not even ask for help, because i really do not know what questions to ask in what order... sigh
This topic has been locked by a moderator. New replies are not allowed.
With your permission, GameDev.net uses analytics cookies to understand how people use the platform. You can accept analytics or continue with necessary cookies only. Learn more