Joystick code + buttons

Hi, so as an alternative control system for people who can’t operate their iPhones and iPads using tilt ive been looking into an onscreen joystick that just moves left and right with buttons for shoot and loop the loop for Starsceptre.

I’ve got a good example for the joystick but when I click on a button it overrides the joystick turning it off.

I’m using touch and not current touch. I’m wondering how I retain two touches. Is it by using touch.id one set for joystick and one each for the two buttons? I’m also trying to fix the joystick in one place and I’ve almost got that working.

This is the joystick code that creates the stick and locks it to horizontal controls

if controltype=="c" and game== go and touch.y < scY*50 and touch.y > scY*0 and touch.x < scX*35 and touch.x > scX*10 and touch.state == BEGAN then
        control.centre = vec2(touch.x,scY*25)
        control.touch = vec2(scX*25,scY*25)
        control.active = true
    elseif controltype=="c" and game== go and touch.y < scY*50 and touch.y > scY*0 and touch.x < scX*50 and touch.x > scX*0 and touch.state == MOVING then
        control.touch = vec2(touch.x,scY*25)
        if control.touch:dist(control.centre) > control.radius - control.innerRadius then
            control.touch = control.centre + (control.touch - control.centre):normalize()*(control.radius - control.innerRadius)
        end
    else
      control.active = false
    end

And here is the button for shooting. Do both sets need different Touch ID for you each?


    if game==go and sh.state ~= sh.deady and sh.state~=sh.jumpin and sh.state~=sh.pullup and sh.state~=sh.dive and paralysis <1 and levelend==false
--and CurrentTouch.y < scY*35+yy 
and (((controltype=="b" or controltype=="c") and touch.y < scY*29 and touch.y > scY*15
and touch.x < scX*78 and touch.x > scX*65) or (controltype=="a"
and touch.y < scY*75+yy))
and pauser==false
then
      if touch.state==BEGAN and auto==true then
         autofire=true
        if safetybullet==0 then
            fire=true
        end

      end
        if touch.state==ENDED then
         if auto==true then
             autofire=false
        if (level==3 and timer<5100) then 
        else
            safetybullet=0
         end
            else
                if level==3 then
                    if leveltimer<5100 then
                        if safetybullet==0 then
                        fire=true
                        end
                    else 
                        if safetybullet==0 then                     
                        fire = true
                        end
                    end
                else
                    if safetybullet==0 then                      
                        fire = true
                        end
                end
            end
         end  
    else autofire=false
    end

Thanks
Rich

@Majormorgan If you want to keep buttons or any type of screen touch seperate from other ones, then you’re correct by saying you need to use Touch ID.

Thanks @dave1707 !! I’ll get on that. Brilliant!!!

@Majormorgan Actually, you don’t need to use Touch ID. Here’s another way. I’m using a name that I give to each button. It might be easier to keep track of each button that way. To use the joyStick, slide your finger back and forth. The shoot button shoots a bullet each time it’s hit. The loop button just spins the ship and doesn’t let you shoot while spinning.

supportedOrientations(PORTRAIT_ANY)
displayMode(FULLSCREEN)

function setup()
    a=0
    miss={}
    rectMode(CENTER)
    jsx,jsy=WIDTH/2,200
    joyStick=button(WIDTH/2,100,400,50,"Joy Stick")
    shoot=button(100,100,100,50,"Shoot")
    loop=button(WIDTH-100,100,100,50,"Loop")
end

function draw()
    background(40, 40, 50)
    fill(255)
    joyStick:draw()
    for a,b in pairs(miss) do
        sprite("Tyrian Remastered:Fire Rock",b.x,b.y)
        b.y=b.y+10
        if b.y>HEIGHT then
            table.remove(miss,a)
        end
    end
    shoot:draw()
    loop:draw()
    translate(jsx,jsy)
    if looping then
        a=a+10
        rotate(a)
        if a>360 then
            a=0
            looping=false
        end
    end
    sprite("Space Art:Red Ship",0,0)
end

function touched(t)
    joyStick:touched(t)
    shoot:touched(t)
    loop:touched(t)    
end

button=class()

function button:init(x,y,w,h,n)
    self.x=x
    self.y=y
    self.w=w
    self.h=h
    self.name=n
end

