0 votes

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

at this link https://gamedev.stackexchange.com/questions/27923/implementing-simple-proportional-navigation-for-a-homing-missile/27983

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
export(float) var navigation_const = 3
var _time_to_use_pn = 0.5
var current_velocity = Vector2()
var enemy_target
var _last_los = 0

func set_proportional_navigation(delta):

    _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 rotation_vel = deg2rad(5)

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 by (96 points)
edited by

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:

https://github.com/JohnMeadow1/GodotGeometryElements/blob/master/final/targeting/scripts/rocket.gd

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.
https://github.com/JohnMeadow1/GodotGeometryElements/tree/master/godot_3/final/targeting

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

1 Answer

+1 vote
Best answer

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
Welcome to Godot Engine Q&A, where you can ask questions and receive answers from other members of the community.

Please make sure to read How to use this Q&A? before posting your first questions.
Social login is currently unavailable. If you've previously logged in with a Facebook or GitHub account, use the I forgot my password link in the login box to set a password for your account. If you still can't access your account, send an email to webmaster@godotengine.org with your username.