Howdy, Stranger!

It looks like you're new here. If you want to get involved, click one of these buttons!

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

Comments

  • Posts: 91
    -- 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
    
    
    
  • dave1707dave1707 Mod
    Posts: 9,725

    @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.

  • dave1707dave1707 Mod
    Posts: 9,725

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

  • edited November 21 Posts: 2,496
    @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.
  • dave1707dave1707 Mod
    Posts: 9,725

    @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
    
  • dave1707dave1707 Mod
    Posts: 9,725

    @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
    
  • Posts: 91

    @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.

  • dave1707dave1707 Mod
    Posts: 9,725

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

  • Posts: 2,496

    @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.

  • dave1707dave1707 Mod
    edited November 21 Posts: 9,725

    @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) 
    
  • dave1707dave1707 Mod
    Posts: 9,725

    @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
    
  • Posts: 91

    @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.

  • dave1707dave1707 Mod
    edited November 22 Posts: 9,725

    @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

  • Posts: 91

    @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
    
  • edited November 23 Posts: 2,496

    @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.

  • Posts: 91

    @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.

  • Posts: 2,496

    @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.

  • edited November 24 Posts: 91

    @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
    
  • dave1707dave1707 Mod
    Posts: 9,725

    @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.

  • Posts: 2,496

    @Scotty - in short I used your code, listing attached - change the names to avoid clash with other project. The compass I drew myself, needs a little adjustment.


    — Landing function setup() -- X,Y = WIDTH/2, HEIGHT/2 commonColors() createImageDrone() landingZone() drawDroneLights() end -- This function gets called once every frame function draw() -- This sets a dark background color background(40, 40, 50) sprite(land,WIDTH//2,HEIGHT//2) sprite(drone,WIDTH//2,HEIGHT//4) end function landingZone() local size = 50 land = image(size,size) setContext(land) pushMatrix() translate(size//2,size//2) 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() setContext() saveImage(asset.documents.."landing",land) end function createImageDrone() -- Creates an image that represents the drone w/o lights myImageDrone = image(X,Y) setContext(myImageDrone) drawDroneBody() setContext() saveImage(asset.documents.."drone2",drone) end function drawDroneBody(x,y) -- Image of drone w/o lights. local x,y = x,y local mSize = 50 local size = 110 local droneColor = gray drone = image(size,size) setContext(drone) pushMatrix() translate(size//2,size//2) pushStyle() -- arms lineCapMode(ROUND) stroke(droneColor) fill(droneColor) for i=1,4 do -- background(255) 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() setContext() end function drawDroneLights() -- Drone's flashing lights. local x,y = x,y local mSize = 60 local droneColor = gray setContext(drone) pushMatrix() translate(55,100) pushStyle() -- tail light outline strokeWidth(2) stroke(black) -- line(-6,-mSize/2-48, 6,-mSize/2-48) -- head light outline fill(white) ellipse(0,mSize/2-44, 12) -- myTimer(2) -- tail light strokeWidth(5) -- line(-8,-mSize/2-8, 8,-mSize/2-8) stroke(black) -- head light -- ellipse(-4,mSize/2-108, 4,16) line(-8,-76,8,-76) stroke(green) strokeWidth(4) line(-7,-76,7,-76) popStyle() popMatrix() setContext() saveImage(asset.documents.."droneLit",drone) 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
  • dave1707dave1707 Mod
    edited November 25 Posts: 9,725

    @Scotty Here’s my original example that might be closer to what you’re after. The drone can move forward or backward (up or down), but not side to side yet. You can rotate the ground under the drone and the drone will still go forward or backward. To move the drone, move your finger up or down on the top half of the screen. To rotate the ground, move your finger side to side on the bottom half of the screen. The drone will continue to move if you lift your finger. To stop the drone, double tap on the top half of the screen.

    PS. I think I know how to move the drone side to side too. I’ll work on that next.

    viewer.mode=FULLSCREEN
    
    function setup()
        rectMode(CENTER)
        ground=image(4000,4000)
        setContext(ground)
        background(236, 174, 67)
        stroke(255)
        strokeWidth(2)
        for x=1,4000,50 do
            for y=1,4000,50 do
                line(x,y,x,HEIGHT)
                line(x,y,WIDTH,y)
            end
        end    
        fill(255)
        noStroke()
        rect(2000,2000,60)
        fill(255,0,0)
        fontSize(50)
        text("H",2000,2000)
        text("N",2000,2200)
        text("S",2000,1800)
        setContext()
    
        sx,sy=0,0
        x,y=0,0
        dx,dy=0,0
        ang=0
    end
    
    function draw()
        background()    
        pushMatrix()
        translate(WIDTH/2,HEIGHT/2)
        rotate(ang)
        sprite(ground,x,y)
        popMatrix()
        sprite(asset.builtin.Tyrian_Remastered.Twisted,WIDTH/2,HEIGHT/2)  
        calc() 
    end
    
    function calc() 
        ang=ang%360
        if ang>=315 then
            ang1=360-ang
            x=x+math.tan(math.rad(ang1))*dy
            y=y-dy
        elseif ang>=225 and ang<315 then
            ang1=270-ang
            y=y+math.tan(math.rad(ang1))*dy
            x=x+dy
        elseif ang>=135 and ang<225 then
            ang1=180-ang
            x=x-math.tan(math.rad(ang1))*dy
            y=y+dy
        elseif ang>45 and ang<135 then
            ang1=90-ang
            y=y-math.tan(math.rad(ang1))*dy
            x=x-dy
        elseif ang<=45 then
            ang1=ang
            x=x-math.tan(math.rad(ang1))*dy
            y=y-dy
        end
    end
    
    function touched(t)
        if t.y<HEIGHT/2 then
            ang=ang-t.deltaX
        end
        if t.y>HEIGHT/2 then
            if t.state==BEGAN then
                sx=t.x
                sy=t.y
            elseif t.state==CHANGED then
                dx=(sx-t.x)/40
                dy=-(sy-t.y)/40
            elseif t.state==ENDED and t.tapCount==2 then
                dx,dy=0,0
            end          
        end      
    end
    
  • dave1707dave1707 Mod
    Posts: 9,725

    @Scotty Here’s the latest example for the drone movement. You can now move in any direction and rotate the ground under the drone. Some changes for this example. Move up/down/left/right on right side of screen. Rotate ground by moving up/down on left side of screen. Drone continues to move when your finger is lifted. Double tap to stop drone.

    All the calculations are done in calcUDLR, so you can move that to your code and work it with your joysticks.

    viewer.mode=FULLSCREEN
    
    function setup()
        rectMode(CENTER)
        ground=image(4000,4000)
        setContext(ground)
        background(236, 174, 67)
        stroke(255)
        strokeWidth(2)
        for x=1,4000,50 do
            for y=1,4000,50 do
                line(x,y,x,HEIGHT)
                line(x,y,WIDTH,y)
            end
        end    
        fill(255)
        rect(2000,2000,60)
        fill(255,0,0)
        fontSize(50)
        text("H",2000,2000)
        text("N",2000,2200)
        text("S",2000,1800)
        setContext()
    
        sx,sy,x,y,dx,dy,ang=0,0,0,0,0,0,0
    end
    
    function draw()
        background()   
    
        pushMatrix()
        translate(WIDTH/2,HEIGHT/2) -- center of screen
        rotate(ang)
        sprite(ground,x,y)
        popMatrix()
    
        sprite(asset.builtin.Tyrian_Remastered.Twisted,WIDTH/2,HEIGHT/2)  
        calcUDLR() 
    end
    
    function calcUDLR()   -- calculate x,y movement based on angle and direction
        ang=ang%360    
        if ang<=45 then
            ang1=ang
            x=x-math.tan(math.rad(ang1))*dy+dx
            y=y-math.tan(math.rad(ang1))*dx-dy
        elseif ang>=45 and ang<135 then
            ang1=90-ang
            y=y-math.tan(math.rad(ang1))*dy-dx
            x=x+math.tan(math.rad(ang1))*dx-dy
        elseif ang>=135 and ang<225 then
            ang1=180-ang
            x=x-math.tan(math.rad(ang1))*dy-dx
            y=y-math.tan(math.rad(ang1))*dx+dy
        elseif ang>=225 and ang<315 then
            ang1=270-ang
            y=y+math.tan(math.rad(ang1))*dy+dx
            x=x-math.tan(math.rad(ang1))*dx+dy
        elseif ang>=315 then
            ang1=360-ang
            x=x+math.tan(math.rad(ang1))*dy+dx
            y=y+math.tan(math.rad(ang1))*dx-dy
        end
    end
    
    function touched(t)
        if t.tapCount==2 then
            dx,dy=0,0
        end          
        if t.x<WIDTH/2 then -- change angle based on y changes
            ang=ang-t.deltaY/2
        end
        if t.x>WIDTH/2 then
            if t.state==BEGAN then  -- start x,y values
                sx=t.x
                sy=t.y
            elseif t.state==CHANGED then    -- calc change in touch
                dx=(sx-t.x)/40
                dy=-(sy-t.y)/40
            end
    
        end      
    end
    
  • Posts: 91

    @dave1707 , @Bri_G

    This is indeed getting close to the mark.

    I've mashed Dave's example, which includes calcUDLR(), with Bri's. I've put in my touched() code so rotate and translate are controlled by left and right fingers respectively. When fingers are released motion stops.

    I made some minor tweeks noted by lowercase "spg" that dose not change the overall by much. Has to do more with trading screen numbers for WIDTH or HEIGHT. I changed some sprite calls due to my ancient iPad. That sort of thing.

    A more CONSEQUENTIAL change is noted by uppercase "SPG". This has all to do with moving the ground is under the driver.

    The one sticky thing still remaining is diagonal motion seems to be off by about 90°.

    I hope my explanation makes sense.

    Thanks again for all the help and have a HAPPY THANKSGIVING.

    --viewer.mode = FULLSCREEN
    displayMode(FULLSCREEN)
    
    function setup()
        X,Y = WIDTH//2,HEIGHT//2
    
        --ground=image(1536,2048)
        ground=image(WIDTH*2,HEIGHT*2)
        setContext(ground)
        background(145, 164, 185, 255)
            spriteMode(CENTER)
            --sprite(asset.builtin.Blocks.Leaves,768,1024,1536,2048)
            stroke(255)
            strokeWidth(2)
            for x=1,WIDTH*2,64 do -- spg
                for y=1,HEIGHT*2,64 do -- spg
                    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",WIDTH,HEIGHT) --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(WIDTH/2,HEIGHT/2+200) --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(string.format("Angle: %.f",ang),WIDTH/2,HEIGHT/2+125) --spg
        myTimer(2)
    
        calcUDLR()
    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
            --sprite(asset.drone2,X,Y) --bg
            sprite("Project:drone2",X,Y) --spg
        else
            --sprite(asset.droneLit,X,Y) --bg
            sprite("Project:droneLit",X,Y) --spg
        end
    end
    
    function calcUDLR()   -- calculate x,y movement based on angle and direction
        ang=ang%360 
        if ang<=45 then
            ang1=ang
            x=x-math.tan(math.rad(ang1))*dy+dx
            y=y-math.tan(math.rad(ang1))*dx-dy
        elseif ang>=45 and ang<135 then
            ang1=90-ang
            y=y-math.tan(math.rad(ang1))*dy-dx
            x=x+math.tan(math.rad(ang1))*dx-dy
        elseif ang>=135 and ang<225 then
            ang1=180-ang
            x=x-math.tan(math.rad(ang1))*dy-dx
            y=y-math.tan(math.rad(ang1))*dx+dy
        elseif ang>=225 and ang<315 then
            ang1=270-ang
            y=y+math.tan(math.rad(ang1))*dy+dx
            x=x-math.tan(math.rad(ang1))*dx+dy
        elseif ang>=315 then
            ang1=360-ang
            x=x+math.tan(math.rad(ang1))*dy+dx
            y=y+math.tan(math.rad(ang1))*dx-dy
        end
    end
    
  • dave1707dave1707 Mod
    Posts: 9,725

    @Scotty To fix the movement being off, make this change in your touched function.
    Make it a + instead of a - .

            dx=dx+t.deltaX/10
    
  • Posts: 91

    @dave1707, @Bri_G and if you change

            sprite(ground, x,y)  --SPG
    

    to

            sprite(ground, -x,-y)  --SPG
    

    in draw() I believe it will then work perfectly.

    I'll see if I can apply these concepts to my original drone project and see what happens.

    Thank you very much for all the help.

  • Posts: 91

    And change

            da=da-t.deltaX/10
    

    to an addition operation in touched()

            da=da+t.deltaX/10
    

    That should do it.

Sign In or Register to comment.