SKSpriteNode collision detection error - ios7

I am creating a game where a ball is suppose to bounce off from platforms. I have set up physics properties for the ball and the platform(platform only attains physics property when it's below the ball). My problem is: the ball is not bouncing (I have applied impulse in didbegincontact method) when the ball makes contact with the platform, it however detects contact.
Here is my didBeginContact Code:
- (void) didBeginContact:(SKPhysicsContact *)contact {
SKSpriteNode *firstNode, *secondNode;
firstNode = (SKSpriteNode*) contact.bodyA.node;
secondNode = (SKSpriteNode*) contact.bodyB.node;
if ((contact.bodyA.categoryBitMask == ballCategory) && (contact.bodyB.categoryBitMask == solidPlatformCategory)) {
NSLog(#"Platform Hit");
CGPoint contactPoint = contact.contactPoint;
[_ball.physicsBody applyImpulse:CGVectorMake(0, 4) atPoint:contactPoint];
}
}
///// Here is the code for SKSpriteNode Ball
- (void) addBall {
_myBall = [SKSpriteNode spriteNodeWithImageNamed:#"ball.png"];
if (UI_USER_INTERFACE_IDIOM() == UIUserInterfaceIdiomPad) {
_myBall.scale = 0.4;
} else {
_myBall.scale = 0.3;
}
_ball.position = CGPointMake(self.frame.size.width/2, _solidPlatform.position.y + 2.5*_ball.size.height);
_ball.zPosition = 2;
_ball.name = #"doodle";
_ball.physicsBody = [SKPhysicsBody bodyWithRectangleOfSize:_myDoodle.frame.size];
_ball.physicsBody.mass = 1.0;
_ball.physicsBody.restitution = 0.8;
_ball.physicsBody.dynamic = YES;
_ball.physicsBody.allowsRotation = NO;
_ball.physicsBody.usesPreciseCollisionDetection = YES;
_ball.physicsBody.categoryBitMask = ballCategory;
_ball.physicsBody.collisionBitMask = solidPlatformCategory;
_ball.physicsBody.contactTestBitMask = solidPlatformCategory;
//SKAction *moveUpAction = [SKAction moveByX:0.0 y:8*numberOfPlatforms duration:0.5];
[self addChild:_ball];
}
////Platform has been defined as (not a complete code):
_solidPlatform7.physicsBody = [SKPhysicsBody bodyWithRectangleOfSize:_solidPlatform7.frame.size];
_solidPlatform7.physicsBody.dynamic = NO;
_solidPlatform7.physicsBody.affectedByGravity = NO;
_solidPlatform7.physicsBody.usesPreciseCollisionDetection = YES;
_solidPlatform7.physicsBody.categoryBitMask = solidPlatformCategory;
PS: I am not getting any collusion detection if I define platform as bodyWithEdgeFromRect

difficult to answer without seeing some code, but I'll try:
If the ball does not bounce at all, check the restitution property. Higher values provide a higher "bounciness":
ball.physicsBody.restitution=0.8;
If you want the ball to bounce endless between bottom and ceiling you can invert the gravity after each collision:
self.physicsWorld.gravity = CGVectorMake(0, self.physicsWorld.gravity.dy * (-1));
Hope that helps. If not, please share some code.
I've tried your code. With some smaller changes it works:
- (void) addBall {
// Bottom platforms
for (int i=0; i<10; i++) {
SKSpriteNode *mySprite = [SKSpriteNode spriteNodeWithColor:[UIColor redColor] size:CGSizeMake(40, 20)];
CGPoint location = CGPointMake(i*40+60, 10);
//mySprite.size =CGSizeMake(20, 40);
mySprite.position=location;
mySprite.physicsBody=[SKPhysicsBody bodyWithRectangleOfSize:mySprite.size];
mySprite.physicsBody.dynamic=false;
mySprite.physicsBody.categoryBitMask=solidPlatformCategory;
[self addChild:mySprite];
}
_ball = [SKSpriteNode spriteNodeWithImageNamed:#"ball.png"];
if (UI_USER_INTERFACE_IDIOM() == UIUserInterfaceIdiomPad) {
_ball.scale = 0.4;
} else {
_ball.scale = 0.3;
}
_ball.position = CGPointMake(self.frame.size.width/2, self.frame.size.width/2);
_ball.zPosition = 2;
_ball.name = #"doodle";
//_ball.physicsBody = [SKPhysicsBody bodyWithRectangleOfSize:_myDoodle.frame.size];
_ball.physicsBody=[SKPhysicsBody bodyWithCircleOfRadius:_ball.size.width/2];
_ball.physicsBody.mass = 1.0;
_ball.physicsBody.restitution = 1;
_ball.physicsBody.dynamic = YES;
_ball.physicsBody.allowsRotation = NO;
_ball.physicsBody.usesPreciseCollisionDetection = YES;
_ball.physicsBody.categoryBitMask = ballCategory;
_ball.physicsBody.collisionBitMask = solidPlatformCategory;
_ball.physicsBody.contactTestBitMask = solidPlatformCategory;
//SKAction *moveUpAction = [SKAction moveByX:0.0 y:8*numberOfPlatforms duration:0.5];
[self addChild:_ball];
}

Related

Objective C Sprite Kit Multiple Collisions

I am currently working on a sprite kit game, with Objective-C. So far, I have everything working in order, however I am trying to accomplish multiple collisions and can't seem to accomplish this.
This is my code:
- (void)didBeginContact:(SKPhysicsContact *)contact
{
NSLog(#"contact!");
SKNode * nodeA = contact.bodyA.node;
SKNode *nodeB =contact.bodyB.node;
NSLog(#"%#", nodeA.name);
if ([contact.bodyA.node.name isEqualToString:#"david"] == YES ||
[contact.bodyB.node.name isEqualToString:#"david"] == NO) {
[self runAction:self.soundAction];
SKNode * nodeB = contact.bodyB.node;
SKAction * remove = [SKAction removeFromParent];
[nodeB runAction:remove];
counter++;
updateLabel = true;
}
else if([contact.bodyA.node.name isEqualToString:#"rock"] == YES ||
[contact.bodyB.node.name isEqualToString:#"rock"] == YES) {
SKAction * remove = [SKAction removeFromParent];
[nodeB runAction:remove];
for (int i = 0; i < 100; i++) {
SKSpriteNode * rock = [[SKSpriteNode alloc] initWithColor:[SKColor orangeColor] size:CGSizeMake(10, 10)];
rock.position = CGPointMake(CGRectGetMidX(self.frame) + skRand(-40, 40), CGRectGetMidY(self.frame) + 100 + skRand(-40, 40));
rock.name = #"rock";
rock.physicsBody = [SKPhysicsBody bodyWithRectangleOfSize:rock.size];
rock.physicsBody.usesPreciseCollisionDetection = YES;
rock.physicsBody.contactTestBitMask = 0x1;
[self addChild:rock];
CGVector dir = CGVectorMake(skRand(-5, 5), skRand(1, 4));
[rock.physicsBody applyImpulse:dir];
}
}
}

Collision Detection Not Working in SpriteKit

I am stuck on this for several hours now which seems like a very simple task. I have an SKSpriteNode call Ball which is declared below:
-(instancetype) initWithType:(BallType) ballType
{
if(ballType == UserBall) {
self = [super initWithImageNamed:USER_BALL_IMAGE];
self.physicsBody.categoryBitMask = BBPhysicsCategoryUserBall;
self.physicsBody.contactTestBitMask = BBPhysicsCategoryRedBall | BBPhysicsCategoryWorld;
self.physicsBody.collisionBitMask = 0;
}
// setup physical aspects of the ball
self.physicsBody = [SKPhysicsBody bodyWithCircleOfRadius:self.size.width/2];
self.physicsBody.friction = 0.0f;
self.physicsBody.dynamic = YES;
return self;
}
I create another SKSpriteNote "redBall" as below:
SKSpriteNode *redBall = [[SKSpriteNode alloc] initWithImageNamed:RED_BALL_IMAGE];
redBall.physicsBody.categoryBitMask = BBPhysicsCategoryRedBall;
redBall.physicsBody.contactTestBitMask = BBPhysicsCategoryUserBall;
redBall.physicsBody = [SKPhysicsBody bodyWithCircleOfRadius:redBall.size.width/2];
redBall.physicsBody.friction = 0.0f;
redBall.position = CGPointMake(self.size.width/2 , self.size.height/4);
[self addChild:redBall];
[redBall.physicsBody applyImpulse:CGVectorMake(0.0, 0.5)];
My GameScene inherits from SKScene and implements the SKPhysicsContactDelegate protocol. I assign the contactDelegate as follows:
-(void) setupPhysicsWorld {
self.physicsBody = [SKPhysicsBody bodyWithEdgeLoopFromRect:self.frame];
self.physicsWorld.gravity = CGVectorMake(0.0f, 0.0f);
self.physicsBody.friction = 0.0f;
self.physicsBody.categoryBitMask = BBPhysicsCategoryWorld;
self.physicsWorld.contactDelegate = self;
}
But for some reason the didBeginContact is NEVER CALLED!!! I am going crazy as why it is not being called?
-(instancetype) initWithType:(BallType) ballType
{
if(ballType == UserBall) {
self = [super initWithImageNamed:USER_BALL_IMAGE];
self.physicsBody.categoryBitMask = BBPhysicsCategoryUserBall;
self.physicsBody.contactTestBitMask = BBPhysicsCategoryWorld;
self.physicsBody.collisionBitMask = 0;
}
// setup physical aspects of the ball
self.physicsBody = [SKPhysicsBody bodyWithCircleOfRadius:self.size.width/2];
self.physicsBody.friction = 0.0f;
self.physicsBody.dynamic = YES;
return self;
}
SKSpriteNode *redBall = [[SKSpriteNode alloc] initWithImageNamed:RED_BALL_IMAGE];
redBall.physicsBody.categoryBitMask = BBPhysicsCategoryRedBall;
redBall.physicsBody.contactTestBitMask = BBPhysicsCategoryUserBall;
redBall.physicsBody = [SKPhysicsBody bodyWithCircleOfRadius:redBall.size.width/2];
redBall.physicsBody.friction = 0.0f;
redBall.position = CGPointMake(self.size.width/2 , self.size.height/4);
[self addChild:redBall];
[redBall.physicsBody applyImpulse:CGVectorMake(0.0, 0.5)];
you are setting redBall.physicsBody.contactTestBitMask twice

Gravity not affecting a node when endless background is enabled in sprite kit

When I implement the following code, my main character is not affected by gravity.
SKSpriteNode *ball = [SKSpriteNode spriteNodeWithImageNamed:#"ball.png"];
ball.physicsBody = [SKPhysicsBody bodyWithCircleOfRadius:ball.size.height/2];
ball.physicsBody.dynamic = YES;
ball.physicsBody.linearDamping = NO;
ball.physicsBody.angularDamping = NO;
ball.physicsBody.friction = 0;
ball.physicsBody.usesPreciseCollisionDetection = YES;
ball.position = CGPointZero;
ball.zPosition = 1;
[myWorld addChild:ball];
Here's the background
for (int i = 0; i < 2; i++) {
SKSpriteNode * bg = [SKSpriteNode spriteNodeWithImageNamed:#"background"];
//bg.anchorPoint = CGPointZero;
bg.position = CGPointMake(i * bg.size.width, 0);
bg.name = #"background.png";
//bg.physicsBody = [SKPhysicsBody bodyWithEdgeFromPoint:CGPointMake(-bg.frame.size.width/2, -bg.frame.size.height/2 +20) toPoint:CGPointMake(bg.frame.size.width/2, -bg.frame.size.height/2 + 20)];
bg.physicsBody = [SKPhysicsBody bodyWithEdgeLoopFromRect:bg.frame];
[self addChild:bg];
}
I tried the code here Stack discussion here and everything works as expected, except the gravity on the y axis.
Gravity in the scene:
if (self = [super initWithSize:size]) {
self.anchorPoint = CGPointMake(0.5, 0.5);
self.physicsWorld.gravity = CGVectorMake(0, -2);
[self startGravityIncrease];
........
- (void)startGravityIncrease {
SKAction *blockAction = [SKAction runBlock:^{
CGVector gravity = self.physicsWorld.gravity;
gravity.dx += 0.6;
gravity.dy -= 0.3;
self.physicsWorld.gravity = gravity;
}];
SKAction *waitAction = [SKAction waitForDuration:5];
SKAction *sequenceAction = [SKAction sequence:#[waitAction, blockAction]];
SKAction *repeatAction = [SKAction repeatActionForever:sequenceAction];
[self runAction:repeatAction];
}
What am I doing wrong here?

UIPinchGestureRecognizer trouble

Ok, i've read a few posts on this one (ex. UIImageView Gestures (Zoom, Rotate) Question) but I can't seem to fix my problem.
I have the following setup: an SKScene, an SKNode _backgroundLayer and 9 SKSpriteNodes that are tiles that make up the background and are attached to the _backgroundLayer.
Since these 9 tiles make a 3x3 square and they are quite large, I need to be able to zoom in and look at other SKSpriteNodes that will be on top of these 9 background images.
There are two problems:
1) When I pinch to zoom in or zoom out it seems like it is zooming in/out from location (0,0) of the _backgroundLayer and not from the touch location.
2) I have added some bounds so that the user can not scroll out of the 9 background images. In general it works. However, if I zoom in then move towards the top of the 9 background images and then zoom out the bounding conditions go berserk and the user can see the black space outside the background images. I need a way to limit the amount of zooming out that the user can do depending on where he's at.
Any ideas? Thanks!
I include my code below:
#import "LevelSelectScene.h"
#import "TurtleWorldSubScene.h"
#interface LevelSelectScene ()
#property (nonatomic, strong) SKNode *selectedNode;
#end
#implementation LevelSelectScene
{
SKNode *_backgroundLayer;
}
-(id)initWithSize:(CGSize)size {
if (self = [super initWithSize:size]) {
/* Setup your scene here */
_backgroundLayer = [SKNode node];
_backgroundLayer.name = #"backgroundLayer";
if (UI_USER_INTERFACE_IDIOM() == UIUserInterfaceIdiomPad) {
[_backgroundLayer setScale:0.76];
} else if (UI_USER_INTERFACE_IDIOM() == UIUserInterfaceIdiomPhone && IS_WIDESCREEN) {
} else {
[_backgroundLayer setScale:0.36];
}
[self addChild:_backgroundLayer];
SKTexture *backgroundTexture = [SKTexture textureWithImageNamed:#"levelSelect"];
int textureID = 0;
for (int i = 0; i<3; i++) {
for (int j = 0; j<3; j++) {
SKSpriteNode *background = [SKSpriteNode spriteNodeWithTexture:backgroundTexture];
background.anchorPoint = CGPointZero;
background.position = CGPointMake((background.size.width)*i, (background.size.height)*j);
background.zPosition = 0;
background.name = [NSString stringWithFormat:#"background%d", textureID];
textureID++;
[_backgroundLayer addChild:background];
}
}
[TurtleWorldSubScene displayTurtleWorld:self];
}
return self;
}
- (void)didMoveToView:(SKView *)view {
UIPanGestureRecognizer *panGestureRecognizer = [[UIPanGestureRecognizer alloc] initWithTarget:self action:#selector(handlePanFrom:)];
[[self view] addGestureRecognizer:panGestureRecognizer];
//UITapGestureRecognizer * tapRecognizer = [[UITapGestureRecognizer alloc] initWithTarget:self action:#selector(handleTap:)];
// [self.view addGestureRecognizer:tapRecognizer];
UIPinchGestureRecognizer *pinchGestureRecognizer = [[UIPinchGestureRecognizer alloc] initWithTarget:self action:#selector(handlePinch:)];
[[self view] addGestureRecognizer:pinchGestureRecognizer];
}
- (void)handlePanFrom:(UIPanGestureRecognizer *)recognizer {
if (recognizer.state == UIGestureRecognizerStateBegan) {
CGPoint touchLocation = [recognizer locationInView:recognizer.view];
touchLocation = [self convertPointFromView:touchLocation];
SKNode *node = [self nodeAtPoint:touchLocation];
_selectedNode = node;
} else if (recognizer.state == UIGestureRecognizerStateChanged) {
CGPoint translation = [recognizer translationInView:recognizer.view];
translation = CGPointMake(translation.x, -translation.y);
CGPoint initialPosition = CGPointAdd(_backgroundLayer.position, translation);
_backgroundLayer.position = [self boundLayerPos:initialPosition];
[recognizer setTranslation:CGPointZero inView:recognizer.view];
} else if (recognizer.state == UIGestureRecognizerStateEnded) {
float scrollDuration = 0.2;
CGPoint velocity = [recognizer velocityInView:recognizer.view];
CGPoint pos = [_backgroundLayer position];
CGPoint p = CGPointMultiplyScalar(velocity, scrollDuration);
CGPoint newPos = CGPointMake(pos.x + p.x, pos.y - p.y);
newPos = [self boundLayerPos:newPos];
[_backgroundLayer removeAllActions];
SKAction *moveTo = [SKAction moveTo:newPos duration:scrollDuration];
[moveTo setTimingMode:SKActionTimingEaseOut];
[_backgroundLayer runAction:moveTo];
}
}
- (void)handlePinch:(UIPinchGestureRecognizer *) recognizer
{
if (UI_USER_INTERFACE_IDIOM() == UIUserInterfaceIdiomPad) {
if(_backgroundLayer.xScale*recognizer.scale < 0.76) {
//SKSpriteNode *backgroundTile = (SKSpriteNode *)[_backgroundLayer childNodeWithName:#"background0"];
[_backgroundLayer setScale:0.76];
} else if(_backgroundLayer.xScale*recognizer.scale > 2) {
[_backgroundLayer setScale:2.0];
} else {
[_backgroundLayer runAction:[SKAction scaleBy:recognizer.scale duration:0]];
}
} else if (UI_USER_INTERFACE_IDIOM() == UIUserInterfaceIdiomPhone && IS_WIDESCREEN) {
} else {
if(_backgroundLayer.xScale*recognizer.scale < 0.36) {
[_backgroundLayer setScale:0.36];
} else if(_backgroundLayer.xScale*recognizer.scale > 2) {
[_backgroundLayer setScale:2.0];
} else {
[_backgroundLayer runAction:[SKAction scaleBy:recognizer.scale duration:0]];
}
}
recognizer.scale = 1;
}
- (CGPoint)boundLayerPos:(CGPoint)newPos {
SKSpriteNode *backgroundTile = (SKSpriteNode *)[_backgroundLayer childNodeWithName:#"background0"];
CGPoint retval = newPos;
retval.x = MIN(retval.x, 0);
retval.x = MAX(retval.x, -(backgroundTile.size.width*_backgroundLayer.xScale*3)+self.size.width);
retval.y = MIN(retval.y, 0);
retval.y = MAX(retval.y, -(backgroundTile.size.height*_backgroundLayer.xScale*3)+self.size.height);
return retval;
}

iOS7 SKScene how to make a sprite bounce off the edge of the screen?

I'm building a game with balls bouncing within the iPad's screen. Similar to a Pong game. I see that SKScene's SKPhysicsWorld has gravity property, and also controls how objects collide with each other.
Is there some way I can automatically detect if a sprite's edge has collided with the screen's edge, so it can bounce off that? Or do I need to write my own collision code?
You don't need to write much code to make the ball bounce off the edge of the screen. the physics environment can handle all of the above, you just need to instantiate the sprites in the correct way.
First, you will have to set the physicsBody of the scene as such:
self.physicsBody = [SKPhysicsBody bodyWithEdgeLoopFromRect:self.frame];
In your scene's .h file create two bitmask categories:
static const uint8_t ballCategory = 1;
static const uint8_t wallCategory = 2;
And give the scene's physics body a category:
self.physicsBody.categoryBitMask = wallCategory;
Then create your sprite:
SKSpriteNode *spriteNode = [SKSpriteNode spriteNodeWithImageNamed:#"ball.png"];
spriteNode.name = #"ball";
spriteNode.physicsBody = [SKPhysicsBody bodyWithCircleOfRadius:25];
spriteNode.position = CGPointMake(10.0,10.0); //or anything you want
spriteNode.physicsBody.dynamic = YES;
spriteNode.physicsBody.affectedByGravity = NO;
spriteNode.physicsBody.categoryBitMask = ballCategory;
spriteNode.physicsBody.collisionBitMask = wallCategory;
spriteNode.physicsBody.restitution = 1.0;
spriteNode.physicsBody.friction = 0.0;
spriteNode.physicsBody.linearDamping = 0.0;
spriteNode.physicsBody.angularDamping = 0.0;
[self addChild:spriteNode];
Instead of giving the spriteNode a [SKAction moveTo: duration:], apply an impulse to it's physics body.
CGVector impulse = CGVectorMake(1.0,1.0);
[spriteNode.physicsBody applyImpulse:impulse];
And voila! The ball will be bouncing off the walls with nothing to stop it.
This is what the .h file should look like:
#import <SpriteKit/SpriteKit.h>
static const uint8_t ballCategory = 1;
static const uint8_t wallCategory = 2;
#interface BallBounce : SKScene
#end
And this is how the .m file should look like:
#import "BallBounce.h"
#implementation BallBounce
-(instancetype)initWithSize:(CGSize)size
{
if (self = [super initWithSize:size])
{
self.physicsBody = [SKPhysicsBody bodyWithEdgeLoopFromRect:self.frame];
self.physicsBody.categoryBitMask = wallCategory;
SKSpriteNode *spriteNode = [SKSpriteNode spriteNodeWithColor:[UIColor greenColor] size:CGSizeMake(50, 50)];
spriteNode.name = #"ball";
spriteNode.physicsBody = [SKPhysicsBody bodyWithCircleOfRadius:25];
spriteNode.position = CGPointMake(10.0,10.0); //or anything you want
spriteNode.physicsBody.dynamic = YES;
spriteNode.physicsBody.affectedByGravity = NO;
spriteNode.physicsBody.categoryBitMask = ballCategory;
spriteNode.physicsBody.collisionBitMask = wallCategory;
spriteNode.physicsBody.restitution = 1.0;
spriteNode.physicsBody.friction = 0.0;
spriteNode.physicsBody.linearDamping = 0.0;
spriteNode.physicsBody.angularDamping = 0.0;
[self addChild:spriteNode];
CGVector impulse = CGVectorMake(100.0,100.0);
[spriteNode.physicsBody applyImpulse:impulse];
}
return self;
}
#end
Have you tried setting the physics body of SKScene
[self setPhysicsBody:[SKPhysicsBody bodyWithEdgeLoopFromRect:self.frame]]; //Physics body of Scene
That should keep your node's inside the frame.
W
This is from the http://www.raywenderlich.com/49721/how-to-create-a-breakout-game-using-spritekit
SKPhysicsBody* borderBody = [SKPhysicsBody bodyWithEdgeLoopFromRect:self.frame];
// 2 Set physicsBody of scene to borderBody
self.physicsBody = borderBody;
// 3 Set the friction of that physicsBody to 0
self.physicsBody.friction = 0.0f;
// 1
SKSpriteNode* ball = [SKSpriteNode spriteNodeWithImageNamed: #"Sphere.png"];
ball.name = #"ball";
ball.position = CGPointMake(self.frame.size.width/3, self.frame.size.height/3);
[self addChild:ball];
// 2
ball.physicsBody = [SKPhysicsBody bodyWithCircleOfRadius:ball.frame.size.width/2];
// 3
ball.physicsBody.friction = 0.0f;
// 4
ball.physicsBody.restitution = 1.0f;
// 5
ball.physicsBody.linearDamping = 0.0f;
// 6
ball.physicsBody.allowsRotation = NO;
ball.physicsBody.affectedByGravity = NO;
[ball.physicsBody applyImpulse:CGVectorMake(10.0f, 10.0f)];
This will bring your node in the frame in Swift:
self.physicsBody = SKPhysicsBody (edgeLoopFromRect: self.frame)