function button:draw()
    pushStyle()
    fill(255)
    rect(self.x,self.y,self.w,self.h)
    fill(255,0,0)
    text(self.name,self.x,self.y)
    popStyle()
end

function button:touched(t)
    if t.x>self.x-self.w/2 and t.x<self.x+self.w/2 and
            t.y>self.y-self.h/2 and t.y<self.y+self.h/2 then
        if t.state==BEGAN then
            if self.name=="Shoot" and not looping then
                sound("Game Sounds One:Pistol")
                table.insert(miss,vec2(jsx,240))
            end
            if self.name=="Loop" then
                looping=true
            end
        end
        if t.state==MOVING then
            if self.name=="Joy Stick" then
                jsx=jsx+t.deltaX
            end
        end
    end    
end

Thanks @dave1707

I’m down the Touch ID route at present. Do I need to create an array to store Touch IDs or am I missed by something simple, I was hoping to assign a touch.id to the joystick and one to fire button and loop button

@Majormorgan I think Touch ID is best used when you have multiple objects that don’t remain in a fixed position. You might touch multiple objects and move them somewhere on the screen. I can’t think of a good example at the moment. As for using an array, that depends. When you touch an object, then the table would need the ID along with other info for that object. If you use a class, then you would save the ID in the instance of the button. I think the easiest way for fixed buttons would be what I show above. You give each button a unique name and you use the name to identify which button is used. The name in this case is replacing the ID number. One thing about a Touch ID, it’s a number that’s different each time the screen is touched. I don’t know how the ID is calculated.

Ah that makes sense @dave1707 - I’m gonna implement your code! Thank you

One question @dave1707 is the two buttons will be fixed but the joystick itself is moving. Hence the code for the stick. perhaps if I have the button as the base thing behind like your long and wide button and the circle that will look like the button will intact just be something that sticks to your finger when you move. Hmmmm…

I didn’t fully understand what you were saying, but were you after something like this.

supportedOrientations(PORTRAIT_ANY)
displayMode(FULLSCREEN)

function setup()
    a=0
    miss={}
    rectMode(CENTER)
    jsx,jsy=WIDTH/2,200
    stick=button(WIDTH/2,100,80,100,"Stick")
    shoot=button(100,100,100,50,"Shoot")
    loop=button(WIDTH-100,100,100,50,"Loop")
end

function draw()
    background(40, 40, 50)
    fill(255)
    stick:draw()
    for a,b in pairs(miss) do
        sprite("Tyrian Remastered:Fire Rock",b.x,b.y)
        b.y=b.y+10
        if b.y>HEIGHT then
            table.remove(miss,a)
        end
    end
    shoot:draw()
    loop:draw()
    translate(jsx,jsy)
    if looping then
        a=a+10
        rotate(a)
        if a>360 then
            a=0
            looping=false
        end
    end
    sprite("Space Art:Red Ship",0,0)
end

function touched(t)
    stick:touched(t)
    shoot:touched(t)
    loop:touched(t)    
end

button=class()

function button:init(x,y,w,h,n)
    self.x=x
    self.y=y
    self.w=w
    self.h=h
    self.name=n
end

function button:draw()
    pushStyle()
    fill(255)
    if self.name=="Stick" then
        ellipse(self.x,self.y,80)
    else
        rect(self.x,self.y,self.w,self.h)
    end
    fill(255,0,0)
    text(self.name,self.x,self.y)
    popStyle()
end

function button:touched(t)
    if t.x>self.x-self.w/2 and t.x<self.x+self.w/2 and
            t.y>self.y-self.h/2 and t.y<self.y+self.h/2 then
        if t.state==BEGAN then
            if self.name=="Shoot" and not looping then
                sound("Game Sounds One:Pistol")
                table.insert(miss,vec2(jsx,240))
            elseif self.name=="Loop" then
                looping=true
            end
        end
        if t.state==MOVING then
            if self.name=="Stick" then
                self.x=t.x
                if self.x<200 then
                    self.x=200
                end
                if self.x>WIDTH-200 then
                    self.x=WIDTH-200
                end
                jsx=WIDTH/2-(WIDTH/2-self.x)*2
            end
        end
    end    
end

