Another multi touch related issue

Hello guys,

I think I have a Lua coding issue here, but I seem to be unable to handle multi touch so that a Joystick appears when touching the right side of the stage, and button(s) on the left ( or viceversa ) at very same time.
This is what my Main currently looks like, and with regards to the buttons I’ve placed over my classes all worked fine so far. However I need to handle multi touch so that One_Player_Game will display virtual joy plus button.
Just to let you have a clearer scenario, with single touch I’ve no issues displaying virtual joy or button, thou I can’t make them showing up meantime. How would touch function look like in my One_Player_Game class?

displayMode(FULLSCREEN)
supportedOrientations(PORTRAIT_ANY)


function setup()
    touches = {}
    Intro = Intro()
    Menu = Menu(WIDTH/2,HEIGHT/2)
    BattleConfig = BattleConfig()
    OnePlayerGame = One_Player_Game()

end


function draw()
    supportedOrientations(PORTRAIT)
    background(0,0,0)
    if Intro_Status == 0 then
        Intro:draw()
    else
        if Menu_Status == 0 then        
            Menu:draw()
        else
            if BattleConfig_Status == 0 then
                BattleConfig:draw()
            else
                if OnePlayerGame_Status == 0 then
                    OnePlayerGame:draw()
                end
            end
        end 
    end 
end

function touched(touch)
        

        Menu:touched(touch)
        BattleConfig:touched(touch)
        Intro:touched(touch)
        OnePlayerGame:touched(touch)           

end

If anyone could help, he would make me a happy lad :slight_smile:
Thanks a lot
Regards
Alex

Can I see your full code and make some modifications to make it function properly?

If you need an example of how to handle multiple touches properly, there’s an excellent one under ‘Example Projects’ in Codea.

Hi @Invad3rZIM, thanks for your availability.
The code I’m posting here does not strictly relate to the game I originally asked help for, but it pretty sums up the same issue in a bunch only of classes.

I’d need to make the VirtualButton sprite to show up when the virtual stick is in movement.
If there’s better approach ( and I’m sure there is ) please give me some guide lines :slight_smile:
Any help will be appreciated
thanks
A.




function setup()
    print("This example tracks multiple touches and colors them based on their ID")
    
    -- keep track of our touches in this table
    touches = {}
    pos = vec2(WIDTH/2, 300)
    steer = vec2(0,0)
    speed = 400
     
    controller = VirtualStick {
        moved = function(v) steer = v end,
        released = function(v) steer = vec2(0,0) end
    }    


end

-- This function gets called whenever a touch
--  begins or changes state
function touched(touch)
    if touch.state == ENDED then
        -- When any touch ends, remove it from
        --  our table
        touches[touch.id] = nil
    else
        -- If the touch is in any other state 
        --  (such as BEGAN) we add it to our
        --  table
        touches[touch.id] = touch
        controller:activate()
    end
end

function draw()
    background(0, 0, 0, 255)
    
    for k,touch in pairs(touches) do

        controller:draw()

    end
end


-- Base class for controllers
--
-- Controllers translate touch events into callbacks to functions
-- that do something in the app. (Model/View/Controller style).
--
-- Controllers can draw a representation of their current state on
-- the screen, but you can choose not to.
--
-- A controller can be installed as the global handler for touch
-- events by calling its activate() method

Controller = class()

function Controller:activate()
    touched = function(t)
        self:touched(t)
    end
end

function Controller:draw()
    -- nothing
end

-- Utility functions

function touchPos(t)
    return vec2(t.x, t.y)
end

function clamp(x, min, max)
    return math.max(min, math.min(max, x))
end

function clampAbs(x, maxAbs)
    return clamp(x, -maxAbs, maxAbs)
end

function clampLen(vec, maxLen)
    if vec == vec2(0,0) then
        return vec
    else
        return vec:normalize() * math.min(vec:len(), maxLen)
    end
end

-- projects v onto the direction represented by the given unit vector
function project(v, unit)
    return v:dot(unit)
end

function sign(x)
    if x == 0 then
        return 0
    elseif x < 0 then
        return -1
    elseif x > 0 then
        return 1
    else
        return x -- x is NaN
    end
end

function doNothing()
end

-- A virtual analogue joystick with a dead-zone at the center,
-- which activates wherever the user touches their finger
--
-- Arguments:
--     radius - radius of the stick (default = 100)
--     deadZoneRadius - radius of the stick's dead zone (default = 25)
--     moved(v) - Called when the stick is moved
--         v : vec2 - in the range vec2(-1,-1) and vec2(1,1)
--     pressed() - Called when the user starts using the stick (optional)
--     released() - Called when the user releases the stick (optional)

VirtualStick = class(Controller)

function VirtualStick:init(args)
    self.radius = args.radius or 100
    self.deadZoneRadius = args.deadZoneRadius or 25
    self.releasedCallback = args.released or doNothing
    self.steerCallback = args.moved or doNothing
    self.pressedCallback = args.pressed or doNothing

end

function VirtualStick:touched(t)
    local pos = touchPos(t)

    if t.state == BEGAN and self.touchId == nil then
        self.touchId = t.id
        self.touchStart = pos
        self.stickOffset = vec2(0, 0)
        self.pressedCallback()
    elseif t.id == self.touchId then
        if t.state == MOVING then
            self.stickOffset = clampLen(pos - self.touchStart, self.radius)
            self.steerCallback(self:vector())
        elseif t.state == ENDED or t.state == CANCELLED then
            self:reset()
            self.releasedCallback()
        end
    
    end
end 

function VirtualStick:vector()
    local stickRange = self.radius - self.deadZoneRadius
    local stickAmount = math.max(self.stickOffset:len() - self.deadZoneRadius, 0)
    local stickDirection = self.stickOffset:normalize()
    
    return stickDirection * (stickAmount/stickRange)
