PDA

View Full Version : Optimize My Touch

neil.b
Dec 18, 2008, 04:01 AM
In my app view I'm detecting if the user has touch within one of 16 defined rectangles;

-(int) whichBoxIsTouched:(CGPoint)theTouch
{

NSArray *hitBoxArray = [NSArray arrayWithObjects:
@"{{64,404},{48,48}}",
@"{{112,404},{48,48}}",
@"{{160,404},{48,48}}",
@"{{208,404},{48,48}}",
@"{{64,356},{48,48}}",
@"{{112,356},{48,48}}",
@"{{160,356},{48,48}}",
@"{{208,356},{48,48}}",
@"{{64,308},{48,48}}",
@"{{112,308},{48,48}}",
@"{{160,308},{48,48}}",
@"{{208,308},{48,48}}",
@"{{64,260},{48,48}}",
@"{{112,260},{48,48}}",
@"{{160,260},{48,48}}",
@"{{208,260},{48,48}}",
nil];

CGRect theHitRect;
for (int i = 0; i < [hitBoxArray count]; i++)
{
theHitRect = CGRectFromString([hitBoxArray objectAtIndex:i]);
if (CGRectContainsPoint(theHitRect, theTouch))
{
return i;
}
}
return [hitBoxArray count];
}

I've been trying to look at ways to optimize the hit test. I thought of putting the CGRects in an array so that I don't have to do the CGRectFromString conversion in the loop but I can't figure out how to store the rect information in an array.

Can anyone offer a different solution that would be faster than my current method?

As you can see the hit rectangles are in fact all squares of the same size so maybe that fact can be used.

Thanks

ghayenga
Dec 18, 2008, 04:23 PM
In my app view I'm detecting if the user has touch within one of 16 defined rectangles;

-(int) whichBoxIsTouched:(CGPoint)theTouch
{

NSArray *hitBoxArray = [NSArray arrayWithObjects:
@"{{64,404},{48,48}}",
@"{{112,404},{48,48}}",
@"{{160,404},{48,48}}",
@"{{208,404},{48,48}}",
@"{{64,356},{48,48}}",
@"{{112,356},{48,48}}",
@"{{160,356},{48,48}}",
@"{{208,356},{48,48}}",
@"{{64,308},{48,48}}",
@"{{112,308},{48,48}}",
@"{{160,308},{48,48}}",
@"{{208,308},{48,48}}",
@"{{64,260},{48,48}}",
@"{{112,260},{48,48}}",
@"{{160,260},{48,48}}",
@"{{208,260},{48,48}}",
nil];

CGRect theHitRect;
for (int i = 0; i < [hitBoxArray count]; i++)
{
theHitRect = CGRectFromString([hitBoxArray objectAtIndex:i]);
if (CGRectContainsPoint(theHitRect, theTouch))
{
return i;
}
}
return [hitBoxArray count];
}

I've been trying to look at ways to optimize the hit test. I thought of putting the CGRects in an array so that I don't have to do the CGRectFromString conversion in the loop but I can't figure out how to store the rect information in an array.

Can anyone offer a different solution that would be faster than my current method?

As you can see the hit rectangles are in fact all squares of the same size so maybe that fact can be used.

Thanks

Since your hitBoxArray never changes why wouldn't you make that an instance variable and initialize it to be an array of the CGRects that you want instead of re-initializing the that array on every touch and then then converting every string to a CGRect on the fly?

Niiro13
Dec 18, 2008, 07:22 PM
What he said...if you're still wondering how that would look, here's the code.

At the top of the class, under the import line and above the implementation, put:
static NSArray *hitBoxArray = nil;

Now in the init method (or awakefromnib, viewwillappear, etc.) put:

hitBoxArray = [[NSArray init] initWithObjects:
CGRectMake(64,404,48,48),
CGRectMake(112,404,48,48),
CGRectMake(160,404,48,48),
CGRectMake(208,404,48,48),
CGRectMake(64,356,48,48),
CGRectMake(112,356,48,48),
CGRectMake(160,356,48,48),
CGRectMake(208,356,48,48),
CGRectMake(64,308,48,48),
CGRectMake(112,308,48,48),
CGRectMake(160,308,48,48),
CGRectMake(208,308,48,48),
CGRectMake(64,260,48,48),
CGRectMake(112,260,48,48),
CGRectMake(160,260,48,48),
CGRectMake(208,260,48,48),
nil]];

Lastly, change what you gave us to:

-(int) whichBoxIsTouched:(CGPoint)theTouch
{
for (int i = 0; i < [hitBoxArray count]; i++)
{
if (CGRectContainsPoint([hitBoxArray objectAtIndex:i], theTouch))
{
return i;
}
}
return [hitBoxArray count];
}

Basically, what he told you, only here's the code for it.

neil.b
Dec 19, 2008, 08:44 AM
Thanks for the tips guys. :)

I thought you couldn't put CGRects in an array - I'd tried a few ways without much success. I'll give your suggestion a try.

neil.b
Dec 19, 2008, 08:58 AM
That code throws up two errors.

Firstly on the arrray init;

error: incompatible type for argument 1 of 'initWithObjects:'

And then in the for...loop;

error: incompatible type for argument 1 of 'initWithObjects:'

Any ideas?

neil.b
Dec 19, 2008, 10:12 AM
Sorted it. I created a NSObject class "HitBoxRect" that has a CGRect as a property;

Interface:

@interface HitBoxRect : NSObject {
CGRect rect;
}

-(id) initWithCGRect:(CGRect)aRect;
@property (nonatomic) CGRect rect;

@end

Implementation:

#import "HitBoxRect.h"

@implementation HitBoxRect
@synthesize rect;

-(id) initWithCGRect:(CGRect)aRect
{
rect = aRect;
return self;
}

@end

Then created a NSMutableArray "hitBoxArray" and populated it;

-(void) initHitBoxArray
{
hitBoxArray = [[NSMutableArray alloc] init];
NSArray *boxes = [NSArray arrayWithObjects:
@"{{64,404},{48,48}}",
@"{{112,404},{48,48}}",
@"{{160,404},{48,48}}",
@"{{208,404},{48,48}}",
@"{{64,356},{48,48}}",
@"{{112,356},{48,48}}",
@"{{160,356},{48,48}}",
@"{{208,356},{48,48}}",
@"{{64,308},{48,48}}",
@"{{112,308},{48,48}}",
@"{{160,308},{48,48}}",
@"{{208,308},{48,48}}",
@"{{64,260},{48,48}}",
@"{{112,260},{48,48}}",
@"{{160,260},{48,48}}",
@"{{208,260},{48,48}}",
nil];

CGRect aRect;
HitBoxRect *hRect;
for (int i = 0; i < [boxes count]; i++)
{
aRect = CGRectFromString([boxes objectAtIndex:i]);
hRect = [[HitBoxRect alloc] initWithCGRect:aRect];
[hRect release];
}
}

So I can then access the custom objects with;

-(int) whichBoxIsTouched:(CGPoint)theTouch
{
for (int i = 0; i < [hitBoxArray count]; i++)
{
if (CGRectContainsPoint([[hitBoxArray objectAtIndex:i] rect], theTouch))
{
return i;
}
}