Adding and touching multiple sprites in cocos2d - objective-c

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.

Related

CCPanZoomController + Tappable Sprites

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.

Getting location of sprite within array cocos2d`

I need to be able to touch a specific moving sprite in my array and perform an action on it. However when I perform my MoveTo action, the sprite location doesn't update. Help!
Array:
int numbreds = 7;
redBirds = [[CCArray alloc] initWithCapacity: numbreds];
for( int i = 1; i<=numbreds; i++){
int xvalue = ((-50*i) + 320);
int yvalue= 160;
if (i==4)
{
CCSprite *parrot = [CCSprite spriteWithFile:#"taco.png"];
[birdLayer addChild:parrot];
[self movement]; //the action that moves the array horizontally
parrot.position = ccp(xvalue,yvalue);
parrot.tag=100;
Touch
-(void)ccTouchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
{
UITouch *touch = [touches anyObject];
CGPoint location = [touch locationInView:[touch view]];
location = [[CCDirector sharedDirector] convertToGL:location];
CCSprite *mark = (CCSprite *)[birdLayer getChildByTag:100];
if (CGRectContainsPoint([mark boundingBox], location))
{
CCLOG(#"YAY!");
}
THe problem is that the location of the CCSprite doesn't actually update or move. YAY! only is generated at the origin location of the sprite.
Try this:
CCSprite *temp = [CCSprite spriteWithFile:#"taco.png"];
temp = [birdLayer getChildByTag:100];
if (temp.position.x == location.x) {
// do stuff...
}

cocso2d pinch-zoom like Fieldrunners

I've been trying to set up the parallax node correctly but need some help with this.
This http://goo.gl/Piqy5 would be the frame of the game and for the parallax node area I have 3 layers:
background layer (zoomable but not scrollable, z order -1)
layer 1 (z order 1)
layer 2 (z order 2)
//Adding the layers to the parallax node
CGPoint offsetLayer = ccp(0,0);
//background layer
[parallaxNode addChild:backgroundLayer z:-1 parallaxRatio: ccp(0,0) positionOffset: offsetLayer];
//layer 1
[parallaxNode addChild:secondParallaxLayer z:1 parallaxRatio: ccp(0.5,0) positionOffset: offsetLayer];
//layer 2
[parallaxNode addChild:firstParallaxLayer z:2 parallaxRatio: ccp(1.1,0) positionOffset: offsetLayer];
//the pan/zoom & scroll controller
_controller = [[CCPanZoomController controllerWithNode:baseLayer] retain];
_controller.boundingRect = boundingRect;
_controller.zoomOutLimit = _controller.optimalZoomOutLimit;
_controller.zoomInLimit = 2.0f;
[_controller centerOnPoint:CGPointMake(screenSize.width/2.0, screenSize.height/2.0)];
[_controller enableWithTouchPriority:-2 swallowsTouches:YES];
I think I should fix using:
//Setting the touch delegate to my CCScene
#interface GameScene : CCScene <CCStandardTouchDelegate>
//and add register to touch delegate
[[CCTouchDispatcher sharedDispatcher] addStandardDelegate:self priority:2];
- (CGPoint)convertPoint:(CGPoint)point fromNode:(CCNode *)node {
return [self convertToNodeSpace:[node convertToWorldSpace:point]];
}
- (void)ccTouchesMoved:(NSSet *)touches withEvent:(UIEvent *)event {
#define CLAMP(x,y,z) MIN(MAX(x,y),z)
if ([[[event allTouches] allObjects] count] == 2) {
UITouch* touch1 = [[[event allTouches] allObjects] objectAtIndex:0];
UITouch* touch2 = [[[event allTouches] allObjects] objectAtIndex:1];
// calculate scale value
double prevDistance = ccpDistance([touch1 previousLocationInView:[touch1 view]], [touch2 previousLocationInView:[touch2 view]]);
double newDistance = ccpDistance([touch1 locationInView:[touch1 view]], [touch2 locationInView:[touch2 view]]);
CGFloat relation = newDistance / prevDistance;
CGFloat newScale = self.scale * relation;
if ((newScale >= MIN_SCALE) && (newScale <= MAX_SCALE)) {
CGPoint touch1Location = [baseLayer convertTouchToNodeSpace:touch1];
CGPoint touch2Location = [baseLayer convertTouchToNodeSpace:touch2];
// calculate center point between two touches
CGPoint centerPoint = ccpMidpoint(touch1Location, touch2Location);
// store center point location (ScrollableView space)
CGPoint centerPointInParentNodeSpace = [self convertPoint:centerPoint fromNode:baseLayer];
CGPoint oldPoint = ccp(centerPointInParentNodeSpace.x * (self.scale), centerPointInParentNodeSpace.y * (self.scale));
self.scale = newScale;
CGPoint newPoint = ccp(centerPointInParentNodeSpace.x * (self.scale), centerPointInParentNodeSpace.y * (self.scale));
CGPoint diff = ccp(oldPoint.x - newPoint.x , oldPoint.y - newPoint.y);
[baseLayer setPosition:ccp(baseLayer.position.x + (diff.x*(1/self.scale)), baseLayer.position.y + (diff.y*(1/self.scale)))];
}
} else if ([[[event allTouches] allObjects] count] == 1) {
// get touch locations
UITouch *touch = [[event allTouches] anyObject];
CGPoint touchPosition = [[CCDirector sharedDirector] convertToGL:[touch locationInView:[touch view]]];
CGPoint oldPosition = [[CCDirector sharedDirector] convertToGL:[touch previousLocationInView:[touch view]]];
// calculate difference in position
CGPoint diff = ccpSub(touchPosition, oldPosition);
self.position = ccpAdd(self.position, diff);
}
#undef CLAMP
}
Any remarks or help would be great! :)
Check out CCLayerPanZoom in the Cocos2d Extensions on Github. I have used this class to enable pinch zoom and pan in my game

CGRectContainsPoint problems

In my test game i have some sprites (Bubbles = NSMutableArray) wich are appear in random location at bottom of the screen.
I have addBubble and spawBubble methods:
- (void) addBubble {
CGSize winSize = [[CCDirector sharedDirector] winSize];
bubbles = [[NSMutableArray alloc] init];
[[CCSpriteFrameCache sharedSpriteFrameCache] addSpriteFramesWithFile:#"bubbleSpriteList.plist"];
CCSpriteBatchNode *bubbleSpriteList = [CCSpriteBatchNode batchNodeWithFile:#"bubbleSpriteList.png"];
[self addChild:bubbleSpriteList];
bigBubble = [CCSprite spriteWithSpriteFrameName:#"bubble"];
[self addChild:bigBubble];
[bubbles addObject:bigBubble];
for (CCSprite *bubble in bubbles) {
int minX = bubble.contentSize.width/2;
int maxX = winSize.width-bubble.contentSize.width/2;
int rangeX = maxX - minX;
int actualX = (arc4random() % rangeX) + minX;
bubble.position = ccp(actualX, 0);
int minSpeed = 15.0;
int maxSpeed = 20.0;
int rangeSpeed = maxSpeed - minSpeed;
int actualSpeed = (arc4random() % rangeSpeed) + minSpeed;
ccBezierConfig bubblePath;
bubblePath.controlPoint_1 = ccp(200, winSize.height/3);
bubblePath.controlPoint_2 = ccp(-200, winSize.height/1.5);
bubblePath.endPosition = ccp(0, winSize.height+bubble.contentSize.height/2);
id bezierMove = [CCBezierBy actionWithDuration:actualSpeed bezier:bubblePath];
[bubble runAction:bezierMove];
}}
-(void)spawBubble:(ccTime)dt {
[self addBubble];}
Then in my init method i added background and spawBubble method with random time interval
[self schedule:#selector(spawBubble:) interval:actualTime];
I'm trying to make every bubble from Bubbles blow, when it was touched, with this code
- (BOOL)ccTouchBegan:(UITouch *)touch withEvent:(UIEvent *)event {
CGPoint touchLocation = [touch locationInView:[touch view]];
touchLocation = [[CCDirector sharedDirector] convertToGL:touchLocation];
touchLocation = [self convertToNodeSpace:touchLocation];
- (BOOL)ccTouchBegan:(UITouch *)touch withEvent:(UIEvent *)event {
CGPoint touchLocation = [touch locationInView:[touch view]];
touchLocation = [[CCDirector sharedDirector] convertToGL:touchLocation];
touchLocation = [self convertToNodeSpace:touchLocation];
for (CCSprite *bubble in bubbles) {
CGRect bubbleRect = CGRectMake(bubble.position.x - (bubble.contentSize.width/2),
bubble.position.y - (bubble.contentSize.height/2),
bubble.contentSize.width,
bubble.contentSize.height);
if (CGRectContainsPoint(bubbleRect, touchLocation)) {
NSLog(#"%i", [bubbles count]);
[bubble setDisplayFrame:[[CCSpriteFrameCache sharedSpriteFrameCache] spriteFrameByName:#"bubbleBlow"]];
id disappear = [CCFadeTo actionWithDuration:0.1 opacity:0];
[bubble runAction:disappear];
}
}
return TRUE;}
Every bubbles blowing perfectly if only one bubble in the screen, but if one bubble on the screen and another one was appeared, only last one is detects touches.
What am i doing wrong?
Hard to tell without seeing more code - but here's where I'd start:
Above this line:
for (CCSprite *bubble in bubbles) {
add:
NSLog(#"%i", [bubbles count]);
EDIT:
Based on the code you added:
Your problem is with this line:
bubbles = [[NSMutableArray alloc] init];
which is effectively wiping out your bubbles array every time you add a new bubble.
So, there will only ever be one bubble in the array - the last one.
Hence the problem you're running into.
I changed addBubble methods by removing bubbles = [[NSMutableArray alloc] init];, and adding it to init method...
Now everything works perfectly! )

Determine overlapping UIImageViews and exchange their location

I have around 16 UIImage views, can be moved by touch. I can drop over UIImage over another UIImage and then the UIImages would exchange their position. Being newcomer to ObjectiveC programming, I am struggling to figure out the UIImageViews and exchange their positons. Till now I am trying to implement in touchesEnded method
-(void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event{
UITouch *touch = [touches anyObject];
[self.superview exchangeSubviewAtIndex:[[self.superview subviews] indexOfObject:self] withSubviewAtIndex:[[self.superview subviews] indexOfObject:[touch view]]];
}
But this does not work. Any help would be appreciated.
UIImages are created by following code
TouchImageView *touchImageView = [[TouchImageView alloc]initWithImage:displayImage];
touchImageView.identy = [NSString stringWithFormat:#"Image ID %d",i];
So each touch image is associated some string to describe itself.
In touch end, I have just added following code to know ID of image views
UITouch *touch = [touches anyObject];
TouchImageView *dragImage = (TouchImageView*)[touch view];
NSLog(#"Ended %a%a",[dragImage identy],[self identy]);
But the o/p I got is totally different
2011-11-21 11:50:34.404 OrganizeMe[882:f803] Ended 0x1.6d96006a6d96p-9170x1.807p-1022
FInal code
-(void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event{
__block int tag = -1;
__block float distance = 100000.0;
[[self.superview subviews] enumerateObjectsUsingBlock:^(UIView *view, NSUInteger index, BOOL *stop) {
BOOL interSect = CGRectIntersectsRect([self frame], [view frame]);
if(interSect && ([self tag]!=[view tag])){
[self.image CGImage];
CGPoint currPoint = [[touches anyObject]locationInView:[self superview]];
CGPoint underPoint = view.center;
if(distance >= [self distanceBetweenPoint1:currPoint Point2:underPoint]){
distance = [self distanceBetweenPoint1:currPoint Point2:underPoint] ;
tag = [view tag];
}
}
}];
NSLog(#"Tag and Distance %d,%f",tag,distance);
TouchImageView* imageView1 = (TouchImageView*)[self.superview viewWithTag:tag];
CGRect point1 = [imageView1 frame];
CGRect point2 = [self frame];
if(tag != -1){
[imageView1 setFrame:point2];
[self setFrame:point1];
}else{
CGPoint lastTouch = [[touches anyObject]previousLocationInView:[self superview]];
self.center = lastTouch;
}
[self.superview setNeedsLayout];
}
The index is just the UIView's position in the subview array. It does not set the location within the superview.
Relayout the superview based on the subview's index.
In your above method, also call setNeedsLayout in order to relayout the superview.
-(void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event
{
UITouch *touch = [touches anyObject];
[self.superview exchangeSubviewAtIndex:[[self.superview subviews] indexOfObject:self] withSubviewAtIndex:[[self.superview subviews] indexOfObject:[touch view]]];
[self.superview setNeedsLayout]; // Should call the method below
}
Implement the following method for the superview:
- (void)layoutSubviews
{
// Iterate over all subviews
[self.subviews enumerateObjectsUsingBlock:^(UIView *view, NSUInteger index, BOOL *stop) {
// This block will be executed for each subview. You have the subview's index.
// Now set the subview's position accordingly to the index.
// I don't know your layout logic, but let's assume you have 14 subviews spread over 4 columns, this will be the subviews' position:
/* 0 1 2 3
4 5 6 7
8 9 10 11
12 13 */
int col = index % 4; // Column from 0 to 3
int row = (int)(index / 4); // Row starting from 0
int w = view.frame.size.width;
int h = view.frame.size.height;
CGPoint p = CGPointMake(col * w + w / 2, row * h + h / 2);
[view setCenter:p];
}];
}
It's been a long time I last used Cocoa touch, so I can't guarantee that the code is working.