I usually use box2d for collision detection between two sprites like below code
- (void)beginContact:(b2Contact *)contact {
b2Fixture *fixtureA = contact->GetFixtureA();
b2Fixture *fixtureB = contact->GetFixtureB();
b2Fixture *fixtureC = contact->GetFixtureA();
b2Body *bodyA = fixtureA->GetBody();
b2Body *bodyB = fixtureB->GetBody();
b2Body *bodyC = fixtureC->GetBody();
CCSprite *spriteA = (CCSprite *) bodyA->GetUserData();
CCSprite *spriteB = (CCSprite *) bodyB->GetUserData()
LHBezierNode* bez = (LHBezierNode *) bodyC->GetUserData();
if ((spriteB == _enemy && spriteB == _hero) ||
(spriteA == _enemy && spriteA == _hero)) {
NSLog(#"enemy touched");
}
}
But I am confused how to detect collision detection between bazier and sprite.any help is appreciated.thanks
This is my bezier
LHBezierNode* myBezier = [LH bezierNodeWithUniqueName:#"BezierName"];
note : i use level helper for bezier.
I have solved it by adding tag to brezier, thanks
Related
I want to be able to visualize the planes that my ARKit app detects. How do I do that?
This is what I want to be able to do
Create a new AR project in Xcode with SceneKit and Obj-C, then add these to ViewController.m:
//as a class or global variable:
NSMapTable *planes;
//add to viewWillAppear:
configuration.planeDetection = ARPlaneDetectionHorizontal;
//to viewDidLoad:
planes = [NSMapTable mapTableWithKeyOptions:NSMapTableStrongMemory
valueOptions:NSMapTableWeakMemory];
//new functions:
- (void)renderer:(id<SCNSceneRenderer>)renderer didAddNode:(SCNNode *)node forAnchor:(ARAnchor *)anchor {
if( [anchor isKindOfClass:[ARPlaneAnchor class]] ){
[planes setObject:anchor forKey:node];
ARPlaneAnchor *pa = anchor;
SCNNode *pn = [SCNNode node];
[node addChildNode:pn];
pn.geometry = [SCNPlane planeWithWidth:pa.extent.x height:pa.extent.z];
SCNMaterial *m = [SCNMaterial material];
m.emission.contents = UIColor.blueColor;
m.transparency = 0.1;
pn.geometry.materials = #[m];
pn.position = SCNVector3Make(pa.center.x, -0.002, pa.center.z);
pn.transform = SCNMatrix4MakeRotation(-M_PI / 2.0, 1, 0, 0);
}
}
- (void)renderer:(id<SCNSceneRenderer>)renderer didUpdateNode:(SCNNode *)node forAnchor:(ARAnchor *)anchor {
if( [anchor isKindOfClass:[ARPlaneAnchor class]] ){
[planes setObject:anchor forKey:node];
ARPlaneAnchor *pa = anchor;
SCNNode *pn = [node childNodes][0];
SCNPlane *pg = pn.geometry;
pg.width = pa.extent.x;
pg.height = pa.extent.z;
pn.position = SCNVector3Make(pa.center.x, -0.002, pa.center.z);
}
}
- (void)renderer:(id<SCNSceneRenderer>)renderer didRemoveNode:(nonnull SCNNode *)node forAnchor:(nonnull ARAnchor *)anchor{
[planes removeObjectForKey:node];
}
You'll see translucent planes, give m.emission.contents a texture if you feel so.
Alternatively get the Example App from Apple in Swift
What am I missing here? Log says "Ball hits player" every time it collides with frame border or even floor objects if added.
(I´m very new to Sprite Kit and it is my first time working with collisions. This is making me crazy :D)
#import "MyScene.h"
static const int starHitCategory = 1;
static const int playerHitCategory = 2;
static const int ballHitCategory = 3;
#interface MyScene ()
#end
#implementation MyScene{
SKSpriteNode *player;
SKSpriteNode *redball;
SKSpriteNode *star;
}
-(id)initWithSize:(CGSize)size {
if (self = [super initWithSize:size]) {
/* Setup your scene here */
self.physicsWorld.contactDelegate = self;
self.physicsWorld.gravity = CGVectorMake(0.0f, -3.0f);
SKPhysicsBody* borderBody = [SKPhysicsBody bodyWithEdgeLoopFromRect:self.frame];
self.physicsBody = borderBody;
self.physicsBody.friction = 0.0f;
//initalizing player node
player = [SKSpriteNode spriteNodeWithImageNamed:#"player.png"];
player.physicsBody.categoryBitMask = playerHitCategory;
player.physicsBody.contactTestBitMask = playerHitCategory;
player.physicsBody.collisionBitMask = playerHitCategory;
player.position = CGPointMake(75,101);
player.physicsBody = [SKPhysicsBody bodyWithRectangleOfSize:player.size];
player.physicsBody.friction = 0.0f;
player.physicsBody.linearDamping = 0.5f;
player.physicsBody.mass = 10;
player.physicsBody.dynamic = YES;
player.physicsBody.usesPreciseCollisionDetection = YES;
player.physicsBody.allowsRotation = NO;
player.name = #"player";
[self addChild:player];
//initalizing Redball node
redball = [SKSpriteNode spriteNodeWithImageNamed:#"redbdall.png"];
redball.physicsBody = [SKPhysicsBody bodyWithCircleOfRadius:redball.frame.size.width/2];
redball.position = CGPointMake(290,200);
redball.physicsBody.usesPreciseCollisionDetection = YES;
redball.physicsBody.categoryBitMask = ballHitCategory;
redball.physicsBody.contactTestBitMask = ballHitCategory;
redball.physicsBody.collisionBitMask = ballHitCategory;
redball.physicsBody.friction = 2.0f;
redball.physicsBody.restitution = 1.0f;
redball.physicsBody.linearDamping = 0.0f;
redball.physicsBody.allowsRotation = NO;
[self addChild:redball];
//initalizing Star node
star = [SKSpriteNode spriteNodeWithImageNamed:#"star.png"];
star.physicsBody = [SKPhysicsBody bodyWithRectangleOfSize:star.size];
star.physicsBody.categoryBitMask = starHitCategory;
star.physicsBody.contactTestBitMask = starHitCategory;
star.physicsBody.collisionBitMask = starHitCategory;
star.position = CGPointMake(205,125);
[self addChild:star];
}
return self;
}
-(void)didBeginContact:(SKPhysicsContact *)contact
{
SKPhysicsBody *firstBody, *secondBody;
firstBody = contact.bodyA;
secondBody = contact.bodyB;
if(firstBody.categoryBitMask == playerHitCategory || secondBody.categoryBitMask == ballHitCategory)
{
NSLog(#"ball hits player");
}
}
#end
Thanks in advance
Your if statement will be satisfied if the player/ball object will collide with whatever object it can collide with since you are not also checking that the other object is of the required category.
You should change you if statement to be more strict since you also do not know which object of the two is the ball or the player or the floor:
if((firstBody.categoryBitMask == playerHitCategory && secondBody.categoryBitMask == ballHitCategory) ||
(firstBody.categoryBitMask == ballHitCategory && secondBody.categoryBitMask == playerHitCategory))
{
NSLog(#"ball hits player");
}
This way you can seperate the collision between the objects. Do the same for other object you desire to detect collision with.
Note that you are also setting the bit masks wrong for each physics body object. Read here
look for the section : "Collision and Contact Example: Rockets in Space"
It is very important to understand what each mask property is used for collisions to be detected correctly. (otherwise you indeed can go nuts about it;))
I have been trying all my resources and thanks to my dear friend Sangon, a member here and the IOS Games by tutorial book from Ray Wenderlich and what I am trying to do is to first of all set categories for each node and then if my player's bullet gets collided with the enemy, I want the enemy to disappear. Now I got some suggestions where to start from but this thing is driving me nuts I have tried and tried and the enemy does not disappear. Please help me on this. thanks.
static const uint32_t EnemyCategory = 1;
static const uint32_t bulletCategory = 2;
- (void) fire:(float)targetAngle {
bullet = [SKSpriteNode spriteNodeWithImageNamed:#"cannonbullet"];
bullet.position = _Player.position;
bullet.physicsBody = [SKPhysicsBody
bodyWithCircleOfRadius:bullet.frame.size.width/2];
bullet.physicsBody.categoryBitMask = bulletCategory;
bullet.physicsBody.collisionBitMask = bulletCategory | EnemyCategory;
bullet.physicsBody.contactTestBitMask = bulletCategory | EnemyCategory;
[self addChild:bullet];
int x = _Player.position.x + 1000 * cos(targetAngle);
int y = _Player.position.y + 1000 * sin(targetAngle);
[bullet runAction:[SKAction moveTo:CGPointMake(x, y) duration:2]];
}
- (void) didBeginContact:(SKPhysicsContact *)contact {
SKPhysicsBody *temp;
if (contact.bodyA.categoryBitMask < contact.bodyB.categoryBitMask) {
temp = contact.bodyB;
} else {
temp = contact.bodyA;
}
if (temp.categoryBitMask == EnemyCategory) {
[temp.node removeFromParent];
}
}
- (void) Enemy:(CGSize)size {
Enemy = [SKSpriteNode spriteNodeWithImageNamed:#"enemy1#2x"];
Enemy.position = CGPointMake(self.size.width/2, self.size.height/2);
Enemy.physicsBody = [SKPhysicsBody bodyWithRectangleOfSize:Enemy.frame.size];
Enemy.physicsBody.dynamic = NO;
Enemy.physicsBody.categoryBitMask = EnemyCategory;
[self addChild:Enemy];
}
The only problem that I can see is that you didn't set the SKPhysicsContactDelegate delegate properly
You will have to do something like this:
#interface MyScene : SKScene<SKPhysicsContactDelegate>
Once you do that you will also have to set the scene as the contactDelegate. It would look something like this:
self.physicsWorld.contactDelegate = self;
And after this is done your scene will receive the didBeginContact: and didEndContact: messages automatically. Those methods are not something that you call manually.
Here you can find a tutorial that uses collision detection (and removes the objects after collision detection)
From the documentation ’SKPhysicsContactDelegate’
An object that implements the SKPhysicsContactDelegate protocol can respond when two physics bodies are in contact with each other in a physics world. To receive contact messages, you set the contactDelegate property of a SKPhysicsWorld object. The delegate is called when a contact starts or ends.
Hope this helps
I have a contact listener to detect collisions between a bullet and an asteroid. Inside the beginContact function i check for the sprite types and fire a block from within this function.
The block is set by the scene class when it is allocated, before the contact listener is assigned to the box2dworld. The bullets and asteroids both get box2d bodies as well as sprites.
The problem is that i can access the sprites and do whatever i want with them (run actions, stop actions etc.).
However, as soon as i call the removeChild function on the sprite, I get an EXC_BAD_ACCESS in the b2Contact::Update on listener->BeginContact(this).
Can anyone point me in the right direction?
My tick function:
//Physics simulation
_world->Step(dt, 10, 10);
for(b2Body *b = _world->GetBodyList(); b; b=b->GetNext()) {
if (b->GetUserData() != NULL) {
CCSprite *sprite = (CCSprite *)b->GetUserData();
b2Vec2 b2Position = b2Vec2(sprite.position.x/PTM_RATIO,
sprite.position.y/PTM_RATIO);
float32 b2Angle = -1 * CC_DEGREES_TO_RADIANS(sprite.rotation);
b->SetTransform(b2Position, b2Angle);
}
}
for(CCSprite *sprite in spritesToDelete) {
[self removeSprite:sprite];
[spritesToDelete removeObject:sprite];
sprite = nil;
}
My contact listener callback block:
contactListener->contactBlock = ^(CCSprite *A, CCSprite *B) {
NSLog(#"A: %#", [A class]);
NSLog(#"B: %#", [B class]);
CCSprite *bullet = nil , *asteroid = nil;
if ([(NSString *)A.userData isEqualToString:#"asteroid"] && [(NSString *)B.userData isEqualToString:#"bullet"])
{
asteroid = A;
bullet = B;
} else if ([(NSString *)B.userData isEqualToString:#"asteroid"] && [(NSString *)A.userData isEqualToString:#"bullet"])
{
asteroid = B;
bullet = A;
}
if (asteroid != nil && bullet != nil) {
NSLog(#"Asteroid Hit!");
[asteroid stopAllActions];
[bullet stopAllActions];
//If I keep the line below uncommented, I get the error. Adding actions and stuff does not make an issue, only removal of sprite is when I get the error.
[spritesToDelete addObject:bullet];
}
};
I guess you put a pointer to your sprite in b2UserData of b2Body or b2Fixure in order to retrieve when collision happens. When you delete the sprite, this pointer will point to a deleted instance and thus you receive EXC_BAD_ACCESS.
You should nullify the pointer to eliminate the problem
I have a sprite sheet in which there a four images(bat1,bat2,bat3.bat4) , in Image there is a person with holding the bat and when all images are combined in animation then it looks like playing baseball.
below is the code that i have used to add sprite sheet.
[[CCSpriteFrameCache sharedSpriteFrameCache] addSpriteFramesWithFile:#"baseball.plist"];
spriteSheet = [CCSpriteBatchNode batchNodeWithFile:#"baseball.plist"];
[self addChild:spriteSheet];
background = [CCSprite spriteWithSpriteFrameName:#"bat4.png"];
background.position = ccp(220, 185);
background.tag = 10;
[self addChild:background];
for(int i = 1; i < 5; i++) {
[walkAnimFrames addObject:[[CCSpriteFrameCache sharedSpriteFrameCache] spriteFrameByName:[NSString stringWithFormat:#"bat%d.png", i]]];
}
CCAnimation *walkAnim = [CCAnimation animationWithSpriteFrames:walkAnimFrames delay:5.0f];
self.walkAction = [CCRepeatForever actionWithAction:[CCAnimate actionWithAnimation:walkAnim]];
[background runAction:_walkAction];
And i am detecting the collision of ball with bat. by below code
for (CCSprite *monster in _monsters) {
if (CGRectIntersectsRect(background.boundingBox, monster.boundingBox)) {
if (((background.position.x -5 > monster.position.x + (monster.contentSize.width/2)) && background.position.y > monster.position.y)) {
isCollision = 1;
[monstersToDelete addObject:monster];
}
// [monstersToDelete addObject:monster];
//[self addFishToBoat];
}
}
here wat happen is collision is detected but it always detect collision with rect of bat4.. as the bat is moving and the cgrect of all bat is different so even when bas is very much far away from bat1 then it will detect the collision because ball's rect will intersect with rect of bat4.
But i want the collision will de detect only when the ball will collide wiith different bats, i mean when bal hit with bat1,bat2,bat3,bat4 then only it will detect the collision rather then detect with bat4 always
Here is great tutorials by raywenderlich on sprites collision detection.
Or
You can try this tutorial also Sprite Collision
Hope these will help you.
You try this code it may help you...
_contactListener = new MyContactListener();
_world->SetContactListener(_contactListener);
// Preload effect
[[SimpleAudioEngine sharedEngine] preloadEffect:#"hahaha.caf"];
std::vector<b2Body *>toDestroy;
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();
if (bodyA->GetUserData() != NULL && bodyB->GetUserData() != NULL) {
CCSprite *spriteA = (CCSprite *) bodyA->GetUserData();
CCSprite *spriteB = (CCSprite *) bodyB->GetUserData();
if (spriteA.tag == 1 && spriteB.tag == 2) {
toDestroy.push_back(bodyA);
} else if (spriteA.tag == 2 && spriteB.tag == 1) {
toDestroy.push_back(bodyB);
}
}
}
std::vector<b2Body *>::iterator pos2;
for(pos2 = toDestroy.begin(); pos2 != toDestroy.end(); ++pos2) {
b2Body *body = *pos2;
if (body->GetUserData() != NULL) {
CCSprite *sprite = (CCSprite *) body->GetUserData();
[_spriteSheet removeChild:sprite cleanup:YES];
}
_world->DestroyBody(body);
}
if (toDestroy.size() > 0) {
[[SimpleAudioEngine sharedEngine] playEffect:#"hahaha.caf"];
}
Fore more detail about this code visit
http://www.raywenderlich.com/606/how-to-use-box2d-for-just-collision-detection-with-cocos2d-iphone