CCSprite
Makes My Life EasierWhile developing a couple of games that I am currently working on I ended up
finding a need to have a CCSprite
that would trigger a particular selector.
I wanted something lightweight that I could just throw into anything. So I went
about writing a quick subclass of CCSprite
that at first I thought would be a
bit of a throw away one time use thing. It turned out however to be simple and
reusable and I find myself reusing the class all over the place, enter
ClickableSprite
.
The concept is dead simple create a subclass of CCSprite
adhering to
CCTargetedTouchDelegate
, and give it a couple of properties that will store a
target and selector. Here's the class definition, don't forget to synthesize
those properties though!
@interface ClickableSprite : CCSprite <CCTargetedTouchDelegate>
@property (nonatomic, assign) id<NSObject> target;
@property (nonatomic) SEL selector;
@end
In the implementation file we’ll implement a function
(containsTouchLocation:
) that will test whether or not a touch is contained
by the sprite. This method was lifted from some place or another, I don't
really remember.
- (BOOL)containsTouchLocation:(UITouch *)touch {
CGPoint p = [self convertTouchToNodeSpaceAR:touch];
CGSize size = self.contentSize;
CGRect r = CGRectMake(-size.width*0.5,
-size.height*0.5,
size.width,
size.height);
return CGRectContainsPoint(r, p);
}
Next we want to implement ccTouchBegan:withEvent
and ccTouchEnded:withEvent
such that when a user touches the sprite and then lifts their finger without
moving it off of the sprite it will fire it's selector.
- (BOOL)ccTouchBegan:(UITouch *)touch withEvent:(UIEvent *)event {
return [self containsTouchLocation:touch];
}
- (void)ccTouchEnded:(UITouch *)touch withEvent:(UIEvent *)event {
if(![self containsTouchLocation:touch])
return;
[self.target performSelector:self.selector withObject:self];
}
Finally we will want to add the sprite as a targeted delegate to the
CCTouchDispatcher
singleton, we'll just do this in the onEnter
. And of
course we'll want to remove the sprite from the delegate list when it goes away
(otherwise we'll get EXC_BAD_ACCESS
when the CCTouchDispatcher
tries to get
at it).
- (void)onEnter {
[super onEnter];
[[CCTouchDispatcher sharedDispatcher] addTargetedDelegate:self
priority:0
swallowsTouches:YES];
}
- (void)onExit {
[[CCTouchDispatcher sharedDispatcher] removeDelegate:self];
[super onExit];
}
That's great, it works pretty well, but sometimes you want to be able to press
a sprite that is kind of small, let's just add another property clickableSize
and synthesize it.
@property (nonatomic) CGSize clickableSize;
Then we can modify our containsTouchLocation
function to check for this like
so…
- (BOOL)containsTouchLocation:(UITouch *)touch {
CGPoint p = [self convertTouchToNodeSpaceAR:touch];
CGSize size = self.contentSize;
if(!CGSizeEqualToSize(self.clickableSize, CGSizeZero))
size = self.clickableSize;
CGRect r = CGRectMake(-size.width*0.5,
-size.height*0.5,
size.width,
size.height);
return CGRectContainsPoint(r, p);
}
Oh, and one more thing you might like is to be able to set the priority.
CCTouchDispatcher
will go through all of its delegates in order of priority,
lower being called first. I haven't found that I need to set the priority after
it has been added to the scene so I haven't bothered to implement that
functionality. At any rate we’ll add another property touchPriority
@property (nonatomic) int touchPriority;
And then simply modify our onEnter
function
- (void)onEnter {
[super onEnter];
[[CCTouchDispatcher sharedDispatcher] addTargetedDelegate:self
priority:self.priority
swallowsTouches:YES];
}
To use the sprite you initialize it as you would a normal CCSprite
, set the
relevant properties, and add to a node like any other sprite as well.
ClickableSprite *sprite = [ClickableSprite spriteWithFile:@"myAwesomeSprite.png"];
sprite.target = self;
sprite.selector = @selector(myAwesomeSelector);
// Or with the sprite as an argument sprite.selector = @selector(myAwesomeSelector:);
// which would call a function that would look like
// - (void)myAwesomeSelector:(ClickableSprite *)clickableSprite
sprite.clickableSize = CGSizeMake(100,100);
sprite.touchPriority = 2;
Now we have a nice simple class that we can reuse over and over again.