Zaki
Zaki

Reputation: 129

increasing and maintaing a constant speed of an object in objective C

I have posted something similar before and I got some suggestion which I have tried but I am still having problems...thus, will like to ask for any assistance on reviewing this code.

I am actually trying to get the ball object in the code to have a better or smoother animation and to increase in its speed constantly until it attains a maximum speed but right now what I am getting is the ball starting at the specified speed and then its movement becomes erratic as the speed increases and after a few second the ball just moves off the iphone simulator screen.

Can someone help me run this code(please add an image to represent the ball in IB) to see what I am not doing correctly.

Thanks in advance for your help.

   .h

@interface ViewController : UIViewController 
{
    UIImageView *ball;
    int speedX;
    int speedY;
    CGPoint ballMovement;
}
@property (nonatomic, retain) IBOutlet UILabel *scoreLabel;
@property (nonatomic, retain) IBOutlet UIImageView *ball;

- (void)initializeTimer;
- (void)animateBall:(NSTimer *)theTimer;

@end



.m

@implementation ViewController
@synthesize ball;

- (void)dealloc {
    [ball release];
    [super dealloc];
}

- (void)viewDidLoad {
    [super viewDidLoad];

    ball.center = CGPointMake(50,50);

    speedX = 1;
    speedY = 1;

    ballMovement = CGPointMake(speedX,speedY);

    [self initializeTimer];
}

- (void)initializeTimer {
    float theInterval = 0.1f;
    [NSTimer scheduledTimerWithTimeInterval:theInterval target:self selector:@selector(animateBall:) userInfo:nil repeats:YES];
}

- (void)animateBall:(NSTimer *)theTimer {
    ball.center = CGPointMake(ball.center.x+speedX, ball.center.y+speedY);
    speedX += 1;
    speedY += 1;

    NSLog(@"%d,%d", speedX, speedY);

    if (speedX <= 1)    
        speedX ==1;
    if (speedY <= 1)
        speedY ==1;

    if(ball.center.x > 300 || ball.center.x < 20)
        speedX = -abs(speedX);
    if(ball.center.y > 440 || ball.center.y < 40)
        speedY = -abs(speedY);
}

Upvotes: 0

Views: 544

Answers (3)

Nuoji
Nuoji

Reputation: 3448

If you want the ball to bounce off the bottom, with gravity along the y axis:

- (void)animateBall:(NSTimer *)theTimer {
    ball.center = CGPointMake(ball.center.x+speedX, ball.center.y+speedY);
    speedY += 1;

    if(ball.center.x > 300) {
        speedX = -abs(speedX);
    }
    if(ball.center.y > 440)
        speedY = -abs(speedY);
}

The main cause of the erratic movement you see is probably because of the bounce off the top/side of the screen. For instance:

if(ball.center.y > 440 || ball.center.y < 40)
    speedY = -abs(speedY);

What happens when the ball is speeding towards the top here? Say ball.center.y = 30, speedY = -4. Your speedY will then stay at -4. Next pass it's -3, then -2, then -1, and then it'll oscillate between -1 and 0. The same happens for speedX eventually.

The question is what sort of movement you are looking for. Your original code had something that looked like constant acceleration in the (1, 1) direction, with a bounce effect in the right and bottom corner, but your description said you wanted acceleration to a maximum speed.

For an accelerating ball with a maximum speed:

@interface ViewController : UIViewController {

    UIImageView *ball;
    double speedX;
    double speedY;
    int maxSpeed;
    double acceleration;
    CGPoint ballMovement;
}
@property (nonatomic, retain) IBOutlet UIImageView *ball;

- (void)initializeTimer;
- (void)animateBall:(NSTimer *)theTimer;

@end
@implementation ViewController

@synthesize ball;

- (void)dealloc {
    [ball release];
    [super dealloc];
}

- (void)viewDidLoad {
    [super viewDidLoad];

    ball.center = CGPointMake(50,50);

    speedX = 1;
    speedY = 1;
    maxSpeed = 20;
    acceleration = 1;
    ballMovement = CGPointMake(speedX,speedY);

    [self initializeTimer];
}

- (void)initializeTimer {
    float theInterval = 0.1f;
    [NSTimer scheduledTimerWithTimeInterval:theInterval target:self selector:@selector(animateBall:) userInfo:nil repeats:YES];
}

- (void)animateBall:(NSTimer *)theTimer {

    ball.center = CGPointMake(ball.center.x + speedX, ball.center.y + speedY);

    // Discover the actual speed.
    double actualSpeed = sqrt(speedX * speedX + speedY * speedY);

    // Increase the speed by the acceleration (actually acceleration * time interval),
    // Cap at max speed
    double newSpeed = MIN(acceleration + actualSpeed, maxSpeed);

    // Increase velocity uniformly along both x and y axis
    // (This example does not properly handle when speedX and speedY both start at zero)
    speedX = speedX * newSpeed / actualSpeed;  
    speedY = speedY * newSpeed / actualSpeed;

    // Make sure to bounce
    if (ball.center.x > 300) speedX = -abs(speedX);
    if (ball.center.x < 20) speedX = abs(speedX);
    if (ball.center.y > 440) speedY = -abs(speedY);
    if (ball.center.y < 40) speedY = abs(speedY);
}

@end

Upvotes: 0

Nuoji
Nuoji

Reputation: 3448

You are not capping the speed anywhere, so how is it supposed to reach a constant speed? From your code, once you pass ball.center.x > 300, you reverse the speed.

The rows

if (speedX <= 1) speedX ==1;
if (speedY <= 1) speedY ==1;

Do nothing, since they don't actually assign a value to speedX / speedY. It looks like this is what you mean:

if (speedX <= 1) speedX = 1;
if (speedY <= 1) speedY = 1;

Even so, this won't actually do what you want. If you want a max speed, then it should be something like this:

// Compact version
speedX = MIN(speedX + 1, TARGET_SPEED_X);

// More like your code:
speedX += 1;
if (speedX > TARGET_SPEED_X) speedX = TARGET_SPEED_X;

Upvotes: 1

Max
Max

Reputation: 16719

Acceleration = Speed / Delta_time

So you have to modify the code:

- (void)animateBall:(NSTimer *)theTimer {
    ball.center = CGPointMake(ball.center.x+speedX, ball.center.y+speedY);

    static float acceleration = 1.0f;

    speedX += acceleration*theTimer.timeInterval;
    speedY += acceleration*theTimer.timeInterval;

    NSLog(@"%d,%d", speedX, speedY);

    if (speedX <= 1)    
        speedX ==1;
    if (speedY <= 1)
        speedY ==1;

    if(ball.center.x > 300 || ball.center.x < 20)
        speedX = -abs(speedX);
    if(ball.center.y > 440 || ball.center.y < 40)
        speedY = -abs(speedY);
}

Upvotes: 0

Related Questions