Hello! I'm trying to implement a Proportional Navigation (https://en.wikipedia.org/wiki/Proportional_navigation) and I found this formula:

Required Acceleration = LOS * LOSRate * NC + APNbias

I want to create a homing missile in 2D but it is not working as I expected and also I don't know the formula for 2D.
I don't need to use precisely the same formula as above. I just want to create a homing missile that moves elliptical and misses the target if it moves.

Here is the code:

``````export(float) var speed = 600
var _time_to_use_pn = 0.5
var current_velocity = Vector2()
var enemy_target
var _last_los = 0

_time_to_use_pn -= delta
if _time_to_use_pn > 0:

var cur_rot = get_global_rot()
var forward_dir = Vector2(sin(cur_rot), cos(cur_rot))
current_velocity = forward_dir.normalized() * speed * (-1)

return

if enemy_target != null:
var my_pos = get_global_pos()
var target_pos = enemy_target.get_global_pos()
var cur_rot = get_global_rot()

var los = atan2(my_pos.y - target_pos.y, my_pos.x - target_pos.x)
var los_rate = los - _last_los
_last_los = los

var angle = los_rate * navigation_const

rotate(angle)

var forward_dir = Vector2(sin(cur_rot), cos(cur_rot))
current_velocity = forward_dir.normalized() * speed * (-1)
``````

Edit: I'm using Godot 2.1.4.
Edit 2: The code above contains another formula for 2d.

Edit 3: I found this link: https://gamemechanicexplorer.com/#homingmissiles-1, and I tried to translate the code to Godot but it doesn't work. My code:

``````var my_pos = parent_weapon.get_global_pos()
var target_pos = enemy_target.get_global_pos()
var cur_rot = parent_weapon.get_global_rot()

var angle = angle_between(target_pos, my_pos)

# Gradually (rotation_vel) aim the missile towards the target angle
if cur_rot != angle:
# Calculate difference between the current angle and targetAngle
var delta = angle - cur_rot

# Keep it in range from -180 to 180 to make the most efficient turns.
if delta > PI: delta -= PI * 2
if delta < -PI: delta += PI * 2

if delta > 0:
# Turn clockwise
cur_rot += rotation_vel
else:
# Turn counter-clockwise
cur_rot -= rotation_vel

# Just set angle to target angle if they are close
if abs(delta) < rotation_vel:
cur_rot = angle

parent_weapon.set_global_rot(cur_rot)

var forward_dir = Vector2(sin(cur_rot), cos(cur_rot))
current_velocity = forward_dir.normalized() * parent_weapon.w_speed

missile_shot.parent_weapon.global_translate(missile_shot.current_velocity * p_delta)
``````
in Engine
edited

The maths in the linked articles is annoyingly wrong.

For example in the stackoverflow-answer it says `Required Acceleration = LOS * LOS_Rate * NC + APN_bias` and `APN_bias = LOS_Rate/delta_T * ( NC/2 )`. So since LOS is a vector and all the other terms are scalars that would mean adding a vector and a scalar.

And in the article on moddb it says

``````# Now, calculate the final lateral acceleration required for our missile
# to home into our target.
latax = RTM_new * N * Vc * LOS_Rate + LOS_Delta * Nt * ( 0.5 * N )
``````

So the formula is supposed to result in a LATERAL acceleration - but the second part of the addition contains the factor Nt which is supposed to describe the relative acceleartion of the target - so if the target moves without acceleration it's 0. (The article talks about flying missiles and therefore factors in gravity, but let's talk about the special case of movement in a 2D plane first to check the formula - after all it should work just as well in that special case.

So this leaves us with

``````latax = RTM_new * N * Vc * LOS_Rate
``````

and again RTMnew is a vector and all the other terms are scalars. So the result will have the same direction as RTMnew, and that vector is pointing directly at the target, so we end up accelerating at the target, not at a point in front of the target.

I'll try to find the time and test it myself, but these two sources seem too faulty to simply copy and modify.

And how about the code above? I used another formula for 2D. I forgot to mention it. I tried to rotate using only LOS rate and navigation constant but it doesn't work.

I have written something like this:

This will work in Godot 2.1.4

Thank you! I found you repository very interesting. The rocket script is very cool but I need something more similar to the link in my 3rd edit on this question. The code in the link worked but when I translated to Godot, something gone wrong and it not worked.
I ran your code downloaded from github. For some reason it didn't work in my game. It worked only when I changed the orientation initial value to 90 degrees and set the object rotation to orientation - 90 degrees. I have no idea why.

The problem might be related to where 0 degree is.
In math 0 degrees is RIGHT. But different math library implementations sometimes implement 0 degrees as UP. In my project, all sprites are oriented right so i do not need any +-90 degrees. Although I had this problem when changing to Godot 3.
I have uploaded Godot 3 version of the targeting project.

If you can share your project I can look into it.

+1 vote

Thank you all for the help! The problem was the initial rotation as JohnMeadow1 said.
I ended up using the following code:

``````func set_proportional_navigation_1(p_delta):
var ninety_deg = (PI * 0.5) # = (PI / 2)

_time_to_use_pn -= p_delta
if _time_to_use_pn > 0:
var cur_rot = parent_weapon.get_global_rot() + ninety_deg
var forward_dir = Vector2(cos(cur_rot), -sin(cur_rot))
current_velocity = forward_dir.normalized() * parent_weapon.w_speed
return

if enemy_target != null:

var my_pos = parent_weapon.get_global_pos()
var target_pos = enemy_target.get_global_pos()
var cur_rot = parent_weapon.get_global_rot()

var los = my_pos.angle_to_point(target_pos)
var los_rate = los - _last_los
_last_los = los

var max_angle = PI * 0.083333 # = (PI / 12) = 15 degrees

var angle = los_rate * navigation_const

angle = clamp(angle, -max_angle, max_angle)

parent_weapon.rotate(angle)

cur_rot = parent_weapon.get_global_rot() + ninety_deg

var forward_dir = Vector2(cos(cur_rot), -sin(cur_rot))
current_velocity = forward_dir.normalized() * parent_weapon.w_speed
``````
by (96 points)
selected by