Need some help with homing missiles.

I just discovered how great Polar Coords and I am trying to get homing missies to work. For some reason my missile will occasionally spaz out and continue in the opposite direction. Any ideas why? I’m still trying to get a better understanding of using angles.

function setup()
    parameter.watch("Distance")
    targets = {}
    target = nil
    x = WIDTH/2
    y = 0
    length = 5
    angle = math.deg(math.atan(y/x))
    inc = 4
    tAngle = 0
    for i=1,5 do
        table.insert(targets,{x=math.random(1,WIDTH),y=math.random(1,HEIGHT),select = false,alive = true})
    end
    
    missleSelect()
end


function draw()
    background(255,255,255,255)
    for i=1,#targets do
        drawtarget(targets[i])
    end
    drawMissle()
    home()
end

function drawtarget(v)
    if v.alive then
        pushMatrix()
        pushStyle()
        translate(v.x,v.y)
        if v.select then c = color(60, 254, 1, 255)else c= color(255,0,0,255)  end
        fill(c)
        ellipse(0,0,25)
        fill(214, 213, 213, 255)
        ellipse(0,0,15)
        fill(c)
        ellipse(0,0,10)
        popStyle()
        popMatrix()
    end
end

function drawMissle()
    x = x + length * math.cos(math.rad(angle))
    y = y + length * math.sin(math.rad(angle))
    pushMatrix()
    pushStyle()
    translate(x,y)
    fill(255, 233, 0, 255)
    ellipse(0,0,15)
    popStyle()
    popMatrix()
end