Hi @dave1707 that works great in its own project and is quite close to my vision, but trying to incorporate that code into my game is proving a bitch as there are a number of things that are affected by the code from different parts of the game engine.

That’s why I’m trying to implement the id aspect to the code I have. Here’s a quick video demo:
https://instagram.com/p/BXXT9wkH79T/

As I can only detect one touch the joystick control is turning off the autofire but holding down on the joystick still lets me move the ship.

My two priorities to solve are multi touch or multi hold really, plus when You take your hand off the joystick I’ll code it to move back to the centre.

if I could understand how to code in multi touch using Touch ID that would be ace!

Thank you so much for all your help

Best
Rich

@Majormorgan See the Codea example Multi Touch. See if that gives you the info you need. If not let me know and I’ll help more.

Thanks @dave1707 - you advice is helping me understand all this stuff so much better and very much appreciated!

I’ll keep on with the multitouch examples and if I hit any specifics I’ll ask.

One quick question, I still have another touch function running at the same time as this new touch t - and the other touch example is above the touch t code in main. Can two touch codes exist at the same time?

Thanks

@Majormorgan -here is an example of multi-touch which does some of what you want.



function setup()
    
    JOYSTICK=1
    
    -- keep track of our touches in this table
    touches = {}
    tsup={} -- tracks the characteristics of the touch and associates with the touches table through the  touch.id.  I can't find a way of associating this additional info directly with the touches table above
    
    --player parameters
    shipx=WIDTH/2
    shipy=500
    shipspd=0
    fireon=0
    bullets={}
    parameter.number("intertia",0.9,1.0,0.9) --the smaller this value the shorter time it takes the ship to slow
    parameter.number("sensitivity",10,100,50) --how much the ship moves relative to finger movement - larger value closer to finger tracking
    
    --button paraemeters
    firex=WIDTH*0.25
    firey=150
    firer=100
    loopx=WIDTH*0.75
    loopy=150
    loopr=100
    
    counter=0
end

-- This function gets called whenever a touch
--  begins or changes state
function touched(touch)
    if touch.state==ENDED or touch.state==CANCELLED then
        processTouch(touch)
        touches[touch.id] = nil
        tsup[touch.id]=nil
    else
        touches[touch.id] = touch
        --if there is no supplementary info associated with the current touch then add it
        if tsup[touch.id]==nil then
            --check to see if there is an exisitng touch event assigned to the movement, and log in the "kind variable
            local k=JOYSTICK
            for i,ts in pairs (tsup) do
                if ts.kind==JOYSTICK then k=0 end
            end
            tsup[touch.id]={startx=touch.x,starty=touch.y,startt=ElapsedTime,kind=k}

            --you could add event triggers here for new non-movement assigned touches (e.g. normally a second or subsequent touch)
            --for example the following switches on the autofire for any second touch
            --these will only trigger when the new touch initiates
            if k==0 then
                fireon=1
            end
        end
    end
end


function processTouch(touch)
    --this is called when the any touch event is ended.  You could add in event triggers here too
    
    --If you take your finger of over the loop button it will initiate it - doesn't matter how long you hold it for
    if vec2(tsup[touch.id].startx,tsup[touch.id].starty):dist(vec2(loopx,loopy))<loopr/2 then
        --initiate the loop the loop here
        sound(SOUND_POWERUP, 46775)
    end
    
    --example of toggling a switch - needs to be in a certain location and lasting less than a set time
    if vec2(touch.x,touch.y):dist(vec2(firex,firey))<firer/2 and (ElapsedTime-tsup[touch.id].startt)<0.5 then
        fireon = fireon + 1
        if fireon>1 then fireon=0 end
    end
    
--what would be really interesting to explore would be to detect a vertical swipe to do your loop the loop

end

