Skip to content

Instantly share code, notes, and snippets.

@JMV38
Created November 13, 2012 18:59
Show Gist options
  • Save JMV38/4067678 to your computer and use it in GitHub Desktop.
Save JMV38/4067678 to your computer and use it in GitHub Desktop.
--# Main
-- Test of various controls
displayMode(FULLSCREEN)
function setup()
pos = vec2(WIDTH/2, HEIGHT/2)
steer = vec2(0,0)
speed = 400 -- pixels per second
local func = function(v)
if v == "red" then tint(255, 0, 0, 255) end
if v == "green" then tint(0, 255, 31, 255) end
if v == "blue" then tint(0, 49, 255, 255) end
if v == "normal" then tint(255, 255, 255, 255) end
end
info = InfoBloc({side="right", x0=0.6 , x1=0.8 } )
local textfunc = function(v)
if v == "short" then info:changeTxt("coucou") end
if v == "empty" then info:changeTxt() end
if v == "long" then info:changeTxt("this text has to be really really long because "
.."want to have a carriage retirn that occurs") end
if v == "popup enable" then info.popupEnabled = true end
if v == "popup disable" then info.popupEnabled = false end
end
local echo = function(v) info:changeTxt(v) end
local list
menu1 = Menu({side="bottom", x0=0.9, x1=1, title="menu A",
list={"nothing",true,"reset",true}, callback=resetTest})
menu2 = Menu({side="top", pos=0.8, width=0.15,
list={"empty",true,"short",true,"long",true,
"popup enable",true,"popup disable",true,
}, callback=textfunc})
menu3 = Menu({side="left", pos=0.5, width=0.15,
list={"red",true,"green",false,"blue",true,"normal",true}, callback=func})
menu4 = Menu({side="right", pos=0.9, width=0.15,
list={"red",false,"green",false,"blue",true,"normal",true}, callback=func})
menuType2 = Menu2({ side="top", pos=0.65, width=0.15, title="menu2",
list={ "section a", false,"group1",
"choice1", true,"group1",
"choice2", true,"group1",
"choice3", true,"group1",
"section b", false,"group2",
"b1", true,"group2",
"b2", true,"group2",
"b3", true,"group2" },
callback=echo })
menuType2:select("choice1")
menuType2:select("b2")
controller2 = VirtualStick {
moved = function(v) steer = v end,
released = function(v) steer = vec2(0,0) end,
x0=0.8,x1=1,y0=0.25,y1=0.49,name="moving stick",nameSide="right"}
controller4 = VirtualSlider{
moved = function(v) steer = vec2(0,v) end,
released = function(v) steer = vec2(0,0) end,
orientation = vec2(0,1), timeout=2,
x0=0.2,x1=0.4,y0=0.5,y1=1,name="slider",nameSide="top"}
controller5 = TwoTouch{
pressed1 = function(v) info:changeTxt("pressed") end,
moved1 = function(v) info:changeTxt("TwoTouch: x=" .. v.x .." y=" .. v.y) end,
released1 = function() info:changeTxt("released") end,
pressed2 = function(v) info:changeTxt("pressed 2") end,
moved2 = function(v) info:changeTxt("position: x=" .. v.x .." y=" .. v.y) end,
released2 = function() info:changeTxt("released2") print("released2") end,
timeout=2,
x0=0.05,x1=0.4,y0=0,y1=0.49,name="two touch",nameSide="bottom"}
controller6 = OneTouch{
pressed = function(v)
info:changeTxt("pressed")
controller6.lastPos = v
controller6.speed = vec2(0,0)
speed = vec2(0,0)
steer = 1
pos = v
end,
moved = function(v)
pos = v
local dv = (v-controller6.lastPos)/DeltaTime
controller6.lastPos = v
controller6.speed = controller6.speed*0.8 + dv*0.2
info:changeTxt("pos: x=" .. v.x .." y=" .. v.y)
end,
released = function(v)
speed = controller6.speed
info:changeTxt("speed: x=" .. speed.x .." y=" .. speed.y)
end,
x0=0.41,x1=0.8,y0=0,y1=0.49,name="one touch => flick",nameSide="bottom"}
confirmBox = ConfirmBox()
allControllers = All({controller1,controller2,controller3,controller4,
menu1, menu2, menuType2, menu3, menu4, info, confirmBox,controller5,
controller6})
fps = FPS()
end
function resetTest(v)
if v=="reset" then
confirmBox:popup("Do you really want to reset all controllers?",
function() print(confirmBox.choice) end)
--allControllers:resetDemo()
end
end
function draw()
background(0, 0, 0, 255)
allControllers:draw()
pos = pos + steer*speed*DeltaTime
sprite("Planet Cute:Character Boy", pos.x, pos.y)
fps:draw()
end
function touched(touch)
allControllers:touched(touch)
end
--# ConfirmBox
ConfirmBox = class()
function ConfirmBox:init()
self.message = "confirm this decision?"
self.choice = nil
self.active = false
self.callback = nil
-- outer box
self.x0,self.y0,self.w0,self.h0 = WIDTH/2, HEIGHT/2, WIDTH/8, HEIGHT/8
-- text message
self.x1,self.y1,self.w1,self.h1 = WIDTH/2, HEIGHT*(1/2+1/20), WIDTH/9, HEIGHT/32
-- confirm
self.x2,self.y2,self.w2,self.h2 = WIDTH*(1/2-1/16), HEIGHT*(1/2-1/16), WIDTH/20, HEIGHT/32
-- cancel
self.x3,self.y3,self.w3,self.h3 = WIDTH*(1/2+1/16), HEIGHT*(1/2-1/16), WIDTH/20, HEIGHT/32
end
function ConfirmBox:popup(message,callback)
self.message = message
self.callback = callback
self.active = true
self.choice = nil
end
function ConfirmBox:draw()
if self.active then
pushStyle() pushMatrix()
font("AmericanTypewriter-Bold")
rectMode(RADIUS)
textMode(RADIUS)
strokeWidth(5)
fill(127, 127, 127, 255)
stroke(0, 0, 0, 255)
fontSize(20)
rect(self.x0,self.y0,self.w0,self.h0)
rect(self.x2,self.y2,self.w2,self.h2)
rect(self.x3,self.y3,self.w3,self.h3)
fill(0, 0, 0, 255)
textWrapWidth(self.w1*2)
text("confirm",self.x2,self.y2)
text("cancel",self.x3,self.y3)
text(self.message,self.x1,self.y1)
popStyle() popMatrix()
end
end
function ConfirmBox:touched(touch)
local choice = nil
if self.active then
if math.abs((touch.x- self.x2))<self.w2
and math.abs((touch.y- self.y2))<self.h2
then choice = true
elseif math.abs((touch.x- self.x3))<self.w3
and math.abs((touch.y- self.y3))<self.h3
then choice = false
end
self.choice = choice
if choice~=nil then
self.active = false
if self.callback then self.callback() end
end
end
end
--# InfoBloc
InfoBloc = class()
function InfoBloc:init(args)
-- layout
self.title = args.title or "info"
self.txt = args.txt or "the side bar is highlighted when info is present"
self.side = args.side or "bottom"
self.textPos = args.textPos or vec2(WIDTH/2,HEIGHT-100)
self.textWidth = args.textWidth or 700
self.popupEnabled = args.popupEnabled or true
self.t0 = ElapsedTime
self.timeout = 0.5 -- seconds before txt vanishes
-- settings by pos and w
local x = args.pos
local w = args.width or 0.09
-- or by x0 and x1
if args.x0 and args.x1 then
x = (args.x0 + args.x1)/2
w = args.x1 - args.x0
end
local w1,w2,h1,h2
local pos
local dx0,dy0,dx,dy
local htxt = 30
local w0,h0 = math.floor( w * WIDTH ), math.floor( w * HEIGHT )
if self.side == "top" then
w1,w2,h1,h2 = w0,w0,htxt,htxt
pos = vec2(x*WIDTH, HEIGHT-h1/2)
dx0,dy0,dx,dy = 0, -h1-5, 0, -h2-2
elseif self.side == "bottom" then
w1,w2,h1,h2 = w0,w0,htxt,htxt
pos = vec2(x*WIDTH, h1/2)
dx0,dy0,dx,dy = 0,h1+5, 0, h2+2
elseif self.side == "left" then
w1,w2,h1,h2 = htxt,w0,h0,htxt
pos = vec2(w1/2, x*HEIGHT)
dx0,dy0,dx,dy = w1/2+5+w2/2, h1/2-h2/2, 0, -h2-2
elseif self.side == "right" then
w1,w2,h1,h2 = htxt,w0,h0,htxt
pos = vec2(WIDTH-w1/2, x*HEIGHT)
dx0,dy0,dx,dy = -w1/2-5-w2/2, h1/2-h2/2, 0, -h2-2
end
self.img = self:calcImg(self.title,w1,h1,self.side)
-- position of images
local x,y = pos.x,pos.y
self.pos = {}
self.radius = {}
self.pos["title"] = pos
self.radius["title"] = vec2(w1/2,h1/2)
-- manage various drawing states
self.deployed = false
self.highlighted = true -- the title is highlighted
end
function InfoBloc:resetDemo()
-- do nothing
end
function InfoBloc:changeTxt(txt)
if txt then
self.txt = txt
self.highlighted = true -- the title is highlighted
else
self.txt = nil
self.highlighted = false -- the title is not highlighted
end
-- reset timing
if self.popupEnabled then
self.t0 = ElapsedTime
self.finishing = true
end
end
function InfoBloc:calcImg(txt,w,h,rot)
local w0,h0
pushStyle() pushMatrix()
if rot=="left" or rot=="right"
then w0,h0 = h,w
else w0,h0 = w,h
end
local img0 = image(w0,h0)
setContext(img0)
font("AmericanTypewriter-Bold")
rectMode(CENTER)
textMode(CENTER)
strokeWidth(1)
background(255, 255, 255, 184)
fill(0, 0, 0, 255)
stroke(0, 0, 0, 255)
fontSize(20)
text(txt,w0/2,h0/2)
setContext()
local img = image(w,h)
setContext(img)
background(255, 255, 255, 184)
spriteMode(CENTER)
translate(w/2,h/2)
if rot=="left" then rotate(-90) end
if rot=="right" then rotate(90) end
sprite(img0,0,0)
setContext()
popStyle() popMatrix()
return img
end
function InfoBloc:draw()
pushStyle()
spriteMode(CENTER)
-- menu title
if self.highlighted then
local n = 1
tint(255,255,255,128 + (math.cos(ElapsedTime*2*math.pi*n)+1)/2*128 )
else tint(255,255,255,128)
end
local pos = self.pos
-- menu button
sprite(self.img, pos["title"].x, pos["title"].y)
-- deployed or finishing
if self.deployed or self.finishing then
local isel = self.selected
local delta = (self.timeout-(ElapsedTime - self.t0))/self.timeout
if delta<0 then finishing = false end
local alpha = 255
if self.finishing and not self.deployed then alpha = 255*delta end
font("AmericanTypewriter-Bold")
textMode(CENTER)
textAlign(LEFT)
textWrapWidth(self.textWidth)
strokeWidth(1)
fill(194, 194, 194, alpha)
stroke(0, 0, 0, 255)
fontSize(20)
if self.txt then text(self.txt,self.textPos.x,self.textPos.y) end
end
popStyle()
end
function InfoBloc:titleTouched(touch)
local pos,radius= self.pos["title"],self.radius["title"]
local goodZone = false
if math.abs((touch.x-pos.x))<radius.x
and math.abs((touch.y-pos.y))<radius.y
then
goodZone = true
end
self.choiceDone = false
return goodZone
end
function InfoBloc:touched(t)
if self:titleTouched(t) and t.state == BEGAN then
self.deployed=true
self.highlighted = false -- stop flickering
end
if t.state == ENDED and self.deployed then
self.deployed=false
-- reset timing
self.t0 = ElapsedTime
self.finishing = true
end
end
--# Menu
Menu = class()
function Menu:init(args)
-- actions
self.callback = args.callback or doNothing
-- layout
self.title = args.title or "menu"
local list = args.list or {"choice1",true,"choice2",true,"choice3",true}
self.list,self.disabled = {},{}
local imax = #list/2
for i=1,imax do
self.list[i] = list[2*i-1]
self.disabled[i] = not list[2*i]
end
self.selected = args.selected or 1
self.side = args.side or "bottom"
-- settings by pos and w
local x = args.pos
local w = args.width or 0.09
-- or by x0 and x1
if args.x0 and args.x1 then
x = (args.x0 + args.x1)/2
w = args.x1 - args.x0
end
local w1,w2,h1,h2
local pos
local dx0,dy0,dx,dy
local htxt = 30
local w0,h0 = math.floor( w * WIDTH ), math.floor( w * HEIGHT )
if self.side == "top" then
w1,w2,h1,h2 = w0,w0,htxt,htxt
pos = vec2(x*WIDTH, HEIGHT-h1/2)
dx0,dy0,dx,dy = 0, -h1-5, 0, -h2-2
elseif self.side == "bottom" then
w1,w2,h1,h2 = w0,w0,htxt,htxt
pos = vec2(x*WIDTH, h1/2)
dx0,dy0,dx,dy = 0,h1+5, 0, h2+2
elseif self.side == "left" then
w1,w2,h1,h2 = htxt,w0,h0,htxt
pos = vec2(w1/2, x*HEIGHT)
dx0,dy0,dx,dy = w1/2+5+w2/2, h1/2-h2/2, 0, -h2-2
elseif self.side == "right" then
w1,w2,h1,h2 = htxt,w0,h0,htxt
pos = vec2(WIDTH-w1/2, x*HEIGHT)
dx0,dy0,dx,dy = -w1/2-5-w2/2, h1/2-h2/2, 0, -h2-2
end
-- create text images
self.img = {}
for i,txt in ipairs(self.list) do
self.img[i] = self:calcImg(txt,w2,h2)
end
self.img["title"] = self:calcImg(self.title,w1,h1,self.side)
-- position of images
local x,y = pos.x,pos.y
self.pos = {}
self.radius = {}
self.pos["title"] = pos
self.radius["title"] = vec2(w1/2,h1/2)
for i,img in ipairs(self.img) do
if i==1 then x,y = x+dx0,y+dy0
else x,y = x+dx,y+dy end
self.pos[i] = vec2(x,y)
self.radius[i] = vec2(w2/2,h2/2)
end
-- manage various drawing states
self.deployed = false
self.highlighted = nil -- the choice highlighted
end
function Menu:resetDemo()
-- do nothing
end
function Menu:calcImg(txt,w,h,rot)
local w0,h0
pushStyle() pushMatrix()
if rot=="left" or rot=="right"
then w0,h0 = h,w
else w0,h0 = w,h
end
local img0 = image(w0,h0)
setContext(img0)
font("AmericanTypewriter-Bold")
rectMode(CENTER)
textMode(CENTER)
strokeWidth(1)
-- background(255, 255, 255, 184)
background(255, 255, 255, 255)
fill(0, 0, 0, 255)
stroke(0, 0, 0, 255)
fontSize(20)
text(txt,w0/2,h0/2)
setContext()
local img = image(w,h)
setContext(img)
-- background(255, 255, 255, 184)
background(255, 255, 255, 255)
spriteMode(CENTER)
translate(w/2,h/2)
if rot=="left" then rotate(-90) end
if rot=="right" then rotate(90) end
sprite(img0,0,0)
setContext()
popStyle() popMatrix()
return img
end
function Menu:draw()
pushStyle()
spriteMode(CENTER)
-- menu title
if self.deployed then
tint(196, 196, 196, 255)
else tint(211, 211, 211, 127)
end
local pos = self.pos
-- menu button
sprite(self.img["title"], pos["title"].x, pos["title"].y)
-- deployed
if self.deployed then
local isel = self.selected
for i,img in ipairs(self.img) do
pushStyle()
if self.disabled[i] then tint(72, 72, 72, 255)
elseif i==isel then tint(255,255,255,255)
else tint(127, 127, 127, 255) end
sprite(img, pos[i].x, pos[i].y)
popStyle()
end
end
popStyle()
end
function Menu:titleTouched(touch)
local pos,radius= self.pos["title"],self.radius["title"]
local goodZone = false
if math.abs((touch.x-pos.x))<radius.x
and math.abs((touch.y-pos.y))<radius.y
then
goodZone = true
end
self.choiceDone = false
return goodZone
end
function Menu:selectTouched(touch)
local pos,radius
for i,v in ipairs(self.pos) do
pos,radius = self.pos[i],self.radius[i]
if math.abs((touch.x-pos.x))<radius.x
and math.abs((touch.y-pos.y))<radius.y
and not self.disabled[i]
then self.selected = i self.choiceDone=true
end
end
end
function Menu:touched(t)
if self:titleTouched(t) and t.state == BEGAN then
self.deployed=true
self.initialSelect = self.selected
end
if self.deployed then
self:selectTouched(t)
end
if t.state == ENDED and self.deployed then
self.deployed=false
if self.choiceDone then
self.callback(self.list[self.selected])
end
end
end
--# Menu2
Menu2 = class()
function Menu2:update()
end
function Menu2:init(args)
-- actions
self.callback = args.callback or doNothing
-- layout
self.title = args.title or "menu"
-- the
local list = args.list
self.list,self.enabled,self.selected,self.group = {},{},{},{}
self.groups = {}
local i,key
local imax = #list/3
for i=1,imax do
self.list[i] = list[(i-1)*3+1]
self.enabled[i] = list[(i-1)*3+2]
self.selected[i] = false
self.group[i] = list[(i-1)*3+3]
end
self.side = args.side or "bottom"
-- settings by pos and w
local x = args.pos
local w = args.width or 0.09
-- or by x0 and x1
if args.x0 and args.x1 then
x = (args.x0 + args.x1)/2
w = args.x1 - args.x0
end
local w1,w2,h1,h2
local pos
local dx0,dy0,dx,dy
local htxt = 30
local w0,h0 = math.floor( w * WIDTH ), math.floor( w * HEIGHT )
if self.side == "top" then
w1,w2,h1,h2 = w0,w0,htxt,htxt
pos = vec2(x*WIDTH, HEIGHT-h1/2)
dx0,dy0,dx,dy = 0, -h1-5, 0, -h2-2
elseif self.side == "bottom" then
w1,w2,h1,h2 = w0,w0,htxt,htxt
pos = vec2(x*WIDTH, h1/2)
dx0,dy0,dx,dy = 0,h1+5, 0, h2+2
elseif self.side == "left" then
w1,w2,h1,h2 = htxt,w0,h0,htxt
pos = vec2(w1/2, x*HEIGHT)
dx0,dy0,dx,dy = w1/2+5+w2/2, h1/2-h2/2, 0, -h2-2
elseif self.side == "right" then
w1,w2,h1,h2 = htxt,w0,h0,htxt
pos = vec2(WIDTH-w1/2, x*HEIGHT)
dx0,dy0,dx,dy = -w1/2-5-w2/2, h1/2-h2/2, 0, -h2-2
end
-- create text images
self.img = {}
for i,txt in ipairs(self.list) do
self.img[i] = self:calcImg(txt,w2,h2)
end
self.img["title"] = self:calcImg(self.title,w1,h1,self.side)
-- position of images
local x,y = pos.x,pos.y
self.pos = {}
self.radius = {}
self.pos["title"] = pos
self.radius["title"] = vec2(w1/2,h1/2)
for i,txt in ipairs(self.list) do
if i==1 then x,y = x+dx0,y+dy0
else x,y = x+dx,y+dy end
self.pos[i] = vec2(x,y)
self.radius[i] = vec2(w2/2,h2/2)
end
-- manage various drawing states
self.deployed = false
self.highlighted = nil -- the choice highlighted
end
function Menu2:select(key)
local i,v
for i,v in ipairs(self.list) do
if self.list[i]==key then self.selected[i]=true end
end
end
function Menu2:groupEnable(key)
local i,v
for i,v in ipairs(self.group) do
if self.group[i]==key then self.enabled[i]=true end
end
end
function Menu2:groupDisable(key)
local i,v
for i,v in ipairs(self.group) do
if self.group[i]==key then self.enabled[i]=false end
end
end
function Menu2:resetDemo()
-- do nothing
end
function Menu2:calcImg(txt,w,h,rot)
local w0,h0
pushStyle() pushMatrix()
if rot=="left" or rot=="right"
then w0,h0 = h,w
else w0,h0 = w,h
end
local img0 = image(w0,h0)
setContext(img0)
font("AmericanTypewriter-Bold")
rectMode(CENTER)
textMode(CENTER)
strokeWidth(1)
-- background(255, 255, 255, 184)
background(255, 255, 255, 255)
fill(0, 0, 0, 255)
stroke(0, 0, 0, 255)
fontSize(20)
text(txt,w0/2,h0/2)
setContext()
local img = image(w,h)
setContext(img)
-- background(255, 255, 255, 184)
background(255, 255, 255, 255)
spriteMode(CENTER)
translate(w/2,h/2)
if rot=="left" then rotate(-90) end
if rot=="right" then rotate(90) end
sprite(img0,0,0)
setContext()
popStyle() popMatrix()
return img
end
function Menu2:draw()
pushStyle()
spriteMode(CENTER)
-- menu title
if self.deployed then
tint(196, 196, 196, 255)
else tint(211, 211, 211, 127)
end
local pos = self.pos
-- menu button
sprite(self.img["title"], pos["title"].x, pos["title"].y)
-- deployed
if self.deployed then
local isel = self.choice
for i,txt in ipairs(self.list) do
pushStyle()
if self.enabled[i] == false then tint(72, 72, 72, 255)
elseif self.selected[i] == true or i==isel then tint(255,255,255,255)
elseif self.enabled[i] == true then tint(127, 127, 127, 255)
end
sprite( self.img[i], pos[i].x, pos[i].y)
popStyle()
end
end
popStyle()
end
function Menu2:titleTouched(touch)
local pos,radius= self.pos["title"],self.radius["title"]
local goodZone = false
if math.abs((touch.x-pos.x))<radius.x
and math.abs((touch.y-pos.y))<radius.y
then
goodZone = true
end
self.choiceDone = false
return goodZone
end
function Menu2:selectTouched(touch)
local pos,radius
for i,v in ipairs(self.pos) do
pos,radius = self.pos[i],self.radius[i]
if math.abs((touch.x-pos.x))<radius.x
and math.abs((touch.y-pos.y))<radius.y
then
self.choice = i
self.choiceDone=true
end
end
end
function Menu2:touched(t)
local group
if self:titleTouched(t) and t.state == BEGAN then
self.deployed=true
end
if self.deployed then
self:selectTouched(t)
end
if t.state == ENDED and self.deployed then
self.deployed=false
if self.choiceDone and self.enabled[self.choice] then
group = self.group[self.choice]
for i,_ in ipairs(self.group) do
if self.group[i] == group then self.selected[i] = false end
end
self.selected[self.choice] = true
self.callback( self.list[self.choice] )
end
self.choice = nil
end
end
--# Controller
-- 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(input)
self.x0 = input.x0 or 0
self.x1 = input.x1 or 1
self.y0 = input.y0 or 0
self.y1 = input.y1 or 1
self.cx = (self.x0+self.x1)/2 *WIDTH
self.cy = (self.y0+self.y1)/2 *HEIGHT
self.wx = (self.x1- self.x0)/2 *WIDTH
self.wy = (self.y1- self.y0)/2 *HEIGHT
self.name = input.name
self.nameSide = input.nameSide
if self.nameSide then
self:textPanel()
end
self.timeout = input.timeout or 2
self.t0 = ElapsedTime
end
function Controller:resetDemo()
self.t0 = ElapsedTime
end
function Controller:textPanel()
local w,h = 20,20
local side = self.nameSide
if side=="top" or side=="bottom" then w = self.wx*2
elseif side=="left" or side=="right" then w = self.wy*2
else print("the text for nameSide is not recognized")
end
local img = image(w,h)
setContext(img)
pushStyle() pushMatrix()
strokeWidth(1)
rectMode(RADIUS)
background(127, 127, 127, 57)
fill(0, 0, 0, 255)
fontSize(20)
text(self.name,w/2,h/2)
popStyle() popMatrix()
setContext()
local imgActive = image(w,h)
setContext(imgActive)
pushStyle() pushMatrix()
strokeWidth(1)
rectMode(RADIUS)
background(255, 255, 255, 184)
fill(0, 0, 0, 255)
fontSize(20)
text(self.name,w/2,h/2)
popStyle() popMatrix()
setContext()
local side = self.nameSide
local x,y,r
if side=="top" then
x,y,r = self.cx , self.y1*HEIGHT - img.height/2 , 0
elseif side=="bottom" then
x,y,r = self.cx , self.y0*HEIGHT + img.height/2 , 0
elseif side=="left" then
x,y,r = self.x0*WIDTH + img.height/2 , self.cy , -90
elseif side=="right" then
x,y,r = self.x1*WIDTH - img.height/2 , self.cy , 90
end
self.tx, self.ty, self.tr = x,y,r
self.imgPassive = img
self.imgActive = imgActive
end
function Controller:demo(timeout)
if (ElapsedTime - self.t0)<timeout then
pushStyle() pushMatrix()
strokeWidth(1)
rectMode(RADIUS)
fill(255, 255, 255, 46)
rect(self.cx,self.cy,self.wx,self.wy)
fill(255, 255, 255, 255)
fontSize(20)
text("touch here to",self.cx,self.cy+20)
text(self.name,self.cx,self.cy - 20)
popStyle() popMatrix()
end
end
function Controller:check(touch)
local goodZone = false
if math.abs((touch.x-self.cx))<self.wx
and math.abs((touch.y-self.cy))<self.wy
then
goodZone = true
end
return goodZone
end
function Controller:drawName()
local img
if self.touchId
then img = self.imgActive
else img = self.imgPassive
end
local x,y,r = self.tx, self.ty, self.tr
pushMatrix()
translate(x,y)
rotate(r)
spriteMode(CENTER)
sprite(img ,0,0)
popMatrix()
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
--# Contoller_TwoTouch
TwoTouch = class(Controller)
function TwoTouch:init(args)
self.touches = {}
self.touchStart = {}
self.releasedCallback1 = args.released1 or doNothing
self.steerCallback1 = args.moved1 or doNothing
self.pressedCallback1 = args.pressed1 or doNothing
self.releasedCallback2 = args.released2 or doNothing -- never fires..??
self.zoomCallback2 = args.moved2 or doNothing
self.pressedCallback2 = args.pressed2 or doNothing
self.activated = args.activate or true
-- pre-draw sprites
self.stick = self:createStick()
if self.activated then self:activate(args) end
end
function TwoTouch:touchCount()
local n = 0
for i,v in pairs(self.touches) do n = n + 1 end
return n
end
function TwoTouch:touched(touch)
local goodZone = self:check(touch)
local Nbefore,Nafter
Nbefore = self:touchCount()
if touch.state == BEGAN then
if goodZone and Nbefore<2 then
self.touches[touch.id] = touch -- new touch
self.touchStart[touch.id] = touch
else end -- that touch doesnt concern us
elseif self.touches[touch.id] then -- if this one of my touches
self.touches[touch.id] = touch -- update touch data
end
Nafter = self:touchCount() -- what is the new touch count?
-- if exactly one touch then move
if Nafter==1 and Nbefore<=1 then
--info:changeTxt("1 touch")
self:move(touch)
end
-- if exactly 2 touches then zoom
if Nafter==2 or Nbefore==2 then
-- info:changeTxt("2 touch")
self:zoom()
end
if touch.state == ENDED or touch.state == CANCELLED then
self:reset(touch)
end
end
function TwoTouch:draw()
if self.nameSide then self:drawName() end
if self.name then self:demo(self.timeout) end
for i,t in pairs(self.touches) do
if t then sprite(self.stick, t.x, t.y) end
end
end
function TwoTouch:reset(t)
self.touches[t.id] = nil
self.touchStart[t.id] = nil
end
function TwoTouch:move(t)
if t.state == BEGAN then
self.pressedCallback1(touchPos(t))
elseif t.state == MOVING then
self.steerCallback1(touchPos(t))
elseif t.state == ENDED or t.state == CANCELLED then
self.releasedCallback1()
end
end
function TwoTouch:zoom()
local n = 1
local t = {}
for i,touch in pairs(self.touches) do
t[n] = touch
n = n + 1
end
local v = touchPos(t[1]) - touchPos(t[2])
if t[1].state == BEGAN or t[2].state == BEGAN then
self.pressedCallback2( v )
elseif t[1].state == MOVING or t[2].state == MOVING then
self.zoomCallback2( v )
elseif
t[1].state == ENDED or t[2].state == ENDED or
t[1].state == CANCELLED or t[2].state == CANCELLED
then
self.releasedCallback2()
if not(t[1].state == ENDED or t[1].state == CANCELLED) then
self.pressedCallback1(touchPos(t[2]))
else
self.pressedCallback1(touchPos(t[1]))
end
end
end
function TwoTouch:createStick()
local base = image(56,56)
pushStyle() pushMatrix()
ellipseMode(RADIUS)
strokeWidth(1)
stroke(255, 255, 255, 255)
noFill()
setContext(base)
background(0, 0, 0, 0)
ellipse(base.width/2, base.height/2, 25, 25)
setContext()
popMatrix() popStyle()
return base
end
--# Controller_oneTouch
OneTouch = class(Controller)
function OneTouch:init(args)
self.releasedCallback = args.released or doNothing
self.steerCallback = args.moved or doNothing
self.pressedCallback = args.pressed or doNothing
self.activated = args.activate or true
-- pre-draw sprites
self.stick = self:createStick()
if self.activated then self:activate(args) end
end
function OneTouch:draw()
if self.nameSide then self:drawName() end
if self.name then self:demo(self.timeout) end
if self.touchId then
sprite(self.stick, self.pos.x, self.pos.y)
end
end
function OneTouch:reset()
self.touchId = nil
self.touchStart = nil
end
function OneTouch:touched(t)
local pos = touchPos(t)
local goodZone = self:check(t)
if t.state == BEGAN and self.touchId == nil and goodZone then
self.touchId = t.id
self.touchStart = pos
self.pos = pos
self.pressedCallback(pos)
elseif t.id == self.touchId then
if t.state == MOVING then
self.pos = pos
self.steerCallback(pos)
elseif t.state == ENDED or t.state == CANCELLED then
self:reset()
self.releasedCallback(pos)
end
end
end
function OneTouch:createStick()
local base = image(56,56)
pushStyle() pushMatrix()
ellipseMode(RADIUS)
strokeWidth(1)
stroke(255, 255, 255, 255)
noFill()
setContext(base)
background(0, 0, 0, 0)
ellipse(base.width/2, base.height/2, 25, 25)
setContext()
popMatrix() popStyle()
return base
end
--# Controller_VirtualStick
-- 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
self.activated = args.activate or true
-- pre-draw sprites
self.base = self:createBase()
self.stick = self:createStick()
if self.activated then self:activate(args) end
end
function VirtualStick:createBase()
local base = image(self.radius*2+6,self.radius*2+6)
pushStyle() pushMatrix()
ellipseMode(RADIUS)
strokeWidth(1)
stroke(255, 255, 255, 255)
noFill()
setContext(base)
background(0, 0, 0, 0)
ellipse(base.width/2, base.height/2, self.radius, self.radius)
ellipse(base.width/2, base.height/2, self.deadZoneRadius, self.deadZoneRadius)
setContext()
popMatrix() popStyle()
return base
end
function VirtualStick:createStick()
local base = image(56,56)
pushStyle() pushMatrix()
ellipseMode(RADIUS)
strokeWidth(1)
stroke(255, 255, 255, 255)
noFill()
setContext(base)
background(0, 0, 0, 0)
ellipse(base.width/2, base.height/2, 25, 25)
setContext()
popMatrix() popStyle()
return base
end
function VirtualStick:touched(t)
local pos = touchPos(t)
local goodZone = self:check(t)
if t.state == BEGAN and self.touchId == nil and goodZone 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
if stickDirection:len()>0 then stickDirection = self.stickOffset:normalize() end
return stickDirection * (stickAmount/stickRange)
end
function VirtualStick:reset()
self.touchId = nil
self.touchStart = nil
self.stickOffset = nil
end
function VirtualStick:draw()
if self.nameSide then self:drawName() end
if self.name then self:demo(self.timeout) end
if self.touchId then
sprite(self.base,self.touchStart.x, self.touchStart.y)
sprite(self.stick,
self.touchStart.x+self.stickOffset.x,
self.touchStart.y+self.stickOffset.y)
end
end
--# Controller_VirtualSlider
-- A virtual analogue slider with a dead-zone at the center,
-- which activates wherever the user touches their finger
--
-- Arguments:
-- orientation - A unit vector that defines the orientation of the slider.
-- For example orientation=vec2(1,0) creates a horizontal slider,
-- orientation=vec2(0,1) creates a vertical slider. The slider
-- can be given an arbitrary orientation; it does not have to be
-- aligned with the x or y axis. For example, setting
-- orientation=vec2(1,1):normalize() creates a diagonal slider.
-- radius - Distance from the center to the end of the slider (default = 100)
-- deadZoneRadius - Distance from the center to the end of the dead zone (default = 25)
-- moved(x) - Called when the slider is moved
-- x : float - in the range -1 to 1
-- pressed() - Called when the user starts using the slider (optional)
-- released() - Called when the user releases the slider (optional)
VirtualSlider = class(Controller)
function VirtualSlider:init(args)
self.orientation = args.orientation or vec2(1,0)
self.radius = args.radius or 100
self.deadZoneRadius = args.deadZoneRadius or 25
self.releasedCallback = args.released or doNothing
self.movedCallback = args.moved or doNothing
self.pressedCallback = args.pressed or doNothing
self.activated = args.activate or true
self.base = self:createBase()
self.stick = self:createStick()
if self.activated then self:activate(args) end
end
function VirtualSlider:touched(t)
local pos = touchPos(t)
local goodZone = self:check(t)
if t.state == BEGAN and self.touchId == nil and goodZone then
self.touchId = t.id
self.touchStart = pos
self.sliderOffset = 0
self.pressedCallback()
elseif t.id == self.touchId then
if t.state == MOVING then
local v = pos - self.touchStart
self.sliderOffset = clampAbs(project(v, self.orientation), self.radius)
self.movedCallback(self:value())
elseif t.state == ENDED or t.state == CANCELLED then
self:reset()
self.releasedCallback()
end
end
end
function VirtualSlider:reset()
self.touchId = nil
self.touchStart = nil
self.sliderOffset = nil
end
function VirtualSlider:value()
local range = self.radius - self.deadZoneRadius
local amount = sign(self.sliderOffset) * math.max(math.abs(self.sliderOffset) - self.deadZoneRadius, 0)
return amount/range
end
function VirtualSlider:createBase()
local img = image(self.radius*2+6,self.radius*2+6)
setContext(img)
pushStyle()
ellipseMode(RADIUS)
strokeWidth(3)
stroke(255, 255, 255, 255)
lineCapMode(SQUARE)
noFill()
background(0, 0, 0, 0)
local function polarLine(orientation, fromRadius, toRadius)
local from = orientation * fromRadius + vec2(1,1)*(self.radius + 3)
local to = orientation * toRadius + vec2(1,1)*(self.radius + 3)
line(from.x, from.y, to.x, to.y)
end
polarLine(self.orientation, self.deadZoneRadius, self.radius)
polarLine(self.orientation, -self.deadZoneRadius, -self.radius)
popStyle()
setContext()
return img
end
function VirtualSlider:createStick()
local img = image(56,56)
setContext(img)
pushStyle() pushMatrix()
ellipseMode(RADIUS)
strokeWidth(3)
stroke(255, 255, 255, 255)
lineCapMode(SQUARE)
noFill()
background(0, 0, 0, 0)
strokeWidth(1)
ellipse(28, 28, 25, 25)
popMatrix() popStyle()
setContext()
return img
end
function VirtualSlider:draw()
if self.nameSide then self:drawName() end
if self.name then self:demo(self.timeout) end
if self.touchId then
pushMatrix() pushStyle()
spriteMode(CENTER)
sprite(self.base,self.touchStart.x, self.touchStart.y)
local sliderPos = self.orientation * self.sliderOffset + self.touchStart
strokeWidth(1)
sprite(self.stick, sliderPos.x, sliderPos.y)
popStyle() popMatrix()
end
end
--# Controller_All
All = class(Controller)
-- Forwards each touch event to all the controllers in the table
-- passed to the constructor
function All:init(controllers)
self.controllers = controllers
end
function All:touched(t)
for _, c in pairs(self.controllers) do
c:touched(t)
end
end
function All:resetDemo()
for _, c in pairs(self.controllers) do
c:resetDemo()
end
end
function All:draw()
for _, c in pairs(self.controllers) do
c:draw()
end
end
--# FPS
FPS = class()
function FPS:init()
self.val = 60
end
function FPS:draw()
-- update FPS value with some smoothing
self.val = self.val*0.99+ 1/(DeltaTime)*0.01
-- write the FPS on the screen
fill(208, 208, 208, 255)
fontSize(30)
font("AmericanTypewriter-Bold")
rectMode(CENTER)
text(math.floor(self.val).." fps",50,HEIGHT-25)
end
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment