Cocos2D - NSMutableArray not accessible in ccTouchBegan? - cocos2d-iphone

First, let's be clear on two things. My English is terrible and I'm pretty new in cocos2d. So sorry and sorry again. :D Now for my question.
I have declared these in a .m (Its a CCscene):
//A mutable array global to my class
NSMutableArray *arrayBoutons;
//I use this array like this :
LettreBleue *lettre1 = nil;
lettre1 = [LettreBleue construireObjLettre];
lettre1.position = ccp(80,370);
[self addChild:lettre1];
[arrayBoutons addObject:lettre1];
//The method to register the touch
-(void) registerWithTouchDispatcher
{
[[CCTouchDispatcher sharedDispatcher] addTargetedDelegate:self priority:kCCMenuTouchPriority swallowsTouches:NO];
}
// The classic TouchBegan in which Im trying to access the value Valeur
-(BOOL) ccTouchBegan:(UITouch *)touch withEvent:(UIEvent *)event
// test!
NSLog(#"Debut test ccTouchBegan4");
int cptLettres = 0;
do {
LettreBleue *unBoutontest=[arrayBoutons objectAtIndex: cptLettres];
NSLog(unBoutontest.Valeur);
cptLettres = cptLettres+1;
} while (cptLettres < 16);
The problem is my arrayBoutons doesn't seem to keep my data in my NsMutableArray.

Have you initialized the NSMutableArray, perhaps in an init method? You need to call something like:
arrayBoutons = [[NSMutableArray alloc] init];
Then, you need to release it in your dealloc method:
[arrayBoutons release];

Related

Cocos2d v3 & chipmunk collision detection issues

First try at Chipmunk.
Not getting collision detection registering is the problem.
My code:
#implementation MainPlayScene
{
CCPhysicsNode *_physics;
CCNode *MyPhysicsBody;
CCNode *bottomBody;
}
+ (instancetype)scene
{
return [[self alloc] init];
}
- (instancetype)init
{
// Apple recommend assigning self with supers return value, and handling self not created
self = [super init];
if (!self) return(nil);
_physics = [CCPhysicsNode node];
_physics.debugDraw = YES;
[self addChild:_physics z:1];
/// BOTTOM
CGRect bottomRect = CGRectMake(0, 0, [CCDirector sharedDirector].viewSize.width, 10);
bottomBody = [CCNode node];
bottomBody.physicsBody = [CCPhysicsBody bodyWithPolylineFromRect:bottomRect cornerRadius:0];
bottomBody.physicsBody.collisionCategories = #[#"Bottom"];
bottomBody.physicsBody.type = CCPhysicsBodyTypeStatic;
[_physics addChild:bottomBody];
/// MyBody to bounce around
MyPhysicsBody = [CCSprite spriteWithImageNamed:#"MyBody-64x64-24.png"];
MyPhysicsBody.position = ccp((self.contentSize.width/2),(self.contentSize.height/2));
MyPhysicsBody = [CCNode node];
MyPhysicsBody.physicsBody = [CCPhysicsBody bodyWithRect:(CGRect){CGPointZero, MyPhysicsBody.contentSize.height,MyPhysicsBody.contentSize.width} cornerRadius:0];
MyPhysicsBody.physicsBody.collisionCategories = #[#"MyBody"];
[_physics addChild:MyPhysicsBody z:150];
self.userInteractionEnabled = YES;
return self;
}
Detecting touch events and applying force to physics body to have it bounce up and down onto bottom body
- (void)touchBegan:(UITouch *)touch withEvent:(UIEvent *)event
{
CCLOG(#"Touch Detected");
[MyPhysicsBody.physicsBody applyImpulse:ccp(0, 300.f)];
}
Now I try to detect a collision on “Bottom” but nothing is being registered even though I see debug lines of 2 objects touch.
/// try onCollisionEnter first ... nothing
-(void)onCollisionEnter:(CCNode *)entity collisionPair:(CCPhysicsCollisionPair *)pair
{
if ([entity.physicsBody.collisionCategories isEqual: #"Bottom"]) {
CCLOG(#"Hit bottomBody");
}
}
/// try ccPhysicsCollisionBegin pair ... nothing
-(BOOL)ccPhysicsCollisionBegin:(CCPhysicsCollisionPair *)pair MyBody:(CCNode *) MyBody Botton:(CCNode *)Bottom
{
CCLOG(#"Hit bottomBody");
return TRUE;
}
Obviously I’m missing something critical here …
Any help is VERY appreciated!
Thanks
I am finding it difficult to see the relevant code with all the things you posted.
I will give you an example of 2 CCSprite objects collision detection. One is called _arrowand the other one is _obstacle.
First step is to define the collision types like so :
_arrow.physicsBody.collisionType = #"arrow";
_obstacle.physicsBody.collisionType = #"obstacle";
Second step is to define the callback
-(BOOL)ccPhysicsCollisionBegin:(CCPhysicsCollisionPair *)pair arrow:(CCNode *)arrow obstacle:(CCNode *)obstacle
{
// Do some cool stuff
return TRUE;
}
Notice the naming, arrow and obstacle according to the collision type of theses sprites.
And third, the one that you forgot to do is set the delegate so you actually get these methods called on your physicsNode object
_physics.collisionDelegate = self;
And self (which is typically just your scene) should implement the CCPhysicsCollisionDelegate protocol.
Tibor has the correct answer, but there's an important clue that I was missing which was driving me crazy. From the comments in CCPhysicsNode.h section on the CCPhysicsCollisionDelegate:
The final two parameter names (typeA/typeB) should be replaced
with names used with CCPhysicsBody.collisionType or
CCPhysicsShape.collisionType.
If both final parameter names are "default" then the collision
method is called when a more specific method isn't found.
So I was incorrectly trying to implement something like this:
-(BOOL) ccPhysicsCollisionBegin:(CCPhysicsCollisionPair *)pair typeA:(CCNode *)nodeA typeB:(CCNode *)nodeB
{
// Compare if we're ignored collisions
if (([nodeA.physicsBody.collisionType isEqualToString: #"foo"])
|| ([nodeB.physicsBody.collisionType isEqualToString: #"foo"]) )
{
return NO;
}
return YES;
}
But that's WRONG!!! The correct thing I wanted to implement was this:
-(BOOL) ccPhysicsCollisionBegin:(CCPhysicsCollisionPair *)pair foo:(CCNode *)nodeA foo:(CCNode *)nodeB
{
return NO;
}
Note how the ignored collision type is part of the method signature.
Whew!
EDIT:
For completeness, don't forget to do:
_physics.collisionDelegate = self;
You are setting collision categories which along with collisions mask allow you to discard certain collisions. The collision type property is what works with the delegate methods.

Cocos2D SpriteBuilder simple car with wheels

I'm currently trying out a few things. I already did this tutorial: https://www.makegameswith.us/tutorials/getting-started-with-spritebuilder/
However I'm a little stuck now.
I created a CCNode in SpriteBuilder and added 3 images. carbody.png and 2 wheel.png.
I made all objects physicsObjects.
In code I tried to connect them with joints and move them. However the carBody moves but the wheels keep staying on their place.
#import "Car.h"
#implementation Skater{
CCNode *_wheel1;
CCNode *_wheel2;
CCNode *_carBody;
CCPhysicsJoint *_bodyWheelJoint1;
CCPhysicsJoint *_bodyWheelJoint2;
}
- (id)init {
self = [super init];
if (self) {
[_wheel1.physicsBody setCollisionGroup: _carBody];
[_wheel2.physicsBody setCollisionGroup: _carBody;
[_carBody.physicsBody setCollisionGroup: _carBody];
_bodyWheelJoint1 = [CCPhysicsJoint connectedPivotJointWithBodyA:_wheel1.physicsBody bodyB: _carBody.physicsBody anchorA:_wheel1.anchorPointInPoints];
_bodyWheelJoint2 = [CCPhysicsJoint connectedPivotJointWithBodyA:_wheel2.physicsBody bodyB: _carBody.physicsBody anchorA:_wheel2.anchorPointInPoints];
}
return self;
}
And in Gameplay.m I only did this:
-(void)didLoadFromCCB{
[_car runAction:
[CCActionMoveTo actionWithDuration:10 position:CGPointMake(2000,_car.position.y)]];
}
The carBody moves, the wheels don't...
What am I missing?
Edit:
I made a few changes now but my wheels still keep staying on their place...
#import "Car.h"
#implementation Car{
CCNode *_wheel1;
CCNode *_wheel2;
CCNode *_carBody;
CCPhysicsJoint *_bodyWheelJoint1;
CCPhysicsJoint *_bodyWheelJoint2;
}
- (id)init {
self = [super init];
if (self) {
CCLOG(#"Car created");
[_wheel1.physicsBody setCollisionGroup:_carBody];
[_wheel2.physicsBody setCollisionGroup:_carBody];
[_carBody.physicsBody setCollisionGroup:_carBody];
_bodyWheelJoint1 = [CCPhysicsJoint connectedPivotJointWithBodyA:_wheel1.physicsBody bodyB:_carBody.physicsBody anchorA:_wheel1.anchorPointInPoints];
_bodyWheelJoint2 = [CCPhysicsJoint connectedPivotJointWithBodyA:_wheel2.physicsBody bodyB:_carBody.physicsBody anchorA:_wheel2.anchorPointInPoints];
}
return self;
}
-(void)moveCar:(int)distance{
CCLOG(#"Car should move");
CGPoint launchDirection = ccp(1, 0);
CGPoint force = ccpMult(launchDirection, distance);
[self.physicsBody applyForce:force];
}
#end
You're simply missing the point that move (and other) actions bypass physics. Once a node has a physics body, the use of CCAction* classes that change position and rotation is a no-no, and most others (ie scale) will not be applied to the physics body either but can still be used safely.
To move a physics object, apply an impulse or force to the physics body.

EXC BAD ACCESS With Cocos2d pushscene that works for blank scene

So I am trying to do a levelup screen with pushscene/popscene. The pushscene works when it's a blank scene, but not when it's my scene that I want. My scene/layer loads completely with all images and text displaying their exact correct content. After all of the images load there is a EXC BAD ACCESS that doesn't seem to be linked to any particular message being sent. Any help or further diagnostic tests would be appreciated.
I have a version where I commented out the sprites and labels and it still crashes. Is there something big that I'm missing?
EDIT: I've added the [self = [super init]] and [super onEnter] methods and still same problem. It's something else. Any ideas?
EDITEDIT: I think this has something to do with the optionsArray I'm using, not sure what objects need to be retained. The array is a CCArray and contains NSDictionaries of differing capacities
#import "LevelupLayer.h"
#import "GameManager.h"
#implementation LevelupLayer
#synthesize optionsArray,spritesArray;
#synthesize confirmLabel;
#synthesize counter;
+(id) scene {
CCScene *scene = [CCScene node];
CCLayer* layer = [LevelupLayer node];
[scene addChild:layer];
return scene;
}
-(void)onEnter
{
counter = 1; // for debugging
//Detemine what levelups are possible
GameManager* gm = [GameManager sharedManager]; //GameManager is a helper that oversees communication between layers and plists
optionsArray = [gm possibleLevelups]; //Access plist and formats data into expected format
[optionsArray retain];
int numPossibilities = [optionsArray count];
//Build Levelup layer based on possible options
CGSize size = [[CCDirector sharedDirector] winSize];
//float positionIncrement = (size.width / numPossibilities) - ((size.width/numPossibilities) * 0.5);
float positionIncrement = (size.width / numPossibilities);
float stripWidth = size.width / numPossibilities;
for (int i = 0; i < numPossibilities; i++) {
int slot = i+1;
NSDictionary* optionDict = [optionsArray objectAtIndex:i];
NSString* name = [optionDict objectForKey:#"name"];
NSString* title = [optionDict objectForKey:#"title"];
NSString* description = [optionDict objectForKey:#"description"];
// Add the sprite
CCSprite* optionSpite = [CCSprite spriteWithSpriteFrameName:[NSString stringWithFormat:#"%#.png",name]];
[self addChild:optionSpite];
[spritesArray addObject: optionSpite];
optionSpite.position = CGPointMake(slot * positionIncrement, size.height*0.60);
[optionSpite setAnchorPoint:CGPointMake(0.5f, 0.5f)];
// Add the description
CCLabelBMFont *optionDescription = [CCLabelBMFont labelWithString:description fntFile:#"bodyFont.fnt" width:stripWidth alignment:kCCTextAlignmentCenter];
[self addChild:optionDescription];
optionDescription.position = CGPointMake(slot * positionIncrement, size.height*0.30);
[optionDescription setAnchorPoint:CGPointMake(0.5f, 0.5f)];
// Add the title
CCLabelBMFont *optionTitle = [CCLabelBMFont labelWithString:title fntFile:#"titleFont.fnt" width:stripWidth alignment:kCCTextAlignmentCenter];
[self addChild:optionTitle];
optionTitle.position = CGPointMake(slot * positionIncrement, size.height*0.90);
[optionTitle setAnchorPoint:CGPointMake(0.5f, 0.5f)];
}
[self scheduleUpdate]; //Update only prints counter to see how many frames it lasts
}
-(BOOL) ccTouchBegan:(UITouch *)touch withEvent:(UIEvent *)event
{
return YES;
}
-(void) update:(ccTime)delta
{
CCLOG(#"counter: %d",counter);
counter++;
}
-(void) onExit
{
[optionsArray release];
}
#end
`
i don't see any [super onEnter]; or [super init] or anything like that ...that's your problem
First add [super onEnter] on the first line in onEnter.
Second add an init method like this:
-(id)init{
if (self=[super init]){}
}
Third add [super onExit] at the end of your onExit method
I figured it out, it wasn't anything to do with the code I posted, so sorry about that. It was just a stupid release call to a non-retained array. Retained the array previously and it worked fine. Sorry about crying wolf

Animations are not working properly(Cocos2d)

My problem is that animation are not working properly during movements of sprite.
Below is the code which i'm using
- (BOOL)ccTouchBegan:(UITouch *)touch withEvent:(UIEvent *)event {
[selSprite resumeSchedulerAndActions];
CGPoint touchLocation = [self convertTouchToNodeSpace:touch];
[self selectSpriteForTouch:touchLocation];
return TRUE;
}
- (void)selectSpriteForTouch:(CGPoint)touchLocation
{
CCSprite * newSprite = nil;
for (CCSprite *sprite in movableSprite) {
if (CGRectContainsPoint(sprite.boundingBox, touchLocation)) {
newSprite = sprite;
break;
}
}
if (newSprite != selSprite) {
[selSprite stopAllActions];
selSprite = newSprite;
_MoveableSpritetouch = TRUE;
}
if(_MoveableSpritetouch==TRUE)
{
movement=0;
CGRect selRect=CGRectMake((SpriteX)-20.0,(SpriteY)-20.0,40.0,40.0);
if(CGRectContainsPoint(selRect, touchLocation))
{
[selSprite stopAllActions];
}
if((selSprite==MarshallCar)&& (!(CGRectContainsPoint(selRect, touchLocation))))
{
movement=1;
[self reorderChild:selSprite z:5];
NSMutableArray *MarshallCarWalkAnimFrames = [NSMutableArray array];
for(int i = MarshallCarTouchStartFrameIndex; i <= MarshallCarTouchEndFrameIndex; ++i) {
[MarshallCarWalkAnimFrames addObject:[[CCSpriteFrameCache sharedSpriteFrameCache] spriteFrameByName:[NSString stringWithFormat:#"mcar_move_%d.png", i]]];
}
MarshallCarWalkAnim = [CCAnimation animationWithFrames:MarshallCarWalkAnimFrames delay:MarshallCarTouchFrameDelay];
walkMarshallCar = [CCRepeatForever actionWithAction:[CCAnimate actionWithAnimation:MarshallCarWalkAnim restoreOriginalFrame:NO]];
[selSprite runAction:walkMarshallCar];
}
}
}
- (void)ccTouchMoved:(UITouch *)touch withEvent:(UIEvent *)event {
if(gameState == TRUE){
CGPoint point = [touch locationInView:[touch view]];
point = [[CCDirector sharedDirector] convertToGL:point];
if (moveDifference.x>0)
{
selSprite.flipX = YES;
}
else {
selSprite.flipX = NO;
}
[selSprite setPosition:point];
}
}
-(void)ccTouchEnded:(UITouch *)touch withEvent:(UIEvent *)event
{
movement=0;
if(selSprite==MarshallCar)
{
[selSprite setDisplayFrame:[[CCSpriteFrameCache sharedSpriteFrameCache] spriteFrameByName:#"mcar_idle.png"]];
}
[selSprite pauseSchedulerAndActions];
}
The animation frames for movement are not playing every time during movements sometimes it plays or sometimes not. It plays properly when you touch and move your sprite for the first time but if touch another sprite and then again move the previous sprite the animations for movement won't play.
Is anyone having any idea why this is happening?
Please tell me the proper code for removing this bug.
Thanks!!!
I believe your problem is the if/else if construct:
if (_MoveableSpritetouch==TRUE)
{
CGRect selRect = CGRectMake(SpriteX - 20, SpriteY - 20, 40, 40);
if(CGRectContainsPoint(selRect, touchLocation))
{
[selSprite stopAllActions];
}
else if(...)
{
...
[selSprite runAction:walkMarshallCar];
}
}
If you don't see it right away: if the touch location is inside the selRect, you call stopAllActions on the selected (new) sprite and do nothing else. Only if the touch location is not within that rectangle you'll run the animation action.
I think the "in rectangle" check is superfluous since you've already called stopAllActions anyway a few lines above.
Allow me a few general remarks about your code:
The method "selectSpriteForTouch" tells me that you're selecting a new sprite. The function does that. But it does not advertise playing an animation. You might want to refactor this out to a seperate "playAnimationOnSelectedSprite" method.
You wrote 20.0 and 40.0 several times. This means you're actually passing a double (8 bytes floating point data type) to a CGPoint which takes floats (4 bytes floating point). Strictly speaking use either 20.0f with the suffixed "f" to denote it as a floating point data type, or use integers since you don't use the floating point part.
Why you put (SpriteX) in brackets is not clear to me, if you want to enhance readability you'll achieve more by adding spaces after commas and operands.
In Objective-C, use YES and NO macros instead of TRUE and FALSE.
The bool variable _MoveableSpritetouch seems superfluous, unless needed someplace else. In any case you should move the following if(_MoveableSpritetouch==TRUE) block to where you set the _MoveableSpritetouch variable to TRUE because it just makes your code harder to read by setting a variable, leaving the code block you were in ( if(selSprite != newSprite) ) just to jump into another block of code ( if(_MoveableSpritetouch==TRUE) ) that you already know you're going to run anyway.
if((selSprite==MarshallCar)&& (!(CGRectContainsPoint(selRect, touchLocation))))
{
movement=1;
[self reorderChild:selSprite z:5];
NSMutableArray *MarshallCarWalkAnimFrames = [NSMutableArray array];
for(int i = MarshallCarTouchStartFrameIndex; i <= MarshallCarTouchEndFrameIndex; ++i) {
[MarshallCarWalkAnimFrames addObject:[[CCSpriteFrameCache sharedSpriteFrameCache] spriteFrameByName:[NSString stringWithFormat:#"mcar_move_%d.png", i]]];
}
MarshallCarWalkAnim = [CCAnimation animationWithFrames:MarshallCarWalkAnimFrames delay:MarshallCarTouchFrameDelay];
walkMarshallCar = [CCRepeatForever actionWithAction:[CCAnimate actionWithAnimation:MarshallCarWalkAnim restoreOriginalFrame:NO]];
[selSprite runAction:walkMarshallCar];
}
I have added [selSprite stopAllActions];
and it started working correctly because in the touch ended method i was pausing the actions
but not resuming them so when i touch the sprite for the second time it was not playing animation because the action was paused.

Cocos2d: Preloading animation causes a crash

I am trying to preload an animation in the init method of my layer. I then call the animation if the screen is touched. The app crashes with no error message as soon as I touch the screen and seems it is to do with calling the preloaded animation. I would like to do it this way as it seems expensive to create the animation every time the screen is touched - which does seems to work though. Any tips greatly appreciated.
Sample Code:
In my header:
#interface Test : CCLayer {
NSMutableArray *wake;
CCSprite* ani;
CCAnimate *animate;
}
#end
In my implementation:
-(id) init {
if( (self=[super init])) {
// enable touches
self.isTouchEnabled = YES;
[[CCSpriteFrameCache sharedSpriteFrameCache] addSpriteFramesWithFile:#"ani.plist" texture:[[CCTexture2D alloc] initWithImage:[UIImage imageNamed:#"ani.png"]]];
ani = [CCSprite spriteWithSpriteFrameName:#"ani1.png"]; //comes from .plist file
ani.anchorPoint=ccp(0,0);
ani.position = ccp(700,65);
[self addChild:ani z:30];
wake = [NSMutableArray array];
for(int i = 1; i <= 4; i++) {
[wake addObject:[[CCSpriteFrameCache sharedSpriteFrameCache] spriteFrameByName:[NSString stringWithFormat:#"ani%d.png",i]]];
}
animate = [CCAnimate actionWithAnimation:[CCAnimation animationWithFrames:wake delay:1.0f] restoreOriginalFrame:FALSE];
}
return self;
}
Handling the touch:
- (void)ccTouchesEnded:(NSSet *)touches withEvent:(UIEvent *)event
{
// run the animation
[ani runAction:animate];
}
Animations in Cocos2d are not designed to be reused. You need to create a new one every time.
Problem solved by creating properties for the array and animation on the class using nonatomic,retain.
You only need to retain the animation but the array can be local.
self.myAnimation = [[CCAnimation animationWithFrames:myAniFramesArray delay:0.1f] retain];
Remember to make the property nonatomic, retain as stated by Chev and to release any objects you retain in the appropriate dealloc method.