Skip to content

Instantly share code, notes, and snippets.

@SkyTheCoder
Created August 26, 2014 19:23
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save SkyTheCoder/284cdc93b17473e73c17 to your computer and use it in GitHub Desktop.
Save SkyTheCoder/284cdc93b17473e73c17 to your computer and use it in GitHub Desktop.
A library for handling touch taps, swipes, and long presses. No setup required. Just paste as a new project and add it as a dependency. Documentation is at the top of Main.
--# Library
Flickable
= {angleOffset = 0, constant = false, useCorners = true}
local touches = {}
local debugtouch = {}
local sign = function(n)
return n / math.abs(n)
end
local inRange = function(x, y, s, px, py)
--return math.abs(x - px) <= w / 2 and math.abs(y - py) <= h / 2
return vec2(x, y):dist(vec2(px, py)) <= s
end
function getDebugFromID(id)
return debugtouch[id]
end
function getDebugFromTouch(touch)
return debugtouch[touch.id]
end
function getTouchFromID(id)
return touches[id]
end
function getTouchFromDebug(deb)
return touches[deb.id]
end
function getNameFromMotion(x, y, human)
local ret = ""
if y > 7.5 then
ret = ret .. "up"
end
if y < -7.5 then
ret = ret .. "down"
end
if x > 7.5 then
if human and ret ~= "" then
ret = ret .. "-"
end
ret = ret .. "right"
end
if x < -7.5 then
if human and ret ~= "" then
ret = ret .. "-"
end
ret = ret .. "left"
end
return ret
end
function getNameFromAngle(angle, human)
local ret = ""
local a = angle + Flickable.angleOffset
local tolerance = 45
if Flickable.useCorners then
tolerance = 67.5
end
if a > 90 - tolerance and a < 90 + tolerance then
ret = ret .. "up"
end
if a < -90 + tolerance and a > -90 - tolerance then
ret = ret .. "down"
end
if a < tolerance and a > -tolerance then
if human and ret ~= "" then
ret = ret .. "-"
end
ret = ret .. "right"
end
if a > 180 - tolerance or a < -180 + tolerance then
if human and ret ~= "" then
ret = ret .. "-"
end
ret = ret .. "left"
end
return ret
end
function getBoolFromMotion(x, y)
return {up = y > 7.5, down = y < -7.5, right = x > 7.5, left = x < -7.5}
end
function getBoolFromAngle(angle)
local a = angle + Flickable.angleOffset
local tolerance = 45
if Flickable.useCorners then
tolerance = 67.5
end
return {up = a > 90 - tolerance and a < 90 + tolerance, down = a < -90 + tolerance and a > -90 - tolerance, right = a < tolerance and a > -tolerance, left = a > 180 - tolerance or a < -180 + tolerance}
end
local nextf = function(f)
tween.delay(0, function()
pcall(f)
end)
end
local handle = function(touch)
touches[touch.id] = touch
if touch.state == BEGAN and debugtouch[touch.id] == nil then
debugtouch[touch.id] = {id = touch.id, start = ElapsedTime, startX = touch.x, startY = touch.y, motX = 0, motY = 0, long = false}
end
local deb = debugtouch[touch.id]
local still = inRange(deb.startX, deb.startY, 20, touch.x, touch.y) and math.abs(deb.motX) <= 15 and math.abs(deb.motY) <= 15
if debugtouch[touch.id] ~= nil then
if sign(touch.deltaX) ~= sign(deb.motX) then
debugtouch[touch.id].motX = debugtouch[touch.id].motX * 0.2 + 0.8 * touch.deltaX
else
debugtouch[touch.id].motX = debugtouch[touch.id].motX * 0.9 + 0.1 * touch.deltaX
end
if sign(touch.deltaY) ~= sign(deb.motY) then
debugtouch[touch.id].motY = debugtouch[touch.id].motY * 0.2 + 0.8 * touch.deltaY
else
debugtouch[touch.id].motY = debugtouch[touch.id].motY * 0.9 + 0.1 * touch.deltaY
end
local rad = math.atan2(deb.motY, deb.motX)
local deg = math.deg(rad)
local offX, offY = math.cos(rad), math.sin(rad)
debugtouch[touch.id].rad = rad
debugtouch[touch.id].deg = deg
debugtouch[touch.id].direction = vec2(offX, offY)
still = inRange(deb.startX, deb.startY, 20, touch.x, touch.y) and math.abs(deb.motX) <= 15 and math.abs(deb.motY) <= 15
if ElapsedTime - deb.start >= 0.8 then
if still and not deb.long then
if type(longPressed) == "function" then
longPressed(touch, deb)
debugtouch[touch.id].long = true
end
end
end
if deb.long then
if type(longPressing) == "function" then
longPressing(touch, deb)
end
end
end
if touch.state == ENDED or touch.state == CANCELLED then
if still then
if ElapsedTime - deb.start <= 0.3 then
if type(tapped) == "function" then
tapped(touch, deb)
end
end
if ElapsedTime - deb.start >= 0.8 and not deb.long then
if type(longPressed) == "function" then
longPressed(touch, deb)
debugtouch[touch.id].long = true
end
end
end
if deb.long then
if type(longPressing) == "function" then
longPressing(touch, deb)
end
end
if ElapsedTime - deb.start <= 0.5 then
if (not inRange(deb.startX, deb.startY, 10, touch.x, touch.y)) and (not (math.abs(deb.motX) + math.abs(deb.motY) <= 10)) then
if type(swiped) == "function" then
swiped({id = touch.id, x = touch.x, y = touch.y, startX = deb.startX, startY = deb.startY, motX = deb.motX, motY = deb.motY, rad = deb.rad, deg = deb.deg, direction = deb.direction }, deb)
end
end
end
nextf(function()
touches[touch.id] = nil
debugtouch[touch.id] = nil
end)
end
if type(debtouched) == "function" then
debtouched(debugtouch[touch.id])
end
end
tween.delay(0, function()
local _touched = touched or function() end
touched = function(...)
handle(...)
_touched(...)
end
local _draw = draw or function() end
draw = function(...)
if Flickable.constant then
for k, v in pairs(touches) do
if v.state == BEGAN or v.state == MOVING then
handle(v)
end
end
end
_draw(...)
end
end)
--# Main
-- Flickable
--[[
#### DOCUMENTATION ####
## API FEATURES ##
# GLOBALS #
Flickable: table:
// Table for storing values to customize the Flickable API
-angleOffset: number, offset of angles when using getNameFromAngle(...), getBoolFromAngle(...)
-constant: boolean, whether to update touches every frame rather than when they move (recommended for detecting long presses moving)
-useCorners: boolean, whether corners should be a possibility when using getNameFromAngle(...), getBoolFromAngle(...)
# FUNCTIONS #
getDebugFromID(id):
//Get a debug value from a touch ID (see debtouched(deb))
-id: number, ID of the touch
getDebugFromTouch(touch):
//Get a debug value from a touch (see debtouched(deb))
-touch: default "touch" metatable
getTouchFromID(id):
//Get a default "touch" metatable from a debug value ID (see debtouched(deb))
-id: number, ID of the debug value
getTouchFromDebug(debug):
//Get a default "touch" metatable from a debug value (see debtouched(deb))
-debug: see debtouched(deb)
getNameFromMotion(x, y, human):
// Get the names of the direction of a motion (output order for corners: horizontal-vertical) (simpler, innaccurate)
-x, y: numbers, x and y values of the motion
-human: boolean, whether the output should be optimized for humans
getNameFromAngle(angle, human):
// Get the names of the direction of an angle (output order for corners: horizontal-vertical) (complicated, accurate)
-angle: number, angle in degrees of the motion
-human: boolean, whether the output should be optimized for humans
getBoolFromMotion(x, y):
// Returns a table of booleans {top, bottom, right, left}, each value determines whether the motion is moving in its direction (simpler, innaccurate)
-x, y: numbers, x and y values of the motion
getBoolFromAngle(angle):
// Returns a table of booleans {top, bottom, right, left}, each value determines whether the angle is pointing in its direction (complicated, accurate)
-angle: number, angle in degrees of the motion
## USER-DEFINED ##
(parameters are supplied by Flickable)
debtouched(deb):
// Called alongside touched(touch), debug information
-deb: table, contains debug information:
--id: number, ID of the debug value/touch
--start: number, ElapsedTime from when touch started
--startX: number, X coordinate from when touch started
--startY: number, Y coordinate from when touch started
--motX: number, X motion of touch
--motY: number, Y motion of touch
--long: boolean, whether the touch had been deemed a long press
tapped(tap, deb):
// Called when a finger has tapped the screen
-tap: default "touch" metatable
-deb: see debtouched(deb)
swiped(swipe, deb):
// Called when a finger has swiped the screen
-swipe: table, stores information about the swipe
--id: number, ID of the touch
--x: number, X position from when touch ended
--y: number, Y position from when touch ended
--startX: number, X position from when touch started
--startY: number, Y position from when touch started
--motX: number, X motion of touch
--motY: number, Y motion of touch
--rad: number, angle of the touch's direction in radians
--deg: number, angle of the touch's direction in degrees
--direction: vec2, UV version of motX and motY, sine/cosine of rad
-deb: see debtouched(deb)
longPressed(press, deb):
// Called when a finger has long pressed the screen (on start of long press qualify)
-press: default "touch" metatable
-deb: see debtouched(deb)
longPressing(press, deb):
// Called when a touch qualified as a long press moves (or if Flickable.constant is true, every frame a touch qualified as long is on the screen), useful for long-press-to-move functionality, such as on the app screen of your iPad
-press: default "touch" metatable
-deb: see debtouched(deb)
--]]
rectMode(CENTER)
-- Use this function to perform your initial setup
function setup()
print("Tap to make the cube bounce")
print("Swipe to make the cube move (diagonally works too!)")
print("Long press the cube to move it around")
Flickable.constant = true
--Flickable.useCorners = false
gridSize = 8
pos = {x = WIDTH / 2 + WIDTH / gridSize / 2, y = HEIGHT / 2 + HEIGHT / gridSize / 2}
vis = {x = pos.x, y = pos.y}
next = {x = pos.x, y = pos.y}
size = {s = 1}
img = image(512, 512)
setContext(img)
background(255)
fill(0)
font("ArialRoundedMTBold")
fontSize(36)
textMode(CORNER)
text("Easy touch library\nBy SkyTheCoder", 8, 8)
local w, h = textSize("Easy touch library\nBy SkyTheCoder")
strokeWidth(2)
stroke(0)
line(0, h + 16, 512, h + 16)
fontSize(72)
textMode(CENTER)
text("Flickable", 256, 256 + (h + 16) / 2)
setContext()
saveImage("Project:Icon", img)
end
-- This function gets called once every frame
function draw()
background(255)
stroke(0)
strokeWidth(1)
for i = 1, gridSize - 1 do
line(0, i / gridSize * HEIGHT, WIDTH, i / gridSize * HEIGHT)
end
for i = 1, gridSize - 1 do
line(i / gridSize * WIDTH, 0, i / gridSize * WIDTH, HEIGHT)
end
pushMatrix()
--translate(vis.x + WIDTH / gridSize / 2, vis.y + HEIGHT / gridSize / 2)
translate(vis.x, vis.y)
scale(size.s)
noStroke()
fill(0)
rect(0, 0, WIDTH / gridSize, HEIGHT / gridSize)
fill(255)
font("ArialRoundedMTBold")
fontSize(12)
text("Flickable")
popMatrix()
tint(255, 255 - (ElapsedTime - 4) * 127)
sprite(img, WIDTH / 2, HEIGHT / 2)
end
function swiped(swipe)
--print("Swiped: " .. swipe.x .. ", " .. swipe.y)
print("Swiped " .. getNameFromAngle(swipe.deg, true))
if tPos ~= nil then
tween.stop(tPos)
pos.x = next.x
pos.y = next.y
vis.x = pos.x
vis.y = pos.y
tPos = nil
end
local bools = getBoolFromAngle(swipe.deg)
local target = {x = pos.x, y = pos.y}
if bools.up then
target.y = pos.y + HEIGHT / gridSize
end
if bools.down then
target.y = pos.y - HEIGHT / gridSize
end
if bools.right then
target.x = pos.x + WIDTH / gridSize
end
if bools.left then
target.x = pos.x - WIDTH / gridSize
end
--[[target.x = target.x or pos.x
target.y = target.y or pos.y--]]
next.x = target.x
next.y = target.y
tPos = tween(0.25, vis, target, tween.easing.quadIn, function()
pos.x, pos.y = vis.x, vis.y
tPos = nil
end)
end
function tapped(tap)
if tSize ~= nil then
tween.stop(tSize)
tSize = nil
size = {s = 1}
end
tSize = tween(0.15, size, {s = 0.75}, tween.easing.quadInOut, function()
tSize = tween(0.5, size, {s = 1}, tween.easing.elasticOut, function()
tSize = nil
end)
end)
end
function longPressed(press)
vis.x, vis.y = press.x, press.y
if tSize ~= nil then
tween.stop(tSize)
tSize = nil
size = {s = 1}
end
tSize = tween(0.5, size, {s = 1.25}, tween.easing.bounceOut, function()
tSize = nil
end)
end
function longPressing(press)
if tPos ~= nil then
tween.stop(tPos)
tPos = nil
end
vis.x, vis.y = press.x, press.y
next.x = math.floor(vis.x / WIDTH * gridSize) * WIDTH / gridSize + WIDTH / gridSize / 2
next.y = math.floor(vis.y / HEIGHT * gridSize) * HEIGHT / gridSize + HEIGHT / gridSize / 2
if press.state == ENDED or press.state == CANCELLED then
if tSize ~= nil then
tween.stop(tSize)
tSize = nil
size = {s = 1.25}
end
tSize = tween(0.5, size, {s = 1}, tween.easing.elasticOut, function()
tSize = nil
end)
tPos = tween(0.25, vis, next, tween.easing.quadIn, function()
pos.x, pos.y = vis.x, vis.y
tPos = nil
end)
end
end
--[[function debtouched(deb)
for k, v in pairs(deb) do
if type(v) ~= "userdata" then
print(k .. ": " .. tostring(v))
end
end
end--]]
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment