[Objective-C] 如何判定UIView裡的可作用區域?


是這樣的,我的遊戲App裡有個地方可以讓玩家隨意放置裝備在飛機上。原本我是不打算檢查玩家放置的位置,想說讓他們自己想擺哪就擺哪,即使位置太過離普也沒關係。不過有人反應這樣似乎太過”自由”了,於是這幾天想做一些調整,讓玩家只能把裝備放在飛機上真正”看的到”的地方,而不能擺在”空氣”上。

原本是想用這種方法,也是一般遊戲裡常用的檢查物件是否碰撞的方式,利用數個rect區域來檢查。也就是在飛機圖片上設立好幾個矩形區域,用來檢查玩家放置裝備的位置是否在某個矩形區域內。但做到一半就發現,這樣實在太麻煩了。因為遊戲裡飛機的類型有好幾種,每種的圖片長像又差很多,如果要慢慢找出那些矩形區域來會很費工。而且,利用矩形去含蓋一個不規則形狀的圖片,如果矩形不夠多、不夠密,還是會有很多死角位置。玩家可能還是會不小心擺到空氣上,或者明明是應該要可以擺放的位置卻不給擺。

於是我又重新試了一個方法,將玩家擺放裝備的位置那一個像素(pixel)的顏色值抓出來做比對。由於遊戲裡的飛機圖片是透色的png 24bit圖片,所以我的方法是抓出RGBA四個顏色值,比對R, G, B, A(alpha)的值是否均為0。如果是的話,就代表玩家擺放位置是在一個透明(transparent)的像素點,那就應該要回傳NO值,告訴上層UI應該要reject這次的動作。其實也可以只比alpha值就好,不過我還是保險一點四個值全做比對。

結果就真的可以work了,不過這種檢查方法應該會比檢查矩形位置耗費的CPU資源多很多。因為要載入圖片資料,還要做一堆處理。不過,在遊戲裡只有一個頁面會需要做這種檢查,而且只有發生在玩家托曳裝備時才需要,出現的時機不會很多,所以還好。不過,使用這個方法,必須要特別注意支援Retina解析度時,檢查的位置必須乘以2,否則會錯!! 以下是我的code:

- (BOOL) checkEquipmentPosition:(CGPoint)pos {
	NSArray *arr = [ImageUtils getRGBAsFromImage:self.img atX:pos.x andY:pos.y count:1];
	UIColor *color = arr[0];

	CGFloat r = 0.0f;
	CGFloat g = 0.0f;
	CGFloat b = 0.0f;
	CGFloat a = 0.0f;
	[color getRed:&r green:&g blue:&b alpha:&a];

	BOOL result = YES;
	log(@"########### color: (%f, %f, %f, %f)", r, g, b, a);
	if( r == 0.0f && g == 0.0f && b == 0.0f && a == 0.0f ) {
		result = NO;
	}
	return result;
}

+ (NSArray*)getRGBAsFromImage:(UIImage*)image atX:(int)xx andY:(int)yy count:(int)count
{
    NSMutableArray *result = [NSMutableArray arrayWithCapacity:count];

    // First get the image into your data buffer
    CGImageRef imageRef = [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);

    // Now your rawData contains the image data in the RGBA8888 pixel format.
    int byteIndex = (bytesPerRow * yy) + xx * bytesPerPixel;
    for (int ii = 0 ; ii < count ; ++ii)
    {
        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;
        byteIndex += 4;

        UIColor *acolor = [UIColor colorWithRed:red green:green blue:blue alpha:alpha];
        [result addObject:acolor];
    }

  free(rawData);

  return result;
}

抓取像素顏色資料的util function原文:
http://stackoverflow.com/questions/448125/how-to-get-pixel-data-from-a-uiimage-cocoa-touch-or-cgimage-core-graphics

Tags: , ,

Leave a Reply