function missleSelect()
    if #targets ~= 0 then
        local rnd = math.random(1,#targets)
        targets[rnd].select = true
        target = rnd
        tAngle = math.deg(math.atan2(targets[rnd].y - y,targets[rnd].x - x))
    end
    
end

function home()
    
    if #targets ~= 0 then
        tAngle = math.deg(math.atan2(targets[target].y-y,targets[target].x - x))
        Distance = (targets[target].y-y) +(targets[target].x - x)
        
        --if angle - tAngle < -180 then angle = angle + 180 end
        if angle < tAngle then
            angle = angle + inc
            else
            angle = angle - inc
        end
        
        if x< targets[target].x +15 and x > targets[target].x -15 and
        y < targets[target].y +15 and y> targets[target].y - 15 then
            -- print("hit")
            
            table.remove(targets,target)
            missleSelect()
            
        end
    end
end

@Briarfox Put a watch in for tAngle. I noticed when you have the problem, tAngle goes positive and negative around 180 but doesn’t seem to get beyond -170 or 170. Hard to explain, but it might be easier to see it happening.

Nice little program!

As @dave1707 says, the problem is that tAngle can oscillate between just over -180 and just under 180 which makes a big difference to what happens next. One solution is to ensure that tAngle is always near to its previous value.

function setup()
    parameter.watch("Distance")
    parameter.watch("tAngle")
    parameter.watch("angle")
    targets = {}
    target = nil
    x = WIDTH/2
    y = 0
    length = 5
    angle = math.deg(math.atan(y/x))
    inc = 4
    tAngle = 0
    for i=1,5 do
        table.insert(targets,{x=math.random(1,WIDTH),y=math.random(1,HEIGHT),select = false,alive = true})
    end

    missleSelect()
end


function draw()
    background(255,255,255,255)
    for i=1,#targets do
        drawtarget(targets[i])
    end
    drawMissle()
    home()
end

function drawtarget(v)
    if v.alive then
        pushMatrix()
        pushStyle()
        translate(v.x,v.y)
        if v.select then c = color(60, 254, 1, 255)else c= color(255,0,0,255)  end
        fill(c)
        ellipse(0,0,25)
        fill(214, 213, 213, 255)
        ellipse(0,0,15)
        fill(c)
        ellipse(0,0,10)
        popStyle()
        popMatrix()
    end
end

function drawMissle()
    x = x + length * math.cos(math.rad(angle))
    y = y + length * math.sin(math.rad(angle))
    pushMatrix()
    pushStyle()
    translate(x,y)
    fill(255, 233, 0, 255)
    ellipse(0,0,15)
    popStyle()
    popMatrix()
end

function missleSelect()
    if #targets ~= 0 then
        local rnd = math.random(1,#targets)
        targets[rnd].select = true
        target = rnd
        tAngle = math.deg(math.atan2(targets[rnd].y - y,targets[rnd].x - x))
    end

end

function home()

    if #targets ~= 0 then
        local oa = tAngle
        tAngle = math.deg(math.atan2(targets[target].y-y,targets[target].x - x))
        if tAngle - oa < -180 then
            tAngle = tAngle + 360
        end
        if tAngle - oa > 180 then
            tAngle = tAngle - 360
        end
        Distance = (targets[target].y-y)^2 +(targets[target].x - x)^2

        --if angle - tAngle < -180 then angle = angle + 180 end
        if angle < tAngle then
            angle = angle + inc
            else
            angle = angle - inc
        end

        if x< targets[target].x +15 and x > targets[target].x -15 and
        y < targets[target].y +15 and y> targets[target].y - 15 then
            -- print("hit")

            table.remove(targets,target)
            missleSelect()

        end
    end
end

Thanks guys, I was so close. when checking if the angle was ±180 I was then ± 180 and not 360.

Here’s another homing missiles example with physics moving the targets.

function setup()
    displayMode(FULLSCREEN)
    parameter.watch("Distance")
    parameter.watch("#targets")
    targets = {}
    bodies = {}
    target = nil
    x = WIDTH/2
    y = 0
    length = 5
    angle = math.deg(math.atan(y/x))
    inc = 15
    tAngle = 0
    for i=1,25 do
        --table.insert(targets,{x=math.random(1,WIDTH),y=math.random(1,HEIGHT),select = false,alive = true})
        --create the physics object
    
    local p_img = physics.body(CIRCLE,25) --physics bodies use radius not diameter
    p_img.x = math.random(2,WIDTH-2) -- choose a random position 
    p_img.y = math.random(2,HEIGHT-2) -- same for the y position
    p_img.angle=math.random(0,360)
    p_img.gravityScale = 0 -- no gravity
    p_img.restitution = 1.0 -- this image is not so bouncy
    p_img.friction = 0.0 -- the amount of friction to be applied to the image
    p_img.linearVelocity = vec2(math.random(500),math.random(500))  -- velocity
    p_img.select = false
    p_img.alive = true
    --p_img.info="mine" --so when collisions occur, we can figure out which object was involved
    table.insert(targets,p_img)
    end
    CreateWalls()
    
    missleSelect()
end


function draw()
    background(0, 0, 0, 255)
    for i=1,#targets do
        drawtarget(targets[i])
    end
    drawMissle()
    home()
    --moveTargets()
end

function drawtarget(v)
    if v.alive then
        pushMatrix()
        pushStyle()
        translate(v.x,v.y)
        if v.select then c = color(60, 254, 1, 255)else c= color(255,0,0,255)  end
        fill(c)
        ellipse(0,0,25)
        fill(214, 213, 213, 255)
        ellipse(0,0,15)
        fill(c)
        ellipse(0,0,10)
        popStyle()
        popMatrix()
    end
end

function drawMissle()
    x = x + length * math.cos(math.rad(angle))
    y = y + length * math.sin(math.rad(angle))
    pushMatrix()
    pushStyle()
    translate(x,y)
    fill(255, 233, 0, 255)
    ellipse(0,0,15)
    popStyle()
    popMatrix()
end

function missleSelect()
    if #targets > 4 then
        local rnd = math.random(1,#targets-4)
        targets[rnd].select = true
        target = rnd
        tAngle = math.deg(math.atan2(targets[rnd].y - y,targets[rnd].x - x))
    end
    
end

function home()
    
    if #targets > 4 then
        tAngle = math.deg(math.atan2(targets[target].y-y,targets[target].x - x))
        Distance = (targets[target].y-y) +(targets[target].x - x)
        
        --if angle - tAngle < -180 then angle = angle + 180 end
        if angle - tAngle < -180 then angle = angle + 360 end
        if angle - tAngle > 180 then angle = angle - 360 end
        if angle < tAngle then
            angle = angle + inc
            else
            angle = angle - inc
        end
        
        if x< targets[target].x +15 and x > targets[target].x -15 and
        y < targets[target].y +15 and y> targets[target].y - 15 then
            -- print("hit")
            targets[target]:destroy()
            table.remove(targets,target)
            x = WIDTH/2
            y = 0
            --length = 5
            angle = 0--math.deg(math.atan(y/x))
            missleSelect()
            
        end
    end
end

function CreateWalls()
    leftWall = CreateWall(1,1,1,HEIGHT-1,1.0) 
    rightWall = CreateWall(WIDTH-1,0,WIDTH-1,HEIGHT-1,1) 
    bottomWall = CreateWall(1,1,WIDTH-1,1,1) 
    topWall = CreateWall(1,HEIGHT-1,WIDTH-1,HEIGHT-1,1)
end
--this function creates one wall (actually just a line)
function CreateWall(x,y,x1,y1,r)
    local w = physics.body(EDGE,vec2(x,y),vec2(x1,y1)) -- vec2
    w.restitution=r --see comment above
    table.insert(targets,w) --keep track of body so we can destroy it later if we want
    return w
end


Lol! When i watch this, I feel pityful for the green ball… Not a chance!