Search code examples
iosuiimagecgimage

Simple puzzle game iOS: Analyzing alpha pixel issues


I create simple puzzle game and use next for this:

I have an class named Piece. Piece class inherits from UIImageView and contain next field:

@property(nonatomic, strong) NSString *pieceName;
@property(nonatomic) CGFloat pos_x;
@property(nonatomic) CGFloat pos_y;
@property(nonatomic) BOOL snapped;

In my game each pieces have color (red, green, blue and other). Each piece have irregular in shape. This is image with a alpha color. When I tap on alpha color I don't have to choose this piece.

For this I use next method:

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

    UITouch *touch = [[event allTouches] anyObject];
    CGPoint touchLocation = [touch locationInView:self.view];

    NSArray *views = [self.view subviews];

    for (UIView *v in views) {
        if([v isKindOfClass:[Piece class]]){
            if (CGRectContainsPoint(v.frame, touchLocation) && ((Piece *)v).snapped == FALSE) { 

                UITouch* touchInPiece = [touches anyObject]; 
                CGPoint point = [touchInPiece locationInView:(Piece *)v];
                BOOL solidColor = [self verifyAlphaPixelImage:(Piece *)v atX:point.x * resolution atY:point.y * resolution];

                if (solidColor) {                

                    NSArray *tempViews = [self.view subviews];
                    [tempViews reverseObjectEnumerator];

                    for (UIView *tv in tempViews) {
                        if (tv == v) {
                            [tv setExclusiveTouch:YES];
                            dragging = YES;
                            oldX = touchLocation.x;
                            oldY = touchLocation.y;
                            piece = (Piece *)v;
                            [self.view bringSubviewToFront:piece];
                            break;
                        }
                    }

               }

            }
        }
    }
}


- (void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event
{
    UITouch *touch = [[event allTouches] anyObject];
    CGPoint touchLocation = [touch locationInView:self.view];

    if (dragging) {        
        CGRect frame = piece.frame;
        frame.origin.x = piece.frame.origin.x + touchLocation.x - oldX;
        frame.origin.y =  piece.frame.origin.y + touchLocation.y - oldY;
        if ([self verifyOutOfScopeCoordX:touchLocation.x CoordY:touchLocation.y]) {
            piece.frame = frame;
        }               
        [self snapPiece];
    }

    oldX = touchLocation.x;
    oldY = touchLocation.y;
}

- (void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event
{    
    dragging = NO;
}

- (BOOL)verifyAlphaPixelImage:(Piece *)image atX:(int)x atY:(int)y{

    CGImageRef imageRef = [image.image CGImage];
    NSUInteger width = CGImageGetWidth(imageRef);
    NSUInteger height = CGImageGetHeight(imageRef);
    CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB();
    unsigned char *rawData = (unsigned char*) calloc(height * width * 4, sizeof(unsigned char));
    NSUInteger bytesPerPixel = 4;
    NSUInteger bytesPerRow = bytesPerPixel * width;
    NSUInteger bitsPerComponent = 8;
    CGContextRef context = CGBitmapContextCreate(rawData, width, height,
                                                 bitsPerComponent, bytesPerRow, colorSpace,
                                                 kCGImageAlphaPremultipliedLast | kCGBitmapByteOrder32Big);
    CGColorSpaceRelease(colorSpace);

    CGContextDrawImage(context, CGRectMake(0, 0, width, height), imageRef);
    CGContextRelease(context);

    int byteIndex = (bytesPerRow * y) + x * bytesPerPixel;

    CGFloat red   = (rawData[byteIndex]     * 1.0) / 255.0;
    CGFloat green = (rawData[byteIndex + 1] * 1.0) / 255.0;
    CGFloat blue  = (rawData[byteIndex + 2] * 1.0) / 255.0;
    CGFloat alpha = (rawData[byteIndex + 3] * 1.0) / 255.0;
    /*
    NSLog(@"red:   %f", red);
    NSLog(@"green: %f", green);
    NSLog(@"blue:  %f", blue);
    NSLog(@"alpha:   %f", alpha);
    */    
    free(rawData);    
    if(alpha==0 && red == 0 && green == 0 && blue == 0) return NO;
    else return YES;

}

I move my pieces without any problem. But sometime I get strange error with a verifyAlphaPixelImage method in this line CGFloat red = (rawData[byteIndex] * 1.0) / 255.0; I get EXC_BAD_ACCESS.

verifyAlphaPixelImage - I need this method for analyzing alpha pixel. If I tap on alpha pixel I don't have to choose this piece.

Now the main problem is a EXC_BAD_ACCESS on line where I use rawData.

Now I use this:

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

    UITouch *touch = [[event allTouches] anyObject];
    CGPoint touchLocation = [touch locationInView:self.view];

    NSArray *views = [self.view subviews];

    for (UIView *v in views) {
        if([v isKindOfClass:[Piece class]]){

            CGFloat pos_x = touchLocation.x - v.frame.origin.x;
            CGFloat pos_y = touchLocation.y - v.frame.origin.y;         

            if (CGRectContainsPoint(v.frame, touchLocation) && ((Piece *)v).snapped == FALSE) { 


                BOOL solidColor = [self verifyAlphaPixelImage:(Piece *)v atX:pos_x * resolution atY:pos_y * resolution];

                if (solidColor) {                

                    NSArray *tempViews = [self.view subviews];
                    [tempViews reverseObjectEnumerator];

                    for (UIView *tv in tempViews) {
                        if (tv == v) {
                            [tv setExclusiveTouch:YES];
                            dragging = YES;
                            oldX = touchLocation.x;
                            oldY = touchLocation.y;
                            piece = (Piece *)v;
                            [self.view bringSubviewToFront:piece];
                            break;
                        }
                    }

                }

            }
        }
    }
}

and this work...


Solution

  • Notice that in your first approach you are using two different touches for the same purpose.

    First you do :

    UITouch *touch = [[event allTouches] anyObject];
    CGPoint touchLocation = [touch locationInView:self.view];
    

    For checking if your touch is within a piece view.

    But then you take another touch with :

    UITouch* touchInPiece = [touches anyObject]; 
    CGPoint point = [touchInPiece locationInView:(Piece *)v];
    

    Incase you have more than one touch event the second touch might not be the first touch and might not even be inside the view which the first touch was detected in. The second touch might be out of range for the piece view detected by the first touch