end

function VirtualStick:reset()
    self.touchId = nil
    self.touchStart = nil
    self.stickOffset = nil
end

function VirtualStick:draw()
    if self.touchId ~= nil then
        pushStyle()
        ellipseMode(RADIUS)
        strokeWidth(1)
        stroke(255, 255, 255, 255)
        noFill()
        
        pushMatrix()
        translate(self.touchStart.x, self.touchStart.y)
        ellipse(0, 0, self.radius, self.radius)
        ellipse(0, 0, self.deadZoneRadius, self.deadZoneRadius)
        translate(self.stickOffset.x, self.stickOffset.y)
        --ellipse(0, 0, 25, 25)
        tint(255,255,255,180)        
        sprite("Cargo Bot:Star Filled",0,0,130)
        popMatrix()
        
        popStyle()

    end
end

VirtualButton = class()

function VirtualButton:init(x,y)
    self.x = x
    self.y = y
end

function VirtualButton:draw()
    
    pushStyle()
    pushMatrix()
    
    sprite("Cargo Bot:Star", self.x,self.y)
    popStyle()
    popMatrix()
    
    
    
end


Lemme see what I can do ^.^ gimme a good hour and I’ll get back to you mr deactive

Edit: finished first part let me now just make a sprite show up where you press the left part of the screen

@deactive

Okay here’s some sample code I made up to demonstrate:

  1. controlling both parts of the screen
  2. implementing multiple touches
  3. a simple virtual joystick I like to use that gets the job done and is much less effort ^.^

I used a physics ball as a medium to demonstrate touch control




function setup()
    
    touches = {} -- table of touches, used to store touch id and their coords

    ball = physics.body(CIRCLE, 45)
    ball.gravityScale = 0
    ball.x, ball.y = WIDTH/2, HEIGHT/2

    r = 100
    g = 100
    b = 100
end

-- This function gets called whenever a touch
--  begins or changes state
function touched(t)
    if t.state == BEGAN then
        aa = 0
        
        for a,b in pairs(touches) do --every time a touch is ended, its id is reset to 0
            if b.id == 0 then        --this cycles through, looking for a table value with
                b.id = t.id          --an id of 0. if found, it uses that one because the
                b.sx = t.x           --table slot isnt currently being used. if all slots
                b.cx = t.x           --are in use, it creates a new table slot using 'aa'
                b.sy = t.y           --as the boolean control for that condition.
                b.cy = t.y           --s stands for start; c stands for current
                                     --example: b.sx = b.start x
                b.side = 0
                
                if b.sx > WIDTH/2 then --side can be used to track which side of the
                    b.side = 1         --screen the touch is from like so
                end
                
                if b.sx < WIDTH/2 then --every time a touch is began on the left side,
                                       --rgb values of the ball are randomised
                    r = math.random(255)
                    g = math.random(255)
                    b = math.random(255)
                end
                aa = 1
            end
        end
        
        if aa == 0 then --see above for explanation on aa
            table.insert(touches, NewTouch(t.id,t.x,t.y))
        end
    end
    
    if t.state == MOVING or t.state == ENDED then
        for a,b in pairs(touches) do
            b:touched(t)
        end
    end
end

function draw()
    background(0, 0, 0, 255)

    for a,b in pairs(touches) do
        b:draw()
    end
    fill(color(r,g,b))
    noStroke()
    ellipse(ball.x,ball.y,ball.radius*2)
end

NewTouch = class()

function NewTouch:init(id,x,y) --initialises values
    self.id = id
    self.sx,self.cx = x,x
    self.sy,self.cy = y,y
end

function NewTouch:touched(t)
    if t.id == self.id then
        if t.state == MOVING then
            if self.sx > WIDTH/2 then --how to control things on the right of the screen...
                self.cx, self.cy = t.x,t.y
                
                if self.cx < WIDTH/2 then
                    self.cx = WIDTH/2
                end
              -- ellipse(self.cx,self.cy,5)
               local dist = math.sqrt(math.pow((self.cx - self.sx),2) + math.pow(self.cy - self.sy,2))
               while dist > 100 do --binds the controller to the outer ring of the joystick
                    if self.cx-self.sx > 0 then self.cx = self.cx * .99 end
                    if self.cx-self.sx < 0 then self.cx = self.cx * 1.01 end
                    if self.cy-self.sy > 0 then self.cy = self.cy * .99 end
                    if self.cy-self.sy < 0 then self.cy = self.cy * 1.01 end
            
                    dist = math.sqrt(math.pow((self.cx - self.sx),2) + math.pow(self.cy - self.sy,2))
                end
            
                ball:applyForce(vec2((self.cx-self.sx)*1/2, (self.cy-self.sy)*1/2))
            end
        end
        if t.state == ENDED then --resets id to 0
            self.id = 0
        end
    end
end

function NewTouch:draw()
    if self.id > 0 then --draws image of Cat girl if the start value of the touch is
      if self.sx < WIDTH/2 then --on the left of the screen
         sprite("Planet Cute:Character Cat Girl", self.sx,self.sy)   
      end
      if self.sx > WIDTH/2 then --draws joystick if startX is on the right
        noFill()
        strokeWidth(5)
        stroke(51, 255, 0, 255)
        
        ellipse(self.sx,self.sy,200)
        
        sprite("Cargo Bot:Star Filled",self.cx,self.cy)
      end
    end
end

Tried to use meaningful variable names and support them with good, detailed explanations. Let me know if you have any further questions.

Hi @Invad3rZIM, thank to your effort I clearly got how multi touch works.
However I eventually used the classes at this URL https://github.com/npryce/codea-controllers