Created
November 13, 2012 18:59
-
-
Save JMV38/4067678 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
--# 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