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...
}
Related
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.
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
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! )
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.
I have a spear like sprite. It rotation is decided by touchesMoved method. whenever the user slides his finger it points towards that touch. This is my method:
- (void)ccTouchesMoved:(NSSet *)touches withEvent:(UIEvent *)event {
UITouch* touch = [touches anyObject];
CGPoint location = [touch locationInView: [touch view]];
float angleRadians = atanf((float)location.y / (float)location.x);
float angleDegrees = CC_RADIANS_TO_DEGREES(angleRadians);
spear.rotation = -1 * angleDegrees;
}
This kinda works, but only from 0 to 45 degrees. and it goes opposite. So as I am moving from finger for bottom to top, it rotates clockwise (it should follow direction of fnger and rotate counter clockwise). From 45 to 90, it works fine (moves counter clickwise) but only if i start the touch in upper diagonal of the screen.
What am i doing wrong?
Thanks
#define PTM_RATIO 32
- (void)ccTouchesMoved:(NSSet *)touches withEvent:(UIEvent *)event{
for( UITouch *touch in touches ) {
CGPoint location = [touch locationInView: [touch view]];
location = [[CCDirector sharedDirector] convertToGL: location];
b2Vec2 locationWorld = b2Vec2(location.x/PTM_RATIO, location.y/PTM_RATIO);
spriteBody->SetTransform(locationWorld,spriteBody->GetAngle());
}
}
-(void) tick: (ccTime) dt
{
//It is recommended that a fixed time step is used with Box2D for stability
//of the simulation, however, we are using a variable time step here.
//You need to make an informed choice, the following URL is useful
//http://gafferongames.com/game-physics/fix-your-timestep/
int32 velocityIterations = 8;
int32 positionIterations = 1;
// Instruct the world to perform a single step of simulation. It is
// generally best to keep the time step and iterations fixed.
m_world->Step(dt, velocityIterations, positionIterations);
// for (int i = 0; i < (int)birds.size(); i++)
// birds[i]->render();
//Iterate over the bodies in the physics world
for (b2Body* b = m_world->GetBodyList(); b; b = b->GetNext())
{
if (b->GetUserData() != NULL) {
//Synchronize the AtlasSprites position and rotation with the corresponding body
CCSprite *myActor = (CCSprite*)b->GetUserData();
myActor.position = CGPointMake( b->GetPosition().x * PTM_RATIO, b->GetPosition().y * PTM_RATIO);
myActor.rotation = -1 * CC_RADIANS_TO_DEGREES(b->GetAngle());
}
}
}
Figured out what was wrong. I needed to change the CGPoint I got from the touches into GL point like this:
CGPoint location = [touch locationInView: [touch view]];
location = [[CCDirector sharedDirector] convertToGL: location];
Silly me. Should have thought about this before.
CGPoint touchLocation = [touch locationInView: [touch view]];
touchLocation = [[CCDirector sharedDirector] convertToGL:touchLocation];
touchLocation = [self convertToNodeSpace:touchLocation];
GPoint diff = ccpSub(touchLocation, _player.position);
//rotate to face the touch
CGPoint diff = ccpSub(_player.position, touchLocation);
float angleRadians = atanf((float)diff.y / (float)diff.x);
float angleDegrees = CC_RADIANS_TO_DEGREES(angleRadians);
float cocosAngle = -1 * angleDegrees;
if(diff.x < 0)
{
cocosAngle += 180;
}
id actionRotateTo = [CCRotateTo actionWithDuration:0.1 angle:cocosAngle];
[_player runAction:actionRotateTo];