VirtualRelativeJoystick
VirtualRelativeJoystick Class
This is a VirtualRelativeJoystick Class for Gideros by @snooks and @antix (https://forum.gideros.rocks/discussion/7171/virtual-onscreen-controls-gamepad-does-anybody-want-to-share-theirs).
In my projects, I usually create a "classes" folder and put all my classes in it. You can create the "classes" folder, then create a new file which you can call "VirtualRelativeJoystick.lua" and copy the below code for that Class.
The Onscreen joystick only appears when the lower left or right quadrant of the screen is touched, you can change its parameters like size and color, plus it has functions you can call, etc...
-- VirtualRelativeJoystick for Gideros
--
-- Onscreen joystick that only appears when lower left quadrant
-- of screen is touched
--
VirtualRelativeJoystick = Core.class(Sprite)
function VirtualRelativeJoystick:init(options)
-- default options
self.x = 100
self.y = 100
self.outerRadius = 100
self.padColor = 0xaaaadd
self.knobColor = 0xaaddaa
self.onPressed = nil
self.onDragged = nil
self.onReleased = nil
self.left = false -- displays joystick on right side of the screen by default
self.xpos = 0
self.ypos = 0
self.strength = 0
self.angle = 0
self.enabled = false
-- set user supplied options
if options then
for key, value in pairs(options) do self[key]= value end
end
self.outerCircle = self:getNewCircle(self.outerRadius, self.padColor, false)
self:addChild(self.outerCircle)
self.innerNubbin = self:getNewCircle(self.outerRadius * .5, self.knobColor, true)
self:addChild(self.innerNubbin)
self:setPosition(self.x, self.y)
self:setVisible(false)
self:addEventListener(Event.TOUCHES_BEGIN, self.onTouchesBegin, self)
self:addEventListener(Event.TOUCHES_MOVE, self.onTouchesMove, self)
self:addEventListener(Event.TOUCHES_END, self.onTouchesEnd, self)
end
function VirtualRelativeJoystick:onTouchesCancel(e)
self:onTouchesEnd(e)
end
function VirtualRelativeJoystick:onTouchesBegin(e)
local tx, ty = e.touch.x, e.touch.y
local mx, my = application:getContentWidth() * 0.5, application:getContentHeight() * 0.5
if self.left then
if tx > mx or ty < my then return end
else
if tx < mx or ty < my then return end
end
self.controlTouchId = e.touch.id
self:setPosition(tx, ty)
self:setVisible(true)
self.enabled = true -- enable control
if self.onPressed then self.onPressed() end -- run code
e:stopPropagation()
end
function VirtualRelativeJoystick:onTouchesMove(e)
local cos, sin, sqrt, atan2 = math.cos, math.sin, math.sqrt, math.atan2
if not self.enabled then return end
if e.touch.id == self.controlTouchId then
local x, y = self:globalToLocal(e.touch.x, e.touch.y)
local radius = self.outerRadius
local distance = sqrt(x * x + y * y) >< radius -- limit distance to outer radius
-- normalized strength for use with angle
local strength = (distance >< radius) / radius
local angle = ^>atan2(y, x) + 90
local ra = ^< self.angle
y = -distance * cos(ra)
x = distance * sin(ra)
self.innerNubbin:setPosition(x, y)
self.xpos, self.ypos = x / radius, -y / radius
if self.onPressed then
self.onDragged(angle, distance, strength) -- run code
end
self.angle, self.distance, self.strength = angle, distance, strength
e:stopPropagation()
end
end
function VirtualRelativeJoystick:onTouchesEnd(e)
if not self.enabled then return end
if e.touch.id == self.controlTouchId then
self.innerNubbin:setPosition(0, 0)
self.xpos, self.ypos = 0, 0
self:setVisible(false)
self.enabled = false
if self.onReleased then self.onReleased() end -- run code
e:stopPropagation()
end
self.controlTouchId = nil
end
function VirtualRelativeJoystick:getNewCircle(r, color, fill)
local p = Path2D.new()
p:setSvgPath(("M %s 0 a %s %s 0 0 0 %s 0 a %s %s 0 0 0 %s 0 Z"):format(-r, r, r, 2 * r, r, r, -2 * r))
p:setLineThickness(10) -- Outline width
p:setFillColor(color, fill and .6 or .0) --Fill color
p:setLineColor(color, .6) --Line color
return p
end
Demo
In this demo you will control a tank using the VirtualRelativeJoystick Class. The tank speed is relative to the position of the joystick and shoots when you release your finger!
You can copy (or better write) the code below under the VirtualRelativeJoystick Class itself or in any other file (the main.lua file for example).
--!NEEDS:classes/VirtualRelativeJoystick.lua
--
-- virtual joystick DEMO
--
application:setBackgroundColor(0x6c6c6c)
local options = {}
options.padColor = 0xff0000
options.knobColor = 0x00ff00
options.outerRadius = 64
options.left = true -- activate the joystick on the left side of the screen
local vrj = VirtualRelativeJoystick.new(options)
-- player1 sprite
local player1 = Sprite.new()
local player1body = Pixel.new(0xffff00, 1, 64, 64)
local player1head = Pixel.new(0x00ff00, 1, 32, 32)
local player1debug = Pixel.new(0xff0000, 1, 8, 8)
player1head:setPosition(16, (-64+32))
player1debug:setPosition(32-4, 32-4)
player1:addChild(player1body)
player1:addChild(player1head)
player1:addChild(player1debug)
player1:setAnchorPoint(0.5, 0.5)
-- postion
player1:setPosition(application:getContentWidth()/2, application:getContentHeight()/2)
-- order
stage:addChild(player1)
stage:addChild(vrj)
-- bullets
local bullets = {}
function Bullet(angle, x, y)
local bullet=Pixel.new(0x00ffff, 2, 16, 16)
bullet:setAnchorPoint(0.5, 0.5)
bullet:setRotation(angle)
bullet.x, bullet.y = x, y
bullet:setPosition(bullet.x, bullet.y)
bullet.dx, bullet.dy = math.cos(^<angle), math.sin(^<angle)
bullet.speed = 5
bullet.timer = 60
stage:addChild(bullet)
bullets[#bullets + 1] = bullet
end
-- joystick functions
vrj.onPressed = function()
-- print("joystick pressed")
end
vrj.onDragged = function()
-- print("joystick dragged")
end
vrj.onReleased = function()
-- print("joystick released")
Core.asyncCall(Bullet, vrj.angle-90, player1:getPosition())
end
-- game loop
local x, y = player1:getPosition()
local playerspeed = 4
stage:addEventListener(Event.ENTER_FRAME, function(e)
-- player1
if vrj.enabled then
-- print(vrj.angle, vrj.distance, vrj.strength)
x += math.cos(^<(vrj.angle-90)) * vrj.strength * playerspeed
y += math.sin(^<(vrj.angle-90)) * vrj.strength * playerspeed
player1:setRotation(vrj.angle)
player1:setPosition(x, y)
end
-- bullets
for v = #bullets, 1, -1 do
bullets[v].x += bullets[v].dx * bullets[v].speed
bullets[v].y += bullets[v].dy * bullets[v].speed
bullets[v]:setPosition(bullets[v].x, bullets[v].y)
bullets[v].timer -= 1
if bullets[v].timer < 0 then
stage:removeChild(bullets[v])
table.remove(bullets, v)
end
end
end)
Touch and drag the lower left quadrant of the screen to move the player.