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?
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*)&target, 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...
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*)&target, 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.