Drone Simulator Misbehaving

I’ve hit a roadblock with my drone flight-simulator project and asking for a pointer or two to help get me back on track.

Description:
The drone is always located in the center of the screen. The point of view (POV) is always directly overhead. As you fly the drone the ground beneath translates and rotates giving the sensation of motion. All motion if controlled by two joysticks. Slight finger movement provides plenty of motion and motion stops when fingers are released.

At startup, fly the drone forward, backward, left and right by moving the RIGHT joystick up, down, left and right respectively. The ground appears to move opposite to the stick because in reality you are moving the ground and not the drone. The goal is that to fly the drone forward you push the RIGHT stick forward.

Next, yaw (rotate) the drone left and right by moving the LEFT stick left or right. Notice the ground rotating directly underneath the drone. Left stick provides altitude using scale so it’s a bit primitive.

The Problem:
If you try to translate the drone with the RIGHT stick AFTER yawing the drone with the LEFT stick the control is messed up by what appears to be an amount equal to the Yaw amount. As an example: If Yaw is -45 degrees the drone should be facing NorthWest. If you wish to move to the NorthWest you should only have to push the stick forward. But instead if I push the stick forward the drone moves North rather than NorthWest.

Creating an image of the ground then translating and rotating it ahead of the sprite call works great as long as the drone was headed North. Adding yaw into the mix throws everything off. The drone needs to fly in the direction the RIGHT stick is pushed.

I’ve struggled with this problem for about two weeks and would greatly appreciate any leads to solving this problem.

I’m running this on a 3rd gen (iOs = 9.3.5) iPad that’s getting close to ten years old. Too old for any Codea updates and unable to zip files.

I am looking forward to any advice offered.
Thank you very much.
Scotty

-- DroneSim V5

supportedOrientations(ANY)
displayMode(FULLSCREEN)

function setup()
    
    X,Y     = WIDTH/2, HEIGHT/2
    LR,FB   = 0,0
    Alt,Yaw = 1,0
  
    -- modes (stick sensitivity):
    cine   = .10
    normal = .25
    sport  = .50
    mode = normal
      
    -- Set up common colors
    commonColors()

    -- JoyStick class setup:
    JSYA   = JoyStick(175, 200, green, "Yaw: < Left/Right >",
        "Altitude: ^ Up/Down v")
    JSFBLR = JoyStick(WIDTH-175,200,red, "Translate: < Left/Right >",
        "Translate: ^ Forward/Backward v")    
      
    -- For ground definition
    rows,cols,sizeGeo = 20,20,100
    imgSize = vec2(cols*sizeGeo, rows*sizeGeo)
            
    -- Create images using setContext()
    createImageGround()    
    createImageDrone()  
end


