简体   繁体   中英

iOS Multiple Bouncing Balls

Below is the working view controller that allows multiple balls to be created based on tap location and then calculates the x and y velocity for the ball. After a ball is created, it moves based on the calculated velocity and bounces off the screen perimeter. I use an NSTimer to animate the balls movement. The issue is only the most recently created ball moves. After a new ball is created, the previous ball stops moving. Methinks this is a concurrency type problem. I've done some research and tried a few things with threads, but couldn't get it to work. Maybe using an NSTimer is not the best approach?

Anyways, the ultimate goal here is to have all the created balls moving simultaneously.

#import "ViewController.h"
#import "Ball.h"
#import "Constants.h"

NSTimeInterval lastTouch;
NSTimeInterval eventTime;
Ball *currentBall;
CGPoint ballStartPosition;
NSMutableArray *balls;
// get iPhone display size & aspect ratio
CGSize screen_size;

@interface ViewController () {

}
@property (strong, nonatomic) EAGLContext *context;

- (void)setupGL;
- (void)tearDownGL;
- (void)setupOrthographicView;


@end

@implementation ViewController

- (void)viewDidLoad
{
    [super viewDidLoad];

    self.context = [[EAGLContext alloc] initWithAPI:kEAGLRenderingAPIOpenGLES1];

    if (!self.context) {
        NSLog(@"Failed to create ES context");
    }

    GLKView *view = (GLKView *)self.view;
    view.context = self.context;
    view.drawableDepthFormat = GLKViewDrawableDepthFormat24;


    balls = [[NSMutableArray alloc] initWithObjects: nil];

    [self setupGL];

    self.motionManager = [[CMMotionManager alloc] init];
    self.motionManager.accelerometerUpdateInterval = .2;
    [self.motionManager startAccelerometerUpdatesToQueue:[NSOperationQueue    currentQueue]
            withHandler:^(CMAccelerometerData  *accelerometerData, NSError *error) {
                [self updateAcceleration:accelerometerData.acceleration];
                if(error){ NSLog(@"%@", error); }
            }];}

- (void)dealloc
{   
    [self tearDownGL];

    if ([EAGLContext currentContext] == self.context) {
        [EAGLContext setCurrentContext:nil];
    }
}

- (void)didReceiveMemoryWarning
{
    [super didReceiveMemoryWarning];

    if ([self isViewLoaded] && ([[self view] window] == nil)) {
    self.view = nil;

    [self tearDownGL];

    if ([EAGLContext currentContext] == self.context) {
        [EAGLContext setCurrentContext:nil];
    }
    self.context = nil;
}

// Dispose of any resources that can be recreated.
}

- (void)update
{
[self setupOrthographicView];
}

- (void)setupGL
{
[EAGLContext setCurrentContext:self.context];
}

- (void)tearDownGL
{
[EAGLContext setCurrentContext:self.context];
}

- (void)setupOrthographicView
{
screen_size= self.view.bounds.size;
// set viewport based on display size
glViewport(0, 0, screen_size.width, screen_size.height);


// set up orthographic projection
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
glOrthof(0, screen_size.width, 0, screen_size.height, -1.0f, 1.0f);

}

- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
{
lastTouch = [NSDate timeIntervalSinceReferenceDate];

// get iPhone display size & aspect ratio
CGSize screen_size = self.view.bounds.size;

// get the touch point and fix coordinates
CGPoint touch_point = [[touches anyObject] locationInView:self.view];
touch_point.y = screen_size.height - touch_point.y;
touch_point = CGPointMake(touch_point.x, touch_point.y);

ballStartPosition = touch_point;

// generate random RGB values and store them in an array
float r = RANDOM_FLOAT_BETWEEN(0.0, 1.0);
float g = RANDOM_FLOAT_BETWEEN(0.0, 1.0);
float b = RANDOM_FLOAT_BETWEEN(0.0, 1.0);

NSMutableArray *random_color = [[NSMutableArray alloc] init];
[random_color addObject:[NSNumber numberWithFloat:r]];
[random_color addObject:[NSNumber numberWithFloat:g]];
[random_color addObject:[NSNumber numberWithFloat:b]];

currentBall = [[Ball alloc] init];
int initialBallRadius = 2;
[currentBall makeBallWithRadius: initialBallRadius position:touch_point color:random_color];
[currentBall setStart_position:ballStartPosition];

[balls addObject:currentBall];
}

