+1 vote

I want my bullets (RigidBody2D) to be able to track other nodes like a homing missile. Having lots of issues setting up this system. I read over the recommendations here and here and tried to go with a PID controller. Unfortunately it's not working, and I'm not sure where to go next.

var Kp = 1000
var Ki = 1000
var Kd = 100
var prevError = 0
var P =0 
var I =0 
var D =0
func _get_PID_output(currentError, delta):
    P = currentError;
    I += P * delta;
    D = (P - prevError) / delta;
    prevError = currentError;

    return P*Kp + I*Ki + D*Kd;
func _track_target(delta):
    var angle_btw = get_global_pos().angle_to(target.get_global_pos())
    var angle_diff = get_global_rot() - angle_btw
    var torque = _get_PID_output(angle_diff,delta)
    prevError = angle_diff
    set_applied_torque(torque)
    _set_vel_from_angle(get_global_rot())
    get_node("Sprite 2").set_global_rot(angle_btw) #debugging, show desired angle

Blue is the rigid body bullet, red is "Sprite 2" that is supposed to be pointing the the anglebtw direction. It looks like the angleto is not even the right direction!

enter image description here

Project

Yay it works! That took forever, thanks @mollusca

var Kp = 1000.0
var Ki = 100.0
var Kd = 1000.0
var _prev_error = 0
var _P =0 
var _I =0 
var _D =0
func _get_PID_output(currentError, delta):
    _P = currentError;
    _I += _P * delta;
    _D = (_P - _prev_error) / delta;
    _prev_error = currentError;

    return _P*Kp + _I*Ki + _D*Kd;
func _track_target(delta):
    var angle_btw = get_global_pos().angle_to_point(target.get_global_pos()) + PI/2
    var error = get_global_rot() - angle_btw
    #deal with angle discontinuity
    #https://stackoverflow.com/questions/10697844/how-to-deal-with-the-discontinuity-of-yaw-angle-at-180-degree
    if(error > PI): 
       error = error - PI * 2
    elif(error < -PI):
       error = error + PI * 2

    var torque = _get_PID_output(error,delta)
    set_applied_torque(torque)
    _set_vel_from_angle(get_global_rot())
asked May 10, 2017 in Engine by jarlowrey (574 points)
edited May 11, 2017 by jarlowrey

1 Answer

+1 vote
Best answer

Use angle_to_point instead of angle_to. You'll also have to do something about the discontinuity of the angle error.

answered May 11, 2017 by mollusca (1,506 points)
selected May 11, 2017 by jarlowrey

Hmmm unfortunately that didn't do the trick. Why is angle_to_point better? I've tried both before, they seem very similar in the docs. Still seems like they are trying to shoot off in the wrong direction.

func _track_target(delta):
    var angle_btw = get_global_pos().angle_to_point(target.get_global_pos())
    var error = get_global_rot() - angle_btw
    if(error > PI):
       error = error - PI * 2
    elif(error < -PI/2):
       error = error + PI * 2

    var torque = _get_PID_output(error,delta)
    prevError = error
    set_applied_torque(torque)
    _set_vel_from_angle(get_global_rot())
    get_node("Sprite 2").set_global_rot(angle_btw)

enter image description here

It seems if I add PI/2 to the angle_btw, it is now pointing in the right direction! Bullets still are orbiting nothing, but it's a step in the right direction. Any idea why this value is off by 90 deg?

I changed my gain values to 1k, 100, 1k and it's working! Good call on the angle discontinuity!

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.