ccTouchesEnded Ends All Actions - cocos2d-iphone

So I have been having some problems with the cctouchesEnded where when one touch ended it triggered the end actions for all my buttons. I understand why this is happening but to fix it I was unsure what to do. If I were to make a class for the buttons and made a new instance of the class for every new button would each cctoucheEnded be separate? If so is there a good example of how to use classes to create simple buttons in cocos2d? Any help would be appreciated. Here is how I am using buttons at the moment.
-(void)ccTouchesBegan:(NSSet*)touches withEvent:(UIEvent*)event {
for(UITouch *touch in [event allTouches]) {
CGPoint loc = [touch locationInView: [touch view]];
loc = [[CCDirector sharedDirector] convertToGL:loc];
if (CGRectContainsPoint(button2.boundingBox, loc)) { //Left button pressed
[self.character stopAction:self.walkAction];
[self.character setScaleX:-2];
isLeft = YES;
isRight = NO;
right = NO;
left = YES;
if(!isRunning && !isJumping && !isSpin)
{
// [self.character stopAction:self.jumpAction];
[self.character runAction:self.runAction];
isRunning = YES;
}
else if(isSpin)
{
[self.character stopAction:self.runAction];
[self.character stopAction:self.walkAction];
[self.character stopAction:self.jumpAction];
isRunning = NO;
isStanding = NO;
}
}
if (CGRectContainsPoint(button.boundingBox, loc)) { //Right button pressed
[self.character stopAction:self.walkAction];
[self.character setScaleX:2];
isLeft = NO;
isRight = YES;
right = YES;
left = NO;
if(!isRunning && !isJumping && !isSpin)
{
[self.character stopAction:self.jumpAction];
[self.character runAction:self.runAction];
isRunning = YES;
isStanding = NO;
}
else if(isSpin)
{
[self.character stopAction:self.runAction];
[self.character stopAction:self.walkAction];
[self.character stopAction:self.jumpAction];
isRunning = NO;
isStanding = NO;
}
}
}
}
-(void)ccTouchesMoved:(NSSet *)touchesT withEvent:(UIEvent *)event
{
NSMutableArray *touches = [NSMutableArray arrayWithCapacity:100];
for (UITouch *tmpTouch in [event allTouches]) {
[touches addObject:tmpTouch];
}
CGPoint locOne = [[touches objectAtIndex:0] locationInView: [[touches objectAtIndex:0] view]];
locOne = [[CCDirector sharedDirector] convertToGL:locOne];
CGPoint locTwo = ccp(-100, -100);
if ([touches count] > 1) {
locTwo = [[touches objectAtIndex:1] locationInView: [[touches objectAtIndex:1] view]];
locTwo = [[CCDirector sharedDirector] convertToGL:locTwo];
}
CGPoint locThree = ccp(-100, -100);
if ([touches count] > 2) {
locThree = [[touches objectAtIndex:2] locationInView: [[touches objectAtIndex:2] view]];
locThree = [[CCDirector sharedDirector] convertToGL:locThree];
}
if(!(((CGRectContainsPoint(button2.boundingBox, locOne) || CGRectContainsPoint(button2.boundingBox, locTwo) || CGRectContainsPoint(button2.boundingBox, locThree)))|| ((CGRectContainsPoint(button.boundingBox, locOne) || CGRectContainsPoint(button.boundingBox, locTwo) || CGRectContainsPoint(button.boundingBox, locThree)))))
{
if(!isJumping && !isSpin && !isStanding){
[self.character stopAction:self.jumpAction]; //No buttons
[self.character stopAction:self.runAction];
[self.character runAction:self.walkAction];
isRunning = NO;
isStanding = YES;
}
}
if (!(CGRectContainsPoint(button2.boundingBox, locOne) || CGRectContainsPoint(button2.boundingBox, locTwo) || CGRectContainsPoint(button2.boundingBox, locThree))) {
left = NO;
} else { //Left button
[self.character setScaleX:-2];
left = YES;
isLeft = YES;
isRight = NO;
if(!isRunning && !isJumping && !isSpin && !isShooting)
{
[self.character stopAction:self.jumpAction];
[self.character stopAction:self.walkAction];
[self.character runAction:self.runAction];
isRunning = YES;
isStanding = NO;
}
else if(isSpin)
{
[self.character stopAction:self.runAction];
[self.character stopAction:self.walkAction];
[self.character stopAction:self.jumpAction];
isRunning = NO;
isStanding = NO;
}
}
if (!(CGRectContainsPoint(button.boundingBox, locOne) || CGRectContainsPoint(button.boundingBox, locTwo) || CGRectContainsPoint(button.boundingBox, locThree))) {
right = NO;
} else {
[self.character setScaleX:2]; //Right button
right = YES;
isRight = YES;
isLeft = NO;
if(!isRunning && !isJumping && !isSpin)
{
[self.character stopAction:self.jumpAction];
[self.character stopAction:self.walkAction];
[self.character runAction:self.runAction];
isRunning = YES;
isStanding = NO;
}
else if(isSpin)
{
[self.character stopAction:self.runAction];
[self.character stopAction:self.walkAction];
[self.character stopAction:self.jumpAction];
isRunning = NO;
isStanding = NO;
}
}
}
-(void)ccTouchesEnded:(NSSet *)touches withEvent:(UIEvent *)event
{
right = NO;
left = NO; //Touch ended
isRunning = NO;
if(!isJumping){
[self.character stopAction:self.runAction];
[self.character stopAction:self.jumpAction];
if(isStanding && !isSpin){
[self.character stopAction:self.walkAction];
isStanding = NO;
}
if(!isStanding && !isSpin){
[self.character stopAction:self.runAction];
[self.character runAction:self.walkAction];
isStanding = YES;
}
}
}

