Accurate speed of a swipe

Hi all,

I having an issue getting the (max) speed properly for a swipe / gesture along the Y axis in the down movement using the following implementation:

local flickStartTime = 0 local function touchListener(event) if event.phase == "began" then flickStartTime = system.getTimer() if timerStarted == false then timerStarted = true Runtime:addEventListener( "enterFrame", timerText ) end elseif event.phase == "moved" then local flickDuration = system.getTimer() - flickStartTime local speedY = (event.yStart - event.y) / flickDuration if secondsLeftt \> 0 then if speedY \< 0 then myObject.angularVelocity = (speedY \* 500 ) \* -1 end end end end

For the most part this works fine… HOWEVER the issue is when I try to get the max speed and store that value, the max speed jumps to 135 M/S even though I’m really averaging speed of 15 - 30 M/S see below how I am getting max speed below:

if speed \> maxSpeed then maxSpeed = speed end

Basically if the max Speed is greater then speed it sets that speed to the max and I’m just taking the speed from myObject.wheelAngularVelocity in the line before it.

It seems to be a real quick blip that it jumps to 135 speed and then continues on at the proper speed

Can someone spot my mistake here? Is there some limitation or a bug / glitch? 

Has any one had experience accurately getting the speed of a swipe if doing this quickly over a certain time period?

Just need someone to point out what might be the issue here.

See example Gif below, I am swiping down at a fairly constant speed then it jumps to 135 for a moment.

Hi leojharris,

I would measure average speed like that 

local mAbs&nbsp; = math.abs local mSqrt = math.sqrt local function distance ( x1, y1, x2, y2 ) return mSqrt( ( x1 - x2 ) \* ( x1 - x2 ) + ( y1 - y2 ) \* ( y1 - y2 ) ) end &nbsp;&nbsp; local rect = display.newRect( sceneGroup, display.contentCenterX, display.contentCenterY, 100, 100 ) local startTime, endTime, avgSpeed function rect:touch( event ) if event.phase == 'began' then display.getCurrentStage():setFocus( self ) &nbsp; self.isFocus = true startTime = system.getTimer() endTime&nbsp; &nbsp;= startTime elseif self.isFocus then if event.phase == 'moved' then endTime = system.getTimer()&nbsp; avgSpeed = distance( event.xStart, event.yStart, event.x, event.y ) / ( ( endTime - startTime ) / 1000 ) print( string.format( 'Average speed pixels per seconds=%8.5f, time=%8.5f, distance=%8.5f',&nbsp; avgSpeed,&nbsp; ( endTime - startTime ) / 1000, distance( event.xStart, event.yStart, event.x, event.y ) ) ) elseif event.phase == 'ended'&nbsp; or event.phase == 'cancelled' then display.getCurrentStage():setFocus( nil ) &nbsp; self.isFocus = false end &nbsp;&nbsp; end end rect:addEventListener( 'touch' )

Note:

  • If you want more accurate results you need compute average speed in small periods of time. Let say 100ms,
  • If you need compute average speed along only one of axles  your calculations can be simplified.

Have a nice day:)

ldurniat

