In my game i have some monsters and my hero. when i fire some bullets i remove bodies on collision either with monster or ground body, and also added a timer event to remove the bullet as
-(void) removeProjectile:(CCPhysicsSprite*)projectile{
dispatch_time_t removeProjectile = dispatch_time(DISPATCH_TIME_NOW, (int64_t)(1* NSEC_PER_SEC));
dispatch_after(removeProjectile, dispatch_get_main_queue(), ^(void){
int i;
for(i=0;i<deadBodyCount;i++)
{
if(deadBodies[i] == projectile.b2Body)
{
break;
}
}
if(i==deadBodyCount)
{
deadBodies[deadBodyCount]=projectile.b2Body;
deadBodyCount++;
}
});
}
but what happens my code crashes, don't know. please help me. If you have any approach to do this.
i am removing deadbodies in
-(void)removeDeadBodies{
for (int i=0; i<deadBodyCount; i++) {
if(deadBodies[i]){
if([self getPSTag:deadBodies[i]]==3){
CCPhysicsSprite *temp=(CCPhysicsSprite *)deadBodies[i]->GetUserData();
NSLog(#"%#",temp);
[[self getChildByTag:kTagParentNode]removeChild:temp cleanup:YES];
}
else if([self getPSTag:deadBodies[i]]==2){
for (int j=0; j<monsterCount; j++) {
if (monsters[j].b2Body==deadBodies[i]) {
[[self getChildByTag:kTagParentNode]removeChild:monsters[j] cleanup:YES];
monsters[j]=NULL;
}
}
}
_world->DestroyBody(deadBodies[i]);
deadBodies[i]=NULL;}
}
deadBodyCount=0;
}
-(int)getPSTag:(b2Body *)body{ //physics sprite tag value from a b2Body
CCPhysicsSprite *sprite=(CCPhysicsSprite*)body->GetUserData();
//Here is the error------------------------------------
if(sprite)
return (int)sprite.tag;
else
return 0;
}
and this is my begin contact method
-(void)beginContact:(b2Contact *)contact{
b2Body *bodyA=contact->GetFixtureA()->GetBody();
b2Body *bodyB=contact->GetFixtureB()->GetBody();
int collideCheck=[self checkForCollisionGroups:bodyA And:bodyB];
if(!collideCheck)
return;
switch (collideCheck) {
case 1:
for (int i=0; i<monsterCount; i++) {
if(monsters[i].isVisible){
//Checking if Projectile hitting the monster
//bodyA is monster and bodyB is projectile
if([self getPSTag:bodyA]==2&&[self getPSTag:bodyB]==3){
[monsters[i] monsterHit];
[self doExplosionAtPoint:monsters[i].position];
deadBodies[deadBodyCount++]=monsters[i].b2Body;
CCPhysicsSprite *x=(CCPhysicsSprite *)bodyB->GetUserData();
for (int i=0; i<10; i++) {
if(deadBodies[i]==x.b2Body)
return;
}
deadBodies[deadBodyCount++]=x.b2Body;
[self.top updateScore:100];
}//bodyA is Projectile and bodyB is Monster
else if([self getPSTag:bodyA]==3&&[self getPSTag:bodyB]==2){//if one of the body is projectile
[monsters[i] monsterHit];
[self doExplosionAtPoint:monsters[i].position];
CCPhysicsSprite *x=(CCPhysicsSprite *)bodyA->GetUserData();
for (int i=0; i<10; i++) {
if(deadBodies[i]==x.b2Body)
return;
}
deadBodies[deadBodyCount++]=x.b2Body;
deadBodies[deadBodyCount++]=monsters[i].b2Body;
[self.top updateScore:100];
}
}
}
break;
case 2:
if (jumping==YES) {
jumping=NO;
}
break;
case 3: //bullet hitting with any object
if([self getPSTag:bodyA]==3){//if one of the body is projectile
CCPhysicsSprite *x=(CCPhysicsSprite *)bodyA->GetUserData();
for (int i=0; i<10; i++) {
if(deadBodies[i]==x.b2Body)
return;
}
deadBodies[deadBodyCount++]=x.b2Body;
}
else if ([self getPSTag:bodyB]==3){
CCPhysicsSprite *x=(CCPhysicsSprite *)bodyB->GetUserData();
for (int i=0; i<10; i++) {
if(deadBodies[i]==x.b2Body)
return;
}
deadBodies[deadBodyCount++]=x.b2Body;
}
break;
case 4:
for (int i=0; i<monsterCount; i++) {
if(monsters[i].isVisible){
if( (bodyA==monsters[i].b2Body&&bodyB==_heroBody)||(bodyA==_heroBody&&bodyB==monsters[i].b2Body)) {
_heroBody->SetLinearVelocity(b2Vec2(-3*hero.scaleX, 5));
lifeCount=[_top updateHealthMeterWithDamage:10];
NSLog(#"collide");
moving=false;
}
}
}
break;
/*case 6:if([self getPSTag:bodyA]==33){
movingTile.position=((CCSprite*)bodyA->GetUserData()).position;
}
else
movingTile.position=((CCSprite*)bodyB->GetUserData()).position;*/
default:
break;
}
}
I looked at your code. I dont understand the use of removeProjectile: CCPhysicsSprite*)projectile{} method while you indirectly actually adding the projectiles to the deadBodies[] in the beginContact(). So i guess here you have two pointers pointing to same body. And when you run the removeDeadBodies() for loop, it actually first deletes the body and then in next iteration when again it gets the dangling pointer with no body then it crashes.
I commented the line [self removeProjectile:projectile]; in the createProjectileAt() method. And now everything is working fine.
I know it might be late to implement but a suggestion, to have better hold on your b2Bodies and safe deletion from world, you can take a struct datatype with data members as a Sprite and a BOOL variable say isDelete. And use this struct as the userdata of the body. So now you can just set this bool variable to YES inside beginContact() call back. And in you update: tick method after the world->step is over just iterate through the world of bodies and check the BOOL var that can be deleted. HEre you can safely delete the bodies. No need to maintain an array of deadBodies etc. Hence there will not be any synchronization issue.
Related
- (void) loadStartingTiles //16 by 24
{
[[CCSpriteFrameCache sharedSpriteFrameCache] addSpriteFramesWithFile:
#"clonespritesheet.plist"];
for(int x = 0; x < 16; x++) //minus 1 for one at the begining
{
for(int y = 0; y < 26; y++)
{
CCSprite *tempsprite;
switch (currentscreen[x][y])
{
case 0:
tempsprite = [CCSprite spriteWithSpriteFrameName:#"block0.png"];
break;
case 1:
tempsprite = [CCSprite spriteWithSpriteFrameName:#"block1.png"];
break;
}
tempsprite.position = ccp(y*20+10,(16-x)*20-10); //+10 for align for tile size
[self addChild:tempsprite z:3];
[tiles addObject:tempsprite];
}
}
}
So I make a bunch of sprites from an int array that tells them where they should be then i add them into the nsmutable array tile. then i move everything in the array to the left slowly, and im losing around 20 FPS. what is a more efficient way to make a tile system? my goal is to make randomly generated tiles later on.
- (void) manageTiles:(CGFloat)dt
{
int tileamount = [tiles count];
for(int i = 0; i < tileamount; i++)
{
CCSprite *tempsprite = [tiles objectAtIndex:i];
tempsprite.position = ccp(tempsprite.position.x-20*dt,tempsprite.position.y);
}
}
EDIT: the awnser is
int themap = -20;
- (void) manageTiles:(CGFloat)dt
{
tiles.position = ccp(tiles.position.x-10*dt,tiles.position.y);
NSLog(#"%d",themap);
if(tiles.position.x < themap)
{
CCSprite *tempsprite;
for(int i = 0; i < 16; i++)
{
[tiles removeChildAtIndex:0 cleanup:YES];
}
for(int i = 0; i < 16; i++)
{
switch (tilewall[i])
{
case 0:
tempsprite = [CCSprite spriteWithSpriteFrameName:#"block1.png"];
break;
case 1:
tempsprite = [CCSprite spriteWithSpriteFrameName:#"block1.png"];
break;
}
tempsprite.position = ccp((themap*-1)+500+10,((16-i)*20-10));
[tiles addChild:tempsprite];
}
themap = themap-20;
}
}
Where you are going wrong is, you are not using a CCSpriteBatchNode. A CCSpriteBatchNode will draw all of the tiles in one draw operation instead of doing one draw operation per tile. The drawbacks are, each tile in the batch node will have the same zOrder (in a way), and it all must use one source spritesheet per batch node. SO basically if you wanted different layers at different zOrders, or different layers which use different source images for the tiles, you would have to create multiple batch nodes, one for each.
http://www.cocos2d-iphone.org/api-ref/0.99.5/interface_c_c_sprite_batch_node.html
Preload the tempsprite variable outside your loop:
CSprite *sprite0 = [CCSprite spriteWithSpriteFrameName:#"block0.png"];
CSprite *sprite1 = [CCSprite spriteWithSpriteFrameName:#"block1.png"];
And then refer them to them in the loop:
switch (currentscreen[x][y])
{
case 0:
tempsprite = sprite0;
break;
case 1:
tempsprite = sprite1;
break;
}
or even better:
tempsprite = currentscreen[x][y] ? sprite1 : sprite0;
Oh, and your inner loop should refer to 24, not 26.
I have two methods, the generateRandomCard method gets called within the testMethod, where there is a for loop that runs 100 times. That way it works perfect, but if I set the for loop limit to 1000 or any other number greater than 100 it crashes. Can you see what's wrong??
- (void)testMethod {
Globals *myGlobals = [Globals sharedInstance];
int rankOfFirst = 0;
int rankOfSecond = 0;
int playerOneWin = 0;
int playerTwoWin = 0;
int ties = 0;
float firstPercent = 0;
float secondPercent = 0;
float tiePercent = 0;
FiveEval *evaluator = [FiveEval theEvaluator];
for (int i = 0; i < 100; i++) {
short fPF = [self generateRandomCard];
short fPS = [self generateRandomCard];
short sPF = [self generateRandomCard];
short sPS = [self generateRandomCard];
short fFlop = [self generateRandomCard];
short sFlop = [self generateRandomCard];
short tFlop = [self generateRandomCard];
short tur = [self generateRandomCard];
short riv = [self generateRandomCard];
rankOfFirst = [evaluator getRankOfSeven:fFlop
:sFlop
:tFlop
:tur
:riv
:fPF
:fPS];
rankOfSecond = [evaluator getRankOfSeven:fFlop
:sFlop
:tFlop
:tur
:riv
:sPF
:sPS];
if (rankOfFirst > rankOfSecond) {
playerOneWin++;
} else if (rankOfSecond > rankOfFirst) {
playerTwoWin++;
} else {
ties++;
}
[myGlobals.alreadyPickedCards removeAllObjects];
}
firstPercent = ((float)playerOneWin/(float)10000)*100;
secondPercent = ((float)playerTwoWin/(float)10000)*100;
tiePercent = ((float)ties/(float)10000)*100;
NSLog(#"First Player Equity: %f", firstPercent);
NSLog(#"Second Player Equity: %f", secondPercent);
NSLog(#"Tie Equity: %f", tiePercent);
}
- (short)generateRandomCard {
Globals *myGlobals = [Globals sharedInstance];
short i = arc4random()%51;
for (int j = 0; j < [myGlobals.alreadyPickedCards count]; j++) {
if (i == [[myGlobals.alreadyPickedCards objectAtIndex:j] shortValue]) {
[self generateRandomCard];
}
}
[myGlobals.alreadyPickedCards addObject:[NSNumber numberWithShort:i]];
return i;
}
You're probably overflowing your stack in the recursive call to -generateRandomCard. If you generate a card that's already been picked, you call yourself recursively (and ignore the result, which is a different bug). So, if your random number stream gave you an unlucky sequence that kept returning cards you've already picked, then you'll recurse infinitely until the stack overflows.
Change your card selection algorithm so that instead of using rejection sampling with the potential for infinite looping/recursion, it uses an algorithm with a bounded runtime such as the Fisher-Yates shuffle.
Not sure if this could in any way lead to the crash - It may be unrelated. However, it does look like you have a bug in the way you recursively call generateRandomCard when a card is found in the alreadyPickedCards array. Instead of
[self generateRandomCard];
I think you should have
return [self generateRandomCard];
You have in -testMethod:
[myGlobals.alreadyPickedCards removeAllObjects];
and in -generateRandomCard you have:
for (int j = 0; j < [myGlobals.alreadyPickedCards count]; j++) {
if (i == [[myGlobals.alreadyPickedCards objectAtIndex:j] shortValue]) {
[self generateRandomCard];
}
}
I can't bet for sure, but this looks like a situation where you removeAllObjects in 1 loop and access an out of bound index in another loop.
If you wanna play like this with arrays, I suggest you make copies of arrays and remove items from those copied arrays.
"Assertion failed: (m_bodyCount < m_bodyCapacity), function Add, file libs/Box2D/Dynamics/b2Island.h, line 65."
That is what it the crash leaves in the console.
[self removeChild:(CCSprite*)body->GetUserData() cleanup:YES];
body->SetTransform(b2Vec2(30, 30), 0); //moving the body out of the scene so it doesnt collide anymore!
world->DestroyBody(body);
I think im doing the right stuff..
#property (nonatomic, assign) b2Body *body;
Here is how i "make it" a property
I dont understand why it doesnt work, "body" is a proper pointer because I can retrieve infromation from the bodys UserData like tags that are set in the creatin of the body, so that shouldnt be a problem.. Do anyone know whats wrong with my code?
Thank you.
Edited in:
-(void) tick: (ccTime) dt //Main loop
{
if (ballFired) {
Magnet *aMagnet = [magnetArray objectAtIndex:0];
world->DestroyBody(aMagnet.body); //It crashes here!
}
int32 velocityIterations = 8;
int32 positionIterations = 1;
// Instruct the world to perform a single step of simulation. It is
// generally best to keep the time step and iterations fixed.
world->Step(dt, velocityIterations, positionIterations);
//Iterate over the bodies in the physics world
for (b = world->GetBodyList(); b; b = b->GetNext())
{
if (b->GetUserData() != NULL) {
//Synchronize the AtlasSprites position and rotation with the corresponding body
CCSprite *myActor = (CCSprite*)b->GetUserData();
myActor.position = CGPointMake( b->GetPosition().x * PTM_RATIO, b>GetPosition().y * PTM_RATIO);
myActor.rotation = -1 * CC_RADIANS_TO_DEGREES(b->GetAngle());
}
}
}
Isnt this outside the world step?
Edit 2:
ContactListener::ContactListener(){};
void ContactListener::BeginContact(b2Contact* contact)
{
// Box2d objects that collided
b2Fixture* fixtureA = contact->GetFixtureA();
b2Fixture* fixtureB = contact->GetFixtureB();
CCSprite* actorA = (CCSprite*) fixtureA->GetBody()->GetUserData();
CCSprite* actorB = (CCSprite*) fixtureB->GetBody()->GetUserData();
if(actorA == nil || actorB == nil) return;
b2WorldManifold* worldManifold = new b2WorldManifold();
contact->GetWorldManifold(worldManifold);
Kollisjon *kollisjon = [Kollisjon sharedKollisjon];
if (actorA.tag == 1) {
NSLog(#"OK1");
kollisjon.kollidertBody = fixtureB->GetBody();
kollisjon.world->DestroyBody(kollisjon.kollidertBody); //Isnt this ok?
}
else if (actorB.tag == 1) {
NSLog(#"OK2");
kollisjon.kollidertBody = fixtureA->GetBody();
kollisjon.world->DestroyBody(kollisjon.kollidertBody); //Isnt this ok?
}
}
Is it not outside the timestep? Please help me here...
Thank you
You must scan for contacts, store all contacts in an array, and then AFTER all contacts have been checked, you remove your bodies.
Checking for contacts:
std::vector<MyContact>::iterator pos;
for(pos = _contactListener->_contacts.begin();
pos != _contactListener->_contacts.end(); ++pos)
{
MyContact contact = *pos;
b2Body *bodyA = contact.fixtureA->GetBody();
b2Body *bodyB = contact.fixtureB->GetBody();
// Rocket explosion rect
if(bodyA->GetUserData() == NULL)
{
NSLog(#"NULL collision detected. (BODY A)");
hasDoneRocketCollisions = YES;
CCSprite *sprite = (CCSprite*) bodyB->GetUserData();
if(sprite.visible == NO) continue;
if(sprite.tag >= 200 && sprite.tag < 300)
{
index = sprite.tag - 200;
if([spriteTracker containsObject:sprite]) continue;
[spriteTracker addObject:sprite];
bodiesToKill[counter] = bodyB;
[enemyChargerIsAlive replaceObjectAtIndex:(int)(sprite.tag-200) withObject:[NSNumber numberWithInt:0]];
[ParticleController spawnExplosion:sprite.position inParent:currentDefaultNode];
}
else if(sprite.tag >= 300 && sprite.tag < 400)
{
index = sprite.tag - 300;
if([spriteTracker containsObject:sprite]) continue;
[spriteTracker addObject:sprite];
bodiesToKill[counter] = bodyB;
[enemyShooterIsAlive replaceObjectAtIndex:(int)(sprite.tag-300) withObject:[NSNumber numberWithInt:0]];
[ParticleController spawnExplosion:sprite.position inParent:currentDefaultNode];
counter++;
}
}
}
Later in your method after all contacts have been checked:
b2Body *dyingBody;
for(int i = 0; i < counter; i++)
{
CCSprite *dyingSprite;
dyingSprite = [spriteTracker objectAtIndex:i];
dyingSprite.visible = NO;
// Is player projectile
if(dyingSprite.tag >= 100 && dyingSprite.tag < 200)
{
CCParticleSystemQuad *dyingParticle;
dyingParticle = [particlesToKill objectAtIndex:particleIndex];
particleIndex++;
[dyingParticle stopSystem];
dyingBody = bodiesToKill[i];
dyingBody->SetActive(false);
[ParticleController spawnExplosion:dyingSprite.position inParent:currentDefaultNode];
[AudioController playExplosion];
dyingSprite.visible = NO;
if([_player currentShotType] == 1)
{
rocketHitBody->SetTransform(b2Vec2(dyingSprite.position.x/PTM_RATIO, dyingSprite.position.y/PTM_RATIO), 0.0);
rocketHitBody->SetActive(true);
}
}
}
Take note that these are just random chunks of code I have copy and pasted in. They are for example only and may only confuse you if you try to read them as exact instructions.
The point here is: You can not remove a body while it is being accessed by the step or contact listener. Finish using the contact listener and then remove your bodies.
How can i use a 'break' statement within a for-loop which continues form a specified label?
ex;
outer: for(int i = 0;i<[arABFBmatches count];i++){
for(int i = 0;i<[arABFBmatches count];i++){
//
break _____;
}
}
How to break to outer?
Hard to say from your question. I'd interpret it that you want to skip the rest of the iterations of the inner loop and continue the outer loop?
for (int i = 0; i < [arABFBmatches count]; i++) {
for (int j = 0; j < [arABFBmatches count]; j++) {
if (should_skip_rest)
break; // let outer loop continue iterating
}
}
Note that I changed the name of your inner loop invariant; using i in both is inviting insanity.
If you want to break from both loops, I wouldn't use a goto. I'd do:
BOOL allDoneNow = NO;
for (int i = 0; i < [arABFBmatches count]; i++) {
for (int j = 0; j < [arABFBmatches count]; j++) {
if (should_skip_rest) {
allDoneNow = YES;
break;
}
}
if (allDoneNow) break;
}
Roughly:
for(int i = 0;i<[arABFBmatches count];i++){
for(int j = 0;j<[arABFBmatches count];j++){
//
goto outer_done;
}
}
outer_done:
Objective-C does not have labelled break.
From Apple's Objective-C docs:
Objective-C is defined as a small but powerful set of extensions to
the standard ANSI C language.
So break and continue can be used wherever they are permitted in C.
continue can be used in looping constructs (for, while and do/while loops).
break can be used in those same looping constructs as well as in switch statements.
BOOL done = NO;
for(int i = 0;i<[arABFBmatches count] && !done; i++)
{
for(int i = 0;i<[arABFBmatches count] && !done;i++)
{
if (termination condition)
{
// cleanup
done = YES;
}
}
}
'break' will only get you out of the innermost loop or switch. You can use 'return' to exit out of a function at any time.Please have a look at the this link.
I am creating a game where where you complete shapes and the area gets filled in. However, if there is an enemy bird within your shape, it will not fill in. I want to make it so that if you do trap a bird within your shape, you will lose a life. How can I write an if statement that pretty much says if the below code doesn't take place, then you lose a life. If it helps losing a life is called doDie in my code.
-(void)fillMutablePath{
CGPoint movePoint = CGPointFromString([pointsToFillArray objectAtIndex:0]);
CGPathMoveToPoint(fillPath, NULL, movePoint.x, movePoint.y);
for (int i=0; i<[pointsToFillArray count]; i++) {
CGPoint tempPoint = CGPointFromString([pointsToFillArray objectAtIndex:i]);
CGPathAddLineToPoint(fillPath, NULL, tempPoint.x, tempPoint.y);
}
CGContextAddPath(gameViewObj._myContext, fillPath);
CGContextFillPath(gameViewObj._myContext);
CGPathRelease(fillPath);
[pointsToFillArray removeAllObjects];
}
if(fillMutablePath doesn't take place when making a shape){
[self doDie];
}
Like i said above, the reason fillMutablePath wouldn't take place is because a bird would be trapped within the shape. Any help would be much appreciated!!
I'm not entirely sure how and where you check if the bird is in the path. I think that right before filling you path you should do (see that if-else):
-(void)fillMutablePath{
CGPoint movePoint = CGPointFromString([pointsToFillArray objectAtIndex:0]);
CGPathMoveToPoint(fillPath, NULL, movePoint.x, movePoint.y);
for (int i=0; i<[pointsToFillArray count]; i++) {
//...
}
CGContextAddPath(gameViewObj._myContext, fillPath);
if(CGPathContainsPoint(fillPath, nil, bird.center, false)){
[self doDie];
}
else {
CGContextFillPath(gameViewObj._myContext);
}
CGPathRelease(fillPath);
[pointsToFillArray removeAllObjects];
}
If the bird is in the path die. Else, draw.
Edit after the clarification:
//...
CGContextAddPath(gameViewObj._myContext, fillPath);
CGContextFillPath(gameViewObj._myContext);
if(CGPathContainsPoint(fillPath, nil, bird.center, false)){
[self doDie];
}
CGPathRelease(fillPath);
[pointsToFillArray removeAllObjects];
}
Could the method not return a BOOL?
That way if the bird is in the shape, the method returns yes, and if the bird is not in the shape, it returns no.
Then you can use the return value in an if later on.