I am going to post an example for a "double touch button" that used in a project earlier this year (cocos2d, objective-c). You pass it a CCNode (e.g. CCSprite) when you create it and it will do a sequence of actions (see the .mm file) when you press it down and when you release it. It will execute a selector for both the up and down (I was using it for a fire button...needed to support holding down on the button). You should be able to modify it to do single action if you need it that way.
DoubleActionButton.h
#import <Foundation/Foundation.h>
#import "cocos2d.h"
// This button executes an action on the NODE, when the button is pressed down and when the
// button is released.
#interface DoubleActionButton : CCNode <CCTargetedTouchDelegate>
+(DoubleActionButton*)buttonWithTarget:(id)target andDownSelector:(SEL)selD andUpSelector:(SEL)selU andNode:(CCNode*)node;
#end
DoubleActionButton.mm
#import "DoubleActionButton.h"
#import "SimpleAudioEngine.h"
#import "CommonSTL.h"
#import "UIConstants.h"
#import "UIUtilities.h"
#interface DoubleActionButton()
{
id _target;
SEL _selUp;
SEL _selDown;
CCAction * _actUp;
CCAction * _actDown;
BOOL _down;
BOOL _animating;
}
#property (nonatomic, assign) id _target;
#property (nonatomic, assign) BOOL _down;
#property (nonatomic, assign) SEL _selUp;
#property (nonatomic, assign) SEL _selDown;
#property (nonatomic, retain) CCAction * _actUp;
#property (nonatomic, retain) CCAction * _actDown;
#property (nonatomic, retain) CCNode * _node;
#property (nonatomic, assign) BOOL _animating;
#end
#implementation DoubleActionButton
#synthesize _target;
#synthesize _actDown;
#synthesize _actUp;
#synthesize _selDown;
#synthesize _selUp;
#synthesize _animating;
#synthesize _down;
#synthesize _node;
#pragma mark Setters
#pragma mark Life Cycle
-(CCAction*)createDownAction
{
// specify the visual actions that should occur when a user interacts with the button
CCFiniteTimeAction* action0 = [CCCallFunc actionWithTarget:self selector:#selector(buttonDownStart)];
CCFiniteTimeAction* action1 = [CCScaleTo actionWithDuration:0.1 scale:1.1f];
CCFiniteTimeAction* action2 = [CCCallFunc actionWithTarget:self selector:#selector(buttonDownEnd)];
CCSequence* seq = [CCSequence actions:action0,action1,action2,nil];
return seq;
}
-(CCAction*)createUpAction
{
// specify the visual actions that should occur when a user interacts with the button
CCFiniteTimeAction* action0 = [CCCallFunc actionWithTarget:self selector:#selector(buttonUpStart)];
CCFiniteTimeAction* action1 = [CCScaleTo actionWithDuration:0.1 scale:1.0f];
CCFiniteTimeAction* action2 = [CCCallFunc actionWithTarget:self selector:#selector(buttonUpEnd)];
CCSequence* seq = [CCSequence actions:action0,action1,action2,nil];
return seq;
}
-(DoubleActionButton*)init
{
self = [super init];
if(self != nil)
{
_animating = NO;
_down = NO;
self._actUp = [self createUpAction];
self._actDown = [self createDownAction];
self._selUp = nil;
self._selDown = nil;
self._node = nil;
}
return self;
}
-(DoubleActionButton*)initButtonWithTarget:(id)target
andDownSelector:(SEL)selD
andUpSelector:(SEL)selU
andNode:(CCNode*)node
{
self = [super init];
if(self != nil)
{
self._actUp = [self createUpAction];
self._actDown = [self createDownAction];
self._target = target;
self._selDown = selD;
self._selUp = selU;
self.anchorPoint = ccp(0.5,0.5);
self._node = node;
[self addChild:node z:100];
}
return self;
}
+(DoubleActionButton*)buttonWithTarget:(id)target
andDownSelector:(SEL)selD
andUpSelector:(SEL)selU
andNode:(CCNode*)node
{
DoubleActionButton* button = [[DoubleActionButton alloc] initButtonWithTarget:target andDownSelector:selD andUpSelector:selU andNode:node];
[button autorelease];
return button;
}
-(void)dealloc
{
self._actDown = nil;
self._actUp = nil;
self._node = nil;
[super dealloc];
}
#pragma mark Cocos2D Life Cycle
-(void)onEnterTransitionDidFinish
{
[super onEnterTransitionDidFinish];
[[[CCDirector sharedDirector] touchDispatcher] addTargetedDelegate:self priority:0 swallowsTouches:YES];
}
-(void)onExitTransitionDidStart
{
[super onExitTransitionDidStart];
[[[CCDirector sharedDirector] touchDispatcher] removeDelegate:self];
}
#pragma mark General Stuff
-(void)buttonDownStart
{
_animating = YES;
_down = YES;
if(_selDown && _target)
{
[_target performSelector:_selDown];
}
[[SimpleAudioEngine sharedEngine] playEffect:[UIConstants FILE_AUDIO_BUTTON_CLICK]];
}
-(void)buttonDownEnd
{
_animating = NO;
}
-(void)buttonUpStart
{
if(_selUp && _target)
{
[_target performSelector:_selUp];
}
_animating = YES;
}
-(void)buttonUpEnd
{
_down = NO;
_animating = NO;
}
#pragma mark Touch events
- (BOOL) touchInButton:(UITouch*)touch
{
CCNode* node = self._node;
if(node)
{
// Is the touch inside node0's AABB?
CGPoint touchPt = [UIUtilities TouchToScreen:touch];
CGPoint local = [node convertToNodeSpace:touchPt];
CGRect rect = CGRectMake(touchPt.x, touchPt.y, node.contentSize.width, node.contentSize.height);
rect.origin = CGPointZero;
if(CGRectContainsPoint( rect, local ) )
{ // Not in the AABB of node0.
return YES;
}
}
return NO;
}
- (BOOL) ccTouchBegan:(UITouch*)touch withEvent:(UIEvent*)event
{
if (!_animating && !_down && [self touchInButton:touch] )
{
if ( _actDown )
{
[self stopAllActions];
[self runAction:_actDown];
}
return YES;
}
return NO;
}
- (void) ccTouchMoved:(UITouch*)touch withEvent:(UIEvent*)event
{
// Finger moved off the button?
if (_down && ![self touchInButton:touch] )
{ // Looks like it.
if ( _actUp )
{
[self stopAllActions];
[self runAction:_actUp];
}
}
}
- (void) ccTouchEnded:(UITouch *)touch withEvent:(UIEvent *)event
{
if ( _actUp && _down)
{
[self stopAllActions];
[self runAction:_actUp];
}
}
-(void)ccTouchCancelled:(UITouch *)touch withEvent:(UIEvent *)event
{
if ( _actUp && _down)
{
[self stopAllActions];
[self runAction:_actUp];
}
}
#end
Here is an example of using it:
// Button 1
DoubleActionButton* dblBtn;
node0 = [CCSprite spriteWithSpriteFrameName:[UIConstants FILE_IMAGE_FIRE_BUTTON_UP]];
[UIUtilities SetSpriteSize:(CCSprite*)node0 toSize:btnMaxSize];
dblBtn = [DoubleActionButton buttonWithTarget:self andDownSelector:#selector(button1Down) andUpSelector:#selector(button1Up) andNode:node0];
dblBtn.position = ccp(leftEdge,botEdge);
[self addChild:dblBtn];
Feel free to use as much of the code as you need to.
Was this helpful?

Related

trying to touch an item in an array not working

I have An Array inside my touches began method (I would like to be able to touch the sprite
and for it to NSlog the touch) is there something im forgetting to do or am doing something wrong?
I can log the touch on the screen but when I touch the bubbles nothing happens.
any help would be great.
-(id) init
{
if((self=[super initWithColor:ccc4(10, 10, 10,10)]) ) //sand 101, 116, 88
{
size = [[CCDirector sharedDirector] winSize];
self.touchEnabled = YES;
//other stuff here
Bubble01 = [[Bubble alloc]initWithBubbleWithLabel:#"_Bubble.png" opacity:255 gloss:#"_Bubble_overlay.png" posX:0 posY:0 data:[NSString stringWithFormat:#"%#", [Sortingarray objectAtIndex:BubbleAnswerBubble_1_IndexValue]]];
Bubble02 = [[Bubble alloc]initWithBubbleWithLabel:#"_Bubble.png" opacity:255 gloss:#"_Bubble_overlay.png" posX:0 posY:0 data:[NSString stringWithFormat:#"%#", [Sortingarray objectAtIndex:BubbleAnswerBubble_2_IndexValue]]];
Bubble03 = [[Bubble alloc]initWithBubbleWithLabel:#"_Bubble.png" opacity:255 gloss:#"_Bubble_overlay.png" posX:0 posY:0 data:[NSString stringWithFormat:#"%#", [Sortingarray objectAtIndex:BubbleAnswerBubble_3_IndexValue]]];
Bubble04 = [[Bubble alloc]initWithBubbleWithLabel:#"_Bubble.png" opacity:255 gloss:#"_Bubble_overlay.png" posX:0 posY:0 data:[NSString stringWithFormat:#"%#", [Sortingarray objectAtIndex:BubbleAnswerBubble_4_IndexValue]]];
Bubble05 = [[Bubble alloc]initWithBubbleWithLabel:#"_Bubble.png" opacity:255 gloss:#"_Bubble_overlay.png" posX:0 posY:0 data:[NSString stringWithFormat:#"%#", [Sortingarray objectAtIndex:BubbleAnswerBubble_5_IndexValue]]];
Bubble06 = [[Bubble alloc]initWithBubbleWithLabel:#"_Bubble.png" opacity:255 gloss:#"_Bubble_overlay.png" posX:0 posY:0 data:[NSString stringWithFormat:#"%#", [Sortingarray objectAtIndex:AnswerBubble_6_IndexValue]]];
//other stuff here
}
return self;
}
touchesbegan
-(void) ccTouchesBegan:(NSSet *)touches withEvent:(UIEvent *)event{
//set up touches
NSSet *allTouch = [event allTouches];
UITouch *touch = [[allTouch allObjects]objectAtIndex:0];
CGPoint location = [touch locationInView:[touch view]];
location = [[CCDirector sharedDirector]convertToGL:location];
//log if touches are working if I touch the screen area
NSLog(#"touches screen");
//create an array from bubble class (CCSprites with labels,
//I need to be able to determine which sprite was touched and run an action on it)
BubbleArray = [[NSMutableArray alloc]initWithObjects:Bubble01,
Bubble02,
Bubble03,
Bubble04,
Bubble05,
Bubble06,
nil];
for(int i = 0; i < [BubbleArray count]; i++)
{
Bubble *sprite = (Bubble *)[BubbleArray objectAtIndex:i];
//create a rect to find the position and size of the sprite
//BackBubble is a sprite that i'm using to detect the content size
CGRect targetRect = CGRectMake(
sprite.BackBubble.position.x - (sprite.BackBubble.contentSize.width/2),
sprite.BackBubble.position.y - (sprite.BackBubble.contentSize.height/2),
sprite.BackBubble.contentSize.width,
sprite.BackBubble.contentSize.height);
//use the rect and touch location to determine hit
if(CGRectContainsPoint(targetRect, location))
//this doesn't work possibly because Bubble class is a CClayer?
//if(CGRectContainsPoint([sprite boundingBox], location))
{
selectedSprite = sprite;
NSLog(#"touches bubble sprite");
}
}
}
Any insight would be great to help me understand what i'm doing wrong.
cheers :)
new array code (getting the height and width of the sprite through the custom class
for(int i = 0; i < [DragItems count]; i++)
{
Bubble *sprite = (Bubble *)[BubbleArray objectAtIndex:i];
location = [sprite convertToNodeSpace:location];
if(CGRectContainsPoint([sprite.BackBubble boundingBox], location))
{
selectedSprite = sprite;
NSLog(#"touches bubble");
}
}
Bubble.m
#import "Bubble.h"
#import "Common.h"
#define ButtonFlashTime .4
#define KBubbleColourTurqoiseBlueFlash 2323
#define ScrollSpeed 5.2f
#define DecoyTextY 5
#define DecoyTextX -2
#implementation Bubble
#synthesize BackBubble,FrontShine,BubbleLabel,startX,startY,currentY,currentX,isNotTouchActivated,myInt,bubblespeed,tagNumber; //isTouched
-(id)init
{
self=[super init];
{
//touches
[[[CCDirector sharedDirector] touchDispatcher] addTargetedDelegate:self priority:0 swallowsTouches:YES];
isNotTouchActivated = false;
isTouched = NO;
bubblespeed = 0;
//start scrolling
[self MoveWithoutProblem];
[self setAnchorPoint:ccp(0.5,0.5)];
BackBubble = [CCSprite spriteWithSpriteFrameName:Bubblepng];
BackBubble.position = ccp(X,Y);
[BackBubble setAnchorPoint:ccp(0,0)];
[self addChild: BackBubble z:5];
NSLog(#"BackBubble, %f %f",BackBubble.position.x,BackBubble.position.y);
//other code here
[self setContentSize:[BackBubble boundingBox].size];
}
return self;
}
-(BOOL) isTouchOnSprite:(CGPoint)touch{
CGPoint local = [self convertToNodeSpace:touch];
CGRect r = self.boundingBox;
r.origin = CGPointZero;
Boolean b = CGRectContainsPoint( r, local );
//CCLOG(#"touch %f : %f : %d",touch.x,touch.y,b);
if (b) {
return YES;
}else {
return NO;
}
}
- (BOOL)ccTouchBegan:(UITouch *)touch withEvent:(UIEvent *)event{
CGPoint touchPoint = [touch locationInView:[touch view]];
touchPoint = [[CCDirector sharedDirector] convertToGL:touchPoint];
if([self isTouchOnSprite:touchPoint]){
//CGPoint move = [self convertTouchToNodeSpace:touch];
isNotTouchActivated = TRUE;
//isTouched = YES;
//NSLog(#"isTouched = %#", self.isTouched ? #"YES" : #"NO");
currentX = touchPoint.x;
currentY = touchPoint.y;
self.position = touchPoint;
return YES;
}
// NSLog(#"isTouched = %#", self.isTouched ? #"YES" : #"NO");
return NO;
}
- (void)ccTouchMoved:(UITouch *)touch withEvent:(UIEvent *)event{
CGPoint touchPoint = [touch locationInView:[touch view]];
touchPoint = [[CCDirector sharedDirector] convertToGL:touchPoint];
if (isNotTouchActivated) {
currentX = touchPoint.x;
currentY = touchPoint.y;
self.position = touchPoint;
}
}
- (void)ccTouchEnded:(UITouch *)touch withEvent:(UIEvent *)event{
isNotTouchActivated = FALSE;
//isTouched = NO;
}
-(void)MoveWithoutProblem{
CGSize size = [[CCDirector sharedDirector] winSize];
int MaxHeightofBubbles = 350;
int minHeightofBubbles = 150;
int RandomNumber = [self generateRandomNumberBetweenMin:minHeightofBubbles Max:MaxHeightofBubbles];
float ConvertedRandom = [[NSNumber numberWithInt: RandomNumber] floatValue];
int MaxWidthofBubbles = 0;
int minWidthofBubbles = 900;
int RandomNumber02 = [self generateRandomNumberBetweenMin:MaxWidthofBubbles Max:minWidthofBubbles];
float ConvertedRandom02 = [[NSNumber numberWithInt: RandomNumber02] floatValue];
startX = ConvertedRandom02;
startY = ConvertedRandom;
currentX = startX+myInt;
currentY = startY;
self.position = ccp(startX,startY);
[self schedule:#selector(startMoving)];
}
-(void)startMoving{
if (!isNotTouchActivated) {
currentX+=bubblespeed;
[self setPosition:ccp(currentX,currentY)];
}
if (self.position.x >= 1024+50) {
//NSLog(#"off screen");
isrestartscrolling = YES;
}
if (isrestartscrolling == YES) {
//[self RandomYPOs];
[self scheduleOnce:#selector(newRandomX) delay:0.2];
isrestartscrolling = NO;
}
}
#end
Your code is a little hard to read but your immediate problem to why the touch doesn't work is because you are assuming your layer (Bubble) has a content width and height. If you did not set this, it wouldn't have it which is why your commented out line for [sprite boundingBox] does not work. Try [sprite.BackBubble boundingBox]. Adding items to a layer does not adjust that layer's content size automatically.
Another thing you can try is to add:
location = [sprite convertToNodeSpace:location];
If your bubble layer or that back bubble is moved at any point then simply adding the back bubble's position to a CGRect is not likely to work. Try the first idea first then try this if it doesn't work.
Hope this helped you Natalie.

EXC_BAD_ACCESS when using NSKeyedUnarchiver to load saved state

I have subclassed a CCSprite to make an object that can be encoded and decoded. I want to save sprites state and load it again at particular positions. Everything seems to be okay apart from decoding with NSKeyedUnarchiver (see loadIconSpriteState below) which gives an EXC_BAD_ACCESS Below is the code:
HelloWorldLayer.h
#interface CCIconSprite : CCSprite {
NSString *iconName;
float iconXPos;
float iconYPos;
}
#property (nonatomic, retain) NSString *iconName;
#property (nonatomic, assign) float iconXPos;
#property (nonatomic, assign) float iconYPos;
+ (id)iconWithType:(NSString*)imageName;
- (id)initWithIconType:(NSString*)imageName;
#end
#interface HelloWorldLayer : CCLayer < NSCoding>
{
CCIconSprite* testSprite;
BOOL savedState;
CGSize size;
CCMoveTo* moveTo;
NSMutableArray* saveSpriteArray;
NSData* savedSpriteData;
}
+(CCScene *) scene;
#end
HelloWorldLayer.m:
CCIconSprite implementation:
#implementation CCIconSprite
#synthesize iconXPos;
#synthesize iconYPos;
#synthesize iconName;
+ (id)iconWithType:(NSString*)imageName
{
return [[[[self class] alloc] initWithIconType:imageName] autorelease];
}
- (id)initWithIconType:(NSString*)imageName
{
self = [super initWithFile:imageName];
if (!self) return nil;
iconName = imageName;
self.position = ccp(iconXPos, iconYPos);
return self;
}
Encoding and decoding:
- (id)initWithCoder:(NSCoder *)decoder {
NSString* imageFileName = [decoder decodeObjectForKey:#"imageFileName"];
self = [self initWithIconType:imageFileName];
if (!self) return nil;
self.iconXPos = [decoder decodeFloatForKey:#"iconXPos"];
self.iconYPos = [decoder decodeFloatForKey:#"iconYPos"];
return self;
}
- (void)encodeWithCoder:(NSCoder *)encoder
{
[encoder encodeObject:iconName forKey:#"imageFileName"];
[encoder encodeFloat:self.iconXPos forKey:#"iconXPos"];
[encoder encodeFloat:self.iconYPos forKey:#"iconYPos"];
}
#end
HelloWorldLayer implementation:
#implementation HelloWorldLayer
+(CCScene *) scene
{
CCScene *scene = [CCScene node];
HelloWorldLayer *layer = [HelloWorldLayer node];
[scene addChild: layer];
return scene;
}
-(id) init
{
if( (self=[super init]) ) {
self.scale = 0.5;
savedState = NO;
size = [[CCDirector sharedDirector] winSize];
testSprite = [CCIconSprite spriteWithFile:#"Icon.png"];
testSprite.position = ccp(size.width/4, size.width/4);
testSprite.anchorPoint = ccp(0,0);
[self addChild:testSprite];
moveTo = [CCMoveTo actionWithDuration:3 position:ccp(3*size.width/4, 3*size.width/4)];
[testSprite runAction:moveTo];
[self schedule:#selector(saveAndLoadSpriteState)];
}
return self;
}
Saving and loading state:
-(void)saveIconSpriteState {
saveSpriteArray = [[NSMutableArray alloc] init];
[saveSpriteArray addObject:testSprite];
savedSpriteData = [NSKeyedArchiver archivedDataWithRootObject:saveSpriteArray];
}
-(void)loadIconSpriteState {
[NSKeyedUnarchiver unarchiveObjectWithData:savedSpriteData];
}
-(void)saveAndLoadSpriteState {
if ((testSprite.position.x > size.width/2) && !savedState) {
savedState = YES;
[self saveIconSpriteState];
}
else if ((testSprite.position.x == 3*size.width/4) && savedState) {
savedState = NO;
[self loadIconSpriteState];
[testSprite runAction:moveTo];
}
}
#end
EDIT
After setting an exception break point I got the following error
Assertion failure in -[CCIconSprite initWithFile:]
pointing to the in line:
NSAssert(filename != nil, #"Invalid filename for sprite");
in the
-(id) initWithFile:(NSString*)filename
method of the CCSprite.m class.
Just a hunch but maybe that's it. When decoding a string, you should copy it because you don't own it at this point, nor are you assigning it to a strong or copy property.
- (id)initWithCoder:(NSCoder *)decoder {
NSString* imageFileName = [[decoder decodeObjectForKey:#"imageFileName"] copy];
self = [self initWithIconType:imageFileName];
if (!self) return nil;
...
}
If that's not it, set a breakpoint and verify that the string is indeed correct when encoding and when decoding.

Changing State for an object in Cocos2d

firstly i would like to apologise for being a complete novice but I have researched the web and can not find a solution to my current problem.
I have 3 objects (sprites) that i have set up individually with switch statements and want to use my main gameplay layer to change their states randomly to an animation that i have already defined in the objects individual header and implementation files.
I have set up a self schedule updater and arc4random method that works but it will not change the state of the object, as it only calls the CCLOG that I have also included in the statement.
I have listed to code below and i know it is a bit of a mess but still very much in my first steps of being a beginner, if anyone can point me in the right direction (that's if i have explained this in a way you can understand!) I would be very grateful.
Thanks in advance for even looking at this question.
//-------------------below is my gameplaylayer header file---------//
// GamePlayLayer.h
#import <Foundation/Foundation.h>
#import "cocos2d.h"
#import "CCLayer.h"
#import "SneakyJoystick.h"
#import "SneakyButton.h"
#import "SneakyButtonSkinnedBase.h"
#import "SneakyJoystickSkinnedBase.h"
#import "Constants.h"
#import "CommonProtocols.h"
#import "TBT.h"
#import "MBT.h"
#import "BBT.h"
#import "BC.h"
#import "GameCharacter.h"
#import <stdlib.h>
#interface GamePlayLayer : CCLayer <GamePlayLayerDelegate> {
CCSprite *vikingSprite;
SneakyJoystick *leftJoystick;
SneakyButton *jumpButton;
SneakyButton *attackButton;
CCSpriteBatchNode *sceneSpriteBatchNode;
}
#property (readwrite) CharacterStates characterState;
-(void)changeState:(CharacterStates)newState;
-(void)addEnemy;
#end
//---------------------Below is my gameplaylayer implementation file-------------//
// GamePlayLayer.m
#import "GamePlayLayer.h"
#implementation GamePlayLayer
#synthesize characterState;
-(void) dealloc {
[leftJoystick release];
[jumpButton release];
[attackButton release];
[super dealloc];
}
-(void)initJoystickAndButtons {
CGSize screenSize = [CCDirector sharedDirector].winSize;
//---DELETED MOST OF THE ABOVE METHOD AS NOT NEEDED FOR THIS QUESTION----//
-(void) update:(ccTime)deltaTime {
CCArray *listOfGameObjects =
[sceneSpriteBatchNode children];
for (GameCharacter *tempChar in listOfGameObjects) {
[tempChar updateStateWithDeltaTime:deltaTime andListOfGameObjects:listOfGameObjects];
}
}
-(void) createObjectOfType: (GameObjectType)objectType
withHealth:(int)initialHealth atLocation:(CGPoint)spawnLocation withZValue:(int)ZValue {
if (objectType == kEnemyType1BT) {
CCLOG(#"creating the 1BT");
TBT *tBT = [[TBT alloc] initWithSpriteFrameName:#"BT_anim_1.png"];
[tBT setCharacterHealth:initialHealth];
[tBT setPosition:spawnLocation];
[sceneSpriteBatchNode addChild:tBT
z:ZValue
tag:k1BTtagValue];
[tBT release];}
if (objectType == kEnemyType3BT){
CCLOG(#"creating the radar enemy");
BBT *bBT = [[BBT alloc] initWithSpriteFrameName:#"BT_anim_1.png"];
[bBT setCharacterHealth:initialHealth];
[bBT setPosition:spawnLocation];
[sceneSpriteBatchNode addChild:bBT
z:ZValue
tag:k3BTtagValue];
[bBT release];
}
if (objectType == kEnemyType2BT){
CCLOG(#"creating the radar enemy");
MBT *mBT = [[MBT alloc] initWithSpriteFrameName:#"BT_anim_1.png"];
[mBT setCharacterHealth:initialHealth];
[mBT setPosition:spawnLocation];
[sceneSpriteBatchNode addChild:mBT
z:ZValue
tag:k2BTtagValue];
[mBT release];
}
}
//--PROBLEM I HAVE IS BELOW--//
-(void)addEnemy {
int x = (arc4random() % 3);
TBT *tBT = (TBT*)
[sceneSpriteBatchNode getChildByTag:kEnemyType1BT];
//--Just using one object(sprite) to begin with--//
if (x>0) {
CCLOG(#"RANDOM KSTATETEST!!!!!!");
[tBT changeState:kStatetest]; <---it is not changing state to kStatetest
}
-(id)init {
self = [super init];
if (self !=nil) {
CGSize screenSize = [CCDirector sharedDirector]. winSize;
self.TouchEnabled = YES;
srandom(time(NULL));
if (UI_USER_INTERFACE_IDIOM() == UIUserInterfaceIdiomPad) {
[[CCSpriteFrameCache sharedSpriteFrameCache]
addSpriteFramesWithFile:#"scene1atlas.plist"]; // 1
sceneSpriteBatchNode =
[CCSpriteBatchNode batchNodeWithFile:#"scene1atlas.png"]; // 2
} else {
[[CCSpriteFrameCache sharedSpriteFrameCache]
addSpriteFramesWithFile:#"scene1atlasiPhone.plist"]; // 1
sceneSpriteBatchNode =
[CCSpriteBatchNode batchNodeWithFile:#"scene1atlasiPhone.png"];// 2
}
[self addChild:sceneSpriteBatchNode z:0]; // 3
[self initJoystickAndButtons]; // 4
BC *viking = [[BC alloc]
initWithSpriteFrame:[[CCSpriteFrameCache
sharedSpriteFrameCache]
spriteFrameByName:#"BCmoving_anim_1.png"]];
//[viking setJoystick:leftJoystick];
[viking setJumpButton:jumpButton];
[viking setAttackButton:attackButton];
[viking setPosition:ccp(screenSize.width * 0.19f,
screenSize.height * 0.19f)];
[viking setCharacterHealth:3];
[sceneSpriteBatchNode
addChild:viking
z:kVikingSpriteZValue
tag:kVikingSpriteTagValue]; heatlh is set to 100
[self schedule:#selector(addEnemy) interval:1.0f];
[self createObjectOfType:kEnemyType1BT withHealth:3 atLocation:ccp(screenSize.width * 0.0439f, screenSize.height * 0.822f) withZValue:10];
[self createObjectOfType:kEnemyType3BT withHealth:3 atLocation:ccp(screenSize.width * 0.0439f, screenSize.height * 0.45f) withZValue:10];
[self createObjectOfType:kEnemyType2BT withHealth:3 atLocation:ccp(screenSize.width * 0.0439f, screenSize.height * 0.638f) withZValue:10];
//Sets up the schedular call that will fire the update method in GamePlayLayer.m every frame.
[self scheduleUpdate];
}
return self;
}
#end
//----------------- below is one of my objects header files--------------//
#import <Foundation/Foundation.h>
#import "GameCharacter.h"
#interface TBT : GameCharacter{
CCAnimation *tiltingAnim;
CCAnimation *transmittingAnim;
CCAnimation *loseLifeAnim;
CCAnimation *throwingAnim;
CCAnimation *afterThrowingAnim;
CCAnimation *shootPhaserAnim;
GameCharacter *vikingCharacter;
id <GamePlayLayerDelegate> delegate;
}
#property (nonatomic,assign) id <GamePlayLayerDelegate> delegate;
#property (nonatomic, retain) CCAnimation *tiltingAnim;
#property (nonatomic, retain) CCAnimation *transmittingAnim;
//#property (nonatomic, retain) CCAnimation *takingAHitAnim;
#property (nonatomic, retain) CCAnimation *loseLifeAnim;
#property (nonatomic, retain) CCAnimation *throwingAnim;
#property (nonatomic,retain) CCAnimation *afterThrowingAnim;
#property (nonatomic,retain) CCAnimation *shootPhaserAnim;
-(void)initAnimations;
#end
//-----------------below is the .m file for one of my objects--------------//
#import "TBT.h"
#implementation TBT
#synthesize delegate;
#synthesize tiltingAnim;
#synthesize transmittingAnim;
#synthesize loseLifeAnim;
#synthesize throwingAnim;
#synthesize afterThrowingAnim;
#synthesize shootPhaserAnim;
-(void) dealloc {
delegate = nil;
[tiltingAnim release];
[transmittingAnim release];
[loseLifeAnim release];
[throwingAnim release];
[afterThrowingAnim release];
[shootPhaserAnim release];
[super dealloc];
}
-(void)shootPhaser {
CGPoint phaserFiringPosition;
PhaserDirection phaserDir;
CGRect boundingBox = [self boundingBox];
CGPoint position = [self position];
float xPosition = position.x + boundingBox.size.width * 0.542f;
float yPosition = position.y + boundingBox.size.height * 0.25f;
if ([self flipX]) {
CCLOG(#"TBT Facing right, Firing to the right");
phaserDir = kDirectionRight;
} else {
CCLOG(#"TBT Facing left, Firing to the left");
xPosition = xPosition * -1.0f;
phaserDir = kDirectionLeft;
}
phaserFiringPosition = ccp(xPosition, yPosition);
[delegate createPhaserWithDirection:phaserDir andPosition:phaserFiringPosition];
}
-(void)changeState:(CharacterStates)newState {
[self stopAllActions];
id action = nil;
[self setCharacterState:newState];
switch (newState) {
case kStatespawning:
CCLOG(#"TBT->Changing State to Spwaning");
[self setDisplayFrame:
[[CCSpriteFrameCache sharedSpriteFrameCache]
spriteFrameByName:#"BT_anim_1.png"]];
break;
case kStateIdle:
CCLOG(#"TBT->schaning state to idle");
[self setDisplayFrame:
[[CCSpriteFrameCache sharedSpriteFrameCache]
spriteFrameByName:#"BT_anim_1.png"]];
break;
case kStatetest:
CCLOG(#"TBT->Changing State to test");
action = [CCSequence actions : [CCDelayTime actionWithDuration:1.5f],[CCAnimate actionWithAnimation:transmittingAnim], nil];
break;
default:
CCLOG(#"unhandled state %d in TBT", newState);
break;
}
if (action !=nil) {
[self runAction:action];
}
}
-(void)updateStateWithDeltaTime: (ccTime)deltaTime andListOfGameObjects:(CCArray*)listOfGameObjects {
if (characterState == kStateDead)
return;
if ((([self numberOfRunningActions] == 0) && (characterState != kStateDead)) ) {
CCLOG(#"TBT Going to Idle");
[self changeState:kStateIdle];
return;
}
}
-(void)initAnimations {
[self setTiltingAnim:[self loadPlistForAnimationWithName:#"tiltingAnim" andClassName:NSStringFromClass([self class])]];
[self setTransmittingAnim:[self loadPlistForAnimationWithName:#"transmittingAnim" andClassName:NSStringFromClass([self class])]];
}
-(id) initWithSpriteFrameName:(NSString*)frameName{
if ((self=[super init])) {
if ((self = [super initWithSpriteFrameName:frameName])) {
CCLOG(#"### TBT initialized");
[self initAnimations];
characterHealth = 3.0f;
gameObjectType = kEnemyType1BT;
[self changeState:kStatespawning];
}}
return self;
}
#end
Finally found out that all I needed was to add the following code to my TBT gameCharacters-(void)updateStateWithDeltaTime: method...
if (characterState == kStateTest) {
if (characterState != kStateTest) {
[self changeState:kStateTest];
return;
}
now it works fine.
Thanks again to Mikael for trying to help me.

Cocos2d animation resume

I've added iAP in cocos2d following the ray tutorials (but changed a few things to fit cocos2d) but after I make a transaction the console log says 2013-08-19 16:32:12.626 Game[2483:907] Buying *product* ...
2013-08-19 16:32:13.208 Game[2483:907] cocos2d: animation stopped
2013-08-19 16:32:16.690 Game[2483:907] completeTransaction...
2013-08-19 16:32:16.725 Game[2483:907] User defaults for *product* are YES
So I know the transaction works, but the game never resumes it just freezes. Is there a way to resume the game after the transaction? [[CCDirector sharedDirector]resume] doesn't work so I think it may have to do with UIAlert View. Any help? Here is my iAPHelper.mm:
#import "IAPHelper.h"
#import <StoreKit/StoreKit.h>
NSString *const IAPHelperProductPurchasedNotification
#"IAPHelperProductPurchasedNotification";
#interface IAPHelper () <SKProductsRequestDelegate, SKPaymentTransactionObserver>
#end
#implementation IAPHelper {
SKProductsRequest * _productsRequest;
RequestProductsCompletionHandler _completionHandler;
NSSet * _productIdentifiers;
NSMutableSet * _purchasedProductIdentifiers;
}
- (id)initWithProductIdentifiers:(NSSet *)productIdentifiers {
if ((self = [super init])) {
// Store product identifiers
_productIdentifiers = productIdentifiers;
[[SKPaymentQueue defaultQueue] addTransactionObserver:self];
// Check for previously purchased products
_purchasedProductIdentifiers = [NSMutableSet set];
for (NSString * productIdentifier in _productIdentifiers) {
BOOL productPurchased = [[NSUserDefaults standardUserDefaults] boolForKey:productIdentifier];
if (productPurchased) {
[_purchasedProductIdentifiers addObject:productIdentifier];
NSLog(#"Previously purchased: %#", productIdentifier);
} else {
NSLog(#"Not purchased: %#", productIdentifier);
}
}
}
return self;
}
- (void)requestProductsWithCompletionHandler:(RequestProductsCompletionHandler)completionHandler {
_completionHandler = [completionHandler copy];
_productsRequest = [[SKProductsRequest alloc] initWithProductIdentifiers:_productIdentifiers];
_productsRequest.delegate = self;
[_productsRequest start];
}
#pragma mark - SKProductsRequestDelegate
- (void)productsRequest:(SKProductsRequest *)request didReceiveResponse:(SKProductsResponse *)response {
NSLog(#"Loaded list of products...");
_productsRequest = nil;
NSArray * skProducts = response.products;
for (SKProduct * skProduct in skProducts) {
NSLog(#"Found product: %# %# %0.2f",
skProduct.productIdentifier,
skProduct.localizedTitle,
skProduct.price.floatValue);
}
_completionHandler(YES, skProducts);
_completionHandler = nil;
}
- (void)request:(SKRequest *)request didFailWithError:(NSError *)error {
NSLog(#"Failed to load list of products.");
_productsRequest = nil;
_completionHandler(NO, nil);
_completionHandler = nil;
}
- (BOOL)productPurchased:(NSString *)productIdentifier {
return [_purchasedProductIdentifiers containsObject:productIdentifier];
}
- (void)buyProduct:(SKProduct *)product {
NSLog(#"Buying %#...", product.productIdentifier);
SKPayment * payment = [SKPayment paymentWithProduct:product];
[[SKPaymentQueue defaultQueue] addPayment:payment];
}
- (void)paymentQueue:(SKPaymentQueue *)queue updatedTransactions:(NSArray *)transactions
{
for (SKPaymentTransaction * transaction in transactions) {
switch (transaction.transactionState)
{
case SKPaymentTransactionStatePurchased:
[self completeTransaction:transaction];
break;
case SKPaymentTransactionStateFailed:
[self failedTransaction:transaction];
break;
case SKPaymentTransactionStateRestored:
[self restoreTransaction:transaction];
default:
break;
}
};
}
- (void)completeTransaction:(SKPaymentTransaction *)transaction {
NSLog(#"completeTransaction...");
[self provideContentForProductIdentifier:transaction.payment.productIdentifier];
[[SKPaymentQueue defaultQueue] finishTransaction:transaction];
}
- (void)restoreTransaction:(SKPaymentTransaction *)transaction {
NSLog(#"restoreTransaction...");
[self provideContentForProductIdentifier:transaction.originalTransaction.payment.productIdentifier];
[[SKPaymentQueue defaultQueue] finishTransaction:transaction];
}
- (void)failedTransaction:(SKPaymentTransaction *)transaction {
NSLog(#"failedTransaction...");
if (transaction.error.code != SKErrorPaymentCancelled)
{
NSLog(#"Transaction error: %#", transaction.error.localizedDescription);
}
[[SKPaymentQueue defaultQueue] finishTransaction: transaction];
}
- (void)provideContentForProductIdentifier:(NSString *)productIdentifier {
[_purchasedProductIdentifiers addObject:productIdentifier];
[[NSUserDefaults standardUserDefaults] setBool:YES forKey:productIdentifier];
[[NSUserDefaults standardUserDefaults] synchronize];
[[NSNotificationCenter defaultCenter] postNotificationName:IAPHelperProductPurchasedNotification object:productIdentifier userInfo:nil];
NSLog(#"User defaults for %# are YES", productIdentifier);
}
- (void)restoreCompletedTransactions {
[[SKPaymentQueue defaultQueue] restoreCompletedTransactions];
}
#end
Before you popup your alert view try running...
[[CCDirector sharedDirector] stopAnimation];
When you get a response back from the user in the alertview callback or transaction completion run...
[[CCDirector sharedDirector] startAnimation];
Cocos doesn't often play well with UIKit/Cocoa callbacks so you need to pause rendering while they do their thing. We have similar behaviour for when the app goes into background/foreground in the app delegate...
-(void) applicationDidEnterBackground:(UIApplication*)application {
[[CCDirector sharedDirector] stopAnimation];
}
-(void) applicationWillEnterForeground:(UIApplication *)application {
[[CCDirector sharedDirector] startAnimation];
}
-(void)applicationDidBecomeActive:(UIApplication *)application {
[[CCDirector sharedDirector] resume];
}
Seems we use resume inside the applicationDidBecomeActive. Can't recall the full logic there but that has seemed to work for a bunch of our projects.

Change color of a line on a button click event in cocos2d

I am a newbie at Cocos2D and i'm just getting started. I have set up a scene, and I have to change the color of a line in Cocos2D. Please can anyone help me out with this? Any help will be appreciated.
This should work:
-(id) init{
if((self = [super init])){
//CGSize winSize = [[CCDirector sharedDirector] winSize];
naughtytoucharray = [[NSMutableArray alloc] init];
self.isTouchEnabled = YES;
CCMenuItem *starMenuItem = [CCMenuItemImage itemFromNormalImage:#"ButtonStar.png" selectedImage:#"ButtonStarSel.png"
target:self selector:#selector(starButtonTapped:)];
CCMenuItem *starMenuItem1 = [CCMenuItemImage itemFromNormalImage:#"ButtonPlus.png" selectedImage:#"ButtonPlusSel.png"
target:self selector:#selector(starButtonTapped1:)];
CCMenuItem *menuItem1 = [CCMenuItemImage itemFromNormalImage:#"Button1.png" selectedImage:#"Button1Sel.png" target:self selector:#selector(button1Tapped:)];
CCMenuItem *menuItem2 = [CCMenuItemImage itemFromNormalImage:#"Button2.png" selectedImage:#"Button2Sel.png" target:self selector:#selector(button2Tapped:)];
CCMenuItem *menuItem3 = [CCMenuItemImage itemFromNormalImage:#"Button3.png" selectedImage:#"Button3Sel.png" target:self selector:#selector(button3Tapped:)];
_scoreLabel = [CCLabelTTF labelWithString:#" MyScore" dimensions:CGSizeMake(150, 30) alignment:UITextAlignmentLeft fontName:#"Arial" fontSize:15];
_scoreLabel.color = ccc3(255, 0, 0);
[_scoreLabel setPosition:ccp(80, 300)];
CCMenu *starMenu = [CCMenu menuWithItems:starMenuItem, nil];
CCMenu *starMenu1 = [CCMenu menuWithItems:starMenuItem1, nil];
CCRadioMenu *radioMenu = [CCRadioMenu menuWithItems:menuItem1, menuItem2, menuItem3, nil];
starMenuItem.position = ccp(60, 60);
starMenuItem1.position = ccp(60, 120);
radioMenu.position = ccp(120, 120);
[radioMenu alignItemsVertically];
radioMenu.selectedItem_ = menuItem1;
[menuItem1 selected];
starMenu.position = CGPointZero;
starMenu1.position = CGPointZero;
[self addChild:starMenu];
[self addChild:starMenu1];
[self addChild:radioMenu];
[self addChild:_scoreLabel];
}
[_scoreLabel setString:[NSString stringWithFormat:#"MyScore: %d", score]];
return self;
}
-(void)ccTouchesMoved:(NSSet *)touches withEvent:(UIEvent *)event
{
UITouch *touch = [touches anyObject];
CGPoint new_location = [touch locationInView:[touch view]];
new_location = [[CCDirector sharedDirector] convertToGL:new_location];
CGPoint oldTouchLocation = [touch previousLocationInView:touch.view];
oldTouchLocation = [[CCDirector sharedDirector] convertToGL:oldTouchLocation];
oldTouchLocation = [self convertToNodeSpace:oldTouchLocation];
[naughtytoucharray addObject:NSStringFromCGPoint(new_location)];
[naughtytoucharray addObject:NSStringFromCGPoint(oldTouchLocation)];
}
-(void)alertView:(UIAlertView *)alertView clickedButtonAtIndex:(NSInteger)buttonIndex
{
if (buttonIndex == 0)
{
exit(0);
NSLog(#"exit");
}
else if(buttonIndex == 1)
{
//CCScene *scene = [CCScene node];
[[CCDirector sharedDirector] replaceScene:[GameScene scene1]];
NSLog(#"Play again");
}
}
-(void)draw
{
glEnable(GL_LINE_SMOOTH);
for (int i = 0; i < [naughtytoucharray count]; i+=2)
{
CGPoint start = CGPointFromString([naughtytoucharray objectAtIndex:i]);
CGPoint end = CGPointFromString([naughtytoucharray objectAtIndex:i+1]);
ccDrawLine(start, end);
glColor4ub(200, 120, 120, 255);