Virtual Joystick


(Posted 1 year ago) RonTek

Virtual Joystick for Monkey-X by Christian Sonntag

BlitzCoder.org - Virtual Joystick for Monkey-X by Christian Sonntag

'Virtual Stick by Christian Sonntag
'http://www.motions-media.de/2012/12/02/monkey-virtual-stick/

Strict
import mojo

class VirtualStickTestApp extends App
    const PLAYFIELD_WIDTH:float = 200
    const PLAYFIELD_HEIGHT:float = 200
    const PLAYER_SPEED:float = 5

    ' our virtual stick
    field mystick:MyStick

    ' whether or not we're "touching"
    #if TARGET="android" then
        field touching:bool[] = new bool[32]
    #else
        field touching:bool
    #endif

    ' the "player"'s location
    Field playerX:Float = PLAYFIELD_WIDTH/2
    field playerY:float = PLAYFIELD_HEIGHT/2
    field playfieldX:float
    field playfieldY:float = 10

    method OnCreate:int()
        mystick = new MyStick
        mystick.SetRing(100, DeviceHeight()-100, 40)
        mystick.SetStick(0, 0, 15)
        mystick.SetDeadZone(0.2)
        mystick.SetTriggerDistance(5)
        playfieldX = DeviceWidth()-PLAYFIELD_WIDTH-10
        SetUpdateRate 30
        Return 0
    end

    method OnUpdate:int()
        ' update the stick usage
        #if TARGET="android" then
            for local i:int = 0 to 31
                if TouchHit(i) then
                    touching[i] = True
                    mystick.StartTouch(TouchX(i), TouchY(i), i)
                elseif TouchDown(i) then
                    mystick.UpdateTouch(TouchX(i), TouchY(i), i)
                elseif touching[i] then
                    touching[i] = false
                    mystick.StopTouch(i)
                end
            end
        #Else
            if MouseHit(0) then
                touching = True
                mystick.StartTouch(MouseX(), MouseY(), 0)
            elseif MouseDown(0) then
                mystick.UpdateTouch(MouseX(), MouseY(), 0)
            elseif touching then
                touching = false
                mystick.StopTouch(0)
            end
        #endif

        ' update the player position
        If mystick.GetVelocity() <> 0 Then
            playerX += mystick.GetDX() * PLAYER_SPEED
            playerY -= mystick.GetDY() * PLAYER_SPEED
            if playerX < 0 then
                playerX = 0
            elseif playerX > PLAYFIELD_WIDTH then
                playerX = PLAYFIELD_WIDTH
            end
            If playerY < 0 Then
                playerY = 0
            elseif playerY > PLAYFIELD_HEIGHT then
                playerY = PLAYFIELD_HEIGHT
            end
        end
        return 0
    end

    method OnRender:int()
        Cls(0,0,0)

        mystick.DoRenderRing()
        mystick.DoRenderStick()
        DrawOutlineRect(playfieldX, playfieldY, PLAYFIELD_WIDTH, PLAYFIELD_HEIGHT)
        DrawCircle(playfieldX + playerX, playfieldY + playerY, 5)

        ' some test info
        DrawText("angle="+mystick.GetAngle(), 10, 10)
        DrawText("vel="+mystick.GetVelocity(), 10, 30)
        DrawText("dx="+mystick.GetDX(), 10, 50)
        DrawText("dy="+mystick.GetDY(), 10, 70)
        return 0
    End
End

class MyStick extends VirtualStick
    method RenderRing:void(x:float, y:float)
        SetColor 0, 0, 255
        super.RenderRing(x, y)
        SetColor 255, 255, 255
    end

    method RenderStick:void(x:float, y:float)
        SetColor 0, 255, 0
        super.RenderStick(x, y)
        SetColor 255, 255, 255
    end
end

class VirtualStick
private
    ' the coordinates and dimensions for the virtual stick's ring (where the user will first touch)
    field ringX:float
    field ringY:float
    field ringRadius:float

    ' the coordinates and dimensions for the stick (what the user is pushing around)
    ' X/Y is relative to the centre of the ring, and positive Y points up
    field stickX:float = 0
    field stickY:float = 0
    field stickRadius:float
    field stickAngle:float
    field stickPower:float

    ' where the user first touched
    field firstTouchX:float
    field firstTouchY:float

    ' power must always be >= this, or we return 0
    field deadZone:float

    ' we need to move the stick this much before it triggers
    field triggerDistance:float = -1
    field triggered:bool = false

    ' the index of the touch event that initiated the stick movement
    field touchNumber:int = -1

    ' clips the stick to be within the ring, and updates angles, etc.
    method UpdateStick:void()
        if touchNumber>=0 then
            local length:float = Sqrt(stickX*stickX+stickY*stickY)
            stickPower = length/ringRadius
            if stickPower > 1 then stickPower = 1

            if stickPower < deadZone then
                stickPower = 0
                stickAngle = 0
                stickX = 0
                stickY = 0
            else
                if stickX = 0 and stickY = 0 then
                    stickAngle = 0
                    stickPower = 0
                elseif stickX = 0 and stickY > 0 then
                    stickAngle = 90
                elseif stickX = 0 and stickY < 0 then
                    stickAngle = 270
                elseif stickY = 0 and stickX > 0 then
                    stickAngle = 0
                elseif stickY = 0 and stickX < 0 then
                    stickAngle = 180
                elseif stickX > 0 and stickY > 0 then
                    stickAngle = ATan(stickY/stickX)
                elseif stickX < 0 then
                    stickAngle = 180+ATan(stickY/stickX)
                else
                    stickAngle = 360+ATan(stickY/stickX)
                end
                if length > ringRadius then
                    stickPower = 1
                    stickX = Cos(stickAngle) * ringRadius
                    stickY = Sin(stickAngle) * ringRadius
                end
            end
        end
    end

public

    ' the angle in degrees that the user is pushing, going counter-clockwise from right
    method GetAngle:float()
        return stickAngle
    end

    ' the strength of the movement (0 means dead centre, 1 means at the edge of the ring (or past it)
    Method GetVelocity:Float()
        return stickPower
    end

    ' based on the angle and velocity, get the DX
    method GetDX:float()
        return Cos(stickAngle) * stickPower
    end

    ' based on the angle and velocity, get the DY
    method GetDY:float()
        return Sin(stickAngle) * stickPower
    end

    ' we just touched the screen at point (x,y), so start "controlling" if we touched inside the ring
    method StartTouch:void(x:float, y:float, touchnum:int)
        if touchNumber < 0 then
            if (x-ringX)*(x-ringX) + (y-ringY)*(y-ringY) <= ringRadius*ringRadius then
                touchNumber = touchnum
                firstTouchX = x
                firstTouchY = y
                triggered = false
                if triggerDistance <= 0 then
                    triggered = true
                    stickX = x-ringX
                    stickY = ringY-y
                end
                UpdateStick()
            end
        end
    end

    ' a touch just moved, so we may need to update the stick
    method UpdateTouch:void(x:float, y:float, touchnum:int)
        if touchNumber>=0 and touchNumber = touchnum then
            if not triggered then
                if (x-firstTouchX)*(x-firstTouchX)+(y-firstTouchY)*(y-firstTouchY) > triggerDistance*triggerDistance then
                    triggered = true
                end
            end
            if triggered then
                stickX = x - ringX
                stickY = ringY - y
                UpdateStick()
            end
        end
    end

    ' we just released a touch, which may have been this one
    method StopTouch:void(touchnum:int)
        if touchNumber>=0 and touchNumber = touchnum then
            touchNumber = -1
            stickX = 0
            stickY = 0
            stickAngle = 0
            stickPower = 0
            triggered = false
        end
    end

    method DoRenderRing:void()
        RenderRing(ringX, ringY)
    end

    method DoRenderStick:void()
        RenderStick(ringX+stickX, ringY-stickY)
    end

    ' draws the stick (may be overridden to do images, etc.)
    method RenderStick:void(x:float, y:float)
        DrawCircle(x, y, stickRadius)
    end

    ' draws the outside ring (may be overridden to do images, etc.)
    method RenderRing:void(x:float, y:float)
        DrawCircle(x, y, ringRadius)
    end

    ' set the location and radius of the ring
    method SetRing:void(ringX:float, ringY:float, ringRadius:float)
        self.ringX = ringX
        self.ringY = ringY
        self.ringRadius = ringRadius
    End

    ' set the location and radius of the stick
    method SetStick:void(stickX:float, stickY:float, stickRadius:float)
        self.stickX = stickX
        self.stickY = stickY
        self.stickRadius = stickRadius
    end

    method SetDeadZone:void(deadZone:float)
        self.deadZone = deadZone
    end

    method SetTriggerDistance:void(triggerDistance:float)
        self.triggerDistance = triggerDistance
    end
end

function Main:int()
    new VirtualStickTestApp
    return 0
end

function DrawOutlineRect:void(x:float, y:float, width:float, height:float)
    DrawLine(x, y, x+width, y)
    DrawLine(x, y, x, y+height)
    DrawLine(x+width, y, x+width, y+height)
    DrawLine(x, y+height, x+width, y+height)
end

Reply To Topic

Please log in to reply