Question

Okay, so I'm trying to make a 'Wheel of Fortune' type of effect with a wheel shape in iOS, where I can grab and spin a wheel. I can currently drag and spin the wheel around to my heart's content, but upon releasing my finger, it stops dead. I need to apply some momentum or inertia to it, to simulate the wheel spinning down naturally.

I've got the velocity calculation in place, so when I lift my finger up I NSLog out a velocity (between a minimum of 1 and a maximum of 100), which ranges from anywhere between 1 and over 1800 (at my hardest flick!), now I'm just trying to establish how I would go about converting that velocity into an actual rotation to apply to the object, and how I'd go about slowing it down over time.

My initial thoughts were something like: begin rotating full circles on a loop at the same speed as the velocity that was given, then on each subsequent rotation, slow the speed by some small percentage. This should give the effect that a harder spin goes faster and takes longer to slow down.

I'm no mathematician, so my approach may be wrong, but if anybody has any tips on how I could get this to work, at least in a basic state, I'd be really grateful. There's a really helpful answer here: iPhone add inertia/momentum physics to animate "wheel of fortune" like rotating control, but it's more theoretical and lacking in practical information on how exactly to apply the calculated velocity to the object, etc. I'm thinking I'll need some animation help here, too.

EDIT: I'm also going to need to work out if they were dragging the wheel clockwise or anti-clockwise.

Many thanks!

Was it helpful?

Solution

I have written something analogous for my program Bit, but my case I think is a bit more complex because I rotate in 3D: https://itunes.apple.com/ua/app/bit/id366236469?mt=8

Basically what I do is I set up an NSTimer that calls some method regularly. I just take the direction and speed to create a rotation matrix (as I said, 3D is a bit nastier :P ), and I multiply the speed with some number smaller than 1 so it goes down. The reason for multiplying instead of subtracting is that you don't want the object to rotate twice as long if the spin from the user is twice as hard since that becomes annoying to wait on I find.

As for figuring out which direction the wheel is spinning, just store that in the touchesEnded:withEvent: method where you have all the information. Since you say you already have the tracking working as long as the user has the finger down this should hopefully be obvious.

What I have in 3D is something like:

// MyView.h
@interface MyView : UIView {
    NSTimer *animationTimer;
}
- (void) startAnimation;
@end


// MyAppDelegate.h
@implementation MyAppDelegate

- (void) applicationDidFinishLaunching:(UIApplication *)application {
    [myView startAnimation];
}

@end

// MyView.m
GLfloat rotationMomentum = 0;
GLfloat rotationDeltaX  = 0.0f;
GLfloat rotationDeltaY  = 0.0f;

@implementation MyView
- (void)startAnimation {
    animationTimer = [NSTimer scheduledTimerWithTimeInterval:(NSTimeInterval)((1.0 / 60.0) * animationFrameInterval) target:self selector:@selector(drawView:) userInfo:nil repeats:TRUE];
}

- (void) drawView:(id)sender {
    addRotationByDegree(rotationMomentum);
    rotationMomentum /= 1.05;
    if (rotationMomentum < 0.1)
        rotationMomentum = 0.1; // never stop rotating completely
    [renderer render];
}

- (void)touchesBegan:(NSSet*)touches withEvent:(UIEvent*)event
{
}

- (void)touchesMoved:(NSSet*)touches withEvent:(UIEvent*)event
{
    UITouch *aTouch = [touches anyObject];
    CGPoint loc = [aTouch locationInView:self];
    CGPoint prevloc = [aTouch previousLocationInView:self];

    rotationDeltaX = loc.x - prevloc.x;
    rotationDeltaY = loc.y - prevloc.y;

    GLfloat distance = sqrt(rotationDeltaX*rotationDeltaX+rotationDeltaY*rotationDeltaY)/4;
    rotationMomentum = distance;
    addRotationByDegree(distance);

    self->moved = TRUE;
}

- (void)touchesEnded:(NSSet*)touches withEvent:(UIEvent*)event
{
}

- (void)touchesCancelled:(NSSet*)touches withEvent:(UIEvent*)event
{
}

I've left out the addRotationByDegree function but what it does is that it uses the global variables rotationDeltaX and rotationDeltaY and applies a rotational matrix to an already stored matrix and then saves the result. In your example you probably want something much simpler, like (I'm assuming now that only movements in the X direction spin the wheel):

- (void)touchesMoved:(NSSet*)touches withEvent:(UIEvent*)event
{
    UITouch *aTouch = [touches anyObject];
    CGPoint loc = [aTouch locationInView:self];
    CGPoint prevloc = [aTouch previousLocationInView:self];

    GLfloat distance = loc.x - prevloc.x;
    rotationMomentum = distance;
    addRotationByDegree(distance);

    self->moved = TRUE;
}

void addRotationByDegree(distance) {
    angleOfWheel += distance; // probably need to divide the number with something reasonable here to have the spin be nicer
}

OTHER TIPS

It's going to be a rough answer as I don't have any detailed example at hand.

If you have the velocity when you lift your finger already then it should not be hard. The velocity you have is at pixels per second or something like that. First you need to convert that linear speed to an angular speed. That can be done by knowing the perimeter of the circle 2*PI*radius and then do 2*PI/perimeter*velocity to get the angular speed in radians per second.

If your wheel didn't have any friction in its axis it would run forever at that speed. Well, you can just arbitrate a value for this friction, which is an acceleration and can be represented at pixels per second squared or radians per second squared for an angular acceleration. Then it's just a matter of dividing the angular speed by this angular acceleration and you get the time until it stops.

With the animation time you can use the equation finalAngle = initialAngle + angularSpeed*animationTime - angularAcceleration/2*animationTime*animationTime to get the final angle your wheel is going to be at the end of the animation. Then just do an animation on the transformation and rotate it by that angle for the time you got and say that your animation should ease out.

This should look realistic enough. If not you'll need to give an animation path for the rotation property of your wheel based on some samples from the equation from above.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top