function draw()
    background(40, 40, 50)
    
    -- trig numbers (req'd?)
    sinLR = math.sin(math.rad(Yaw)) -- x
    cosFB = math.cos(math.rad(Yaw)) -- y
    
    -- Left joystick:
    Yaw=Yaw+JSYA.dX*mode/4
    Alt=Alt-JSYA.dY*mode/1000
    if Alt >  1 then Alt =  1 end
    if Alt < .4 then Alt = .4 end
    
    -- Right joystick:
    LR=LR+JSFBLR.dX*mode   -- left/right
    FB=FB+JSFBLR.dY*mode   -- foreward/backward
    
    -- Manage ground image, landing zone and return-to-home line.
    pushMatrix()
    translate(X,Y)  
    rotate(Yaw)
    pushMatrix()
    scale(Alt) -- The illusion of altitude
    sprite(myImageGround,-LR,-FB)
    landingZone(-LR,-FB)
    returnToHomeLine(-LR,-FB)  
    popMatrix()
    popMatrix()

    -- Drone is stationary while the ground moves underneath.
    sprite(myImageDrone,X,Y) -- Drone w/o lights
    drawDroneLights(X,Y)     -- Lights w/o body
    pushStyle()
    fill(white)
    fontSize(14)
    text(string.format("%.f°",Yaw), X,Y+60)
    popStyle()
          
    -- Joysticks
    JSYA:draw()
    JSFBLR:draw()
    
    -- Compass letters rotate around drone.
    drawCompass()

end


function touched(t)    
    JSYA:touched(t)
    JSFBLR:touched(t)
end



JoyStick = class()

--[[
JoyStick details:
-----------------
Left Stick:
U  +altitude
D  -altitude
L  ccw yaw
R  cw  yaw

Right Stick:
U  Foreward movement
D  Backward movement
L  Left movement
R  Right movement
--]]

function JoyStick:init(x,y,clr,txt1,txt2)
    self.x    = x
    self.y    = y
    self.clr  = clr   -- color
    self.txt1 = txt1
    self.txt2 = txt2

        
    self.dia = 50
    self.max = 25
    self.dX  = 0
    self.dY  = 0
    self.id  = {}  -- touch id table
end


function JoyStick:draw()
    pushStyle()
    
    -- Escutcheon:
    strokeWidth(4)
    stroke(self.clr)
    fill(black)
    ellipse(self.x,self.y,self.dia)

    -- Stick design:
    fill(self.clr)
    stroke(self.clr)
    strokeWidth(10)
    line(self.x, self.y, self.x+self.dX*.5, self.y+self.dY*.5)
    fill(black)
    strokeWidth(2)
    ellipse(self.x+self.dX*.5, self.y+self.dY*.5, self.dia*.3)
    ellipse(self.x+self.dX*.6, self.y+self.dY*.6, self.dia*.3)
       
    -- Reference data and etc. that's not really important to the project:
    fill(white)
    text(string.format("(%.f, %.f)",self.dX,self.dY), self.x,self.y+self.dia)
    text(self.txt1, self.x,self.y-self.dia)
    text(self.txt2, self.x,self.y-self.dia*1.5)

    popStyle()    
end


function JoyStick:touched(t)
    if vec2(t.x,t.y):dist(vec2(self.x,self.y)) <= self.dia*2 then     
        if t.state==BEGAN or t.state==MOVING then
            table.insert(self.id,t.id)
            -- Update deltas
            self.dX=self.dX+t.deltaX
            self.dY=self.dY+t.deltaY
            -- Limit deltas to maximums
            if self.dX> self.max then self.dX =self.max end
            if self.dY> self.max then self.dY =self.max end
            if self.dX<-self.max then self.dX=-self.max end
            if self.dY<-self.max then self.dY=-self.max end
        end
    elseif vec2(t.x,t.y):dist(vec2(self.x,self.y)) > self.dia*2 or t.state==MOVING then     
        for z=#self.id,1,-1 do
            if self.id[z]==t.id then
                table.remove(self.id,z)
            end
        end 
    end 

    if t.state==ENDED then
        -- Clear table
        for z=#self.id,1,-1 do
            if self.id[z]==t.id then
                table.remove(self.id,z)
            end
        end         
        -- Kill motion of released joystick
        if #self.id==0 then
            self.dX=0
            self.dY=0
        end
    end

end

-- Functions

function createImageDrone()
-- Creates an image that represents the drone w/o lights
    myImageDrone = image(X,Y)
    setContext(myImageDrone)
        drawDroneBody()
    setContext()
end


function createImageGround()
-- Creates an image that represents the ground
    myImageGround = image(imgSize.x,imgSize.y)
    setContext(myImageGround) 
        drawGround(imgSize, rows,cols, sizeGeo)
    setContext()    
end



function drawGround(iSize, rows,cols, sizeGeo)
-- Drawing the rectangles that represent the ground
    local iSize     = iSize     -- image size
    local rows,cols = rows,cols -- row and columns of rects
    local sizeGeo   = sizeGeo   -- size of rect
    
    -- Org of image
    local Org  = vec2(-(cols-1)*sizeGeo/2, (rows-1)*sizeGeo/2)
    -- Loc of current rect
    local Loc  = vec2(Org.x,Org.y)    
    local low,high = 180,180 -- Ground color

    pushMatrix()
    translate(iSize.x/2,iSize.y/2)
    pushStyle()
    rectMode(CENTER)
    strokeWidth(1.5)
    stroke(180)
    --fontSize(12) text("Col 1, Row 1", Loc.x,Loc.y) -- rect reference-numbers
    for r=1, rows do
        for c=1, cols do
            fill(math.random(low,high), math.random(low,high),
                 math.random(low,high), math.random(low,high))
            rect(Loc.x,Loc.y, sizeGeo)
            --if c~=1 or r~=1 then text("C"..c..", R"..r, Loc.x,Loc.y) end
            Loc.x = Loc.x+sizeGeo
        end
        Loc.x, Loc.y = Org.x, Loc.y-sizeGeo
    end
    popStyle()
    popMatrix()
end


function returnToHomeLine(x,y)
    local x,y = x,y
    
    local dist = vec2(x,y):dist(vec2(0,0)) -- The distance value 
    local distXY = vec2(x,y)/2 -- Where to place the distance value
    
    pushStyle()
    strokeWidth(4)
    stroke(255, 0, 0, 25)
    line(0,0, x,y)
    
    -- "dist-to-home" label
    pushMatrix()
    fill(red)
    fontSize(14/Alt)
    translate(distXY.x,distXY.y)
    rotate(-Yaw)
    text(string.format("%.f", dist), 0,0)
    popMatrix()    
    popStyle()
end


function landingZone(x,y)
    local x,y = x,y
    
    
    local size = 50
    
    pushMatrix()
    translate(x,y)
    pushStyle()
    rectMode(CENTER)
    strokeWidth(0)
    fill(orange)
    rect(0,0, size)
    fill(white)
    rect(0,0, size*.8)
    fill(black)
    fontSize(size*.6)
    font("Arial-BoldMT")
    text("H", 0,0)
    text("_", 0,0)
    popStyle()
    popMatrix()
end


function drawCompass()
    
    local compassBox={
        "N","NNE","NE","ENE","E","ESE","SE","SSE",
        "S","SSW","SW","WSW","W","WNW","NW","NNW"
    }
    local offset = 100
    local rotAmt = 360/#compassBox
    
    pushMatrix()
    pushStyle()
    fontSize(24)
    fill(red)    -- "N" is red...
    translate(X,Y)
    rotate(Yaw)  
    for i=1,#compassBox do
        pushMatrix()
        rotate(rotAmt)
        rotate(i*-rotAmt)
        translate(0,offset)
        rotate(-rotAmt)
        rotate(-Yaw)
        rotate(i*rotAmt)       
        text(compassBox[i],0,0)
        fill(black) -- ...all else be black.
        fontSize(12)
        popMatrix()
    end
    popStyle()
    popMatrix()
end


function drawDroneBody(x,y)
-- Image of drone w/o lights.
    local x,y = x,y
    local mSize = 60
    local droneColor = gray
    
    pushMatrix()
    translate(WIDTH/4,HEIGHT/4)
    pushStyle()
    -- arms
    lineCapMode(ROUND)            
    stroke(droneColor)  
    fill(droneColor)
    for i=1,4 do
        rotate(90)
        strokeWidth(mSize*.2)
        stroke(black)
        line(0,0, mSize*.625,mSize*.625)
        strokeWidth(mSize*.15)
        stroke(droneColor)
        line(0,0, mSize*.625,mSize*.625)
        strokeWidth(mSize*.01)
        fill(170, 170, 170, 74)
        ellipse(mSize*.625,mSize*.625, 50)
        fill(black)
        ellipse(mSize*.625,mSize*.625, 6)
    end
    
    -- body outline
    stroke(black)
    strokeWidth(mSize*.275)
    line(-mSize*.13, -mSize/2, -mSize*.09, mSize/2) -- l
    line( mSize*.13, -mSize/2,  mSize*.09, mSize/2) -- r
    line(-mSize*.13, -mSize/2,  mSize*.09,-mSize/2) -- b
    line(-mSize*.09,  mSize/2,  mSize*.09, mSize/2) -- t
    
    -- body
    stroke(droneColor)
    strokeWidth(mSize*.2)
    line(-mSize*.13, -mSize/2, -mSize*.09, mSize/2) -- l
    line( mSize*.13, -mSize/2,  mSize*.09, mSize/2) -- r
    line(-mSize*.13, -mSize/2,  mSize*.09,-mSize/2) -- b
    line(-mSize*.09,  mSize/2,  mSize*.09, mSize/2) -- t
    strokeWidth(mSize*.1)
    line(0,-mSize/2, 0,mSize/2) -- fill in what got missed         
    popStyle()
    
    -- forward arrow
    pushStyle()
    stroke(black)
    strokeWidth(3)
    lineCapMode(ROUND)
    line(0,30, 0,-20)
    line(0,30, 5,0)
    line(0,30, -5,0)
    popStyle()    
    popMatrix()
end


function drawDroneLights(x,y)
-- Drone's flashing lights.
    local x,y = x,y
    local mSize = 60
    local droneColor = gray
    
    pushMatrix()
    translate(x,y)
    pushStyle()
    -- tail light outline
    strokeWidth(8)
    stroke(black)
    line(-8,-mSize/2-8, 8,-mSize/2-8)
    
    -- head light outline
    fill(black)
    ellipse(0,mSize/2+8, 12)
    
    myTimer(2)
    
    -- tail light
    strokeWidth(4)
    line(-8,-mSize/2-8, 8,-mSize/2-8)
    
    -- head light
    ellipse(0,mSize/2+8, 8)
    popStyle()
    popMatrix()
end


function myTimer(dT)
-- The work of others.    
    local dT = dT -- How often we want to see change happen.
    
    local eT = math.floor(ElapsedTime) 
    if math.fmod(eT,dT) == 0 then -- Do this color for only one second...
        fill(gray)
        stroke(gray)
        if flag == 0 then
            flag = 1
        end
    else  -- ...then do this for dT seconds.
        fill(white)
        stroke(green)
        if flag == 1 then
            flag = 0
        end
    end
end


function commonColors()
    red    = color(209,0,0,255)
    orange = color(255,102,34,255)
    yellow = color(255,218,33,255)
    green  = color(51, 221, 0, 255)
    blue   = color(16, 51, 204, 255)
    white  = color(255, 255, 255, 255)   
    gray   = color(127, 127, 127, 255)
    black  = color(0, 0, 0, 255)    
    brown  = color(165, 40, 41, 255)
end


@Scotty Looks interesting. Had no trouble copying and pasting the code. I see what you’re talking about with the joysticks. I’ll play with the code and see where the problem is.

@Scotty - if you are rotating the ground why do you need to rotate the drone. Just one should do - and I suggest posting a small compass image in the top right with the angle displayed at it’s center rather than text round the drone. I’ve loaded your code up and shuffled it a bit - looks similar to something I’ve played with a few years ago.

Will post an update shortly when I’ve had more time to play.

@Scotty I created a small program to show what I think you want to happen. Slide your finger up/down/right/left on the right side of the screen to move the ground. Slide your finger right or left on the left side of the screen to rotate the ground. Is that what you want to happen. If so, this might give you an idea of what you might need to look at. If not let me know. I’m looking thru your code while I’m watching football.

viewer.mode=FULLSCREEN

function setup()
    ang=0
    ground=image(2000,2000)
    setContext(ground)
    background(236, 174, 67)
    stroke(255)
    strokeWidth(2)
    for x=1,2000,50 do
        for y=1,2000,50 do
            line(x,y,x,HEIGHT)
            line(x,y,WIDTH,y)
        end
    end
    fill(255,0,0)
    ellipse(1000,1000,20)
    setContext()
    sx,sy=0,0
    x,y=0,0
    dx,dy=0,0
end

function draw()
    background()
    pushMatrix()
    translate(WIDTH/2+x,HEIGHT/2+y)
    rotate(ang)
    sprite(ground,0,0)
    popMatrix()
    
    sprite(asset.builtin.Tyrian_Remastered.Twisted,WIDTH/2,HEIGHT/2)
    x=x+dx
    y=y+dy
end

function touched(t)
    if t.x<WIDTH/2 then
        ang=ang-t.deltaX/2
    end
    if t.x>WIDTH/2 then
        if t.state==BEGAN then
            sx=t.x
            sy=t.y
        elseif t.state==CHANGED then
            dx=(sx-t.x)/30
            dy=(sy-t.y)/30
        elseif t.state==ENDED then
            dx,dy=0,0
        end          
    end      
end

@Scotty Ignore what I said previously. I deleted those posts. I think there’s an easier way, still working on it.

@Scotty I think this might work. Replace the draw function with this code. The returnToHome isn’t working right, but if this is what you’re after, I’ll let you fix the returnToHome.

function draw()
    background(40, 40, 50)
    
    -- trig numbers (req'd?)
    sinLR = math.sin(math.rad(Yaw)) -- x
    cosFB = math.cos(math.rad(Yaw)) -- y
    
    -- Left joystick:
    Yaw=Yaw+JSYA.dX*mode/4
    Alt=Alt-JSYA.dY*mode/1000
    if Alt >  1 then Alt =  1 end
    if Alt < .4 then Alt = .4 end
    
    -- Right joystick:
    LR=LR+JSFBLR.dX*mode   -- left/right
    FB=FB+JSFBLR.dY*mode   -- foreward/backward
    
    -- Manage ground image, landing zone and return-to-home line.
    pushMatrix()
    translate(WIDTH/2-LR,HEIGHT/2-FB)  
    rotate(Yaw)
    pushMatrix()
    scale(Alt) -- The illusion of altitude
    sprite(myImageGround,0,0)
    landingZone(0,0)
    returnToHomeLine(LR,FB)  
    popMatrix()
    popMatrix()
    
    -- Drone is stationary while the ground moves underneath.
    sprite(myImageDrone,X,Y) -- Drone w/o lights
    drawDroneLights(X,Y)     -- Lights w/o body
    pushStyle()
    fill(white)
    fontSize(14)
    text(string.format("%.f°",Yaw), X,Y+60)
    popStyle()
    
    -- Joysticks
    JSYA:draw()
    JSFBLR:draw()
    
    -- Compass letters rotate around drone.
    drawCompass()    
end

@dave1707 thanks for the sample program. I’ll play with it while watching the Vikings. Another dismal season for the Vikes.

@Bri_G As stated in my description the drone is always centered on the screen while the ground beneath translates and rotates using the two joysticks. All motion seems fine as long as you are facing North.

@Scotty Did you see the draw function that I posted after my sample program.

@scotty - oops my bad, it’s the compass settings that rotate around the drone. The drone always stays vertical.

I take it you’re from Minnesota who are playing the packers today. Just think yourself lucky - my team is Man U and they’re on a real downer this year.

@Scotty In my draw function above, put the 3 lines below above the comment

-- Drone is stationary while the ground moves underneath.

I just have a white line from Home to the Drone. You can change it to add whatever you have in the function returnToHomeLine.

I think that should fix the problem.

    stroke(255)
    strokeWidth(5)
    line(WIDTH/2,HEIGHT/2,WIDTH/2-LR,HEIGHT/2-FB) 

@Scotty Heres what I came up with for the returnToHomeLine.

function returnToHomeLine(x,y)
    local dist = vec2(x,y):dist(vec2(WIDTH/2,HEIGHT/2)) -- The distance value     
    pushStyle()
    strokeWidth(4)
    stroke(255, 0, 0,25)
    line(WIDTH/2,HEIGHT/2, x,y)
    -- "dist-to-home" label
    pushMatrix()
    fill(red)
    fontSize(14/Alt)
    text(string.format("%.f", dist),(WIDTH/2+x)/2,(HEIGHT/2+y)/2)
    popMatrix() 
    popStyle()
end

@dave1707 Sorry to say but these changes seem to get the project further from my goal. I place the blame all on my explanation of the problem.

Picture the Home base fixed to the ground. It moves along with the ground. When the drone translates and rotates the ground and Home base translates and rotates but in the opposite direction. But the important thing to remember is all rotation happens directly under the drone. If you are looking down from the drone and start rotating, the spot underneath is your pivot point and not some point off in the distance.

The one thing that was really out of whack was translating after some yaw was added. Your changes to draw() fixed the translation part – translating in the direction the stick is pushed. But it messed up the rotation under the drone part. It’s now rotating around the Home base.

I’ll be up early hammering away at it.

@Bri_G no worries. Yes, I’m in Minnesota. The Vikings un-characteristically snatched victory from the yaws of defeat for a change.

Good night, guys.

@Scotty That makes more sense. I’ll start over and see what I can do.

These 2 statements are no longer supported.

supportedOrientations(ANY)
displayMode(FULLSCREEN)

Remove the supportedOrientations and replace the other one with viewer.mode=FULLSCREEN

@dave1707 describing the problem and project goals always seem to be the challenging part of this process. I put a great deal of effort into the description but still I fall short of the mark sometimes.

I’ve noodled around with the sample project you posted and have reposted it with some mods. Righthand translation works very well – even with some lefthand rotation added. The main problem that remains if that rotation is not happening under the drone (UFO in this example).

My ultimate goal is to control motion using two joysticks.

As for the “viewer.mode” comment, I’ll try and remember that when posting. Due to the age of iPad I’m running on my version of Codea is long out of date.

Thanks again for the assistance.

viewer.mode = FULLSCREEN

function setup()
    ground=image(2000,2000)
    
    setContext(ground)
    background(100, 104, 138, 255) 
    stroke(255)
    strokeWidth(2)
    for x=1,2000,50 do
        for y=1,2000,50 do
            line(x,y,x,HEIGHT)
            line(x,y,WIDTH,y)
        end
    end
    fill(255,0,0)
    rectMode(CENTER)
    rect(1000,1000,20)
    setContext()
    
    x,y=0,0
    dx,dy=0,0
    da,ang=0,0
end


function draw()
    background()
    
    pushMatrix()
    translate(WIDTH/2+x,HEIGHT/2+y)
    ang=ang+da
    rotate(ang)
    sprite(ground, 0,0)
    popMatrix()
    sprite("Space Art:UFO",WIDTH/2,HEIGHT/2)
    
    x=x+dx
    y=y+dy
end


function touched(t)
    --Left (rotation)
    local max = 2
    if t.x<WIDTH/2 then
        da=da-t.deltaX/10
        if da> max then da= max end
        if da<-max then da=-max end
        if t.state==ENDED then da=0 end          
    end
        
    --Right (translation)   
    if t.x>WIDTH/2 then
        dx=dx-t.deltaX/10
        dy=dy-t.deltaY/10
        if dx> max then dx= max end
        if dy> max then dy= max end
        if dx<-max then dx=-max end
        if dy<-max then dy=-max end
        if t.state==ENDED then dx,dy=0,0 end          
    end
end

@Scotty @dave1707 - had a play with @dave1707 code and slimmed down @Scotty code by building sprites. Also added a compass and used a large graphic to add a realistic look to the app. Not intended as replacement just a few ideas to consider.

May not download due to size of graphic, if so will remove graphic and you can add your own.

Edit: removed large graphic and added one of blocks from assets in Codea. Have a few other ideas to add - will post later.

@Bri_G I appreciate the efforts you have put into this , however the age of my iPad prevents me from accessing zip files. I have a gen 3 that’s close to 10 years old.

Thanks again.

@Scotty - ooops, my bad. Too busy trying to get some code to work in my window of opportunity. Here is the code and attached images:


-- viewer.mode = FULLSCREEN
displayMode(FULLSCREEN)
function setup()
    ground=image(1536,2048)
    X,Y = WIDTH//2,HEIGHT//2
    setContext(ground)
        spriteMode(CENTER)
        sprite(asset.builtin.Blocks.Leaves,768,1024,1536,2048)
        stroke(255)
        strokeWidth(2)
        for x=1,1600,64 do
            for y=1,2088,64 do
                line(x,y,x,HEIGHT)
                line(x,y,WIDTH,y)
            end
        end
        fill(255,0,0)
        spriteMode(CENTER)
        sprite(asset.landing,768,1024)
    setContext()
    x,y=0,0
    dx,dy=0,0
    da,ang=0,0
end

function draw()
    --
   background()
    pushMatrix()
        spriteMode(CENTER)
        translate(WIDTH/2+x,HEIGHT/2+y)
        ang=ang+da
        rotate(ang)
        sprite(ground, 0,0)
    popMatrix()
    pushMatrix()
        translate(704,964)
        rotate(ang)
        sprite(asset.Compas3,0,0,108,100)
    popMatrix()
    fill(255)
    textMode(CENTER)
    text("Angle : "..ang,664,884)
    myTimer(2)
    x=x+dx
    y=y+dy
end

function touched(t)
    --Left (rotation)
    local max = 2
    if t.x<WIDTH/2 then
        da=da-t.deltaX/10
        if da> max then da= max end
        if da<-max then da=-max end
        if t.state==ENDED then da=0 end          
    end
    --Right (translation)   
    if t.x>WIDTH/2 then
        dx=dx-t.deltaX/10
        dy=dy-t.deltaY/10
        if dx> max then dx= max end
        if dy> max then dy= max end
        if dx<-max then dx=-max end
        if dy<-max then dy=-max end
        if t.state==ENDED then dx,dy=0,0 end          
    end
end

function myTimer(dT)
    -- The work of others.    
    local dT = dT -- How often we want to see change happen.
    local eT = math.floor(ElapsedTime) 
    if math.fmod(eT,dT) == 0 then -- Do this color for only one 
        sprite(asset.drone2,X,Y)
    else  -- ...then do this for dT seconds.
        sprite(asset.droneLit,X,Y)
    end
end

Hope that’s OK.

@Bri_G thanks for the code.

I see what you were referring to for the compass. Very nice.

Pretty slick use of the drone sprites in myTimer(). I’m always learning something new. I’m curious to know how you created the images without the usual calls to setContext() from inside the project.

I’ve made some changes to your code, some minor due to, perhaps, screen size and some notated with uppercase “SPG”. These “SPG” changes allow the ground to always rotate directly underneath the drone. That is what is desired.

That leaves one remaining problem: If you retate the drone in a direction other than North and attempt to go in that new direction the drone appears to head toward North. You can force it to go in the proper direction but you have to slide your right finger in a direction other than the +Y direction. It is off by the amount of angle drone is rotated.

Looking forward to when your “window of opportunity” is open again.

Thanks again.

-- DroneBG


viewer.mode = FULLSCREEN
--displayMode(FULLSCREEN)
function setup()
    
    ground=image(1536,2048)
    X,Y = WIDTH//2,HEIGHT//2
    setContext(ground)
    background(145, 164, 185, 255)
        spriteMode(CENTER)
        -- can't access this sprite as iPad is too old.
        sprite(asset.builtin.Blocks.Leaves,768,1024,1536,2048)
        stroke(255)
        strokeWidth(2)
        for x=1,1600,64 do
            for y=1,2088,64 do
                line(x,y,x,HEIGHT)
                line(x,y,WIDTH,y)
            end
        end
        fill(255,0,0)
        spriteMode(CENTER)
        sprite(asset.landing,768,1024) --bg
        --sprite("Project:landing",768,1024) --spg
    setContext()
    
    x,y=0,0
    dx,dy=0,0
    da,ang=0,0
end

function draw()
    --
   background()
    pushMatrix()
        spriteMode(CENTER)
        --translate(WIDTH/2+x,HEIGHT/2+y) --bg
        translate(WIDTH/2,HEIGHT/2) --SPG
        ang=ang+da
        rotate(ang)
        --sprite(ground, 0,0) --bg
        sprite(ground, x,y)  --SPG
    popMatrix()
    pushMatrix()
        translate(704,964) --bg
        --translate(704,964/2) --spg
        rotate(ang)
        sprite(asset.Compas3,0,0,108,100) --bg
        --sprite("Project:Compas3",0,0,108,100) --spg
    popMatrix()
    fill(255)
    textMode(CENTER)
    text("Angle : "..ang,664,884) --bg
    --text("Angle : "..ang,664,884/2) --spg
    myTimer(2)
    x=x+dx
    y=y+dy
end


function touched(t)
    --Left (rotation)
    local max = 2
    if t.x<WIDTH/2 then
        da=da-t.deltaX/10
        if da> max then da= max end
        if da<-max then da=-max end
        if t.state==ENDED then da=0 end          
    end
    --Right (translation)   
    if t.x>WIDTH/2 then
        dx=dx-t.deltaX/10
        dy=dy-t.deltaY/10
        if dx> max then dx= max end
        if dy> max then dy= max end
        if dx<-max then dx=-max end
        if dy<-max then dy=-max end
        if t.state==ENDED then dx,dy=0,0 end          
    end
end

function myTimer(dT)
    -- The work of others.    
    local dT = dT -- How often we want to see change happen.
    local eT = math.floor(ElapsedTime) 
    if math.fmod(eT,dT) == 0 then -- Do this color for only one 
        sprite(asset.drone2,X,Y) --bg
        --sprite("Project:drone2",X,Y) --spg
    else  -- ...then do this for dT seconds.
        sprite(asset.droneLit,X,Y) --bg
        --sprite("Project:droneLit",X,Y) --spg
    end
end

@Scotty I’m still looking at your problem when I have time. I can make it go north, south, east, west no matter what the ground rotation angle is, but I haven’t figured out the calculations for the joystick positions in between the NSEW.