Getting a Reasonable Speed From Drag/Swipe

Discussion in 'iOS Programming' started by seepel, Mar 3, 2011.

  1. seepel macrumors 6502

    seepel

    Joined:
    Dec 22, 2009
    #1
    Here's the situation, there is an object on screen the user should touch the object and be able to drag it around and when released it will move around the screen with a velocity based on the speed the user was dragging/swiping the object. My current implementation basically tracks the touchesMoved/touchesEnded callbacks and calculates stores the velocity based on the last/second to last touches.

    Code:
    
        if(isTracking_) {
            UITouch *touch = [touches anyObject];
            CGPoint point = [touch locationInView:self.superview];
            CGPoint previousPoint = [touch previousLocationInView:self.superview];
            NSTimeInterval time = [[NSDate date] timeIntervalSince1970];
            if(previousTouchTime_ != 0) {
                CGFloat velocityX = (point.x-previousPoint.x)/(time-previousTouchTime_);
                CGFloat velocityY = (point.y-previousPoint.y)/(time-previousTouchTime_);
                self.velocity = CGPointMake(velocityX, velocityY);
            }
            previousTouchTime_ = time;
            if([self.delegate ballView:self canMoveToPoint:point]) {
                self.center = point;
            } else {
                [self endTracking];
            }
        }
    
    Here I found it very difficult to control and sometimes would get unsatisfactorily low/high velocities, so I thought I might add in a minimum/maximum velocity to avoid these edge cases.

    Code:
             CGFloat speedX = fabs(velocityX);
                CGFloat speedY = fabs(velocityY);
                if(speedX < MINIMUM_VELOCITY)
                    if(velocityX < 0)
                        velocityX = -MINIMUM_VELOCITY;
                    else
                        velocityX = MINIMUM_VELOCITY;
                if(speedX > MAXIMUM_VELOCITY)
                    if(velocityX < 0)
                        velocityX = -MAXIMUM_VELOCITY;
                    else
                        velocityX = MAXIMUM_VELOCITY;
                if(speedY < MINIMUM_VELOCITY)
                    if(velocityY < 0)
                        velocityY = -MINIMUM_VELOCITY;
                    else
                        velocityY = MINIMUM_VELOCITY;
                if(speedY > MAXIMUM_VELOCITY)
                    if(velocityY < 0)
                        velocityY = -MAXIMUM_VELOCITY;
                    else
                        velocityY = MAXIMUM_VELOCITY;
    
    Which definitely helps and the velocities I get are "sane" velocities as I've arbitrarily defined desired velocities. However I still find it difficult to control where in this velocity range I am in. I'm thinking of trying to some how take an average velocity for a single movement, or maybe trying to compress a scale of human attainable velocities to my desired velocities, for example...

    Reasonable velocities seem to be in the 1000-2000 pixels/s regime, where as I've been able to get velocities between 600-3000 pixels/s. So I was thinking of mapping 1000->600 and 3000->2000 with a linear scale between.

    The other problem seems to be, that when trying to swipe quickly, I almost always end up curving my thumb and I don't get the angle I was intending (which could be due to the fact that I am chopping x/y components to a MAXIMUM_VELOCITY and not the magnitude, so I'll have to go back and check to see if that could fix the problem).

    Before I go digging into this with trial and error I just wanted to see if anyone else has done this before and has any suggestions.
     
  2. chown33 macrumors 604

    Joined:
    Aug 9, 2009
    #2
    The red-hilited code seems ill-advised to me. There is a timestamp in the UIEvent and UITouch parameters associated with the callback. You should be using one of those for calculating velocity.

    When you calculate velocity using the time at which the handler runs, you're using a time uncorrelated to the original event.


    Your min-velocity logic is conceptually flawed. There is no good reason to prohibit a small velocity in one axis. If I throw a stone straight up, its X velocity is 0. Why would that be prohibited in a game? I don't think you've worked out (on paper) a simple numerical example, where the object is moving directly (or almost directly) along the X or Y axis. If you had, you'd see the flaw in the code's logic.

    If you want to limit velocities, you have to work in polar coordinates (magnitude and direction-angle).


    As to the direction vector, you can smooth points by averaging more than two.

    I wouldn't even do any calculations until the touch ends. Just keep a FIFO queue of the relevant data from the last N points. Only when the touch ends is it necessary to calculate a velocity and direction. Continuous intermediate calculations aren't doing anything useful.
     
  3. seepel thread starter macrumors 6502

    seepel

    Joined:
    Dec 22, 2009
    #3
    Good point! I definitely didn't like doing that and simply missed the timestamp in the documentation. Thanks!

    Agreed, I've changed it to normalize the magnitude and leave the direction alone, and it seems to be helping a lot.

    My question then becomes, is this the best way to do it? It seems like it would be non trivial to sort out which points are associated with a certain gesture. For example a user grabs the object, drags it somewhere else, and then swipes it again. The initial drag shouldn't go into the final velocity, I guess I would want to figure out some sort of change in velocity cut off that would signal a new set of events? It isn't immediately obvious to me how I would go about doing this.

    Also something that I was eventually going to fix up, but wanted to get the logic right before I started optimizing.
     
  4. chown33 macrumors 604

    Joined:
    Aug 9, 2009
    #4
    I don't understand where the problem is.
    If the user drags it and releases it, then the touch series has ended. Velocity and direction depend on the last few points. The swipe starts a new series of touches, and the release ends the series. Velocity and direction still depend on the last few points.

    If the user drags it and doesn't release it (i.e. finger is still touching the screen, so touch events are still being delivered), then flicks it with a final release, then velocity and direction still depend on the last few points.

    So exactly how many points should be used? I.e. what number is "few" in "the last few points"?

    Smoothing is always going to be sensitive to the number of elements that are averaged. Consider a recording of Beethoven's Ninth. If you average that signal over a window of 75 minutes, the result will be nothing but silence. This does not imply that the glorious Ninth is nothing but silence, only that your averaging window is inappropriate for the signal.

    If you're not sure how many points to keep for smoothing, then you need to do some testing. Start with two points, and implement it properly (i.e. fix the min-velocity bug). Then try keeping 10 points. If that's too sluggish, split the difference between 2 and 10. Repeat until performance is acceptable. If 10 points isn't smooth enough, double it until it's too sluggish, then start splitting the difference.

    Averaging is inherently a low-pass filter. High-frequency components in the signal are attenuated. That's the point of smoothing: to attenuate the high-frequency fluctuations, which are considered to be noise. Determining which frequencies are attenuated and the rate of rolloff requires some filter design knowledge. If you don't have that knowledge, then you have to experiment and see what works empirically. Doubling or halving the difference is a simple strategy if you have a basic algorithm that works with two or more samples. But you need the basic algorithm working first. You can't do meaningful experiments on a broken apparatus.

    A good answer depends on what you're trying to do. You haven't said whether it's a game or productivity or something else.
     
  5. seepel thread starter macrumors 6502

    seepel

    Joined:
    Dec 22, 2009
    #5
    There would be another scenario where a user drags to the left, stops but doesn't release, and then flicks up. The points used while dragging to the left shouldn't be used in the final velocity. And one can imagine other similar scenarios. But then one could imagine the user moving up and left and switching to just up. Should the up and left be factored into the final velocity? It's not so clear, but I guess if it were easy everyone would do it eh? I guess I'll just have to run through some trial and error tests, which is fine. Just wondering if anyone had come across this before and could save me some leg work.

    It's a game by the way, imagine snood, except you drag and flick the objects. And thanks for all your suggestions, they're good ones.
     

Share This Page