CCPanZoomController + Tappable Sprites - objective-c

I am creating a zoomable and pan-able map for my game (using CCPanZoomController) . Within this map I would like to have a tappable sprite, and when it is tapped, "do something"…
I can get the two things to work perfectly in separate projects, however when I try to combine them, nothing happens when I tap my sprite.
I have included an image to demonstrate further:
//in my init section
self.isTouchEnabled = YES;
mapBase = [CCSprite spriteWithFile:#"MapBase.png"];
mapBase.anchorPoint = ccp(0, 0);
[self addChild:mapBase z:-10];
gym = [CCSprite spriteWithFile:#"Gym.png"];
gym.scale = 0.3;
gym.position = ccp(1620, 250);
[self addChild:gym z:1];
CGRect boundingRect = CGRectMake(0, 0, 2499, 1753);
_controller = [[CCPanZoomController controllerWithNode:self] retain];
_controller.boundingRect = boundingRect;
_controller.zoomOutLimit = _controller.optimalZoomOutLimit;
_controller.zoomInLimit = 2.0f;
[_controller enableWithTouchPriority:1 swallowsTouches:YES];
//end of init
-(void) registerWithTouchDispatcher
{
[[[CCDirector sharedDirector] touchDispatcher] addTargetedDelegate:self
priority:0 swallowsTouches:NO];
}
-(BOOL) ccTouchBegan:(UITouch *)touch withEvent:(UIEvent *)event{
CGPoint touchPoint1 = [touch locationInView:[touch view]];
if (CGRectContainsPoint(gym.boundingBox, touchPoint1)) return YES;
return NO;
}
-(void) ccTouchEnded:(UITouch *)touch withEvent:(UIEvent *)event{
CGPoint touchPoint2 = [touch locationInView:[touch view]];
if (CGRectContainsPoint(gym.boundingBox, touchPoint2)){
CCLOG(#"SPRITE HAS BEEN TAPPED");
}
}
I want to beable to zoom in/out and pan the wholemap (including the ‘Gym’ sprite).
And if the sprite 'Gym' is tapped by the user, then i would like to “do something”.
If anyone can figure this out, I would be extremely grateful!
Thanks.

Related

SK Scene transition background

I've just started to learn how to program with xCode and more specifically with the new SpriteKit. So bare in mind I'm kind of new to this.
I've managed to move to a different scene by pressing on a label, but somehow the background of my scene has gone all wrong. the background should be like the first scene, but it gave me a background with two horizontal bars. I don't think I've done anything wrong since I've just copied the code needed to set the background to the second scene.
1st scene:
http://imageshack.us/photo/my-images/23/kyud.png/
2nd scene:
http://imageshack.us/photo/my-images/198/472h.png/
my code used to set up both backgrounds:
SKSpriteNode *background = [SKSpriteNode spriteNodeWithImageNamed:#"background_start"];
background.position = CGPointMake(CGRectGetMidX(self.frame), CGRectGetMidY(self.frame));
background.xScale = 0.7;
background.yScale = 0.7;
[self addChild:background];
transition is done with:
-(void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event {
/* Called when a touch begins */
UITouch *touch = [touches anyObject];
CGPoint location = [touch locationInNode:self];
SKNode *node = [self nodeAtPoint:location];
if([node.name isEqualToString:#"startgame"]){
Options_Scene *gs = [[Options_Scene alloc] initWithSize:self.size];
SKTransition *doors = [SKTransition doorsOpenVerticalWithDuration:0.5];
[self.view presentScene:gs transition:doors];
}
}
PS: I find it odd why I have to scale it to 0.7 when the background actually has the dimensions 480x320.
As you are using landscape I would swap the
viewWillAppear
With
- (void)viewWillLayoutSubviews
{
[super viewWillLayoutSubviews];
SKView * skView = (SKView *)self.view;
if (!skView.scene) {
//skView.showsFPS = YES;
//skView.showsNodeCount = YES;
// Create and configure the scene.
SKScene * scene = [PMyScene sceneWithSize:skView.bounds.size];
scene.scaleMode = SKSceneScaleModeAspectFill;
// Present the scene.
[skView presentScene:scene];
}
}

Scrolling with two finger gesture

I have a drawing view on a UIScrollView.
What I want to do is draw lines with one finger, and scrolling with two fingers.
The drawing view is to draw lines through touchesMoved as below.
- (void) touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event
{
UITouch *touch;
CGPoint lastTouch, currentTouch;
for (touch in touches)
{
lastTouch = [touch previousLocationInView:self];
currentTouch = [touch locationInView:self];
CGContextRef ctx = CGLayerGetContext(drawLayer);
CGContextBeginPath(ctx);
CGContextMoveToPoint(ctx, lastTouch.x, lastTouch.y);
CGContextAddLineToPoint(ctx, currentTouch.x, currentTouch.y);
CGContextStrokePath(ctx);
}
[self setNeedsDisplay];
}
- (void) touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event
{
CGContextDrawLayerInRect(drawContext, self.bounds, drawLayer);
CGContextClearRect(CGLayerGetContext(drawLayer), self.bounds);
[self setNeedsDisplay];
}
and on a viewController,
_scrollView = [[UIScrollView alloc] initWithFrame:CGRectMake(0, 0, 320, 480)];
[_scrollView setContentSize:CGSizeMake(320, 800)];
[self.view addSubview:_scrollView];
_drawingView = [[DrawingView alloc] initWithFrame:CGRectMake(0, 0, 320, 800)];
[_scrollView addSubview:_drawingView];
for (UIGestureRecognizer *gestureRecognizer in _scrollView.gestureRecognizers)
{
if ([gestureRecognizer isKindOfClass:[UIPanGestureRecognizer class]])
{
UIPanGestureRecognizer *panGR = (UIPanGestureRecognizer *) gestureRecognizer;
panGR.minimumNumberOfTouches = 2;
}
}
It works ok on simulator however the drawing is too slow on a real device. What is wrong and any suggestion?
Ty!
I solved.
Shouldn't draw whole screen with [self setNeedsDisplay]. Should draw a area where need to redraw with [self setNeedsDisplay withRect:]
Better use panGesture recogniger than touchesBegin~End. There's delay between touchesBegin and touchesEnd.

Adding and touching multiple sprites in cocos2d

I have a class where I add multiple sprites as shown in the code below:
CCSprite *b = [CCSprite spriteWithFile:#"b"];
b.position = ccp(100, 160);
CCSprite *b2 = [CCSprite spriteWithFile:#"b2.png"];
b2.position = ccp(115, 150);
CCSprite *b3 = [CCSprite spriteWithFile:#"b3.png"];
b.position = ccp(200, 150);
CCSprite *b4 = [CCSprite spriteWithFile:#"b4.png"];
b4.position = ccp(220, 145);
b.anchorPoint = ccp(0.98, 0.05);
b2.anchorPoint = ccp(0.03, 0.05);
b3.anchorPoint = ccp(0.03, 0.05);
b4.anchorPoint = ccp(0.95, 0.05);
[self addChild:b z:1 tag:1];
[self addChild:b2 z:1 tag:2];
[self addChild:b3 z:1 tag:3];
[self addChild:b4 z:1 tag:4];
Here's the code for the touch event:
-(void)ccTouchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
{
NSSet *allTouch = [event allTouches];
UITouch *touch = [[allTouch allObjects]objectAtIndex:0];
CGPoint location = [touch locationInView:[touch view]];
location = [[CCDirector sharedDirector]convertToGL:location];
//Swipe Detection - Beginning point
beginTouch = location;
for(int i = 0; i < [hairArray count]; i++)
{
CCSprite *sprite = (CCSprite *)[hairArray objectAtIndex:i];
if(CGRectContainsPoint([sprite boundingBox], location))
{
//selectedSprite is a sprite declared on the header file
selectedSprite = sprite;
}
}}
-(void)ccTouchesMoved:(NSSet *)touches withEvent:(UIEvent *)event
{
//Move touched sprite
NSSet *allTouch = [event allTouches];
UITouch *touch = [[allTouch allObjects]objectAtIndex:0];
CGPoint location = [touch locationInView:[touch view]];
location = [[CCDirector sharedDirector]convertToGL:location];
if(selectedSprite != nil)
{
selectedSprite.position = ccp(location.x, location.y);
}}
-(void) ccTouchesEnded:(NSSet *)touches withEvent:(UIEvent *)event
{
//End point of sprite after dragged
NSSet *allTouch = [event allTouches];
UITouch *touch = [[allTouch allObjects]objectAtIndex:0];
CGPoint location = [touch locationInView:[touch view]];
location = [[CCDirector sharedDirector]convertToGL:location];
endTouch = location;
posX = endTouch.x;
//Minimum swipe length
posY = ccpDistance(beginTouch, endTouch);
[self moveSprite];}
Now, the actions itself work just fine but the trouble I'm having is that if I want to drag b2, I have to drag b3 and b4 first. I'm not sure if it has anything to do with the z-index or it is because of the transparent areas that is present for each sprite. Is there something I'm missing here?
if(CGRectContainsPoint([sprite boundingBox], location))
{
//selectedSprite is a sprite declared on the header file
selectedSprite = sprite;
}
This code updates the currently selected sprite as soon as a new one is found while looping on all sprites. This means that if 3 sprites overlap you will get that the selected one is the last one in the array of nodes of the parent.
You can't make any assumptions on the orders so this is not clearly what you want, you have to decide a policy to give sprites priority. Mind that editing the anchorPoint may alter the position of the sprite compared to the bounding box (so that the bounding box is even outside the sprite).
To be sure of it you should enable:
#define CC_SPRITE_DEBUG_DRAW 1
in ccConfig.h. This will render bounding boxes around sprites.

How to get a sprite rect in a convenient way?

In cocos2d game development, CGRectContainsPoint method often used to detect if touch on a CCSprite.
I use code fllow to get a sprite's (which in a CCNode) rect property
- (void)ccTouchEnded:(UITouch *)touch withEvent:(UIEvent *)event {
CCLOG(#"ccTouchEnded");
CGPoint location = [touch locationInView:[touch view]];
location = [[CCDirector sharedDirector] convertToGL:location];
CCLOG(#"location.x:%f, y:%f", location.x, location.y);
CGRect rect;
rect = CGRectMake(self.firstCard.face.position.x-(self.firstCard.face.contentSize.width/2), self.firstCard.face.position.y-(self.firstCard.face.contentSize.height/2),
self.firstCard.face.contentSize.width, self.firstCard.face.contentSize.height);
if (CGRectContainsPoint(rect, location)) {
CCLOG(#"first card touched");
[firstCard open];
}
rect = CGRectMake(self.secondCard.face.position.x-(self.secondCard.face.contentSize.width/2), self.secondCard.face.position.y-(self.secondCard.face.contentSize.height/2),
self.secondCard.face.contentSize.width, self.secondCard.face.contentSize.height);
if (CGRectContainsPoint(rect, location)) {
CCLOG(#"second card touched");
[secondCard open];
}
}
I want to know if there is a convenient way to get a CCSprite 's rect straightforward?
Please use boundingBox i think it will be a great option to use.
Like this:
- ( void ) ccTouchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
{
UITouch *touch = [touches anyObject];
locationTouchBegan = [touch locationInView: [touch view]];
//location is The Point Where The User Touched
locationTouchBegan = [[CCDirector sharedDirector] convertToGL:locationTouchBegan];
//Detect the Touch On sprite
if(CGRectContainsPoint([sprite boundingBox], locationTouchBegan))
{
isSpriteTouched=YES;
}
}
Kobold2D has a convenience method containsPoint as a CCNode extension (Objective-C category) which you can replicate in your project:
-(BOOL) containsPoint:(CGPoint)point
{
CGRect bbox = CGRectMake(0, 0, contentSize_.width, contentSize_.height);
CGPoint locationInNodeSpace = [self convertToNodeSpace:point];
return CGRectContainsPoint(bbox, locationInNodeSpace);
}
Your code then be simplified to this and it will work with rotated and/or scaled sprites as well (the boundingBox method fails to test rotated and scaled sprites correctly).
if ([firstCard.face containsPoint:location]) {
CCLOG(#"first card touched");
}

cocos2d CCSprite not getting coordinates

SOLVED!
to get coordinates of the sprites frame use sprite.boundingBox.origin.x;
Hello!I am implementing a simple code but I cannot understand its behavior:
anewSprite = [CCSprite spriteWithFile:#"grossini.png"];
anewSprite.position = ccp(80, 80);
[self addChild:anewSprite];
anotherSprite = [CCSprite spriteWithFile:#"grossini.png"];
anotherSprite.position = ccp(300, 80);
[self addChild:anotherSprite];
-(BOOL)ccTouchBegan:(UITouch *)touch withEvent:(UIEvent *)event{
CGPoint point= [touch locationInView:[touch view]];
point = [[CCDirector sharedDirector] convertToGL: point];
NSLog(#"point x:%f y:%f", point.x, point.y);
if (CGRectContainsPoint(anewSprite.textureRect, point)){
NSLog(#"contains point");
} else {
NSLog(#"does not contain");
}
return TRUE;
}
-(void)ccTouchMoved:(UITouch *)touch withEvent:(UIEvent *)event{
CGPoint point= [touch locationInView:[touch view]];
point = [[CCDirector sharedDirector] convertToGL: point];
anewSprite.position = point;
if (CGRectContainsRect(anewSprite.textureRect, anotherSprite.textureRect) == TRUE) {
NSLog(#"Intersects");
}
}
The problem is following:
NSLog(#"anotherSpriteTextureRectOrigin X:%f Y:%f", enemy1.textureRect.origin.x, enemy1.textureRect.origin.y);
NSLog(#"anewSpriteTextureRectOrigin X:%f Y:%f", anewSprite.textureRect.origin.x, anewSprite.textureRect.origin.y);
shows:
anotherSpriteTextureRectOrigin X:0.000000 Y:0.000000
anewSpriteTextureRectOrigin X:0.000000 Y:0.000000
Thanks in advance!
To check, whether one sprite intersects/contains another, instead of sprite.frame.rect use sprite.boundingBox
Eg:
if (CGRectContainsRect(sprite1.boundingBox, sprite2.boundingBox)) {
NSLog(#"Contains");
}