NSArray removeObject removes all objects in array - ios

My project creates a bomb, an explosion, then checks for collisions in the explosions and finally delete the bombs that didn't get hit in a collision. This is explained in more detail here. The following code does this.
-(void)placeBomb
{
NSLog(#"Bomb placed");
_circle = [[CCSprite alloc]initWithFile:#"Circle.png"];
CGPoint circle0position = ccp(_cat.position.x , _cat.position.y);
CGPoint c0TileCoordt = [self tileCoordForPosition:circle0position];
CGPoint c0TileCoord = [self positionForTileCoord:c0TileCoordt];
_circle.position = c0TileCoord;
[self addChild:_circle];
id fade = [CCScaleTo actionWithDuration:3.5 scale:0];
[_circle runAction:fade];
double delayInSeconds = 3.0;
dispatch_time_t popTime = dispatch_time(DISPATCH_TIME_NOW, (int64_t)(delayInSeconds * NSEC_PER_SEC));
dispatch_after(popTime, dispatch_get_main_queue(), ^(void){
[self explosionFromPoint:c0TileCoordt withSprite:_circle];
});
}
- (BOOL)isLocationBombable:(CGPoint)tileCoord;
{
if ([self isValidTileCoord:tileCoord] && ![self isWallAtTileCoord:tileCoord])
{
return YES;
}
else
{
return NO;
}
}
-(void)explosionFromPoint:(CGPoint)explosionPoint withSprite:(CCSprite*)sprite;
{
//int
explosionLenght += 1;
if (explosionLenght >= 7) //Just for testing purposes, don't have a way to increase it naturally.
{
explosionLenght = 1;
}
BOOL topB = YES;
BOOL leftB = YES;
BOOL bottomB = YES;
BOOL rightB = YES;
int bombX = (explosionPoint.x + 1);
int bombY = (explosionPoint.y + 1);
int bombNegX = (explosionPoint.x - 1);
int bombNegY = (explosionPoint.y - 1);
CGPoint top = ccp(explosionPoint.x, bombY);
CGPoint left = ccp(bombNegX, explosionPoint.y);
CGPoint bottom = ccp(explosionPoint.x, bombNegY);
CGPoint right = ccp(bombX, explosionPoint.y);
if (![self isLocationBombable:top])
{topB = NO;}
if (![self isLocationBombable:left])
{leftB = NO;}
if (![self isLocationBombable:bottom])
{bottomB = NO;}
if (![self isLocationBombable:right])
{rightB = NO;}
for (int i = 0; i <= explosionLenght; i++) {
int bombX = (explosionPoint.x + i);
int bombY = (explosionPoint.y + i);
int bombNegX = (explosionPoint.x - i);
int bombNegY = (explosionPoint.y - i);
CGPoint top = ccp(explosionPoint.x, bombY);
CGPoint left = ccp(bombNegX, explosionPoint.y);
CGPoint bottom = ccp(explosionPoint.x, bombNegY);
CGPoint right = ccp(bombX, explosionPoint.y);
CCSprite *circleTop = [[CCSprite alloc]initWithFile:#"Circle.png"];
CCSprite *circleLeft = [[CCSprite alloc]initWithFile:#"Circle.png"];
CCSprite *circleBottom = [[CCSprite alloc]initWithFile:#"Circle.png"];
CCSprite *circleRight = [[CCSprite alloc]initWithFile:#"Circle.png"];
int scaleTime = 5;
if ([self isLocationBombable:top] && topB == YES)
{
circleTop.position = [self positionForTileCoord:top];
[self addChild:circleTop];
id fadeTop = [CCSequence actionOne:[CCMoveTo actionWithDuration:1 position:circleTop.position] two:[CCScaleTo actionWithDuration:scaleTime scale:0]];
[circleTop runAction:fadeTop];
}
if ([self isLocationBombable:left] && leftB == YES)
{
circleLeft.position = [self positionForTileCoord:left];
[self addChild:circleLeft];
id fadeLeft = [CCSequence actionOne:[CCMoveTo actionWithDuration:1 position:circleLeft.position] two:[CCScaleTo actionWithDuration:scaleTime scale:0]];
[circleLeft runAction:fadeLeft];
}
if ([self isLocationBombable:bottom] && bottomB == YES)
{
circleBottom.position = [self positionForTileCoord:bottom];
[self addChild:circleBottom];
id fadeBottom = [CCSequence actionOne:[CCMoveTo actionWithDuration:1 position:circleBottom.position] two:[CCScaleTo actionWithDuration:scaleTime scale:0]];
[circleBottom runAction:fadeBottom];
}
if ([self isLocationBombable:right] && rightB == YES)
{
circleRight.position = [self positionForTileCoord:right];
[self addChild:circleRight];
id fadeRight = [CCSequence actionOne:[CCMoveTo actionWithDuration:1 position:circleRight.position] two:[CCScaleTo actionWithDuration:scaleTime scale:0]];
[circleRight runAction:fadeRight];
}
}
[currentBombs addObject:sprite];
int a = [currentBombs count];
NSLog(#"cBCount: %i",a);
NSLog(#"Explosion done, call checkdamage");
[self schedule:#selector(checkDamageForBomb)];
[self performSelector:#selector(removeSprite:) withObject:sprite afterDelay:5];
}
-(void)removeSprite:(CCSprite *)sprite{
int aa = [currentBombs count];
NSLog(#"removeSprite startcall cbc: %i",aa);
if([currentBombs containsObject:sprite])
{
NSLog(#"Found sprite in array, deleted!");
[currentBombs removeObject:sprite];
int a = [currentBombs count];
NSLog(#"containObject cbc: %i",a);
}
else {
NSLog(#"Didn't find the object in array, didn't delete!");
int a = [currentBombs count];
NSLog(#"elseCO cbc: %i",a);
}
if (currentBombs.count == 0)
{
[self stopCheckDamage];
}
}
-(void)stopCheckDamage{
NSLog(#"StopCheckDamage");
[self unschedule:#selector(checkDamageForBomb)];
}
-(void)checkDamageForBomb{
for (CCSprite* bomb in currentBombs)
{
CGPoint bombPos = [self tileCoordForPosition:bomb.position];
for (int i = 0; i <= explosionLenght; i++) {
CGPoint playerPos = [self tileCoordForPosition:_cat.position];
int bombX = (bombPos.x + i);
int bombY = (bombPos.y + i);
int bombNegX = (bombPos.x - i);
int bombNegY = (bombPos.y - i);
CGPoint centre = bombPos;
CGPoint top = ccp(centre.x, bombY);
CGPoint left = ccp(bombNegX, centre.y);
CGPoint bottom = ccp(centre.x, bombNegY);
CGPoint right = ccp(bombX, centre.y);
//pastebin.com/biuQBfnv
if (CGPointEqualToPoint(top, playerPos) || CGPointEqualToPoint(left, playerPos) || CGPointEqualToPoint(bottom, playerPos) || CGPointEqualToPoint(right, playerPos))
{
playerHits += 1;
NSLog(#"Player hit %i",playerHits);
[currentBombs removeObject:bomb];
break;
}
}
}
}
My problem is with the -(void)removeSprite:(CCSprite *)sprite{method. This is supposed to delete only the one it got called with, but instead it kills them all, as you can see in this log.
15:14:02.499 Tile[1549:c07] Bomb placed
15:14:03.816 Tile[1549:c07] Bomb placed
15:14:05.501 Tile[1549:c07] cBCount: 1
15:14:05.501 Tile[1549:c07] Explosion done, call checkdamage
15:14:06.818 Tile[1549:c07] cBCount: 2
15:14:06.819 Tile[1549:c07] Explosion done, call checkdamage
15:14:06.819 Tile[1549:c07] CCScheduler#scheduleSelector. Selector already scheduled. Updating interval from: 0.00 to 0.00
15:14:10.503 Tile[1549:c07] removeSprite startcall cbc: 2 // has 2
15:14:10.503 Tile[1549:c07] Found sprite in array, deleted! //Line above and under
15:14:10.504 Tile[1549:c07] containObject cbc: 0 //Deleted 2, supposed to kill 1
15:14:10.505 Tile[1549:c07] StopCheckDamage
15:14:11.820 Tile[1549:c07] removeSprite startcall cbc: 0
15:14:11.820 Tile[1549:c07] Didn't find the object in array, didn't delete!
15:14:11.821 Tile[1549:c07] elseCO cbc: 0
15:14:11.821 Tile[1549:c07] StopCheckDamage
If you look at the log location in the code above, you will see that it deletes two instead of the one I wanted to. How can I prevent this behaviour or customise it to only kill the proper sprite?
Edit: To clarify
I thought that as I use the spritein the -(void)explosionFromPoint:(CGPoint)explosionPoint withSprite:(CCSprite*)sprite; this would somehow just give an unique ID to the object and know which one I was talking about. I'm new to coding.

You have the same sprite in the array twice. Both entries are removed. If you're going to use removeObject you need to create multiple sprite objects. Otherwise you need to use removeObjectAtIndex.

The problem as #HotLicks mentioned is you push the same instance in the array.
You should use a local CCSprite instance so your scheduled calls will use different instances. The reason for adding the same instance twice is because placeBomb is called twice before anything happens and in the second call you override the first instance you created. Since _circle is a pointer by the time both scheduled tasks will be called _circle will point to the same instance in both.
So change :
_circle = [[CCSprite alloc]initWithFile:#"Circle.png"];
To :
CCSprite *circle = [[CCSprite alloc]initWithFile:#"Circle.png"];
and update the rest of the method with circle and not _circle.

There is something that is going wrong in your code. Try checking something like that when you remove object from array do this:
if([currentBombs count]>1 )
[currentBombs removeObjectAtIndex:1];
If your code works fine. that is only one object removed from your array. Then I suggest you to check your removeSprite method print sprite object to check what's going wrong.
I think you can use tag values if you using same sprite objects.

You probably added the same (indiviudal) sprite twice?
Instead of logging a variable that has the count, you can log the object itself. It will print the value of its description. That will print out the class and address for all NSObject subclasses per default. Actually NSMutableArray (and similar NS... classes) print quite well.
NSLog ("%#",myObj);
Doing so you probably see more clearly what really happens.

Don't use class variable while creating bombs and try....
CCSprite * _circle = [[CCSprite alloc]initWithFile:#"Circle.png"];

Related

IOS - App freezes in game using Cocos2d [closed]

Closed. This question needs debugging details. It is not currently accepting answers.
Edit the question to include desired behavior, a specific problem or error, and the shortest code necessary to reproduce the problem. This will help others answer the question.
Closed 8 years ago.
Improve this question
I have just started developing a game, new to Objective-c. Basically at the moment you can move the spaceship around and the spaceship fires continually and there are asteroids coming from the top of screen. But my app freezes after a minute every time why is this?
CODE
#import "AppDelegate.h"
#pragma mark - HelloWorldLayer
// HelloWorldLayer implementation
#implementation HelloWorldLayer
// Helper class method that creates a Scene with the HelloWorldLayer as the only child.
+(CCScene *) scene
{
// 'scene' is an autorelease object.
CCScene *scene = [CCScene node];
// 'layer' is an autorelease object.
HelloWorldLayer *layer = [HelloWorldLayer node];
// add layer as a child to scene
[scene addChild: layer];
// return the scene
return scene;
}
// on "init" you need to initialize your instance
-(id) init
{
if( (self=[super init]) ) {
self.isTouchEnabled = YES;
moveLeft = NO;
moveRight = NO;
speed = 3;
fireSpeed = 8;
fireArray = [[NSMutableArray alloc] init];
CGSize winSize = [[CCDirector sharedDirector] winSize];
CCSprite *bg = [[CCSprite alloc] initWithFile:#"space_bg.jpg"];
[bg setPosition:ccp(160, 240)];
CGSize imageSize = bg.contentSize;
bg.scaleX = winSize.width / imageSize.width;
bg.scaleY = winSize.height / imageSize.height;
bg.position = ccp(winSize.width/2, winSize.height/2);
[self addChild:bg];
ship = [[CCSprite alloc] initWithFile:#"ship.png"];
[ship setPosition:ccp(100, 100)];
[self addChild:ship];
[self schedule:#selector(fireLoop:)];
NSTimer *timer = [NSTimer scheduledTimerWithTimeInterval:1
target:self
selector:#selector(fireCreate)
userInfo:nil
repeats:YES];
int t = arc4random() % 5;
timer3 = [NSTimer scheduledTimerWithTimeInterval:t
target:self
selector:#selector(asteroidTimer)
userInfo:nil
repeats:YES];
}
return self;
}
NSTimer *timer3;
NSTimer *timer2;
NSTimer *tmpTimer;
-(void)asteroidTimer {
int t = arc4random() % 10;
timer2 = [NSTimer scheduledTimerWithTimeInterval:0
target:self
selector:#selector(asteroidCreate)
userInfo:nil
repeats:YES];
}
-(void)asteroidCreate {
[timer2 invalidate];
[tmpTimer invalidate];
CGSize winSize = [[CCDirector sharedDirector] winSize];
[[CCSpriteFrameCache sharedSpriteFrameCache] addSpriteFramesWithFile:#"asteroids.plist"];
CCSpriteBatchNode *spriteSheet = [CCSpriteBatchNode batchNodeWithFile:#"asteroids.png"];
NSMutableArray *asteroidAnimFrames = [NSMutableArray array];
for(int i=1; i <= 8; i++) {
[asteroidAnimFrames addObject:[[CCSpriteFrameCache sharedSpriteFrameCache] spriteFrameByName:[NSString stringWithFormat:#"asteroid%d.png", i]]];
}
CCAnimation *moveAsteroidAnim = [CCAnimation animationWithFrames:asteroidAnimFrames delay:0.1f];
CCSprite *asteroid = [CCSprite spriteWithSpriteFrameName:#"asteroid1.png"];
int x = arc4random() % 320;
int y = arc4random() % 480;
asteroid.position = ccp(x, 480);
CCAction *asteroidAction = [CCRepeatForever actionWithAction:[CCAnimate actionWithAnimation:moveAsteroidAnim restoreOriginalFrame:NO]];
int q = arc4random() % 320;
int r = arc4random() % 10;
CCAction *moveAction = [CCMoveTo actionWithDuration:r position:ccp(q, -50)];
[asteroid runAction:moveAction];
[asteroid runAction:asteroidAction];
[spriteSheet addChild:asteroid];
[self addChild:spriteSheet];
int t = arc4random() % 10;
tmpTimer = [NSTimer scheduledTimerWithTimeInterval:t
target:self
selector:#selector(asteroidTimer)
userInfo:nil
repeats:YES];
}
-(void)fireLoop:(ccTime)fl {
if(fireArray.count > 0) {
for(int i = 0; i < fireArray.count; i++){
CCSprite *tmpFire = [fireArray objectAtIndex:i];
if(tmpFire.position.y < 500){
[tmpFire setPosition:ccp([tmpFire position].x, [tmpFire position].y + fireSpeed)];
}else{
[fireArray removeObjectAtIndex:i];
}
}
} else
{
}
}
-(void)fireCreate {
int shootPositionX = [ship position].x;
int shootPositionY = ([ship position].y) + 35;
CCSprite *fire;
fire = [[CCSprite alloc] initWithFile:#"fire.png"];
[fire setPosition:ccp(shootPositionX, shootPositionY)];
[fireArray addObject:fire];
[self addChild:fire];
[fire release];
}
-(void)gameLoop:(ccTime)dt {
int shipPositionX = 41/2;
if([ship position].x > shipPositionX){
[ship setPosition:ccp([ship position].x - speed, [ship position].y)];
}
}
-(void)ccTouchesMoved:(NSSet *)touches withEvent:(UIEvent *)event {
for(UITouch *t in touches){
CGPoint point = [self convertTouchToNodeSpace:t];
//if(point.x <= 160){
// moveRight = NO;
// moveLeft = YES;
//}else{
// moveRight =YES;
// moveLeft = NO;
//}
if(allowedToMove)
[ship setPosition:ccp(point.x, point.y + 76)];
}
}
-(void)ccTouchesBegan:(NSSet *)touches withEvent:(UIEvent *)event{
for(UITouch *t in touches){
CGPoint point = [self convertTouchToNodeSpace:t];
int shipX = [ship position].x;
int shipY = [ship position].y;
if (CGRectContainsPoint(CGRectMake (shipX - 20.5, shipY - 96, 50, 50), point))
{
allowedToMove = true;
}
}
}
-(void)ccTouchesEnded:(NSSet *)touches withEvent:(UIEvent *)event{
for(UITouch *t in touches){
CGPoint point = [self convertTouchToNodeSpace:t];
pointXLeft = point.x;
pointYLeft = point.y;
allowedToMove = false;
}
}
// on "dealloc" you need to release all your retained objects
- (void) dealloc
{
// in case you have something to dealloc, do it in this method
// in this particular example nothing needs to be released.
// cocos2d will automatically release all the children (Label)
// don't forget to call "super dealloc"
[super dealloc];
}
In general track down what seems to be causing it. What does the debugger say? Did you stop the execution or set a breakpoint somewhere that might be problematic? What about exception breakpoints? If those don't work take out your timers and see if you have any change. What about your fire methods?
Also your use of timers look weird and should be the first area you look. Side note, for the asteroids why are you not running the two actions you want to run on the new asteroid using CCSpawn?
In addition are you ever removing projectiles that you shoot from the screen or the asteroids that are no longer needed? If not you should.
But to answer your question the problem looks most likely a timer issue. Your use of timers are wrong. You have timer-3 call asteroid timer, which in turns creates timer-2 that calls asteroid create, which in turns invalidates timer-2 (that had an interval of 0 which makes it look a little unnecessary) and also creates a tmp-timer that calls asteroid create. Timer 3 is left to continuously do what it wants, which is all the steps above, at every x number of seconds. Even though tmp-timer seems to be trying to spawn more asteroid creations and take over the process, instead that looks both incorrect and odd. And you are doing this with a few global timers that can, seemingly have a valid timer but is overridden by a new allocation, just to be invalidated by another method randomly soon after even though what it was trying to invalidate technically was leaked and no longer has a pointer to it, thus invalidating the wrong instance to the wrong timer. Keep in mind your tmp-timer and timer-3 has random intervals. If tmp-timer has an interval of 3 seconds and timer-3 has a 9 second interval at some point something looks like it is bound to go wrong. It is hard to say by glancing at it just how, but it looks inevitable.
Or at least I think that is what is happening. Again the code makes very weird and potentially error prone use of timers.
Also timer-1 is going to keep creating sprites unchecked without those sprites ever going away. Something to think about for sure.
Edit (Solution):
If I understand what you are trying to do you want to create timers every variable amount of seconds that, at the end, creates a new asteroid. There are two things that stand out with this. You want something to call the creation of an asteroid, and something else to do the actual create:
- (void)countDownToCreateNextAsteroid
{
int minTime = 1;
int maxTime = 10;
int randomTime = (arc4random() % (maxTime - minTime)) + minTime;
id countdownDelay = [CCDelayTime actionWithDuration:randomTime];
id creationMethod = [CCCallFunc actionWithTarget:self selector:#selector(createAsteroid)];
id countdownToCreateSeq = [CCSequence actions:countdownDelay, creationMethod, nil];
[self stopAllActions];
[self runAction:countdownToCreateSeq];
}
- (void)createAsteroid
{
CGPoint someStartPosition = ccp(1024.0f, 512.0f);
CCSprite* asteroid = [CCSprite spriteWithFile:#"someImage.png"];
asteroid.position = someStartPosition;
// asterdoid dot yada yada = ....
[self addChild:asteroid];
float someDuration = 10.0f;
CGPoint someOffscreenPos = ccp(-1024.0f, 512.0f);
// Move off screen, then remove yourself...
id asteroidMove = [CCMoveTo actionWithDuration:someDuration
position:someOffscreenPos];
id asteroidRemove = [CCCallBlock actionWithBlock:^
{
[asteroid removeFromParentAndCleanup:YES];
}];
id asteroidSeq = [CCSequence actions:asteroidMove, asteroidRemove, nil];
[asteroid runAction:asteroidSeq];
[self countDownToCreateNextAsteroid];
}
Therefore in your onEnter you could have something to kick it all off and that's it:
- (void)onEnter
{
[super onEnter];
// yada yada
[self countDownToCreateNextAsteroid];
}
And the rest would take care of itself. This assumes this quick and simple approach is all you were after. The count down method sets up the count down, calls a method to create an asteroid at the end of that time, then exits. After the asteroid is created a new call to the count down method is invoked, which will setup another asteroid to be created at another random time. This is one quick and simple way to do what it looks you are were actually trying to accomplish with all those timers.

'CALayer position contains NaN: [nan nan]' when working with Bouncing Ball Array in C4

So I'm trying to just recreate a bouncing ball app using xcode and C4. What I'm trying to do right now is re-create some of the examples in Dan Shiffmans Nature of Code book in C4.
So far I've managed to create my own class of "Mover" objects that bounce around the screen. That's all working great. The next step was to start "applying some forces" to the movers and play around with that.
I seem to be getting an exception thats crashing the program now that I've added methods to apply force. I put on exception breakpoints and this line in C4Control.m is where it stops:
if(_animationDuration == 0.0f) super.center = center;
Looking into this more, I notice that neither super.center or center properties have a value assigned to them (both of NaN). I'm not sure what's causing this? This seemed to start once I decided to set each Movers initial velocity/accel to 0 and only assign "fores" (C4Vectors for wind and gravity) to create motion in these objects. When I comment out the 2 lines that apply each force, the problem goes away.
This is the implementation of my mover class:
//Init Method
- (id)initWithPoint:(CGPoint)point andCanvasController:(C4CanvasController *)canvasController {
self = [super init];
if (self) {
canvasFrame = CGPointMake(canvasController.canvas.width,
canvasController.canvas.height);
diam = [C4Math randomInt:70];
mass = diam * 0.5f;
CGRect ellipseFrame = CGRectMake(0, 0, diam, diam);
[self ellipse:ellipseFrame];
self.lineWidth = 0.0f;
self.fillColor = [UIColor colorWithRed:[C4Math randomInt:60]/60
green:[C4Math randomInt:100]/100.0f
blue:0.9f
alpha:0.3f];
int foo = arc4random() % (int)([canvasController.canvas height]);
int bar = arc4random() % (int)([canvasController.canvas width]);
self.location = [C4Vector vectorWithX:foo
Y:bar
Z:0.0f];
self.origin = self.location.CGPoint;
self.accel = [C4Vector vectorWithX:0.00f Y:0.0f Z:0.0f];
self.vel = [C4Vector vectorWithX:0.00f Y:0.0f Z:0.0f];
}
return self;
}
//Method to calculate updated mover position
-(void)update{
[self.vel add:self.accel];
[self.location add:self.vel];
self.origin = self.location.CGPoint;
[self.accel multiplyScalar:0];
[self checkBounds];
}
//Checking if mover has reached edge of screen
-(void)checkBounds{
if (self.origin.x > canvasFrame.x - diam){
self.location.x = canvasFrame.x - diam;
self.vel.x *= -1;
}
else if (self.origin.x < 0){
self.vel.x *= -1;
self.location.x = 0;
}
if(self.origin.y > canvasFrame.y - diam){
self.location.y = canvasFrame.y - diam;
self.vel.y *= -1;
}
else if(self.origin.y < 0){
self.vel.y *= -1;
self.location.y = 0;
}
}
//Start C4Timer to run update every 1/60th of a second
- (void) run{
updateTimer = [C4Timer automaticTimerWithInterval:1/60.0f
target:self
method:#"update"
repeats:YES];
}
//Calculate a force to apply to mover
- (void)applyForce:(C4Vector *)force{
[force divideScalar:mass];
[self.accel add:force];
}
In my C4WorkSpace.m file:
#import "C4Workspace.h"
#import "Mover.h"
#implementation C4WorkSpace{
Mover *testMover;
NSMutableArray *moverArray;
}
-(void)setup {
C4Vector *gravity = [C4Vector vectorWithX:0.01f Y:0.0 Z:0.0f];
C4Vector *wind = [C4Vector vectorWithX:0 Y:0.4 Z:0];
moverArray = [[NSMutableArray alloc]init];
//Not using foo/bar variables right now, but usually use them to create CGPoint p
//int foo = arc4random() % (int)([self.canvas height]);
//int bar = arc4random() % (int)([self.canvas width]);
for (int i = 0; i < 500; i++) {
CGPoint p = CGPointMake(0,0);
Mover *m = [[Mover alloc]initWithPoint:p andCanvasController:self];
[moverArray addObject:m];
}
for (Mover *m in moverArray){
[self addShape:m];
[m run];
[m applyForce:gravity];
[m applyForce:wind];
}
}
#end
Sorry for how long this was, any and all help is much appreciated!

destroy box2d body in cocos2d

I have a web and some insects i am throwing web and having collision detection with insects and destroying them. i have used bod2d for collision detection.
World Method
-(void)createWorld
{
// Define the gravity vector.
b2Vec2 b_gravity;
b_gravity.Set(0.0f, -9.8f);
// Do we want to let bodies sleep?
// This will speed up the physics simulation
bool doSleep = true;
// Construct a world object, which will hold and simulate the rigid bodies.
world = new b2World(b_gravity);
world->SetAllowSleeping(doSleep);
world->SetContinuousPhysics(true);
}
Create Web
-(void) createWeb
{
freeBodySprite = [CCSprite spriteWithFile:#"web1.png"];
[self addChild:freeBodySprite z:2 tag:TAG_WEB];
CGPoint startPos = CGPointMake(100, 320/1.25);
bodyDef.type = b2_staticBody;
bodyDef.position = [self toMeters:startPos];
bodyDef.userData = freeBodySprite;
float radiusInMeters = ((freeBodySprite.contentSize.width * freeBodySprite.scale/PTM_RATIO) * 1.0f);
shape.m_radius = radiusInMeters;
fixtureDef.shape = &shape;
fixtureDef.density = 0.01f;
fixtureDef.friction = 0.1f;
fixtureDef.restitution = 0.1f;
circularObstacleBody = world->CreateBody(&bodyDef);
stoneFixture = circularObstacleBody->CreateFixture(&fixtureDef);
freeBody = circularObstacleBody;
}
I have created my insect animation with spritesheet and then created b2body for that Sprite
-(b2Body *) createMovingBoxObstacle
{
//set this to avoid updating this object in the tick schedule
_ants.userData = (void *)YES;
b2BodyDef bodyDef_Ant;
bodyDef_Ant.type = b2_dynamicBody;
CGPoint startPos = ccp(520,winSize.height/7.6);
bodyDef_Ant.position = [self toMeters:startPos];
bodyDef_Ant.userData = _ants;
b2PolygonShape dynamicBox;
float tileWidth = ((_ants.contentSize.width * _ants.scale/PTM_RATIO) * 0.5f);
float tileHeight = ((_ants.contentSize.height * _ants.scale/PTM_RATIO) * 0.5f);
dynamicBox.SetAsBox(tileWidth, tileHeight);
b2FixtureDef fixtureDef_Ant;
fixtureDef_Ant.shape = &dynamicBox;
fixtureDef_Ant.friction = 0.7;
fixtureDef_Ant.density = 0.1f;
fixtureDef_Ant.restitution = 0.7;
staticBody = world->CreateBody(&bodyDef_Ant);
staticBody->CreateFixture(&fixtureDef_Ant);
VAMovingObstacle* moveableObject = [[VAMovingObstacle alloc] init];
moveableObject.startPoint = ccp(520,winSize.height/7.6);
moveableObject.endPoint = ccp(-50,winSize.height/7.6);
moveableObject.transitionTime = 20.0;
moveableObject.breakTime = 1.0;
moveableObject.obstacleSprite = _ants;
moveableObject.physicalBody = staticBody;
[moveableObject startMovement];
if (!movingObstacles) {
movingObstacles = [[NSMutableArray alloc] init];
}
[movingObstacles addObject:moveableObject];
[moveableObject release];
return staticBody;
}
VAMovingObstacle is a class which helps sprite along with b2body to move from left to right (runaction)
Here is my contact listner EndContact method
void ContactListener::EndContact(b2Contact* contact)
{
b2Body* bodyA = contact->GetFixtureA()->GetBody();
b2Body* bodyB = contact->GetFixtureB()->GetBody();
if (bodyA->GetUserData() != NULL && bodyB->GetUserData() != NULL) {
CCSprite *spriteA = (CCSprite *) bodyA->GetUserData();
CCSprite *spriteB = (CCSprite *) bodyB->GetUserData();
if (spriteA.tag == TAG_WEB && spriteB.tag >= TAG_ANT) {
}
else if (spriteA.tag >= TAG_ANT && spriteB.tag == TAG_WEB) {
[spriteA removeFromParentAndCleanup:YES];
[[HelloWorldLayer sharedLevel] WebCollisionWithInsect:bodyA];
}
}
}
when ever collision happens EndContact method calls WebCollisionWithInsect:bodyA
-(void) WebCollisionWithInsect:(b2Body*) bodyT{
world->DestroyBody(bodyT);
}
On world->DestroyBody(bodyT) line it give an error
Assertion failed: (IsLocked() == false), function DestroyBody, file
/Users/libs/Box2D/Dynamics/b2World.cpp, line 134.
Any Idea what i am doing wrong. ?
Edited
I have added this line in EndContact
destroyCollisionDetectionBody = objBody;
didInsectCollideWithWeb = YES;
Keeping reference of body i want to destroy.
Now i can destroy my body inside tick method on if check
Added these lines in tick method
if(didInsectCollideWithWeb)
{
[self unschedule:#selector(tick:)];
[freeBodySprite removeFromParentAndCleanup:YES]; // this is my web
world->DestroyBody(destroyCollisionDetectionBody);
world->DestroyBody(freeBody);
[self createWeb];
[self schedule:#selector(tick:)];
didInsectCollideWithWeb = NO;
}
But now problem is that when i am destroying my web i.e freebody. i cant recreate it. as i was doing it before by calling [self createWeb];
You are trying to destroy a box2d object while its being used in the simulation phase (as EndContact is called during that phase). The simulation phase is the one executed when you call the world's step method.
So what you should do is keep a reference to the body object you want to remove and after the box2d simulation phase (after the step method returns) execute the WebCollisionWithInsect with the desired body

Set sprite using background position

I am new to cocos2d so please help me if u can
I have background moving from right to left, and the background contains small windows with 3 rows of windows
_spaceDust1 = [CCSprite spriteWithFile:#"bg_front_spacedust.png"];
_spaceDust2 = [CCSprite spriteWithFile:#"bg_front_spacedust.png"];
CGPoint dustSpeed = ccp(0.1 , 0.1);
CGPoint bgSpeed = ccp(0.05 , 0.05);
[_backgroundNode addChild:_spaceDust1 z:0 parallaxRatio:dustSpeed positionOffset:ccp(0,winSize.height / 2)];
[_backgroundNode addChild:_spaceDust2 z:0 parallaxRatio:dustSpeed positionOffset:ccp(_spaceDust1.contentSize.width , winSize.height / 2)];
Now add enemies wich also move from right to left with same speed
_robbers = [[CCArray alloc] initWithCapacity:kNumAstroids];
for (int i = 0; i < kNumAstroids; ++i) {
CCSprite *asteroid = [CCSprite spriteWithSpriteFrameName:#"robber.png"];
asteroid.visible = NO;
[_batchNode addChild:asteroid];
[_robbers addObject:asteroid];
}
in update method:
double curTime = CACurrentMediaTime();
if (curTime > _nextRunemanSpawn) {
float randSecs = [self randomValueBetween:0.20 andValue:1.0];
_nextRunemanSpawn = randSecs + curTime;
float randY = 80.0;
float randY1 = 185.0;
float randY2 = 293.0;
float randDuration = [self randomValueBetween:5.2 andValue:5.2];
float randDuration1 = [self randomValueBetween:1.0 andValue:1.0];
CCSprite *asteroid = [_robbers objectAtIndex:_nextRobber];
_nextRobber++;
if (_nextRobber >= _robbers.count) {
_nextRobber = 0;
}
//[asteroid stopAllActions];
int winChoice = arc4random() % 3;
if (winChoice == 0) {
asteroid.position = ccp(winSize.width +asteroid.contentSize.width / 2 , randY);
asteroid.visible = YES;
}
else if(winChoice == 1){
asteroid.position = ccp(winSize.width +asteroid.contentSize.width / 2 , randY1);
asteroid.visible = YES;
}else {
asteroid.position = ccp(winSize.width +asteroid.contentSize.width / 2 , randY2);
asteroid.visible = YES;
}
[asteroid runAction:[CCSequence actions:[CCMoveBy actionWithDuration:randDuration position:ccp(-winSize.width-asteroid.contentSize.width, 0)],
[CCCallFuncN actionWithTarget:self selector:#selector(setInvisible:)],nil]];
All is going well but i want to set this enemies in to window and in random position
so how can i set x-argument of enemies so it can be fix in to window of the background?
For some reason I cannot comment so this is written as an answer. What exactly are you trying to do? I am a bit confused. It sounds like you want the X position to be set so that it is in one of 3 random positions, is that correct?

CCRepeatForever giving me EXC_BAD ACCESS

SO i'm creating a object called Enemy from the main game class
the enemy object is calling ccrepeatforever on a ccmoveto
i retained the move and repeat but still getting crashes
here is some code
for (Enemy *iceLord in self.children) {
if ([iceLord isKindOfClass:[Enemy class]] && iceLord.isIceLord==YES) { <-----this is where EXC_BAD_ACCESS is showing up
//DO Stuff
}
}
//inside of the enemyClass i have this method i think this is causing the crash but i can not see why
-(void)startWiggle{
int randomX = (arc4random()%30)+15;
int randomY = (arc4random()%40)+15;
int dieroll = arc4random()%2+1;
if (dieroll==1) {
randomY=randomY* -1;
}
if (self.position.y < winSize.height && self.position.y >0) {
id move = [CCMoveBy actionWithDuration:1 position:CGPointMake(-randomX, randomY)];
id forever = [CCRepeatForever actionWithAction:move];
[self runAction:forever];
[move retain];
[forever retain];
}
}

Resources