Detecting separate swipe without lifting finger

Hi there!

I’ll be needing to program something soon and I’m looking for some input before I start coding. I’m a proficient coder so no need to help me write my code - it’s more the general principle behind the code I’m wondering about.

So here’s my situation: I have an 8 x 8 grid in my game, and a player that can move on this grid, one tile at a time and only horizontally or vertically. No diagonals. Now, I wrote some code that detects swipes on the screen, and it works well: as soon as you swipe more than 100 pixels (given an iPad res of 2048 x 1536) vertically or horizontally my player moves. No issues there.

But I found that the instinctive thing to do in my game is to do consecutive swipes without lifting your finger. Sort of like doing “up - up - left - down -up” in a three-second duration, for example. So for this I need some way of detecting when a small swipe ends, the small pause inbetween and when a new one starts - but because the finger isn’t lifted there is no “began” or “ended” event inbetween.

And for the first time since I started using Corona a couple of years ago I am a bit puzzled before starting. I have a vague idea in my head about creating a table that holds the last ten or twenty touch events, saving location and timestamp, but it feels like a lot of overhead…

Any suggestions? I’ll share my code afterwards!

Thx!

Thomas

Will the player be moving while the queuing is happening or will you record a set of movements and only apply them once the finger is finally lifted? Will the position of the tap be tied to the player object or relative to it?

If the moves are applied as you are recording them them, you could do something like:

begin the touch event,

wait until significant movement in a recognisable direction,

trigger the player movement in that direction,

reset your current start point and begin monitoring for a new direction while the player is moving,

wait until significant movement in a recognisable direction,

trigger the player movement in that direction,

etc.

That could give you the capability to be capturing ‘the next move’, but not a future string of moves.

Alternatively, you could record the move as discreet steps (each time you reach the trigger point of distance/direction start a new step), and display this somehow as a path that shows the moves that *will* be taken once the finger is lifted.

This could be drawn and extended as each step is added. You might even be able to trace your steps back to remove unwanted choices before they are applied.

– I realise that this doesn’t necessarily address the fundamental question of whether or not you can force the ‘ending’ of a touch event without lifting the finger. I asked a similar question, though I needed to force a finger lift, some time ago, but wound up working around it as I found no direct solution.

Hi Strange Flight, and thanks for the response!

Some info:

  1. I don’t need to record future movement. The player moves to his new location in 150 milliseconds, and the logic is simple: when a swipe is done while the player is still moving it is ignored.

I essence, my touch module is uncoupled to other modules, so there’s no need to think about the rest of the game. On the other hand, my touch logic has no info about other parts of the game.

  1. I don’t need to force the end of the touch event. I’m just looking for a good method to detect discreet micro-swipes as they are happening.

  2. You’re spot on when you state “Begin monitoring for a new direction” and “Wait until significant movement”, but that is basically my question: any ideas on HOW to monitor or detect that a new significant movement has begun?

Cheers,

Thomas

I know, from your previous posting, that you are far more experienced than I am at this so forgive me if I speak nonsense.

Could you ‘Begin’ the touch event and copy the event.xStart and event.yStart values to variables.

Once the ‘Moved’ phase is detected, evaluate the delta of the current position against the variables until a player move is triggered.

Once the player movement has completed, reset the x and y variables in the touch function with the current finger position and evaluate the deltas again.

Each time 100+ pixels in any cardinal direction is detected, the player movement is triggered, and evaluation is ignored until the player movement ends. The ‘starting point’ variables get reset each time and so a new ‘moved’ phase will result in a new micro-swipes until the touch finally ends.

Good ideas there… Thanks!

I’m going to try out two things tonight I think: one based on swiping-speed and one based on resetting coordinates! I’ll let you know how things work out!

Hi Thomas, you said you don’t need to force an end event, but what about a began one? 

Once the code detects a “up” movement, return xy position, save them and force your function to start again. Since the distance between “a” and “b” position will be always the same, in term of pixels, even if some go up and other left, you should be able to reuse the move phase again. I’d also save a variable to know where the character is moving (from 1 to 4, asigned to all 4 movements) right before you trigger the began phase.

I’d test it with a small code but i’m not at home. Sorry if this is wrong or isn’t what you actually need.

Hi all,

I got something working the way I need it, based purely on speed of swiping! Feel free to try it out - code below, no assets needed!

The code below is designed for an iPad Air. It will work on smaller devices as well, but maybe the specific cut-off speeds need to be tweaked based on screen size.

FYI: to test, swipe in short, fast strokes, up / down / left / right, and pause in between without lifting your finger off the screen.

main.lua:

