Or this one with overhead strikes:
Finally here is a video that shows a large number of missiles chasing another missile:
To create a guided missile like this, you have to do a few things:
- Have a target, that is updated every frame with its current position
- (Optional) set some fixed waypoints on the way to the target to alter the flight path, this creates the overhead strike you see in the video, and is one way to improve accuracy
- Create a "steer point" that will guide your missile towards the next waypoint/target, but corrects for velocity of the chaser and the chased
- Turn the body of the missile until it is facing the steer point.
- When we are happy with the facing of the body, we accelerate it.
- If you want the missile to explode, you can check distance from target, or use Box2D's contact listener
Setting the targeting and updated it's Vector2 position should be easy enough, so I won't explain it here. Let's skip to step 3.
Assuming we have a target, let's face the target
let's correct for velocities:
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
void steerToDesiredFacing() { | |
if (desiredFacing == null) { | |
return; | |
} | |
float facing = getForwardFacing(); | |
// this contrain method keeps the angle between 180 and -180 | |
float diff = constrainAngle180(facing - desiredFacing); | |
// steer within n degrees of desired facing, use this to control precision | |
float angularThreshold = 1; | |
float angularVel = body.getAngularVelocity(); | |
// if we are turning too fast for the remaining turn distance, we slow down with this hack | |
// this can prevent an over-steering behavior that causes wobble | |
if (Math.abs(angularVel / diff) > 0.1f) { | |
body.setAngularVelocity(angularVel * 0.90f); | |
} | |
float torque = rotationForce; | |
// turn one way | |
if (diff >= angularThreshold) { | |
body.applyTorque(-torque, true); | |
// or turn the opposite way | |
} else if (diff <= -angularThreshold) { | |
body.applyTorque(torque, true); | |
// if we are within 5 degrees of desired facing | |
// we want to kill our turn gradually, but strong enough to stop | |
} else if (diff < 5) { | |
if (angularVel < 0.2) { | |
// turning slow enough to be on target | |
body.setAngularVelocity(0); | |
removeDesiredFacing(); | |
} else { | |
body.setAngularVelocity(angularVel * 0.5f); | |
} | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
private Vector2 getVelocityCorrectionSteerpoint(Vector2 waypoint) { | |
if (waypoint == null) { | |
return null; | |
} | |
Vector2 selfVel = body.getLinearVelocity().cpy(); | |
// if this is not a regular waypoint, but instead a point we should chase | |
// (like a moving enemy target) then we want to correct for the target's velocity as well | |
if (isChaseTargetPosition(waypoint)) { | |
// add target's velocity to the waypoint, so we can predict movement | |
Vector2 targVel = targetEntity.body.getLinearVelocity(); | |
waypoint.add(targVel); | |
} | |
Vector2 selfPos = body.getPosition(); | |
// desired velocity is target's position minus our position | |
Vector2 desiredVelocity = waypoint.cpy().sub(selfPos); | |
// The correction we need to make to the waypoint is | |
// the difference between desire velocity and current velocity | |
Vector2 steeringCorrection = desiredVelocity.sub(selfVel); | |
// new waypoint = old waypoint + steering correction | |
Vector2 steeringCorrectionPoint = steeringCorrection.add(waypoint); | |
return steeringCorrectionPoint; | |
} |
Now let's seek the waypoint:
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
void seekWaypoint(Vector2 waypoint) { | |
Vector2 pos = body.getPosition(); | |
float dist = waypoint.dst(pos); | |
// you should set a threshold for arrival distance | |
// that is good enough to be considered "arrived" for your purposes | |
if (dist < arrivalThreshold) { | |
if (waypoints.contains(waypoint)) { | |
// if we are chasing a target, don't remove the waypoint upon reaching arrival distance | |
waypoints.remove(0); | |
waypoint = getCurrentWaypoint(); | |
if (waypoint == null) { | |
return; // no more waypoints | |
} | |
} else if (waypoint.equals(chasePoint)) { | |
// just keep chasing! | |
} | |
} | |
// accelerate towards the waypoint, since it is beyond arrival distance threshold | |
// here is where we used the velocity correction method from above | |
Vector2 correctiveSteerpoint = getVelocityCorrectionSteerpoint(waypoint); | |
Vector2 destination = correctiveSteerpoint; | |
// to avoid over correction, we have a minimum distance from target for correction to occur at | |
// if we are under the minimum, just head towards the original waypoint | |
if (correctiveSteerpoint.dst(pos) < minSteeringDistance) { | |
destination = waypoint; | |
} | |
// we set the desired facing, so we now where to turn to face our destination | |
setDesiredFacing(getAngleFromAtoB(pos, destination)); | |
// only accelerate if we are pointed at the destination | |
if (getSteeringDifference() < maxSteeringErrorForAcceleration) { | |
accelerate(); | |
} | |
} |
And that's about it. Here is the code for constraining the angle to 180 and -180 degrees:
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
public static float constrainAngle180(float angle) { | |
while (angle > 180) { | |
angle = angle - 360; | |
} | |
while (angle < -180) { | |
angle = angle + 360; | |
} | |
return angle; | |
} |
Can you share full of source code?It's not clear how to run it within a full project.Thank you
ReplyDeleteHi Jon, please can you post the whole code on how to make this happen.
ReplyDeleteFrom creating the missile to hitting the target.
That will help alot and as a newbie it's hard as no one wants to help us even though everyone was a newbie themselves.
Please Jon that will be so much and I will mention you in my future projects.
Thank you so much in advance.
Johnny Graham