How to remove a sprite/b2body after a delay of time? - ios

Thanks advance for your time!
I'm doing a project with box2d. When I first touch the screen, a bird will be created in the box2d world, as follows
- (void)createBird
{
//init bird
bird = [CCSprite spriteWithFile:#"bird.png"];
bird.scale = 23/[bird boundingBox].size.width;
bird.position = center;
[self addChild:bird z:12];
}
Then, if the touch ends, the bird will be given a force
//add box2d body
b2BodyDef birdBodyDef;
birdBodyDef.type=b2_dynamicBody;
birdBodyDef.userData = (__bridge void*)bird;
birdBodyDef.position.Set(bird.position.x/PTM_RATIO,bird.position.y/PTM_RATIO);
birdBody=world->CreateBody(&birdBodyDef);
//create circle shape
b2CircleShape birdShape;
birdShape.m_radius=([bird boundingBox].size.width-6)/PTM_RATIO/2;
// Create shape definitio and add to body
b2FixtureDef birdFixtureDef;
birdFixtureDef.shape=&birdShape;
birdFixtureDef.density=1.6f;
birdFixtureDef.friction=0.3f;
birdFixtureDef.restitution=0.3f;
birdFixture = birdBody -> CreateFixture(&birdFixtureDef);
// Apply force
b2Vec2 force = b2Vec2(-2.0f*distance,-2.0f*distance);
birdBody->ApplyForce(force,birdBody->GetWorldCenter());
birdBody->SetLinearDamping(0.2f);
Then, when I touch the screen again, another bird will be created with the "createBird" method.
I want to delete the first bird after it has been created for 5 seconds. But at that time, the "CCSprite *bird" and "b2Body *birdBody" will be pointing to the second bird, so how can I delete the first one?
Thanks!

I'm not sure I understand your question exactly, but this might help. Call before you create the new bird.
if ( bird ) {
id delayTimeAction = [CCDelayTime actionWithDuration:5.0];
id removeMySprite = [CCCallFuncND actionWithTarget:bird selector:#selector(removeFromParentAndCleanup:) data:(void*)NO];
[bird runAction:[CCSequence actions: delayTimeAction, removeMySprite, nil]];
}
edit:
init an array
NSMutableArray *birds = [NSMutableArray new];
Then call this in create bird function
[birds addObject:newBird];
if ( [birds count] > 1 ) {
oldBird = [birds firstObject];
//Call remove function on oldBird
[birds removeObject:oldBird];
}

One way would be giving your bird tag and then saving them into an Nsmutuable dictionary.
Keep traversing that dictionary and then after your bool variable for destroy is been set destroy the body.....
I have done the same thing using classes.... The fact is you will need to destroy body any case. (Else you will face fps issues later)

Related

Obstacles With GameplayKit

Here is my question:
How can I convert an SKSpriteNodearray into an array of GKObstacles so that an agent can appropriately avoid these obstacles by using the goalToAvoidObstacles:(nonnull NSArray<GKObstacle *> *) maxPredictionTime:(NSTimeInterval)/?
It seems that the way in which I am creating the GKObstacle array is not allowing the GKGoal to identify these as obstacles correctly, no matter what weight I give to the goal.
I have currently several SKNodes that are added to an SKScene chapterScene. Each of these nodes are added to an array that I am storing called obstaclesArray. I have set up a test in the following way that works quite well:
WORKING OBSTACLES
- (void)didMoveToView:(nonnull SKView *)view {
[super didMoveToView:view];
// Add three obstacles in a triangle formation around the center of the scene.
NSArray<GKObstacle *> *obstacles = #[
[self addObstacleAtPoint:CGPointMake(CGRectGetMidX(self.frame),
CGRectGetMidY(self.frame) + 150)],
[self addObstacleAtPoint:CGPointMake(CGRectGetMidX(self.frame) - 200,
CGRectGetMidY(self.frame) - 150)],
[self addObstacleAtPoint:CGPointMake(CGRectGetMidX(self.frame) + 200,
CGRectGetMidY(self.frame) - 150)],
];
// The player agent follows the tracking agent.
self.player = [[OPlayer alloc] initWithScene:self
radius:50
position:CGPointMake(CGRectGetMidX(self.frame),
CGRectGetMidY(self.frame))];
self.player.agent.behavior = [[GKBehavior alloc] init];
[self.agentSystem addComponent:self.player.agent];
// Create the seek goal, but add it to the behavior only in -setSeeking:.
self.seekGoal = [GKGoal goalToSeekAgent:self.character];
// Add an avoid-obstacles goal with a high weight to keep the agent from overlapping the obstacles.
[self.player.agent.behavior setWeight:100 forGoal:[GKGoal goalToAvoidObstacles:obstacles maxPredictionTime:1]];
}
- (GKObstacle *)addObstacleAtPoint:(CGPoint)point {
SKShapeNode *circleShape = [SKShapeNode shapeNodeWithCircleOfRadius:50];
circleShape.lineWidth = 2.5;
circleShape.fillColor = [SKColor grayColor];
circleShape.strokeColor = [SKColor redColor];
circleShape.zPosition = 1;
circleShape.position = point;
[self addChild:circleShape];
GKCircleObstacle *obstacle = [GKCircleObstacle obstacleWithRadius:50];
obstacle.position = (vector_float2){point.x, point.y};
return obstacle;
}
While this works just fine, the issue I am having is that I can not get the behavior to identify the SKNode bodies array that I have as obstacles. I can only get these manually created GKCircleObstacle items to register as valid obstacles that the agent avoids. The reason this is an issue is because I am relying on many obstacles that are not in fact simple circles, but polygon structures some complex, some more straightforward. Nevertheless, I was attempting the following but my agent did not seem to avoid any of the obstacles that are of SKNode this way:
NOT WORKING OBSTACLES
NSArray *obstacles = [SKNode obstaclesFromNodePhysicsBodies:obstaclesArray];
/*
The other code seen above
*/
// Add an avoid-obstacles goal with a high weight to keep the agent from overlapping the obstacles.
[self.player.agent.behavior setWeight:100 forGoal:[GKGoal goalToAvoidObstacles:obstacles maxPredictionTime:1]];
Unfortunately, this does not seem to work as no matter how high I set the weight to, the agent never responds to avoiding the obstacles. Here is a log of the array I am passing to it:
2016-07-24 22:32:24.907 <GAME>[1516:475581] obstacles: (
"<GKPolygonObstacle: 0x14d20d70>",
"<GKPolygonObstacle: 0x14d20e10>",
"<GKPolygonObstacle: 0x14d20ba0>",
"<GKPolygonObstacle: 0x14d20bb0>",
"<GKPolygonObstacle: 0x14d20bc0>",
"<GKPolygonObstacle: 0x14d20a60>",
"<GKPolygonObstacle: 0x14d208b0>",
"<GKPolygonObstacle: 0x14d207d0>",
"<GKPolygonObstacle: 0x14d20a70>"
)
Each of which have all been clearly and appropriately initialized and added to the scene. Each of these elements are in fact responsive with the SpriteKit didBeginContact:(SKPhysicsContact *)contact method, so it seems that the nodes are properly handing the boundaries that they should be having.
If someone knows what I am doing wrong or a better way to turn each of these 'obstacle nodes' into GKObstacle's I would be much appreciative. Thanks in advance!
UPDATE: Here is a node with a physics body that I am testing:
SKSpriteNode *innerMap1 = [SKSpriteNode spriteNodeWithImageNamed:#"test"];
innerMap1.physicsBody = [SKPhysicsBody bodyWithTexture:[SKTexture textureWithImageNamed:#"test"] size:CGSizeMake(innerMap1.frame.size.width*0.92, innerMap1.frame.size.height*0.92)];
innerMap1.position = CGPointMake(-220, -140);
innerMap1.physicsBody.dynamic = NO;
[chapterSKSpriteNodes addObject:innerMap1];
Here is what the body looks like with skView.showsPhysics = YES;:
So I know that the physics body is what I need it to be. When I attempt to create the GKObstacle from this body however, it does not seem to register as an obstacle when being assigned in the avoidObstacles goal.
This sounds like a bug. You should check the return array from obstaclesFromNodePhysicsBodies and make sure you are actually getting back the obstacles you expect. I suspect you are not.
You can check the relevant vertices on your obstacles as such:
https://developer.apple.com/library/ios/documentation/GameplayKit/Reference/GKPolygonObstacle_Class/index.html#//apple_ref/occ/cl/GKPolygonObstacle
Make sure they are what you expect.

SCNAction of scenekit only executing action once

I want objects that I created to "fall" down the view of the controller. I also want an infinite amount of objects to fall, so continuing the animation until the the user goes to another view controller. I used a for-loop to make up to 100 objects. Here is the code...
SCNMaterial *blackMaterial = [SCNMaterial material];
blackMaterial.diffuse.contents = [UIColor blackColor];
int xcoordinate = arc4random_uniform(20);
int xcoordinateTwo = arc4random_uniform(20);
for (int i = 0; i < 100; i++){
SCNText *x = [SCNText textWithString:#"X" extrusionDepth: 2.75];
SCNNode *xNode = [SCNNode nodeWithGeometry:x];
xNode.position = SCNVector3Make(xcoordinate, 15.0, -60.0);
xNode.scale = SCNVector3Make(2.0, 2.0, 0.45);
x.materials = #[blackMaterial];
x.chamferRadius = 5.0;
SCNAction *moveTo = [SCNAction moveTo:SCNVector3Make(xcoordinate, -100.0, -60.0)duration:10.0];
[xNode runAction:moveTo];
SCNTorus * torus = [SCNTorus torusWithRingRadius:6.30 pipeRadius:2.30];
SCNNode *torusNode = [SCNNode nodeWithGeometry:torus];
torusNode.position = SCNVector3Make(xcoordinateTwo, 15.0, -60.0);
torus.materials = #[blackMaterial];
torusNode.eulerAngles = SCNVector3Make(-1.5708, 0, 0);
[torusNode runAction:moveTo];
[scene.rootNode addChildNode:torusNode];
[scene.rootNode addChildNode:xNode];
}
The problem for me is that only one of each object is being created instead of a 100. Can I anyone help me with what the issue is.
You really are creating 100 of each object:
NSLog(#"%#", scene.rootNode.childNodes);
They all have the same origin and destination positions because your calls to arc4random_uniform are outside the loop. They're all in the same place so it looks like just one node.
Moving the random number calls inside will disperse your nodes but they'll all be created at the same simulation time. To generate them continuously, you can construct an [SCNAction sequence:[...]] with an array of actions: a block action to create a new node and add it to the tree, followed by a delay action. Wrap that in a repeatActionForever: and make your root node perform that action. You'll also want to remove nodes when they reach destination or go out of view.

Is it possible to deactivate collisions in physics bodies in spriteKit?

I'm looking at doing the best way to collect items with my hero in my spriteKit game for iOs, and after to try a few ways to do it, my conclusion is the best way would be to have an item with a physic body which can detect collisions but don't collide with my hero. Is it possible to do it? to deactivate collisions of a physic body without deactivating its capabilities to detect collisions?? Sounds a bit contradictory I know... Because, the other way would be to create only a SKSpriteNode without physic body, then there wouldn't being collisions, but the way to "detect" collisions would be hand made and much more harder, because i would need to set a coordinate system detection in my hero, that when he will be in those specifics coordinates (over the item) then i'll make the item disappears. Any idea of how to do any of the two ways easier?
Check out collisionBitMask, categoryBitMask, and contactTestBitMask in the SKPhysicsBody class.
Essentially, physics bodies with the same collisionBitMask value will "pass-through" each other.
Correction: If the category and collision bits match, they will interact. If they do not match, those two will not interact. And if the collision bits, and category bits, are both zero, of course that item will interact with nothing whatsoever.
Then, you set the categoryBitMask and contactTestBitMask values to create an SKPhysicsContact Object on contact. Finally, your Class should adopt the SKPhysicsContactDelegate protocol. Use the - didBeginContact: method to detect and handle the SKPhysicsContact object.
static const uint8_t heroCategory = 1;
static const uint8_t foodCategory = 2;
--
food.physicsBody.categoryBitMask = foodCategory;
food.physicsBody.contactTestBitMask = heroCategory;
food.physicsBody.collisionBitMask = 0;
--
hero.physicsBody.categoryBitMask = heroCategory;
hero.physicsBody.contactTestBitMask = foodCategory;
hero.physicsBody.collisionBitMask = 0;
--
-(void)didBeginContact:(SKPhysicsContact *)contact {
SKPhysicsBody *firstBody = contact.bodyA;
SKPhysicsBody *secondBody = contact.bodyB;
}
Short answer:
yourHero.physicsBody.collisionBitMask = 0;
The default value of collisionBitMask is 0xFFFFFFFF (all bits set), that's why the node collides with others
you can do this by setting the categoryBitMask and contactBitMasks of the player and the item objects, but making sure that you do not set the collisionBitMask for either to interact with each other (see below)
static const int playerCategory = 1;
static const int worldCategory = 2;
static const int objectCategory = 4;
....
SKSpriteNode *player, *item;
....
player.physicsBody.categoryBitMask = playerCategory;
player.physicsBody.collisionBitMask = worldCategory;
player.physicsBody.contactTestBitMask = worldCategory;
....
item.physicsBody.categoryBitMask = objectCategory;
item.physicsBody.contactTestBitMask = playerCategory | worldCategory;
item.physicsBody.collisionBitMask = worldCategory;
this way the physics body will pick up collisions between the player and world objects, the item and world objects, but not between the player and items. It will trigger a call to didBeginContact, where you can delete your item node, add health, etc.
Hope this helps!
contactTestBitMask is used to trigger didBeginContact. collisionBitMask is used to activate physics on nodes.
// add a physics body
ship.physicsBody = [SKPhysicsBody bodyWithCircleOfRadius:ship.size.width/2];
// set the category for ship
ship.physicsBody.categoryBitMask = shipCategory;
// detect collisions with asteroids and edges
ship.physicsBody.contactTestBitMask = asteroidCategory | edgeCategory;
// physically collide with asteroids
ship.physicsBody.collisionBitMask = asteroidCategory;

Post Path Projection ( trail ) in cocos2d iOS

i want to create path projection like angry birds.
When i throw my ball i want to show projection on ball that on this path my ball has thrown.
I have seen a post on stackoverflow and i have implemented it .
if(trailtimer % 2 == 0 && isFired)
{
CCSprite * dot_Sprite = [[CCSprite alloc] initWithFile:#"whitedot.png"];
dot_Sprite.position = ccp(b->GetPosition().x * PTM_RATIO,b->GetPosition().y * PTM_RATIO);
dot_Sprite.scale = 0.1;
[self addChild:dot_Sprite z:2 tag:111];
}
}
In this code its working fine . but adding multiple sprites will slow my game.
Is there anyother option/way to implement path projection in my game.
You could try to create the dot like this:
CCSprite * dot_Sprite = [[CCSprite spriteWithFile:#"whitedot.png"];
and see if it helps.
This will add the dot to the auto release pool and make sure memory is free after usage

Collision without physics (Cocos2D + Box2D iOS)

I'm a beginner in the Cocos2D world, and even more in Box2D's. I'm developing an iOS game with these two engines, and what I want, is to have a guy (hero) to pick up coins in a scrolling map.
Well, the problem is the "Contact Listener". I can't seem to understand what it really does, and how I can use it to make the difference between my coins and other bodies (that are all static bodies) around the map.
I set all the coins .isSensor = true to disable collisions. I'm using MyContactListener from raywenderlich.com tutorial.
So, the main question is: How to know what type of body is in collision with the hero's body through the contact listener?
(i've read many tutorials and questions on here and I can't seem to know where to begin)
Thanks!
Edit:
Si, I finally got it to work with sprite.tag, but I can't destroy the bodies and the textures. Sometimes when using [textureB removeFromParentAndCleanup:YES]; I get EXC_BAD_ACCESS.
And as I understood, I can't destroy the body since I'm actually using it.
But when trying to use body->SetUserData(self); in my Hero class
#interface Hero : CCNode {
CCSprite * texture;
CGPoint position;
b2Body* body;
TypeCase type;
}
I get EXC_BAD_ACCESS in my update method:
for(b2Body *b = world_->GetBodyList(); b; b=b->GetNext()) {
if (b->GetUserData() != NULL) {
if ([b->GetUserData() isKindOfClass:[Hero class]])
{
Hero* hero = (Hero*)b->GetUserData();
CCSprite *heroSprite = hero.texture;
heroSprite.position = ccp(b->GetPosition().x * PTM_RATIO,
b->GetPosition().y * PTM_RATIO);
}
else if ([b->GetUserData() isKindOfClass:[Tile class]])
{
Tile* tile = (Tile*)b->GetUserData();
CCSprite *tileSprite = tile.texture;
tileSprite.position = ccp(b->GetPosition().x * PTM_RATIO,
b->GetPosition().y * PTM_RATIO);
}
}
}
I get EXC_BAD_ACCESS when calling isKindOfClass.
Hero userData
b2BodyDef heroBodyDef;
heroBodyDef.fixedRotation = true;
heroBodyDef.type = b2_dynamicBody;
heroBodyDef.position.Set(position.x/PTM_RATIO, position.y/PTM_RATIO);
body = world_->CreateBody(&heroBodyDef);
body->SetUserData(self);
Tile userData
b2BodyDef blockBodyDef;
blockBodyDef.type = b2_staticBody;
blockBodyDef.position.Set(position.x/PTM_RATIO, position.y/PTM_RATIO);
blockBodyDef.userData = self;
b2Body *body = world ->CreateBody(&blockBodyDef);
Don't use physics objects for coins.. this will affect on fps :)
create CCSprite for coins and detect collisions with coin sprite and body->UserData;
Add your coins in CCArray and write something like this in your update method:
CCSprite *sprite = (CCSprite*)yourObject.body->GetUserData();
Coins *coinObject;
CCARRAY_FOREACH(coinsArray, coinObject)
{
if (CGRectIntersectsRect([sprite boundingBox], [coinObject boundingBox]))
{
// collision detected;
}
}
if you want to use physics body for your coins, you need to create typedef for yourObject states, and in your contact listener void ContactListener::EndContact(b2Contact* contact) method change yourObject currentState. currentState is yourObject's property.
Do something like this in void ContactListener::EndContact(b2Contact* contact)
yourObject.currentState = isCollideOnCoin;
and after this in your update add this line:
if (yourObject.currentState == isCollideOnCoin)
{
// collision detected;
}
You can create a custom class (say BodyType) which indicates bodies type (in your case player/coin)
then you can use the user data property on your created bodies :
playerBody->SetUserData(new BodyType(1));
coinBody->SetUserData(new BodyType(2));
In the contact listener there is a method named beginContact which has an argument of type b2Contact* named contact.
Use this argument to get the two bodies :
b2Body* bodyA = contact->GetFixtureA()->GetBody();
b2Body* bodyB = contact->GetFixtureB()->GetBody();
then you can know if your player hit a coin by checking :
if ((bodyA->GetUserData().getType() == 1 && bodyB->GetUserData().getType() == 2) ||
(bodyA->GetUserData().getType() == 2 && bodyB->GetUserData().getType() == 1)) {
// Mark collision or save coin body in a member of your contact listener for removing it later
}
Of course make sure the user data is not null as all bodies will be passed to the contact listener and not all might have the user data member set.
EDIT
Regarding the BAD_ACCESS do the following :
if (b->GetUserData() != NULL) {
id myUserData = (id)b->GetUserData();
if ([myUserData isKindOfClass:[Hero class]])
{
Hero *hero = (Hero *)myUserData;
// Do your stuff
}
}

Resources