collision between two sprites - objective-c

Im trying to get collision between two sprites but it doesn't work they hit and not contact is detected.
In GameScene.h
#interface GameScene : SKScene <SKPhysicsContactDelegate>{
*
In GameScene.m
static const uint32_t ballCategory = 1<<0;
static const uint32_t obs1Category = 1<<1;
*
ball = [SKSpriteNode spriteNodeWithImageNamed:#"ball_cross"];
ball.position = CGPointMake(bx,by);
ball.name = #"ball";
ball.physicsBody = [SKPhysicsBody bodyWithRectangleOfSize:ball.size];
ball.physicsBody.dynamic = YES;
ball.physicsBody.categoryBitMask = ballCategory;
ball.physicsBody.collisionBitMask = obs1Category;
ball.physicsBody.contactTestBitMask = obs1Category;
*
obs1 = [SKSpriteNode spriteNodeWithImageNamed:#"obs1"];
obs1.position = obs1XY;
obs1.name = #"obs1";
obs1.physicsBody = [SKPhysicsBody bodyWithRectangleOfSize:obs1.size];
obs1.physicsBody.categoryBitMask = obs1Category;
obs1.physicsBody.contactTestBitMask = ballCategory;
obs1.physicsBody.collisionBitMask = ballCategory;
obs1.physicsBody.dynamic = YES;
*
-(void)didBeginContact:(SKPhysicsContact *)contact{
NSLog(#"hit");
}

In the collision bit mask and contact test bit masks you need to add the two bodies you want to receive notifications from.
IE:
obs1.physicsBody.categoryBitMask = obs1Category;
obs1.physicsBody.contactTestBitMask = ballCategory;
obs1.physicsBody.collisionBitMask = ballCategory || obs1Category;
ball.physicsBody.categoryBitMask = ballCategory;
ball.physicsBody.collisionBitMask = obs1Category || ballCategory;
ball.physicsBody.contactTestBitMask = obs1Category;
Son when the contact bit mask matched the combination between ball and orbs a notification will be generated.
I hope this helps mate!

Related

Moving two sprites with the same name at the same time

I'm trying to move both a rocket and word attached on the sprite to move up at the same time. However, I've set it such that only the words fly up and not the rocket. I was thinking of renaming rocket (used for words) to something else, but I feel like that would be a terribly inefficient way. Here's what I have so far
- (id)init
{
// Apple recommend assigning self with supers return value
self = [super init];
if (!self) return(nil);
// Enable touch handling on scene node
self.userInteractionEnabled = YES;
//Add a sprite
rocket[0] = [CCSprite spriteWithImageNamed:#"rocket_base_blue.png"];
rocket[0].scale = (0.15f);
rocket[0].positionType = CCPositionTypeNormalized;
rocket[0].position = ccp(0.5f,0.23f);
rocket[1] = [CCSprite spriteWithImageNamed:#"rocket_base_red.png"];
rocket[1].scale = (0.15f);
rocket[1].positionType = CCPositionTypeNormalized;
rocket[1].position = ccp(0.7f,0.23f);
rocket[2] = [CCSprite spriteWithImageNamed:#"rocket_base_green.png"];
rocket[2].scale = (0.15f);
rocket[2].positionType = CCPositionTypeNormalized;
rocket[2].position = ccp(0.9f,0.23f);
[self addChild:rocket[0]];
[self addChild:rocket[1]];
[self addChild:rocket[2]];
[self setupWordRockets:2];
[self intro];
// done
return self;
}
// -----------------------------------------------------------------------
#pragma mark - Game Events
// -----------------------------------------------------------------------
- (void)setupWordRockets:(int) wordLength
{
//randomizes the words
NSInteger rando = arc4random() % 2;
if (rando == 1) {
// Add a sprite
rocket[0] = [CCSprite spriteWithImageNamed:#"b.png"];
rocket[0].scale = (0.16f);
rocket[0].positionType = CCPositionTypeNormalized;
rocket[0].position = ccp(0.5f,0.2f);
rocket[1] = [CCSprite spriteWithImageNamed:#"a.png"];
rocket[1].scale = (.16f);
rocket[1].positionType = CCPositionTypeNormalized;
rocket[1].position = ccp(0.7f,0.2f);
rocket[2] = [CCSprite spriteWithImageNamed:#"g.png"];
rocket[2].scale = (.16f);
rocket[2].positionType = CCPositionTypeNormalized;
rocket[2].position = ccp(0.9f,0.2f);
} else {
rocket[0] = [CCSprite spriteWithImageNamed:#"g.png"];
rocket[0].scale = (0.16f);
rocket[0].positionType = CCPositionTypeNormalized;
rocket[0].position = ccp(0.5f,0.2f);
rocket[1] = [CCSprite spriteWithImageNamed:#"a.png"];
rocket[1].scale = (.16f);
rocket[1].positionType = CCPositionTypeNormalized;
rocket[1].position = ccp(0.7f,0.2f);
rocket[2] = [CCSprite spriteWithImageNamed:#"p.png"];
rocket[2].scale = (.16f);
rocket[2].positionType = CCPositionTypeNormalized;
rocket[2].position = ccp(0.9f,0.2f);
}
[self addChild:rocket[0]];
[self addChild:rocket[1]];
[self addChild:rocket[2]];
}
- (void)launchRocket
{
CCActionMoveTo *actionMove = [CCActionMoveTo actionWithDuration:1.0f position:ccp(0.5f, 0.9f)];
[rocket[0] runAction:actionMove];
CCActionMoveTo *actionMove2 = [CCActionMoveTo actionWithDuration:1.0f position:ccp(0.5f, 0.9f)];
[rocket[1] runAction:actionMove2];
CCActionMoveTo *actionMove3 = [CCActionMoveTo actionWithDuration:1.0f position:ccp(0.5f, 0.9f)];
[rocket[2] runAction:actionMove3];
[self scheduleOnce:#selector(explode:) delay:1];
}
I'm also wondering if there's also a more efficient way in randomizing the words because I feel like this is the brute force method, or at least a more tedious way of doing this. Any feedback would be greatly appreciated. Thanks!
Thats because when setupWordRockets function works, you store words in rocket array and when you call launchRocket, it moves the words.
You should separate those and use 2 arrays (one for words one for rocket sprites), so you can give proper actions to rockets as well.

How to animate SKNodes or SKSpriteNode to collide and bounce?

I have a few questions regarding animating SKNodes and or SKSpriteNodes. First one is how do i get them to move once the view loads? How do i make them collide and bounce off each other and go into the other direction. I am using an SKScene for this. Also is there a way to group SKSriteNodes with SKLabels?
I have:
#import <SpriteKit/SpriteKit.h>
//constants for the collision bitmap
static const uint32_t classicCategory = 1 << 0;
static const uint32_t arcadeCategory = 1 << 1;
static const uint32_t frenzieCategory = 1 << 2;
#interface HomeScene : SKScene <SKPhysicsContactDelegate> {
BOOL isSoundActionCompleted;
}
#end
Here is where I declare the SKSpriteNodes
SKSpriteNode *classicMode = [SKSpriteNode spriteNodeWithImageNamed:#"bubble.png"];
classicMode.size = CGSizeMake(130, 130);
classicMode.position = CGPointMake(self.size.width/2, self.size.height/2+50);
classicMode.name = #"classicMode";
[self addChild:classicMode];
SKLabelNode *classicTitle = [SKLabelNode labelNodeWithFontNamed:#"Noteworthy"];
classicTitle.text = #"Classic";
classicTitle.fontSize = 25;
classicTitle.horizontalAlignmentMode = SKLabelHorizontalAlignmentModeCenter;
classicTitle.verticalAlignmentMode = SKLabelVerticalAlignmentModeCenter;
classicTitle.fontColor = [SKColor darkGrayColor];
classicTitle.position = CGPointMake(self.size.width/2, self.size.height/2+50);
classicTitle.name = #"classicTitle";
[self addChild:classicTitle];
The other one here
SKSpriteNode *arcadeMode = [SKSpriteNode spriteNodeWithImageNamed:#"bubble.png"];
arcadeMode.size = CGSizeMake(130, 130);
arcadeMode.position = CGPointMake(self.size.width/2+68, self.size.height/2-65);
arcadeMode.name = #"arcadeMode";
[self addChild:arcadeMode];
SKLabelNode *arcadeTitle = [SKLabelNode labelNodeWithFontNamed:#"Noteworthy"];
arcadeTitle.text = #"Arcade";
arcadeTitle.fontSize = 25;
arcadeTitle.horizontalAlignmentMode = SKLabelHorizontalAlignmentModeCenter;
arcadeTitle.verticalAlignmentMode = SKLabelVerticalAlignmentModeCenter;
arcadeTitle.fontColor = [SKColor darkGrayColor];
arcadeTitle.position = CGPointMake(self.size.width/2+68, self.size.height/2-65);
arcadeTitle.name = #"arcadeTitle";
[self addChild:arcadeTitle];
Collision:
//collision and contact detection
self.physicsBody.categoryBitMask = arcadeCategory;
//physics and collision detection
classicMode.physicsBody = [SKPhysicsBody bodyWithRectangleOfSize:classicMode.size];
classicMode.physicsBody.dynamic = NO; //no gravity
classicMode.physicsBody.categoryBitMask = arcadeCategory;
arcadeMode.physicsBody = [SKPhysicsBody bodyWithRectangleOfSize:arcadeMode.size];
arcadeMode.physicsBody.dynamic = NO; //no gravity
arcadeMode.physicsBody.categoryBitMask = classicCategory;
And collision detection:
//collision detection
classicMode.physicsBody.categoryBitMask = classicCategory;
classicMode.physicsBody.collisionBitMask = arcadeCategory;
classicMode.physicsBody.contactTestBitMask = arcadeCategory;
arcadeMode.physicsBody.categoryBitMask = arcadeCategory;
arcadeMode.physicsBody.collisionBitMask = classicCategory;
arcadeMode.physicsBody.contactTestBitMask = classicCategory;
1) first problem in your code is that two static bodies never collide make them dynamic. so make atleast one or both bodies dynamic.
2)secondly for linear movement use body.physicsBody.velocity property with dynamic body bodies.
3)change your bitmask categories
classicMode.physicsBody.categoryBitMask = classicCategory;
classicMode.physicsBody.collisionBitMask = arcadeCategory;
classicMode.physicsBody.contactTestBitMask = arcadeCategory;
arcadeMode.physicsBody.categoryBitMask = arcadeCategory;
remember that contactTestBitMask uses in spritekit when two bodies contact or collide with each other and collisionBitMask is used to prevnet them to overlap or pass through each other
i cut arcadeMode.physicsBody.collisionBitMask = classicCategory;
arcadeMode.physicsBody.contactTestBitMask = classicCategory;
from your code because you alreay define it in
classicMode.physicsBody.categoryBitMask = classicCategory;
classicMode.physicsBody.collisionBitMask = arcadeCategory;
classicMode.physicsBody.contactTestBitMask = arcadeCategory; so no need to redfine it
use contactTestBitMask and collisionBitMask only once when you want two category interact with each other
4) handle all your collision and collision logic inside didbegincontact and didEndContact

Box2d: Elastic rope takes time to get back to its initial position

I am creating a rubber band in Box2d. Here is my code.
// init physics
[self initPhysics];
// Create ball body
CCSprite *ball = [CCSprite spriteWithFile:#"rubberband.png"];
ball.position = ccp(100, 100);
ball.tag = 1;
// [self addChild:ball];
//=======Params
// Position and size
b2Vec2 lastPos = b2Vec2(154.0/PTM_RATIO,65.0/PTM_RATIO); //set position first body
float widthBody = 2.0/PTM_RATIO;
float heightBody = 2.0/PTM_RATIO;
// Body params
float density = 0.0;
float restitution = 0.5;
float friction = 0.5;
// Distance joint
float dampingRatio = 0.85;
float frequencyHz = 10;
// Rope joint
float kMaxWidth = 50.0/PTM_RATIO;
// Bodies
int countBodyInChain = 68;
b2Body* prevBody;
//========Create bodies and joints
for (int k = 0; k < countBodyInChain; k++) {
b2BodyDef bodyDef;
if(k==0 || k==countBodyInChain-1) bodyDef.type = b2_staticBody; //first and last bodies are static
else bodyDef.type = b2_dynamicBody;
bodyDef.position = lastPos;
bodyDef.fixedRotation = YES;
b2Body* body = world->CreateBody(&bodyDef);
b2PolygonShape distBodyBox;
distBodyBox.SetAsBox(widthBody, heightBody);
b2FixtureDef fixDef;
fixDef.density = density;
fixDef.restitution = restitution;
fixDef.friction = friction;
fixDef.shape = &distBodyBox;
body->CreateFixture(&fixDef);
if(k>0) {
b2RevoluteJointDef armJointDef;
armJointDef.Initialize(prevBody, body, lastPos);
armJointDef.enableMotor = true;
armJointDef.enableLimit = true;
armJointDef.maxMotorTorque = 1;
world->CreateJoint(&armJointDef);
//Create rope joint
b2RopeJointDef rDef;
rDef.maxLength = (body->GetPosition() - prevBody->GetPosition()).Length() * kMaxWidth;
rDef.localAnchorA = rDef.localAnchorB = b2Vec2_zero;
rDef.bodyA = prevBody;
rDef.bodyB = body;
rDef.collideConnected = false;
world->CreateJoint(&rDef);
} //if k>0
lastPos += b2Vec2(widthBody, 0); //modify b2Vect for next body
prevBody = body;
} //for -loop
[self scheduleUpdate];
}
return self;
}
Problem is that when the app starts, rubber band appears in stretched form in U shape and then it gradually start contracting and coming to become straight horizontally. Can anyone please tell me why it is happening? I want the rubber band to be without being stretched in the beginning.
Best Regards
You don't update lastPos so all bodies occupy the same position initially. Box2D will force them apart and this could lead to the problem.

Box2D ground box

I'm pretty new to Box2D and I'm trying to get a simple scene with some boxes and a ground, but no matter what i do, the boxes just falls through the ground.
This is how i init my Box2D world:
b2Vec2 gravity;
gravity.Set(0.0f, -9.81f);
bool doSleep = true;
world = new b2World(gravity);
world->SetAllowSleeping(doSleep);
world->SetContinuousPhysics(true);
b2BodyDef groundBodyDef;
groundBodyDef.position.Set(0, 0); // bottom-left corner
b2Body* groundBody = world->CreateBody(&groundBodyDef);
b2EdgeShape groundBox;
groundBox.Set(b2Vec2(0,0), b2Vec2(screenSize.width/PTM_RATIO,0));
groundBody->CreateFixture(&groundBox, 0);
groundBox.Set(b2Vec2(0,screenSize.height/PTM_RATIO), b2Vec2(screenSize.width/PTM_RATIO,screenSize.height/PTM_RATIO));
groundBody->CreateFixture(&groundBox, 0);
groundBox.Set(b2Vec2(0,screenSize.height/PTM_RATIO), b2Vec2(0,0));
groundBody->CreateFixture(&groundBox, 0);
groundBox.Set(b2Vec2(screenSize.width/PTM_RATIO,screenSize.height/PTM_RATIO), b2Vec2(screenSize.width/PTM_RATIO,0));
groundBody->CreateFixture(&groundBox, 0);
And this is how i add objects to the scene:
b2BodyDef bodyDef;
bodyDef.type = b2_dynamicBody;
CGPoint p = CGPointMake(sprite.x, sprite.y);
bodyDef.position.Set(p.x/PTM_RATIO, p.y/PTM_RATIO);
bodyDef.userData = (void*)sprite;
b2Body *body = world->CreateBody(&bodyDef);
b2PolygonShape dynamicBox;
dynamicBox.SetAsBox(sprite.width/PTM_RATIO, sprite.height/PTM_RATIO);
b2FixtureDef fixtureDef;
fixtureDef.shape = &dynamicBox;
fixtureDef.density = 13.0f;
fixtureDef.friction = 0.0f;
fixtureDef.restitution = 0.5f;
body->CreateFixture(&fixtureDef);
body->SetType(b2_dynamicBody);
sprite.physicsBody = body;
And this is my ticker:
int32 velocityIterations = 8;
int32 positionIterations = 1;
world->Step(1.0f/60.0f, velocityIterations, positionIterations);
for (b2Body* b = world->GetBodyList(); b; b = b->GetNext())
{
if (b->GetUserData() != NULL)
{
SEPhysicsSprite * sprite = (SEPhysicsSprite*)b->GetUserData();
CGPoint newCenter = CGPointMake(b->GetPosition().x * PTM_RATIO,
b->GetPosition().y * PTM_RATIO);
sprite.x = newCenter.x;
sprite.y = newCenter.y;
sprite.rotation = b->GetAngle();
}
}
This is supposed to create a ground at the bottom of the screen, but my objects just falls through. If i but a box touching the left side of the screen and let it fall, it hits something, its like its a tiny little pixel that it's colliding with, just bouncing off a bit and then keeps falling.
I've looked at many examples both for iphone and c++ and other, and they all have similar init code and I cant see why my code wont work. Please halp!

schedule update issue

This might sound pretty straightforward. I've created a method and I've called it as below in the init method.
[self createNewSpr:ccp(s.width * 0.25,s.height-200)];
[self createNewSpr:ccp(s.width * 0.50,s.height-200)];
[self createNewSpr:ccp(s.width * 0.75,s.height-200)];
[self scheduleUpdate];
I've defined a for loop in my update method that imposes a gravity higher than that of the world on the sprites. Only the last call is affected by the new gravity but the first and second act on the world gravity. I am not sure what is wrong but I suspect it to be the scheduleUpdate. Please Help.
Edit: Update Method :
-(void) update: (ccTime) dt
{
int32 velocityIterations = 8;
int32 positionIterations = 1;
world->Step(dt, velocityIterations, positionIterations);
for (b2Body* b = world->GetBodyList(); b; b = b->GetNext())
{
if (b == sprite)
{
b->ApplyForce( b2Vec2(0.0,20*b->GetMass()),b->GetWorldCenter());
}
}
}
the createNewSpr:
-(void) createNewSpr:(CGPoint)pos {
//CGSize s = [CCDirector sharedDirector].winSize;
b2Vec2 startPos = [self toMeters:pos];
CGFloat linkHeight = 0.24;
CGFloat linkWidth = 0.1;
b2BodyDef bodyDef;
bodyDef.type = b2_dynamicBody;
bodyDef.position = startPos;
b2FixtureDef fixtureDef;
fixtureDef.density = 0.1;
b2PolygonShape polygonShape;
polygonShape.SetAsBox(linkWidth,linkHeight);
fixtureDef.shape = &polygonShape;
//first
b2Body* link = world->CreateBody( &bodyDef );
link->CreateFixture( &fixtureDef );
PhysicsSprite* segmentSprite = [PhysicsSprite spriteWithFile:#"sg.png"];
[self addChild:segmentSprite];
[segmentSprite setPhysicsBody:link];
b2RevoluteJointDef revoluteJointDef;
revoluteJointDef.localAnchorA.Set( 0, linkHeight);
revoluteJointDef.localAnchorB.Set( 0, -linkHeight);
for (int i = 0; i < 10; i++) {
b2Body* newLink = world->CreateBody( &bodyDef );
newLink->CreateFixture( &fixtureDef );
PhysicsSprite* segmentSprite = [PhysicsSprite spriteWithFile:#"sg.png"];
[self addChild:segmentSprite];
[segmentSprite setPhysicsBody:link];
revoluteJointDef.bodyA = link;
revoluteJointDef.bodyB = newLink;
world->CreateJoint( &revoluteJointDef );
link = newLink;//next iteration
}
PhysicsSprite* circleBodySprite = [PhysicsSprite spriteWithFile:#"cb.png"];
[self addChild:circleBodySprite z:1];
b2CircleShape circleShape;
circleShape.m_radius = circleBodySprite.contentSize.width/2 / PTM_RATIO;
fixtureDef.shape = &circleShape;
b2Body* chainBase =world->CreateBody( &bodyDef );
chainBase->CreateFixture( &fixtureDef );
[circleBodySprite setPhysicsBody:chainBase];
sprite = chainBase;
revoluteJointDef.bodyA = link;
revoluteJointDef.bodyB = chainBase;
revoluteJointDef.localAnchorA.Set(0,linkWidth);
revoluteJointDef.localAnchorB.Set(0,linkWidth);
world->CreateJoint( &revoluteJointDef );
}
The problem is with createNewSpr method...
You have assigned sprite to a body... So When you call this method three times, sprite refers to 3rd object only..
When you compare in update method, it just puts gravity on 3rd object...
Hope this helps.. :)