function draw()
    counter = counter + 1
    background(0, 0, 0, 255)
    stroke(218, 161, 30, 255)
    strokeWidth(3)
    fill(255)
    
    for i,t in pairs(touches) do
        ellipse(t.x,t.y,70)
        line(tsup[i].startx,tsup[i].starty,t.x,t.y)
        --take some action based on the status of the touch - in this example it can only be 1 (Joystick) or none - you could add more events
        if tsup[i].kind==JOYSTICK then
            shipspd=(t.x-tsup[i].startx)/sensitivity
        end       
    end
    
    if fireon==1 then
        fill(73, 255, 0, 255)
    else
        fill(255, 13, 0, 255)
    end
    ellipse(firex,firey,firer)
    fill(0)
    text("Fire",firex,firey)
    fill(255, 226, 0, 255)    
    ellipse(loopx,loopy,loopr)
    fill(0)
    text("Loop",loopx,loopy)
    
    for i,b in pairs(bullets) do
        sprite("Tyrian Remastered:Bullet Simple B",b.x,b.y)
        b.y = b.y + 5
        if b.y>HEIGHT then
            table.remove(bullets,i)
        end
    end
    
    if shipspd>0 then
        sprite("Tyrian Remastered:Ship D R1",shipx,shipy,50)
    elseif shipspd<0 then
        sprite("Tyrian Remastered:Ship D L1",shipx,shipy,50)
    else
        sprite("Tyrian Remastered:Ship D",shipx,shipy,50)
    end
    shipx = shipx + shipspd
    if shipx>WIDTH then shipx=WIDTH end
    if shipx<0 then shipx=0 end
    shipspd = shipspd *intertia
    if math.abs(shipspd)<1 then shipspd=0 end
    if fireon==1 and math.fmod(counter,10)==1 then
        table.insert(bullets,{x=shipx,y=shipy})
    end
end

A purely personal point of view but I’m always a little bit disappointed when I see someone on the forum trying to implement an onscreen joystick. The iPad is a touch driven device, and there are so many possibilities to implement interesting control mechanisms for games, but the default seems to be for an onscreen approximation of a physical joystick.

IMHO having to hit a specific location on the screen to control your ship seems a bit ‘meh’ compared to the USP of your game of unique(ish) tilt controls for a shooter. How about detecting gestures to initiate events, e.g. Upward swipe to loop the loop. Tap or second finger hold to toggle auto-fire/fire and primary touch for ship movement.

Thanks @West - I’ll look into that code.

The onscreen or touch to move controls are only an alternative to the tilt mechanism for the ones that don’t like tilting. The titling will always be the main control system for the game, just I’ve had a lot of resistance to the tilt mechanic only so by making the stick variant, even an invisible one, will go some way to gaining more players.

I do get about the swipe action and tap to shoot as a good way to ensure a more expressive and mobile way of playing. I’ve just got to get the balance right as I hate shooters where the ship is stuck to your finger as you obscure the ship and can’t see what you are hitting.

Also the game isn’t an auto-shooter like others as you have to conciously make decisions to shoot or not.

I’ll know once I get this code working properly

Thanks to you and to @dave1707 !!!

Success! I was being a twat - I didn’t ensure that the touch state started checking if it had ENDED first before wrapping the other states under a single Else clause. Instead I had defined all three touch states and it wasn’t tracking the touch through all three states.

So it’s meant to be:

If touch.state==ENDED then
     touches[touch.id] = nil
else
     touches[touch.id] = touch
   [code goes here...]
end

All I have to do now is write code to detect the distance of the joystick as it moves from its centre point so it can adjust the amount the background moves by (slight movement on the stick = slight movement on the background, full movement on the stick = full movement on the background). I’ll be able to crack that.

Thank you both so much for your examples and feedback.

I can see going forwards with new games that @dave1707 approach to making buttons will be worth investigating.

I’ve been a double twat, the code I was using also moves the background almost the way I want, less with less stick, more with more stick, just need to adjust the amount so it is smoother

And here’s the latest code demo that has stick on the left (massive hit area) for left movement plus pull back for tilt. The right side area is a massive hit state for autofire:

https://twitter.com/8bitmagicgames/status/894691373777113089

Looking at that video it seems like the game runs 1.5 - 2 times faster on my iPad Air 2. No wonder it was so hard :open_mouth:

Actually @John that video runs slow as it’s recording the screen and running the code at the same time. The game runs much faster as you said, even on iPhone 4s and iPad 2.

There is something running the code slower at the same time as screen record that I’m trying to locate. Not sure what it is yet as a version of starsceptre about 6 months ago plays and records fine at the same time.

How far did you get in the game?

@Majormorgan I’m not sure what is causing the slowdown exactly but generally you want to use DeltaTime to make sure the game can account for lower than optimal FPS.

I got up to the minefield level.