You can get extremely accurate results in situations where both the target and the projectile move at a constant velocity.
If the target is rapidly changing velocity, then this will not be reliable, unless the projectile is moving very fast relative to the target's speed and relative to the distance between the shooter and the target.
we reason that given an accurate firing angle, that at some time ("t") the distance between the shooter and the projectile will be equal to the distance between the shooter and the target. Thus the projectile and the target may be at the same place at the same time (resulting in a collision). And we reason that if we know this time, then we can simply aim the projectile at a location that the target will be occupying at that time.
"p" = (vector) initial Position of target minus initial position of shooter
"v" = (vector) target Velocity
"s" = projectile Speed
"t" = Time
"d" = Distance from shooter at time ("t")
distance squared = (initial target position (minus initial shooter position) + target velocity times time) squared:
d = sqrt((p + v*t)^2)
distance = projectile speed times time:
d = s*t
substitute "d" in ether equation with the right side of the other equation. square both sides to get rid of square root:
(p + v*t)^2 = s^2*t^2
square stuff in parentheses:
p^2 + v^2*t^2 + 2*p*v*t = s^2*t^2
then put whole thing in standard form:
(v^2-s^2)*t^2 + 2*p*v*t + p^2 = 0
use quadratic formula to solve for time ("t") (note: use dot product when multiplying vectors with vectors):
Code:
//from shooter:
var _vx = target.hspeed;
var _vy = target.vspeed;
var _s = 8; //any projectile speed above zero is fine.
var _px = target.x - x;
var _py = target.y - y;
//-------------------------------------
var _a = _vx * _vx + _vy * _vy - _s * _s; //(v^2-s^2)
var _b = 2 * (_px * _vx + _py * _vy ); //(2*p*v)
var _c = _px * _px + _py * _py; //(p^2)
//-------------------------------------
var _disc = _b * _b - 4 * _a * _c;
if (_disc >= 0) { //if (_disc < 0) then projectile cannot reach target
var _t = (-_b - sqrt(_disc)) / (2 * _a); //doing direct attacks only, not trying to do trick shots where you shoot backwards to hit target
if (_t > 0) { //don't shoot if (_t < 0), because that would indicate negative time solutions (and projectile cannot go backwards in time)
var _aim_x = _px + _vx * _t;
var _aim_y = _py + _vy * _t;
with (instance_create(x,y,obj_bullet)) {
direction = point_direction(0,0,_aim_x,_aim_y);
speed = _s;
}
}
}