- (void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event
{
eventTime = [NSDate timeIntervalSinceReferenceDate];
NSTimeInterval touchBeginEndInterval = eventTime - lastTouch;
// NSLog(@"%f", touchBeginEndInterval );

// get the touch point and fix coordinates
CGPoint touch_point = [[touches anyObject] locationInView:self.view];
touch_point.y = screen_size.height - touch_point.y;
touch_point = CGPointMake(touch_point.x, touch_point.y);


int ball_scale = 15;
[currentBall setRadius:touchBeginEndInterval*ball_scale];

float x_distance = touch_point.x - ballStartPosition.x;
float y_distance = touch_point.y - ballStartPosition.y;
// NSLog(@"dx: %f dy: %f",x_distance, y_distance );

float x_velocity = x_distance / touchBeginEndInterval;
float y_velocity = y_distance / touchBeginEndInterval;
// NSLog(@"vx: %f vy: %f",x_velocity, y_velocity );

[currentBall setX_velocity:x_velocity];
[currentBall setY_velocity:y_velocity];

[NSTimer scheduledTimerWithTimeInterval:0.0001 target:self selector:@selector(doBallTick) userInfo:NULL repeats:YES];
}

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

// get the touch point and fix coordinates
CGPoint touch_point = [[touches anyObject] locationInView:self.view];
touch_point.y = screen_size.height - touch_point.y;
touch_point = CGPointMake(touch_point.x, touch_point.y);
// NSLog(@"x: %f y: %f", touch_point.x, touch_point.y);

[currentBall setPosition:touch_point];
}

- (void) doBallTick {
NSTimeInterval current_time = [NSDate timeIntervalSinceReferenceDate];
[currentBall setCurrent_time:(current_time - eventTime)];
[currentBall update];
//NSLog(@"%f", (eventTime));

 //Checks if the ball is outside bounds
 if ((currentBall.position.x - currentBall.radius) <= 0) {
     eventTime = current_time;
     [currentBall hitLeft];
 }else if ((currentBall.position.x + currentBall.radius) >= screen_size.width){
     eventTime = current_time;
    [currentBall hitRight];
 }else if ((currentBall.position.y - currentBall.radius) <= 0) {
     eventTime = current_time;
     [currentBall hitBottom];
 }else if ((currentBall.position.y + currentBall.radius) >= screen_size.height){
     eventTime = current_time;
     [currentBall hitTop];
 }


}

- (void)updateAcceleration:(CMAcceleration)acceleration
{
// add acceleration code here
}

void GLDrawEllipse (int segments, CGFloat width, CGFloat height, CGPoint center, bool filled)
{
glPushMatrix();
glTranslatef(center.x, center.y, 0.0);
GLfloat vertices[segments*2];
int count=0;
for (GLfloat i = 0; i < 360.0f; i+=(360.0f/segments))
{
    vertices[count++] = (cos(DEGREES_TO_RADIANS(i))*width);
    vertices[count++] = (sin(DEGREES_TO_RADIANS(i))*height);
}
glVertexPointer (2, GL_FLOAT , 0, vertices);
glDrawArrays ((filled) ? GL_TRIANGLE_FAN : GL_LINE_LOOP, 0, segments);
glPopMatrix();
}

void GLDrawCircle (int circleSegments, CGFloat circleSize, CGPoint center, bool filled)
{
GLDrawEllipse(circleSegments, circleSize, circleSize, center, filled);
}

#pragma mark - GLKView and GLKViewController delegate methods

- (void)glkView:(GLKView *)view drawInRect:(CGRect)rect
{
// clear the rendering buffer
glClear(GL_COLOR_BUFFER_BIT);
// enable the vertex array rendering
glEnableClientState(GL_VERTEX_ARRAY);

// draw and dispaly the balls
for(int i = 0; i < [balls count]; i++) {
    Ball *current_ball = (Ball *) [balls objectAtIndex: i];
    NSMutableArray *random_color = [current_ball color];
    glColor4f([[random_color objectAtIndex:0] floatValue], [[random_color objectAtIndex:1] floatValue], [[random_color objectAtIndex:2] floatValue], 1);
    GLDrawCircle(30, [current_ball radius], [current_ball position], true);
}
}

@end

In your doBallTick method, you only move currentBall . You should schedule your selector only one time in the init method (instead of everytime your create a ball), and then loop over all the balls in the balls nsarray in your doBallTick method.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM