Ball makes contact with player but they are not even closer - sprite-kit

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;))

Related

ARKit plane visualization

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

moving several balls around a board sprite kit Xcode

I have a small app that I have several sprite "balls" that move around the board using MotionManager.gravity.
I have code that when one of any of the balls meets an edge the stop at the edge. also if one of any of the balls gets to the corner it stops in the corner. I have also written code in which if two or three of the balls are on any one edge and touch each other (they have collision detection turned on) they stop in they maintain their relative position to the other ball.
Here is my code for that:
//this allocs the motionManager and set up the gravity values.
self.motionManager = [[CMMotionManager alloc] init];
self.motionManager.deviceMotionUpdateInterval = 0.005f;
self.motionQueue = [[NSOperationQueue alloc] init];
self.motionQueue.name = [[[NSBundle mainBundle] bundleIdentifier] stringByAppendingString:#".motion"];
self.updatePosition = NO;
[self.motionManager startDeviceMotionUpdatesToQueue:self.motionQueue withHandler:^(CMDeviceMotion *motion, NSError *error) {
#synchronized(self) {
NSNumberFormatter *formatter = [[NSNumberFormatter alloc] init];
[formatter setNumberStyle:NSNumberFormatterDecimalStyle];
[formatter setMaximumFractionDigits:2];
[formatter setRoundingMode: NSNumberFormatterRoundUp];
{
_numberString = [formatter stringFromNumber:[NSNumber numberWithFloat:motion.gravity.x / 15.0 *200]];
_numberStringy = [formatter stringFromNumber:[NSNumber numberWithFloat:motion.gravity.y / 15.0 *200]];
n = [_numberString intValue];
y = [_numberStringy intValue];
}
self.gravity = motion.gravity;
self.updatePosition = YES;
}
}];
[self startDisplayLink];
//this shows how I stop the ball at the edge
- (void)handleDisplayLink:(CADisplayLink *)displayLink
{
#synchronized(self) {
if (!self.updatePosition)
return;
self.ball.position = CGPointMake(self.ball.position.x + (n*.99), self.ball.position.y + (y*.99));
self.ball2.position = CGPointMake(self.ball2.position.x + (n*.98), self.ball2.position.y +(y*.98));
self.ball3.position = CGPointMake(self.ball3.position.x + n, self.ball3.position.y + y);
if (ball.position.x <=340) {
ball.position = CGPointMake(340, ball.position.y);
}
if (ball.position.x >=684) {
ball.position = CGPointMake(684, ball.position.y);
}
if (ball.position.y <=148) {
ball.position = CGPointMake( ball.position.x,148);
}
if (ball.position.y >=620) {
ball.position = CGPointMake(ball.position.x,620);
}
//this stops it in the corner
if ( ball.position.x >=684 && ball.position.y <=148 ) {
ball.position = CGPointMake(684, 148);
}
if ( ball.position.x >=684 && ball.position.y >=620 ) {
ball.position = CGPointMake(684, 620);
}
if ( ball.position.x <=340 && ball.position.y <=148 ) {
ball.position = CGPointMake(340, 148);
}
if ( ball.position.x <=340 && ball.position.y >=620 ) {
ball.position = CGPointMake(340, 620);
}
//and this shows how I have set it up that if they are on one side and ball1 is in the corner and ball2 is next and ball3 is next they keep their relative positions. I thought if I set the corner ball and the rest had collision detection turned on they would just stop. They do but they push ball1 in the corner out of its position. So I wrote the code
if (ball2.position.y == ball3.position.y && ball.position.y == ball2.position.y && ball.position.x < ball2.position.x && ball2.position.x < ball3.position.x ) {
int s = (self.ball3.position.x - self.ball2.position.x);
int t = ((self.ball2.position.y - self.ball3.position.y));
k = t + s;
int f =(self.ball2.position.x - self.ball.position.x);
int g = ((self.ball2.position.y - self.ball.position.y));
j = f+g;
int h =(self.ball3.position.x - self.ball.position.x);
int i = ((self.ball3.position.y - self.ball.position.y));
l = h + i;
NSLog(#"ball2centerx:%f",ball2.position.x);
NSLog(#"ball2centery:%f",ball2.position.y);
NSLog(#"ballcenterx:%f",ball.position.x);
NSLog(#"ballcentery:%f",ball.position.y);
NSLog(#"ball3centerx:%f",ball3.position.x);
NSLog(#"ball3centery:%f",ball3.position.y);
CGPoint w = CGPointMake(340, 148);
CGPoint x = CGPointMake(340, 620);
CGPoint v = CGPointMake(684, 148);
CGPoint z = CGPointMake(684, 620);
CGPoint m = CGPointMake(370, 148);
CGPoint q = CGPointMake(370, 620);
CGPoint o = CGPointMake(654, 148);
CGPoint p = CGPointMake(654, 620);
if (CGPointEqualToPoint(ball.position,w) ) {
if (f < 30) {
ball2.position = CGPointMake(370, 148);
}
}
if (CGPointEqualToPoint(ball.position,x)) {
if (f<30) {
ball2.position = CGPointMake(370, 620);
}
}
if (CGPointEqualToPoint(ball3.position,v)) {
if (s <30){
ball2.position = CGPointMake(654, 148);
}
}
if (CGPointEqualToPoint(ball3.position,z)) {
if (s <30){
ball2.position = CGPointMake(654, 620);
}
}
if (CGPointEqualToPoint(ball2.position,m)) {
if (s <30){
ball3.position = CGPointMake(400, 148);
}
}
if (CGPointEqualToPoint(ball2.position,o)) {
if (f<30) {
ball.position = CGPointMake(624, 148);
}
}
if (CGPointEqualToPoint(ball2.position,q)) {
if (s <30){
ball3.position = CGPointMake(400, 620);
}
}
if (CGPointEqualToPoint(ball2.position,p)) {
if (f<30) {
ball.position = CGPointMake(624, 620);
}
}
}
The problem is that as I add more ball the code is going to become extremely complex.
is there a way to write it so that if one ball (any ball) is in the corner and another one is in position two it will stay there and so forth. Kind of like a generic code for all ball configuration. instead of writing it for every configuration?
If you are going to have a number of nodes in your scene, it becomes tedious and impractical to create a property for each one. A good solution is to create each node and add it into an array. The array will allow you to keep a reference to it. The important thing is to create each node with its own unique name. You can do this by creating a counter property.
#property (nonatomic) int myCounter;
Then when you create your node set the node's name property like this:
myCounter++;
myNode.name = [NSString stringWithFormat:#"myNode-%i",myCounter];
Once you have fully created the node, add it to a NSMutableArray like this:
[self.myArray addObject:myNode];
You can add as many nodes to the array as you need.
To enumerate through the array you can do this:
for (SKSpriteNode *object in myArray) {
// let's look for a node with the name of myNode-2
if([myNode.name isEqualToString:#"myNode-2"]) {
NSLog(#"Found it");
}
}
Here is an example to enumerate the array to check for each node's position and set its speed to zero dependent on the IF statement:
for (SKSpriteNode *object in myArray) {
if((object.position.x >= 300) && (object.position.y >= 300)) {
object.physicsBody.velocity = CGVectorMake(0, 0);
}
}
Remember that because you are storing the objects in your array as SKSpriteNodes and enumerating your array using SKSpriteNode, you have access to all the corresponding properties like speed, position, name, etc...

Can't get enemy to disappear upon collision

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

detect collision with sprite in sprite sheet

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

How to find collision detection between sprite and bezier

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