Set SKTexture for SKSpriteNode with a given sprite.name - objective-c

I've been trying to run the SKTexture change from a node on touch, said node is created with a loop:
int nameIndex = 0;
for (int i = 1; i <= 3; i++) {
self.card = [SKSpriteNode spriteNodeWithImageNamed:#"front"];
NSString *nodeName = [NSString stringWithFormat:#"node%d", nameIndex];
self.card.name = nodeName;
self.card.position = CGPointMake((((board-self.card.size.width)/3)*i), y);
[self addChild:self.card];
nameIndex++;
}
In the touches began method I have:
-(void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event{
UITouch *touch = [touches anyObject];
NSArray *nodes = [self nodesAtPoint:[touch locationInNode:self]];
for (SKNode *node in nodes) {
if ([node.name isEqualToString:#"node0"]) {
//Change the texture for the given node:
NSLog(#"Node 0");
[self.card setTexture:[SKTexture textureWithImageNamed:nameIndex]];
}
if ([node.name isEqualToString:#"node1"]) {
NSLog(#"Node 1");
}
if ([node.name isEqualToString:#"node2"]) {
NSLog(#"Node 2");
}
}
The touches began method works just fine, it runs the block as soon as one sprite is touched
however, as expected with the current code the texture change only occurs in the last created sprite. I've been looking for a way to run a method on a sprite with X given name, but I haven't fun a way.
Thanks in advance!

Turns out I needed to use the childNodeWithName instead of trying to force a conditional on the .name property
SKSpriteNode *card = (SKSpriteNode*)[self childNodeWithName:#"node0"];
[card setTexture:[SKTexture textureWithImageNamed:#"aTexture"]];
Not sure if this is a helpful question, mods feel free to erase if needed.

Related

SKSpriteNode Targeting

So RIGHT NOW my code presents 7 playing cards. If the playing card is pressed it moves removes the card from the playerCard array and adds it to the playedCards array. The problem is, is move the card I target it with a SKNode in the touchesBegan method;
-(void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event {
UITouch *touch = [touches anyObject];
CGPoint location = [touch locationInNode:self];
SKNode *node = [self nodeAtPoint:location];
if ([node isKindOfClass:[CardSpriteNode class]]) {
Okay, no problem, the card moves up. The problem is, All the other cards left in the playerCards now need to move i.e ( tighten up ) as they were originally. Here's my formula for distances between them;
int cardsLaid = i * (cardDisplay.size.width / 3);
So I can now get the cards that are originally displayed and the ones that are touched, to form a nice gap between them.
What I need to do is to move the cards back to a nice gap, after one of the cards between them has been moved
for (int i = 0; i < [playerCards count]; i++) {
int cardsLaid = i * (cardDisplay.size.width / 3);
CGPoint forPoint = CGPointMake((0.5 - (self.frame.size.width / 4)) + cardsLaid, 0.0);
playerCards[i] //Here I need to target each skspritenode.name for each playerCards
[??? runAction:[SKAction moveTo:forPoint duration:0.6]];
}
So I need to target each SKSpritenode.name by the names saved in the playerCards MutableArray. Then once I've targetted a spritenode I need to move it where the ??? is.
Here's my entire touchesBegan method if it helps.
-(void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event {
UITouch *touch = [touches anyObject];
CGPoint location = [touch locationInNode:self];
SKNode *node = [self nodeAtPoint:location];
if ([node isKindOfClass:[CardSpriteNode class]]) {
int add = (int)[playerCards count] * (cardDisplay.size.width / 3);
CGPoint origPos = CGPointMake(-self.frame.size.width/2.8 + add, -218);
if ([cardsPlayed containsObject:node.name]) {
//Card is already played, return to original position and remove from array.
CGPoint point = origPos;
[node runAction:[SKAction moveTo:point duration:0.6]];
node.zPosition = zPosCount;
zPosCount += 1;
[cardsPlayed removeObject:node.name];
[playerCards addObject:node.name];
NSLog(#"this ran");
} else {
//Card is not already played, position to add card and add to array.
amountOfCardsLaid = (int)[cardsPlayed count] * (cardDisplay.size.width / 3);
CGPoint point = CGPointMake((0.5 - (self.frame.size.width / 4
)) + amountOfCardsLaid, 0.0);
[node runAction:[SKAction moveTo:point duration:0.6]];
node.zPosition = zPosCount;
zPosCount += 1;
[playerCards removeObject:node.name];
[cardsPlayed addObject:node.name];
for (int i = 0; i < [playerCards count]; i++) {
int cardsLaid = i * (cardDisplay.size.width / 3);
CGPoint forPoint = CGPointMake((0.5 - (self.frame.size.width / 4)) + cardsLaid, 0.0);
playerCards[i]
[??? runAction:[SKAction moveTo:forPoint duration:0.6]];
}
}
//Hide.Unhide buttons
if ([cardsPlayed count] == 0) {
if (addButton.hidden == FALSE) addButton.hidden = true;
if (cancelButton.hidden == FALSE) cancelButton.hidden = true;
} else {
if (addButton.hidden == TRUE) addButton.hidden = false;
if (cancelButton.hidden == TRUE) cancelButton.hidden = false;
}
}
}
The gap I need to close is on the bottom playerCards
SKNode *tempNode = [self childNodeWithName:playerCards[i]];
After a few days. fml

-(void)didBeginContact is not being called. Thoughts?

#import "collisionTestMyScene.h"
const static int nodeBitMask = 0x1 << 0;
const static int node1BitMask = 0x1 << 1;;
#implementation collisionTestMyScene
-(id)initWithSize:(CGSize)size {
if (self = [super initWithSize:size]) {
/* Setup your scene here */
self.physicsWorld.contactDelegate = self;
w = 0;
}
return self;
}
-(void) didBeginContact:(SKPhysicsContact *)contact {
NSLog(#"Contact Begin");
if (contact.bodyA.categoryBitMask == nodeBitMask) {
NSLog(#"Node is Body A");
}
if (contact.bodyA.categoryBitMask == node1BitMask) {
NSLog(#"Node is Body B");
}
}
-(void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event {
/* Called when a touch begins */
for (UITouch *touch in touches) {
CGPoint location = [touch locationInNode:self];
node = [SKSpriteNode spriteNodeWithImageNamed:#"block.jpg"];
node.position = location;
[node setScale:0.07];
node.physicsBody.contactTestBitMask = node1BitMask;
node.physicsBody.categoryBitMask = nodeBitMask;
node.physicsBody.collisionBitMask = nodeBitMask;
//node.physicsBody.collisionBitMask = 0;
node1 = [SKSpriteNode spriteNodeWithImageNamed:#"block2.jpg"];
node1.position = CGPointMake(200, 200);
node1.physicsBody.categoryBitMask = node1BitMask;
node1.physicsBody.contactTestBitMask = nodeBitMask;
node1.physicsBody.collisionBitMask = node1BitMask;
//node1.physicsBody.collisionBitMask = 0;
[node1 setScale:0.07];
[self addChild:node];
[self addChild:node1];
node.physicsBody = [SKPhysicsBody bodyWithRectangleOfSize:CGSizeMake(node.size.width, node.size.height)];
node1.physicsBody = [SKPhysicsBody bodyWithRectangleOfSize:CGSizeMake(node1.size.width, node1.size.height)];
SKAction *moveUp = [SKAction moveToX:100 duration:3];
node1.physicsBody.affectedByGravity = NO;
node.physicsBody.affectedByGravity = NO;
[node1 runAction:moveUp];
w = 1;
}
}
It is never NSLogging anything. I have tried changing the bit masks, and more. The CGRectIntersects function would work, however it is not accurate enough. Also, the two nodes are in perfect box shapes. What could I be doing wrong? Thank you in advance!
The problem here are the bit masks. The two nodes are in different category, contact and collision groups (bit masks). Therefore they will not contact nor collide because the bitmasks are compared with AND and only if the result is non-zero will the contact/collision occur.
In short, put them in the same contact bit mask at the least, in order to receive didBeginContact messages.

multi touch error in cocos2d - index 1 beyond bounds

I am trying to get multi touch working so that I can move two sprites at the same time. I followed this tutorial http://www.saturngod.net/detecting-touch-events-in-cocos2d-iphone-ganbaru-games and this is the code I have in ccTouchesBegan:
-(void) ccTouchesBegan:(NSSet *)touches withEvent:(UIEvent *)event {
NSArray *touchArray = [touches allObjects];
// We're going to track the first two touches (i.e. first two fingers)
// Create "UITouch" objects representing each touch
UITouch *fingerOne = [touchArray objectAtIndex:0];
UITouch *fingerTwo = [touchArray objectAtIndex:1];
// Convert each UITouch object to a CGPoint, which has x/y coordinates we can actually use
CGPoint pointOne = [fingerOne locationInView:[fingerOne view]];
CGPoint pointTwo = [fingerTwo locationInView:[fingerTwo view]];
// The touch points are always in "portrait" coordinates
// You will need to convert them if in landscape (which we are)
pointOne = [[CCDirector sharedDirector] convertToGL:pointOne];
pointTwo = [[CCDirector sharedDirector] convertToGL:pointTwo];
if (CGRectContainsPoint(ball.boundingBox, pointOne))
{
//ball.position = ccp(location.x , location.y);
areWeTouchingABall = YES;
//printf("*** ccTouchesBegan (x:%f, y:%f)\n", location.x, location.y);
}
if(CGRectContainsPoint(sp.boundingBox, pointOne)){
areWeTouchingASquare = YES;
// printf("*** ccTouchesBegan (x:%f, y:%f)\n", location.x, location.y);
}
// Only run the following code if there is more than one touch
if ([touchArray count] > 1)
{
if ( CGRectContainsPoint(ball.boundingBox, pointTwo))
{
//ball.position = ccp(location.x , location.y);
areWeTouchingABall = YES;
//printf("*** ccTouchesBegan (x:%f, y:%f)\n", location.x, location.y);
}
if(CGRectContainsPoint(sp.boundingBox, pointTwo)){
areWeTouchingASquare = YES;
// printf("*** ccTouchesBegan (x:%f, y:%f)\n", location.x, location.y);
}
}
}
this is in touchesMoved:
-(void) ccTouchesMoved:(NSSet *)touches withEvent:(UIEvent *)event {
NSArray *touchArray = [touches allObjects];
// We're going to track the first two touches (i.e. first two fingers)
// Create "UITouch" objects representing each touch
UITouch *fingerOne = [touchArray objectAtIndex:0];
UITouch *fingerTwo = [touchArray objectAtIndex:1];
// Convert each UITouch object to a CGPoint, which has x/y coordinates we can actually use
CGPoint pointOne = [fingerOne locationInView:[fingerOne view]];
CGPoint pointTwo = [fingerTwo locationInView:[fingerTwo view]];
// The touch points are always in "portrait" coordinates
// You will need to convert them if in landscape (which we are)
pointOne = [[CCDirector sharedDirector] convertToGL:pointOne];
pointTwo = [[CCDirector sharedDirector] convertToGL:pointTwo];
if (areWeTouchingABall == YES) //
{
ball.position = ccp(pointOne.x , pointOne.y);
ball.zOrder = 1;
sp.zOrder = 0;
}
if (areWeTouchingASquare == YES) //
{
sp.position = ccp(pointOne.x , pointOne.y);
sp.zOrder = 1;
ball.zOrder = 0;
}
// Only run the following code if there is more than one touch
if ([touchArray count] > 1)
{
/*if (areWeTouchingABall == YES && CGRectContainsPoint(ball.boundingBox, pointOne)) //
{
ball.position = ccp(pointOne.x , pointOne.y);
ball.zOrder = 1;
sp.zOrder = 0;
}*/
if (areWeTouchingABall == YES && CGRectContainsPoint(ball.boundingBox, pointTwo)) //
{
ball.position = ccp(pointTwo.x , pointTwo.y);
ball.zOrder = 1;
sp.zOrder = 0;
}
/*if (areWeTouchingASquare == YES && CGRectContainsPoint(ball.boundingBox, pointOne)) //
{
sp.position = ccp(pointOne.x , pointOne.y);
sp.zOrder = 1;
ball.zOrder = 0;
}*/
if (areWeTouchingASquare == YES && CGRectContainsPoint(ball.boundingBox, pointTwo)) //
{
sp.position = ccp(pointTwo.x , pointTwo.y);
sp.zOrder = 1;
ball.zOrder = 0;
}
}
}
and this is in touchesEnded:
-(void) ccTouchesEnded:(NSSet *)touches withEvent:(UIEvent *)event {
areWeTouchingABall = NO;
areWeTouchingASquare = NO;
//printf("*** ccTouchesEnded (x:%f, y:%f)\n", location.x, location.y);
}
Every time I touch anywhere with one finger, I get this error:
"Terminating app due to uncaught exception 'NSRangeException', reason: '* -[__NSArrayI objectAtIndex:]: index 1 beyond bounds [0 .. 0]'",
When I touch with two fingers, the error does not come up but multi touch does not work correctly ( I cannot drag a sprite with a finger each. The second sprite touched jumps straight to the other finger location so that both sprites are under one finger.)
I made sure to add the code:
[glView setMultipleTouchEnabled:YES];
to my AppDelegate.m file and I have touch enabled in my init method.
How can I fix this issue so that multi-touch works properly and the error is removed?
UITouch *fingerOne = [touchArray objectAtIndex:0];
UITouch *fingerTwo = [touchArray objectAtIndex:1];
There's no guarantee that you'll always receive two touches in a touch event. First test how many touches there are in touchArray using
UITouch *fingerOne = [touchArray objectAtIndex:0];
UITouch *fingerTwo = nil;
if (touchArray.count > 1)
{
fingerTwo = [touchArray objectAtIndex:1];
}
Even then this won't work logically because the second touch in the array may not always correspond to finger #2. I suggest to read up on tracking individual fingers here.

Creating a condition while there are more than 1 targets in a stage

I am trying to develop a game with multiple stages using cocos2d. In a level for example, I have 4 sprites, 2 white and 2 black. If the player hits the black sprites, the game is over, if he hits the white sprite, he wins. How can I implement a condition wherein if the player hits the white sprite, it checks if there are other white sprites present on the scene, if there is, the game continues. If there's none, then he goes to the stage clear scene? I tried putting the sprites in two different arrays (arrayBlack and arrayWhite) but I'm stuck with how I'm gonna make the condition for the white sprites. Can anybody please give me an idea or suggestion or a tutorial that shows a good example for this?
UPDATE:
I kind of figured it out myself. Here's my code:
-(id) init
{
if( (self=[super init]) ) {
CGSize winSize = [[CCDirector sharedDirector] winSize];
self.isTouchEnabled = YES;
//These are declared in the .h class
blackArray = [[NSMutableArray alloc]init];
whiteArray = [[NSMutableArray alloc]init];
black1 = [CCSprite spriteWithFile:#"b1.png"];
black1.position = ccp(100, 160);
black2 = [CCSprite spriteWithFile:#"b2.png"];
black2.position = ccp(105, 150);
white = [CCSprite spriteWithFile:#"w1.png"];
white.position = ccp(150, 150);
white2 = [CCSprite spriteWithFile:#"w2.png"];
white2.position = ccp(80, 160);
[self addChild:black1 z:1 tag:1];
[self addChild:black2 z:1 tag:2];
[self addChild:white z:1 tag:3];
[self addChild:white2 z:1 tag:4];
[blackArray addObject:black1];
[blackArray addObject:black2];
[whiteArray addObject:white];
[whiteArray addObject:white2];
}
return self;}
-(void) ccTouchesEnded:(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];
endTouch = location;
posX = endTouch.x;
//Minimum swipe length
posY = ccpDistance(beginTouch, endTouch);
//selectedSprite is a sprite declared in .h file
if(selectedSprite.tag == 1 || selectedSprite.tag == 2)
{
//action here
}
if([whiteArray count] > 0)
{
if(selectedSprite.tag == 3 || selectedSprite.tag == 4)
{
//action here
}
[whiteArray removeObject:selectedSprite];
if([whiteArray count] == 0)
{
//Go to game over
}
}}
This doesn't look pretty but it works. Anyway, if there's a better way to implement this than how I am currently doing it please let me know.
Make your arrays (arrayBlack and arrayWhite) mutable. Then,
if(user hit sprite1)
{
if([arrayBlack containsObject:sprite1])
{
[arrayBlack removeObject:sprite1];
// Game over
}
else
{
[arrayWhite removeObject:sprite1];
if(arrayWhite.count>0)
{
// Continue game
}
else
{
// Stage clear scene
}
}
}

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! )