-- main.lua ----------- display.setStatusBar( display.HiddenStatusBar ) -- hide the status bar local debugText = display.newText("Text", 768,960, native.systemFont, 200) debugText.alpha = 0 local fadeText = function(string) debugText.text = string debugText.alpha = 1 transition.to(debugText, {time = 250, alpha = 0}) end -- fadeText local lastX local lastY local lastTime local swipeDirection = false local lastDeltaX = 0 local lastDeltaY = 0 local screenTouched = function(event) if event.phase == "began" then lastX = event.x lastY = event.y lastTime = event.time elseif event.phase == "moved" then deltaX = event.x - lastX deltaY = event.y - lastY deltaTime = event.time - lastTime local xSpeed = deltaX / deltaTime local ySpeed = deltaY / deltaTime --print(ySpeed) if swipeDirection == false then local xSpeedAbs = math.abs(xSpeed) local ySpeedAbs = math.abs(ySpeed) if xSpeedAbs \> ySpeedAbs then if xSpeed \> 0.8 then swipeDirection = "right" fadeText("right") elseif xSpeed \< -0.8 then swipeDirection = "left" fadeText("left") end else -- ySpeedAbs =\> xSpeedAbs if ySpeed \> 0.8 then swipeDirection = "down" fadeText("down") elseif ySpeed \< -0.8 then swipeDirection = "up" fadeText("up") end end else -- swipeDirection is set in some direction if (math.abs(xSpeed) \< 0.06 and math.abs(ySpeed) \< 0.06) or lastDeltaX\*deltaX \<0 or lastDeltaY\*deltaY \<0 then swipeDirection = false lastDeltaX = 0 lastDeltaY = 0 end end lastX = event.x lastY = event.y lastTime = event.time lastDeltaX = deltaX lastDeltaY = deltaY elseif event.phase == "ended" or event.phase == "cancelled" then swipeDirection = false lastDeltaX = 0 lastDeltaY = 0 end end -- screenTouched Runtime:addEventListener("touch", screenTouched)

config.lua:

application = { launchPad = true, content = { fps = 60, audioPlayFrequency = 44100, width = 1536, height = 2048, scale = "letterBox", antialias = true, }, }

build settings:

settings = { orientation = { default = "portrait", supported = { "portrait", "landscapeLeft", "landscapeRight" }, }, iphone = { plist = { UIAppFonts = { "Montserrat-Bold.otf", "carbon bl.ttf" }, UIPrerenderedIcon = false, UIStatusBarHidden = true, CFBundleIconFile = "Icon.png", CFBundleIconFiles = { "Icon.png", --57x57 (iPhone and iPod touch application icon) "Icon@2x.png", --114x114 (RETINA iPhone and iPod touch application icon) "Icon-iPad.png", --72x72 (iPad 1 application icon) "Icon-72.png", --72X72 (RETINA iPad 2 OLD name) "Icon-iPad@2x.png", --144x144 (RETINA iPad 3) "Icon-Small.png", --29x29 (iPhone and iPod touch Spotlight search results and Settings icon) "Icon-Small@2x.png", --58x58 (RETINA iPhone and iPod touch Spotlight search results and Settings icon) "Icon-Small-50.png", --50x50 (iPad 1 Spotlight search results and Settings icon) "Icon-Small-50@2x.png", --100x100 (RETINA iPads Spotlight search results and Settings icon) }, }, }, }

Will the player be moving while the queuing is happening or will you record a set of movements and only apply them once the finger is finally lifted? Will the position of the tap be tied to the player object or relative to it?

If the moves are applied as you are recording them them, you could do something like:

begin the touch event,

wait until significant movement in a recognisable direction,

trigger the player movement in that direction,

reset your current start point and begin monitoring for a new direction while the player is moving,

wait until significant movement in a recognisable direction,

trigger the player movement in that direction,

etc.

That could give you the capability to be capturing ‘the next move’, but not a future string of moves.

Alternatively, you could record the move as discreet steps (each time you reach the trigger point of distance/direction start a new step), and display this somehow as a path that shows the moves that *will* be taken once the finger is lifted.

This could be drawn and extended as each step is added. You might even be able to trace your steps back to remove unwanted choices before they are applied.

– I realise that this doesn’t necessarily address the fundamental question of whether or not you can force the ‘ending’ of a touch event without lifting the finger. I asked a similar question, though I needed to force a finger lift, some time ago, but wound up working around it as I found no direct solution.

Hi Strange Flight, and thanks for the response!

Some info:

  1. I don’t need to record future movement. The player moves to his new location in 150 milliseconds, and the logic is simple: when a swipe is done while the player is still moving it is ignored.

I essence, my touch module is uncoupled to other modules, so there’s no need to think about the rest of the game. On the other hand, my touch logic has no info about other parts of the game.

  1. I don’t need to force the end of the touch event. I’m just looking for a good method to detect discreet micro-swipes as they are happening.

  2. You’re spot on when you state “Begin monitoring for a new direction” and “Wait until significant movement”, but that is basically my question: any ideas on HOW to monitor or detect that a new significant movement has begun?

Cheers,

Thomas

I know, from your previous posting, that you are far more experienced than I am at this so forgive me if I speak nonsense.

Could you ‘Begin’ the touch event and copy the event.xStart and event.yStart values to variables.

Once the ‘Moved’ phase is detected, evaluate the delta of the current position against the variables until a player move is triggered.

Once the player movement has completed, reset the x and y variables in the touch function with the current finger position and evaluate the deltas again.

Each time 100+ pixels in any cardinal direction is detected, the player movement is triggered, and evaluation is ignored until the player movement ends. The ‘starting point’ variables get reset each time and so a new ‘moved’ phase will result in a new micro-swipes until the touch finally ends.

Good ideas there… Thanks!

I’m going to try out two things tonight I think: one based on swiping-speed and one based on resetting coordinates! I’ll let you know how things work out!

Hi Thomas, you said you don’t need to force an end event, but what about a began one? 

Once the code detects a “up” movement, return xy position, save them and force your function to start again. Since the distance between “a” and “b” position will be always the same, in term of pixels, even if some go up and other left, you should be able to reuse the move phase again. I’d also save a variable to know where the character is moving (from 1 to 4, asigned to all 4 movements) right before you trigger the began phase.

I’d test it with a small code but i’m not at home. Sorry if this is wrong or isn’t what you actually need.

Hi all,

I got something working the way I need it, based purely on speed of swiping! Feel free to try it out - code below, no assets needed!

The code below is designed for an iPad Air. It will work on smaller devices as well, but maybe the specific cut-off speeds need to be tweaked based on screen size.

FYI: to test, swipe in short, fast strokes, up / down / left / right, and pause in between without lifting your finger off the screen.

main.lua:

-- main.lua ----------- display.setStatusBar( display.HiddenStatusBar ) -- hide the status bar local debugText = display.newText("Text", 768,960, native.systemFont, 200) debugText.alpha = 0 local fadeText = function(string) debugText.text = string debugText.alpha = 1 transition.to(debugText, {time = 250, alpha = 0}) end -- fadeText local lastX local lastY local lastTime local swipeDirection = false local lastDeltaX = 0 local lastDeltaY = 0 local screenTouched = function(event) if event.phase == "began" then lastX = event.x lastY = event.y lastTime = event.time elseif event.phase == "moved" then deltaX = event.x - lastX deltaY = event.y - lastY deltaTime = event.time - lastTime local xSpeed = deltaX / deltaTime local ySpeed = deltaY / deltaTime --print(ySpeed) if swipeDirection == false then local xSpeedAbs = math.abs(xSpeed) local ySpeedAbs = math.abs(ySpeed) if xSpeedAbs \> ySpeedAbs then if xSpeed \> 0.8 then swipeDirection = "right" fadeText("right") elseif xSpeed \< -0.8 then swipeDirection = "left" fadeText("left") end else -- ySpeedAbs =\> xSpeedAbs if ySpeed \> 0.8 then swipeDirection = "down" fadeText("down") elseif ySpeed \< -0.8 then swipeDirection = "up" fadeText("up") end end else -- swipeDirection is set in some direction if (math.abs(xSpeed) \< 0.06 and math.abs(ySpeed) \< 0.06) or lastDeltaX\*deltaX \<0 or lastDeltaY\*deltaY \<0 then swipeDirection = false lastDeltaX = 0 lastDeltaY = 0 end end lastX = event.x lastY = event.y lastTime = event.time lastDeltaX = deltaX lastDeltaY = deltaY elseif event.phase == "ended" or event.phase == "cancelled" then swipeDirection = false lastDeltaX = 0 lastDeltaY = 0 end end -- screenTouched Runtime:addEventListener("touch", screenTouched)

config.lua:

application = { launchPad = true, content = { fps = 60, audioPlayFrequency = 44100, width = 1536, height = 2048, scale = "letterBox", antialias = true, }, }

build settings:

settings = { orientation = { default = "portrait", supported = { "portrait", "landscapeLeft", "landscapeRight" }, }, iphone = { plist = { UIAppFonts = { "Montserrat-Bold.otf", "carbon bl.ttf" }, UIPrerenderedIcon = false, UIStatusBarHidden = true, CFBundleIconFile = "Icon.png", CFBundleIconFiles = { "Icon.png", --57x57 (iPhone and iPod touch application icon) "Icon@2x.png", --114x114 (RETINA iPhone and iPod touch application icon) "Icon-iPad.png", --72x72 (iPad 1 application icon) "Icon-72.png", --72X72 (RETINA iPad 2 OLD name) "Icon-iPad@2x.png", --144x144 (RETINA iPad 3) "Icon-Small.png", --29x29 (iPhone and iPod touch Spotlight search results and Settings icon) "Icon-Small@2x.png", --58x58 (RETINA iPhone and iPod touch Spotlight search results and Settings icon) "Icon-Small-50.png", --50x50 (iPad 1 Spotlight search results and Settings icon) "Icon-Small-50@2x.png", --100x100 (RETINA iPads Spotlight search results and Settings icon) }, }, }, }