Unfortunately I am still getting the same issue in that it maxes out at 135. I tried changing the frame rate as well and seemed to have a different value as the max swipe swipe (see gif image - maxes out at 67.5 now and cant get more then that as fast as I swipe down

Is there some threshold maxed out value that I cant get over / past? 

Perhaps I could compute at 100ms that might fix the issue or get more accurate, how could I go about doing this at 100ms?

I cant figure this out at its killing my game atm as my game is using the max speed towards the score and 9/10 my users are getting the same 135 value meaning they are all swiping the same max speed which I highly doubt is correct?

See GIF below I changed the frame rate and now wont go beyond 67.5 - in that it maxes at 67.5 slows down then maxes out again there.

Can’t you just ignore any values that are above a certain ‘impossible’ threshold? 

To take an average you could calculate the distance moved each frame, and store the last 5/6 values in a table in a FILO fashion, as long as they don’t exceed a given amount. 

I’m not even certain it is maxing out tbh. But as in the original post in the first GIF you can see it doesn’t get near 135 until it just jumps right up to it and this is happening 99% of the time. That could be an option though to check if greater or equal to 135 as that’s what it seems to like going to for whatever reason. But I don’t feel that is the right ‘solution’ to the problem

By my original code nothing points out to me it should be doing that odd behavior, i feel this is possibly independent of the code that’s causing the issue.

local flickStartTime = 0 local function touchListener(event) if event.phase == "began" then flickStartTime = system.getTimer() if timerStarted == false then timerStarted = true Runtime:addEventListener( "enterFrame", timerText ) end elseif event.phase == "moved" then local flickDuration = system.getTimer() - flickStartTime local speedY = (event.yStart - event.y) / flickDuration if secondsLeftt \> 0 then if speedY \< 0 then myObject.angularVelocity = (speedY \* 500 ) \* -1 end end end end

use event.time instead of system.getTimer - the “duration” portion of your calcs is flawed due to event queue

Looks like that has resolved the issue  :slight_smile:

Sorry one more issue I seem to be having is the device swipe gesture speed across difference device densities, the swipe speed seems to be higher on higher density devices. 

local speedY = ((event.yStart - event.y) / flickDuration) \* -1

So I think I am having an issue attempting to get the same physical distance on difference devices with different screen density.

Regarding the below, is this giving me the pixels or the density independent units?

event.yStart - event.y

How can I get from this calculation the DP as the distance? Or is it already measured in that? Then I can use that as the distance as I think this calculation is flawed across device screen densities.

Thanks for helping me in the questions above any how as well. Really appreciate the community and support on here :slight_smile: really helpful!!

What does your config.lua file look like.

What kind of scaling are you using?

This is my  config.lua

application = { content = { width = 320, height = 480, scale = "letterbox", fps = 60, --[[imageSuffix = { ["@2x"] = 2, ["@4x"] = 4, }, --]] }, }

I was thinking I could do something like this:

... elseif event.phase == "moved" then local flickDuration = event.time - flickStartTime local dpi = system.getInfo("androidDisplayYDpi") local distanceDpi = (event.yStart - event.y) / dpi ...

Correct me if I am wrong.

From the docs : “androidDisplayYDpi” returns the DPI (dots per inch) of the screen along the y axis, relative to the orientation of the application. This can be used to convert pixels to inches and vice-versa. Returns nil on all other platforms.

https://docs.coronalabs.com/api/library/system/getInfo.html#androiddisplayxdpi

Using letterbox, events should be scaled too.  So the ‘relative rate’ should be the same for all device.

Note: Others will disagree, but I don’t suggest using such a low resolution as your content resolution.  

re: androidDisplayYDpi- I don’t know about that feature.  I don’t use it.

Letterbox only mentions about the fill size space i.e. “letterbox” — scales the content area to fill the screen while preserving the same aspect ratio

You can test this experimentally.

  1. Make basic app with a touch listener that prints the <x,y> of the touch.

  2. Set the config.lua content area use letterbox 320x480.

  3. Install it on your device.

  4. Touch the screen in the upper left and lower right.

  5. Look at the xCode debug console or for Android use adb logcat and see what is printed.

(https://docs.coronalabs.com/guide/basics/debugging/index.html)

  1. Repeat, but use 640x960

The numbers, upper-left numbers should be similar.  The lower right should be 2x the prior.  If so, the touch event is being scaled to the content area.

your results vary with density because you divide by a value that varies with density

if you’re truly trying to measure in “inches per second” then maybe that’s appropriate.

if on the other hand you’re measuring in “content pixels per second” then it’s not appropriate.

figure out what you want, then write the math to match.

I’m looking at inches per second, the physical distance a user swipes along the Y axis and not the amount of pixels on the screen the user has moved across, I am using:

local dpi = system.getInfo("androidDisplayYDpi") local distanceDpi = (event.yStart - event.y) / dpi

And the results swiping on 2 devices are that the speeds match pretty closely regarding the swipe speed I apply. This seems sufficient for what I am after.

still appears to be trying to convert content pixels directly into inches (via a ratio that instead converts hardware pixels into inches)

Are you suggesting the implementation I am using is incorrect? Whats the difference between content pixels and hardware pixels are these not the same thing?

I am just trying to understand what you are meaning here :) 

event coords are in content pixels, they will not vary (except by some potential letterboxing to account for differing content/device aspect ratios) with device resolution.  so if you then divide them by dpi (in device pixels, which will vary with device resolution and physical size) then you get a nonsense value.  the only time it’d work out correctly is if device dimensions exactly equal content dimensions.  so you’d need to first convert content pixels to device pixels (see https://docs.coronalabs.com/api/library/display/contentScaleY.html for example) and then to inches via your dpi.

too much to go into here, read this:  https://docs.coronalabs.com/guide/basics/configSettings/index.html#content-scaling

I see exactly what you mean now regarding the content height.

I think this is what you mean: 

local dpi = system.getInfo("androidDisplayYDpi") local distanceDpi = ((event.yStart- event.y) \* display.contentScaleY) / dpi

Having just tested this outputting the result of the distanceDpi it doesnt appear correct.

When When I do swipe gesture down the full height of both devices the value of the distanceDpi is 1.75 on my lower resolution device (480x800) and it is a 4.5’’ inch screen size, while I get 0.76 on my other device (720 x 1280 pixels) that is 5 inches. The distanceDpi value is about twice as high for my device that is smaller and lower resolution then the other. 

My scale mode is letterbox as well

Hi leojharris,

I would measure average speed like that 

local mAbs&nbsp; = math.abs local mSqrt = math.sqrt local function distance ( x1, y1, x2, y2 ) return mSqrt( ( x1 - x2 ) \* ( x1 - x2 ) + ( y1 - y2 ) \* ( y1 - y2 ) ) end &nbsp;&nbsp; local rect = display.newRect( sceneGroup, display.contentCenterX, display.contentCenterY, 100, 100 ) local startTime, endTime, avgSpeed function rect:touch( event ) if event.phase == 'began' then display.getCurrentStage():setFocus( self ) &nbsp; self.isFocus = true startTime = system.getTimer() endTime&nbsp; &nbsp;= startTime elseif self.isFocus then if event.phase == 'moved' then endTime = system.getTimer()&nbsp; avgSpeed = distance( event.xStart, event.yStart, event.x, event.y ) / ( ( endTime - startTime ) / 1000 ) print( string.format( 'Average speed pixels per seconds=%8.5f, time=%8.5f, distance=%8.5f',&nbsp; avgSpeed,&nbsp; ( endTime - startTime ) / 1000, distance( event.xStart, event.yStart, event.x, event.y ) ) ) elseif event.phase == 'ended'&nbsp; or event.phase == 'cancelled' then display.getCurrentStage():setFocus( nil ) &nbsp; self.isFocus = false end &nbsp;&nbsp; end end rect:addEventListener( 'touch' )

Note:

  • If you want more accurate results you need compute average speed in small periods of time. Let say 100ms,
  • If you need compute average speed along only one of axles  your calculations can be simplified.

Have a nice day:)

ldurniat