Objective C - SpriteKit - Unable to detect contact between ball and paddle node - ios

I am trying to detect the collision between the ball node and either one of the paddle nodes but the message to confirm the collision is not being fired.
Could somebody help me understand where I am going wrong?
//categories for detecting contacts between nodes
static const uint32_t ballCategory = 0x1 << 0;
static const uint32_t paddleCategory = 0x1 << 1;
#interface GameScene ()
#property BOOL contentCreated;
#property(nonatomic) UITouch *playerOnePaddleControlTouch;
#property(nonatomic, weak) UITouch *paddleTouch;
#property(nonatomic) SKSpriteNode *paddleOneNode;
#property(nonatomic) SKSpriteNode *paddleTwoNode;
#property(nonatomic) SKSpriteNode *ballNode;
#property(nonatomic) SKLabelNode *playerOneScoreNode;
#property(nonatomic) SKLabelNode *playerTwoScoreNode;
#property(nonatomic) NSInteger playerOneScore;
#property(nonatomic) NSInteger playerTwoScore;
#end
#implementation GameScene
- (void)didMoveToView:(SKView *)view
{
if (!self.contentCreated)
{
[self createSceneContents];
self.contentCreated = YES;
}
}
- (void) createSceneContents
{
self.backgroundColor = [SKColor blackColor];
self.scaleMode = SKSceneScaleModeAspectFit;
[self addChild: [self newGameNode]];
self.physicsWorld.gravity = CGVectorMake(0, 0);
self.physicsWorld.contactDelegate = self;
// Create border around screen
SKPhysicsBody* borderBody = [SKPhysicsBody bodyWithEdgeLoopFromRect:self.frame];
self.physicsBody = borderBody;
self.physicsBody.friction = 0;
// Create Paddle One
SKSpriteNode *paddleOne = [self newPaddle];
paddleOne.position = CGPointMake(CGRectGetMidX(self.frame)/8, CGRectGetMidY(self.frame));
paddleOne.name = #"paddleOne";
paddleOne.physicsBody.categoryBitMask = paddleCategory;
paddleOne.physicsBody.contactTestBitMask = ballCategory;
[self addChild:paddleOne];
// Create Paddle Two
SKSpriteNode *paddleTwo = [self newPaddle];
paddleTwo.position = CGPointMake((CGRectGetMaxX(self.frame) - CGRectGetMidX(self.frame) / 8), CGRectGetMidY(self.frame));
paddleTwo.name = #"paddleTwo";
paddleTwo.physicsBody.categoryBitMask = paddleCategory;
paddleTwo.physicsBody.contactTestBitMask = ballCategory;
[self addChild:paddleTwo];
// Create ball
SKSpriteNode *ball = [self newBall];
ball.position = CGPointMake(CGRectGetMidX(self.frame), CGRectGetMidY(self.frame));
[self addChild:ball];
ball.physicsBody = [SKPhysicsBody bodyWithCircleOfRadius:ball.frame.size.width/2];
ball.physicsBody.friction = 1.0f; //normally 1.0f
ball.physicsBody.restitution = 1.0f; //normally 1.0f
ball.physicsBody.linearDamping = 0.0f; //normally 0.0f
ball.physicsBody.allowsRotation = NO;
ball.physicsBody.categoryBitMask = ballCategory;
ball.physicsBody.contactTestBitMask = paddleCategory;
[ball.physicsBody applyImpulse:CGVectorMake(1.0f, -1.0f)];
// Create score labels
self.playerOneScoreNode = [SKLabelNode labelNodeWithFontNamed:#"Helvetica"];
self.playerTwoScoreNode = [SKLabelNode labelNodeWithFontNamed:#"Helvetica"];
self.playerOneScoreNode.fontColor = self.playerOneScoreNode.fontColor = [SKColor whiteColor];
self.playerOneScoreNode.fontSize = self.playerTwoScoreNode.fontSize = 90;
self.playerOneScoreNode.position = CGPointMake((CGRectGetWidth(self.frame))* 0.25, (CGRectGetHeight(self.frame)) - 80);
self.playerTwoScoreNode.position = CGPointMake((CGRectGetWidth(self.frame)) * 0.75, (CGRectGetHeight(self.frame)) - 80);
[self addChild:self.playerOneScoreNode];
[self addChild:self.playerTwoScoreNode];
// Set Scores to 0
self.playerOneScore = 7;
self.playerTwoScore = 0;
self.playerOneScoreNode.text = [NSString stringWithFormat:#"%ld",self.playerOneScore];
self.playerTwoScoreNode.text = [NSString stringWithFormat:#"%ld",self.playerTwoScore];
}
- (SKSpriteNode *)newPaddle
{
SKSpriteNode *paddle = [[SKSpriteNode alloc]initWithColor:[SKColor whiteColor] size:CGSizeMake(16,64)];
return paddle;
}
- (SKSpriteNode *)newBall
{
SKSpriteNode *ball = [[SKSpriteNode alloc]initWithColor:[SKColor redColor] size:CGSizeMake(16, 16)];
return ball;
}
- (SKLabelNode *) newGameNode
{
SKLabelNode *gameNode = [SKLabelNode labelNodeWithFontNamed:#"Chalkduster"];
gameNode.text = #" Pong";
gameNode.fontSize = 50;
gameNode.position = CGPointMake(CGRectGetMidX(self.frame), CGRectGetMidY(self.frame));
gameNode.fontColor = [SKColor blueColor];
return gameNode;
}
- (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event
{
self.paddleTouch = [touches anyObject];
UITouch *touch = [touches anyObject];
CGPoint touchPoint = [touch locationInNode:self];
SKNode *paddleOne = [self childNodeWithName:#"paddleOne"];
SKNode *paddleTwo = [self childNodeWithName:#"paddleTwo"];
if (touchPoint.x < CGRectGetMidX(self.frame)) {
paddleOne.position = CGPointMake(paddleOne.position.x, touchPoint.y);
}
else
{
paddleTwo.position = CGPointMake(paddleTwo.position.x, touchPoint.y);
}
}
- (void)touchesMoved:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event
{
if (self.paddleTouch) {
UITouch *touch = [touches anyObject];
CGPoint touchPoint = [touch locationInNode:self];
CGPoint previousPoint = [touch previousLocationInNode:self];
SKSpriteNode *paddleOne = (SKSpriteNode*)[self childNodeWithName:#"paddleOne"];
SKSpriteNode *paddleTwo = (SKSpriteNode*)[self childNodeWithName:#"paddleTwo"];
int paddleOneY = paddleOne.position.y + (touchPoint.y - previousPoint.y);
int paddleTwoY = paddleTwo.position.y + (touchPoint.y - previousPoint.y);
if (touchPoint.x < CGRectGetMidX(self.frame)) {
paddleOne.position = CGPointMake(paddleOne.position.x, paddleOneY);
}
else
{
paddleTwo.position = CGPointMake(paddleTwo.position.x, paddleTwoY);
}
}
}
// React to collision's between nodes/bodies
// Currently not working....need to understand this set of code.
-(void)didBeginContact:(SKPhysicsContact *)contact
{
SKPhysicsBody *firstBody;
SKPhysicsBody *secondBody;
if (contact.bodyA.categoryBitMask < contact.bodyB.categoryBitMask)
{
firstBody = contact.bodyA;
secondBody = contact.bodyB;
}
else
{
firstBody = contact.bodyB;
secondBody = contact.bodyA;
}
if (firstBody.categoryBitMask == ballCategory && secondBody.categoryBitMask == paddleCategory)
{
NSLog(#"Ball has touched Paddle");
}
}
#end

Your paddleOne and paddleTwo does not have SKPhysicsBody.
Add these (change size of physics body if needed)
paddleOne.physicsBody = [SKPhysicsBody bodyWithRectangleOfSize: paddleOne.size];
paddleTwo.physicsBody = [SKPhysicsBody bodyWithRectangleOfSize: paddleTwo.size];

Related

Not detecting collision between boundary frame and SKNode in SpriteKit

Here is my code in my GameScene. I have a simple sprite which falls down under gravity and bounces of the edges of the screen and a particle emitter. All I need is that I should be able to detect collisions between the SKNode and the physicsbody i.e. the frame.
Code Used
#import "GameScene.h"
#interface GameScene () <SKPhysicsContactDelegate>
#property (strong) UITouch *rightTouch;
#property (strong) UITouch *leftTouch;
#property (strong) SKNode *spinnyNode;
#property (assign, nonatomic) int count;
#end
#implementation GameScene {
}
static const uint32_t birdCategory = 1 << 0;
static const uint32_t worldCategory = 1 << 1;
- (void)didMoveToView:(SKView *)view {
self.backgroundColor = [UIColor colorWithRed:134/255.0 green:50/255.0 blue:148/255.0 alpha:1.0];
self.scaleMode = SKSceneScaleModeAspectFit;
self.physicsBody = [SKPhysicsBody bodyWithEdgeLoopFromRect:self.frame];
self.physicsBody.categoryBitMask = worldCategory;
NSString *burstPath = [[NSBundle mainBundle] pathForResource:#"fire" ofType:#"sks"];
SKEmitterNode *burstNode = [NSKeyedUnarchiver unarchiveObjectWithFile:burstPath];
burstNode.position = CGPointMake(0, 0);
[self addChild:burstNode];
self.spinnyNode = [self childNodeWithName:#"ball"];
self.spinnyNode.physicsBody.angularVelocity = 10.0;
self.spinnyNode.hidden = YES;
self.spinnyNode.physicsBody.categoryBitMask = birdCategory;
self.spinnyNode.physicsBody.contactTestBitMask = 0x0;
// self.spinnyNode = ball;
SKNode *leftPaddle = [self childNodeWithName:#"bottom"];
leftPaddle.physicsBody.angularVelocity = 10.0;
}
- (void)touchDownAtPoint:(CGPoint)pos {
self.count = self.count+1;
CGPoint p = pos;
NSLog(#"/n %f %f %f %f", p.x, p.y, self.frame.size.height, self.frame.size.width);
if (self.count == 1)
{
self.spinnyNode.position = p;
self.spinnyNode.physicsBody = [SKPhysicsBody bodyWithCircleOfRadius:40];
self.spinnyNode.physicsBody.velocity=CGVectorMake(203, 10);
self.spinnyNode.hidden = NO;
self.spinnyNode.physicsBody.restitution = 0.8;
self.spinnyNode.physicsBody.linearDamping = 0.0;
self.spinnyNode.physicsBody.angularDamping = 0.3;
self.spinnyNode.physicsBody.friction = 0.1;
self.spinnyNode.physicsBody.angularVelocity = -100.0;
}
}
-(void)didBeginContact:(SKPhysicsContact *)contact
{
NSLog(#"contact detected");
NSString *nameA = contact.bodyA.node.name;
NSString *nameB = contact.bodyB.node.name;
SKPhysicsBody *firstBody;
SKPhysicsBody *secondBody;
if (contact.bodyA.categoryBitMask < contact.bodyB.categoryBitMask)
{
firstBody = contact.bodyA;
secondBody = contact.bodyB;
}
else
{
firstBody = contact.bodyB;
secondBody = contact.bodyA;
}
//Your first body is the block, secondbody is the player.
//Implement relevant code here.
}
- (void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event {
for (UITouch *t in touches) {[self touchDownAtPoint:[t locationInNode:self]];
}
}
-(void)update:(CFTimeInterval)currentTime {
// Called before each frame is rendered
}
#end
This is my first time working with SpriteKit. So please be patient and help me out.
If you want to get the callback to didBeginContact when they touch you will need to set the contact bit mask like this:
self.spinnyNode.physicsBody.contactTestBitMask = worldCategory;
currently you are setting it to 0x0 which means not to report any contacts to the delegate. You also need to set the delegate on the scene:
self.physicsWorld.contactDelegate = self;
UPDATE
You also need to put the lines:
self.spinnyNode.physicsBody.contactTestBitMask = worldCategory;
self.spinnyNode.physicsBody.categoryBitMask = birdCategory;
into the touchDownAtPoint method after you create the physicsBody with:
self.spinnyNode.physicsBody = [SKPhysicsBody bodyWithCircleOfRadius:40];
otherwise they will be reset when you create the physicsBody.

Having issues with touchesBegan method

I have my sound on logo showing up on the screen and the sound off logo appears when i test it. what I want it to do is change from sound on to sound off when I tap on it. The buttons do acknowledge the tap but nothing happens. I don't have much experience in change node images when they're tapped so I don't really know if I used the most efficient code. Can someone look over my code and see what's wrong? Thanks in advance!
#implementation MyScene
{
SKSpriteNode *soundLogo;
SKSpriteNode *soundOff;
}
-(void) addSoundOff:(CGSize)size {
soundOff = [SKSpriteNode spriteNodeWithImageNamed:#"soundOff"];
//resize sprite
soundOff.size = CGSizeMake(soundOff.size.width/2.25, soundOff.size.height/2.25);
//position it
soundOff.position = CGPointMake(65, 25);
//name sound off
soundOff.name = #"soundOff";
soundOff.zPosition = 0;
soundOff.alpha = 0;
//[self addChild:soundOff];
}
-(void) addSoundOn:(CGSize)size {
soundLogo = [SKSpriteNode spriteNodeWithImageNamed:#"soundLogo"];
//resize sprite
soundLogo.size = CGSizeMake(soundLogo.size.width/2.25, soundLogo.size.height/2.25);
//position sprite
CGPoint myPoint = CGPointMake(65, 25);
soundLogo.position = myPoint;
//name sound logo
soundLogo.name = #"soundOn";
//add action
//soundLogo.alpha = 1;
soundLogo.zPosition = 100;
[self addChild:soundLogo];
}
-(void) touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event {
UITouch *touch = [touches anyObject];
CGPoint location = [touch locationInNode:self];
SKNode *node = [self nodeAtPoint:location];
if ([node.name isEqualToString:#"soundOn"]) {
soundOff.zPosition = 100;
soundOff.alpha = 1;
soundLogo.alpha = 0;
soundLogo.zPosition = 0;
// [node removeFromParent];
// [node addChild:soundOff];
NSLog(#"sound on is pressed");
}
if ([node.name isEqualToString:#"soundOff"]) {
soundLogo.zPosition = 100;
soundLogo.alpha = 1;
soundOff.alpha = 0;
soundOff.zPosition = 0;
// [node removeFromParent];
// [node addChild:soundLogo];
NSLog(#"sound off is pressed");
}
}
It seems nothing wrong in your addSoundOff and addSoundOn functions. What you can do as one of the solutions to change alpha and zPosition properties of SKSpriteNode . And also createsoundOff and soundLogo as instance variables under the implementation. So here is the all code:
#import "MyScene.h"
#implementation MyScene
{
SKSpriteNode *soundLogo;
SKSpriteNode *soundOff;
}
-(id)initWithSize:(CGSize)size {
if (self = [super initWithSize:size]) {
/* Setup your scene here */
self.backgroundColor = [SKColor colorWithRed:0.15 green:0.15 blue:0.3 alpha:1.0];
[self addSoundOn:self.size];
[self addSoundOff:self.size];
}
return self;
}
-(void) addSoundOff:(CGSize)size {
soundOff = [SKSpriteNode spriteNodeWithImageNamed:#"soundOff"];
//resize sprite
soundOff.size = CGSizeMake(soundOff.size.width/2.25, soundOff.size.height/2.25);
//position it
soundOff.position = CGPointMake(65, 25);
//name sound off
soundOff.name = #"soundOff";
soundOff.alpha = 0;
soundOff.zPosition = 0;
[self addChild:soundOff];
}
-(void) addSoundOn:(CGSize)size {
SKTexture *soundOn = [SKTexture textureWithImageNamed:#"soundLogo"];
soundLogo = [SKSpriteNode spriteNodeWithTexture:soundOn];
//resize sprite
soundLogo.size = CGSizeMake(soundLogo.size.width/2.25, soundLogo.size.height/2.25);
//position sprite
CGPoint myPoint = CGPointMake(65, 25);
soundLogo.position = myPoint;
//name sound logo
soundLogo.name = #"soundOn";
//add action
soundLogo.zPosition = 100;
[self addChild:soundLogo];
}
-(void) touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event {
UITouch *touch = [touches anyObject];
CGPoint location = [touch locationInNode:self];
SKNode *node = [self nodeAtPoint:location];
//sound logo pressed to turn sound on/off
if ([node.name isEqualToString:#"soundOn"]) {
soundOff.alpha = 1;
soundOff.zPosition = 100;
soundLogo.alpha = 0;
soundLogo.zPosition = 0;
NSLog(#"sound on is pressed");
}
if ([node.name isEqualToString:#"soundOff"]) {
soundOff.alpha = 0;
soundOff.zPosition = 0;
soundLogo.alpha = 1;
soundLogo.zPosition = 100;
NSLog(#"sound off is pressed");
}
}
#end

No colision detection with edges when panning node fast

Im freaking out with this collision issue:
panned ball breaks out edgeloop body when panned fast. Anyone had simillar problem?
Here is scene code ilustrating the issue.
(Replacement MyScene in xcode sample - spritekitgame)
#import "SAMyScene.h"
/* Bitmask for the different entities with physics bodies. */
typedef enum : uint32_t {
SAColliderTypeBall = 0x1 << 1
,SAColliderTypeEdge = 0x1 << 3
} SAColliderType;
NSString * const SABallName = #"ballNode";
#interface SAMyScene()
#property (nonatomic, strong) UIPanGestureRecognizer *panGestureRecognizer;
#property (nonatomic, strong) SKShapeNode *ball;
#end
#implementation SAMyScene
-(id)initWithSize:(CGSize)size {
if (self = [super initWithSize:size]) {
/* Setup your scene here */
NSLog(#"Ball categorie bitmask =%d", SAColliderTypeBall);
NSLog(#"Edges categorie bitmask =%d", SAColliderTypeEdge);
self.backgroundColor = [SKColor colorWithRed:0.15 green:0.15 blue:0.3 alpha:1.0];
self.physicsBody = [SKPhysicsBody bodyWithEdgeLoopFromRect:self.frame];
self.physicsBody.categoryBitMask = SAColliderTypeEdge;
self.physicsBody.usesPreciseCollisionDetection = YES;
self.ball = [self newBallNode];
[self addChild:self.ball];
}
return self;
}
- (SKShapeNode *)newBallNode {
SKShapeNode *ball = [[SKShapeNode alloc] init];
CGMutablePathRef myPath = CGPathCreateMutable();
CGFloat radius = 60.0;
CGPathAddArc(myPath, NULL, 0,0, radius, 0, M_PI*2, YES);
ball.path = myPath;
ball.fillColor = [SKColor yellowColor];
ball.name = SABallName;
ball.position = CGPointMake(CGRectGetMidX(self.frame),CGRectGetMidY(self.frame));
ball.physicsBody = [SKPhysicsBody bodyWithCircleOfRadius:radius];
ball.physicsBody.affectedByGravity = NO;
ball.physicsBody.dynamic = YES;
ball.physicsBody.categoryBitMask = SAColliderTypeBall;
ball.physicsBody.contactTestBitMask = SAColliderTypeEdge;
ball.physicsBody.collisionBitMask = SAColliderTypeEdge;
ball.physicsBody.usesPreciseCollisionDetection = YES;
return ball;
}
-(void)update:(CFTimeInterval)currentTime {
//Called before each frame is rendered
}
- (UIPanGestureRecognizer *)panGestureRecognizer {
if (!_panGestureRecognizer) {
_panGestureRecognizer = [[UIPanGestureRecognizer alloc] initWithTarget:self
action:#selector(handlePanFrom:)];
}
return _panGestureRecognizer;
}
- (void)didMoveToView:(SKView *)view {
if (![[self view].gestureRecognizers containsObject:self.panGestureRecognizer]) {
[[self view] addGestureRecognizer:self.panGestureRecognizer];
}
}
- (void)handlePanFrom:(UIPanGestureRecognizer *)recognizer {
if (recognizer.state == UIGestureRecognizerStateBegan) {
} else if (recognizer.state == UIGestureRecognizerStateChanged) {
CGPoint translation = [recognizer translationInView:recognizer.view];
translation = CGPointMake(translation.x, - translation.y);
[self panForTranslation:translation];
[recognizer setTranslation:CGPointZero inView:recognizer.view];
} else if (recognizer.state == UIGestureRecognizerStateEnded) {
}
}
- (void)panForTranslation:(CGPoint)translation {
CGPoint position = self.ball.position;
CGPoint newPos = CGPointMake(position.x + translation.x, position.y);
self.ball.position = newPos;
}
#end

Prevent player from falling through the ground - Sprite Kit

I've been trying to work on a simple Sprite Kit game that involves dodging red balls. I'm using the built-in gravity mechanism, but I'm having trouble preventing the player from falling through the ground. I've looked up a solution (set ground.physicsBody.dynamic = NO), but the player still falls through. What exactly do I need to do?
Edit: The green and brown texture is the ground. Right now the player is set to not being dynamic, so it is 'flying'
Here is my code in the MyScene.m file:
//
// MyScene.m
// DodgeMan
//
// Created by Cormac Chester on 3/8/14.
// Copyright (c) 2014 Testman Industries. All rights reserved.
//
#import "MyScene.h"
#import "EndGameScene.h"
static const uint32_t redBallCategory = 0x1 << 0;
static const uint32_t playerCategory = 0x1 << 1;
#implementation MyScene
-(id)initWithSize:(CGSize)size
{
if (self = [super initWithSize:size])
{
/* Setup your scene here */
//Sets player location
playerLocX = 50;
playerLocY = 100;
//Sets player score
score = 0;
//Set Background
self.backgroundColor = [SKColor colorWithRed:0.53 green:0.81 blue:0.92 alpha:1.0];
//Set Ground
SKSpriteNode *ground = [SKSpriteNode spriteNodeWithImageNamed:#"ground"];
ground.position = CGPointMake(CGRectGetMidX(self.frame), 34);
ground.xScale = 0.5;
ground.yScale = 0.5;
ground.physicsBody = [SKPhysicsBody bodyWithRectangleOfSize:ground.size];
ground.physicsBody.dynamic = NO;
//Player
self.playerSprite = [SKSpriteNode spriteNodeWithImageNamed:#"character"];
self.playerSprite.position = CGPointMake(playerLocX, playerLocY);
//Set Player Physics
self.playerSprite.physicsBody = [SKPhysicsBody bodyWithRectangleOfSize:self.playerSprite.size];
self.playerSprite.physicsBody.dynamic = YES;
self.playerSprite.physicsBody.categoryBitMask = playerCategory;
self.playerSprite.physicsBody.contactTestBitMask = redBallCategory;
self.playerSprite.physicsBody.collisionBitMask = 0;
self.playerSprite.physicsBody.usesPreciseCollisionDetection = YES;
//Score Label
self.scoreLabel = [SKLabelNode labelNodeWithFontNamed:#"Arial-BoldMT"];
self.scoreLabel.text = #"0";
self.scoreLabel.fontSize = 40;
self.scoreLabel.fontColor = [SKColor blackColor];
self.scoreLabel.position = CGPointMake(50, 260);
//Pause Button
self.pauseButton = [SKSpriteNode spriteNodeWithImageNamed:#"pauseButton"];
self.pauseButton.position = CGPointMake(self.frame.size.width / 2, self.frame.size.height - 40);
self.pauseButton.name = #"pauseButton";
//Add nodes
[self addChild:ground];
[self addChild:self.playerSprite];
[self addChild:self.scoreLabel];
//[self addChild:self.pauseButton];
//Sets gravity
self.physicsWorld.gravity = CGVectorMake(0,-2);
self.physicsWorld.contactDelegate = self;
}
return self;
}
-(void)addBall
{
SKSpriteNode *redBall = [SKSpriteNode spriteNodeWithImageNamed:#"locationIndicator"];
int minY = redBall.size.height / 2;
int maxY = self.frame.size.height - redBall.size.height / 2;
int rangeY = maxY - minY;
int actualY = (arc4random() % rangeY) + minY;
NSLog(#"Actual Y: %i", actualY);
//Initiates red ball offscreen
if (actualY >= 75)
{
//Prevents balls from spawning in the ground
redBall.position = CGPointMake(self.frame.size.width + redBall.size.width/2, actualY);
[self addChild:redBall];
}
redBall.physicsBody = [SKPhysicsBody bodyWithCircleOfRadius:redBall.size.width/2];
redBall.physicsBody.dynamic = YES;
redBall.physicsBody.categoryBitMask = redBallCategory;
redBall.physicsBody.contactTestBitMask = playerCategory;
redBall.physicsBody.collisionBitMask = 0;
redBall.physicsBody.affectedByGravity = NO;
redBall.physicsBody.usesPreciseCollisionDetection = YES;
//Determine speed of red ball
int minDuration = 3.0;
int maxDuration = 5.0;
int rangeDuration = maxDuration - minDuration;
int actualDuration = (arc4random() % rangeDuration) + minDuration;
// Create the actions
SKAction *actionMove = [SKAction moveTo:CGPointMake(-redBall.size.width/2, actualY) duration:actualDuration];
SKAction *actionMoveDone = [SKAction removeFromParent];
SKAction *ballCross = [SKAction runBlock:^{
score++;
self.scoreString = [NSString stringWithFormat:#"%i", score];
self.scoreLabel.text = self.scoreString;
NSLog(#"Score was incremented. Score is now %d", score);
}];
[redBall runAction:[SKAction sequence:#[actionMove, ballCross, actionMoveDone]]];
}
- (void)updateWithTimeSinceLastUpdate:(CFTimeInterval)timeSinceLast
{
self.lastSpawnTimeInterval += timeSinceLast;
if (self.lastSpawnTimeInterval > 0.5) {
self.lastSpawnTimeInterval = 0;
[self addBall];
}
}
-(void)update:(CFTimeInterval)currentTime
{
/* Called before each frame is rendered */
// Handle time delta.
//Prevents bad stuff happening
CFTimeInterval timeSinceLast = currentTime - self.lastUpdateTimeInterval;
self.lastUpdateTimeInterval = currentTime;
if (timeSinceLast > 1) { // more than a second since last update
timeSinceLast = 1.0 / 120.0;
self.lastUpdateTimeInterval = currentTime;
}
[self updateWithTimeSinceLastUpdate:timeSinceLast];
}
NSDate *startTime;
-(void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
{
/* Called when a touch begins */
[super touchesBegan:touches withEvent:event];
//Starts Timer
startTime = [NSDate date];
UITouch *touch = [touches anyObject];
CGPoint location = [touch locationInNode:self];
SKNode *node = [self nodeAtPoint:location];
//Pauses Scene
if ([node.name isEqualToString:#"pauseButton"])
{
NSLog(#"Pause button pressed");
}
}
-(void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event
{
/* Called when a touch ends */
[super touchesEnded:touches withEvent:event];
NSTimeInterval elapsedTime = [startTime timeIntervalSinceNow];
NSString *elapsedTimeString = [NSString stringWithFormat:#"Elapsed time: %f", elapsedTime];
NSLog(#"%#", elapsedTimeString);
for (UITouch *touch in touches)
{
//Gets location of touch
CGPoint location = [touch locationInNode:self];
NSLog(#"Touch Location X: %f \n Touch Location Y: %f", location.x, location.y);
//Prevents destination from being in the ground
if (location.y < 88)
{
location.y = 87.5;
}
//Moves and animates player
//int velocity = elapsedTime * -3000;
int velocity = 800.0/1.0;
NSLog(#"Velocity: %i", velocity);
float realMoveDuration = self.size.width / velocity;
SKAction *actionMove = [SKAction moveTo:location duration:realMoveDuration];
[self.playerSprite runAction:[SKAction sequence:#[actionMove]]];
}
NSLog(#"Touch ended");
}
//Collision between ball and player
- (void)redBall:(SKSpriteNode *)redBall didCollideWithPlayer:(SKSpriteNode *)playerSprite
{
NSLog(#"Player died");
[redBall removeFromParent];
[playerSprite removeFromParent];
SKTransition *reveal = [SKTransition crossFadeWithDuration:0.5];
SKScene *endGameScene = [[EndGameScene alloc] initWithSize:self.size gameEnded:YES];
[self.view presentScene:endGameScene transition: reveal];
}
- (void)didBeginContact:(SKPhysicsContact *)contact
{
SKPhysicsBody *firstBody, *secondBody;
if (contact.bodyA.categoryBitMask < contact.bodyB.categoryBitMask)
{
firstBody = contact.bodyA;
secondBody = contact.bodyB;
}
else
{
firstBody = contact.bodyB;
secondBody = contact.bodyA;
}
//Red ball collides with the player
if ((firstBody.categoryBitMask & redBallCategory) != 0 && (secondBody.categoryBitMask & playerCategory) != 0)
{
[self redBall:(SKSpriteNode *) firstBody.node didCollideWithPlayer:(SKSpriteNode *) secondBody.node];
}
}
#end
you definitely can't set its dynamic to no brother. you need gravity effect that player. (he is not effected by physic world so he is flying right now. we need him to fall down to ground aren't we? :)
So here is the simple solution. idea is that you create a "invisible rectangle block" on the ground surface that has physic body. and you need to set its dynamic to no in order to prevent it falling down
so this block is a node obviously, and its size : as high as the ground , and as wide as the screen. and you need to adjust the position a little bit to put its upper bound right on the ground surface.
good luck
i actually drew a picture but i can't post it here because of my reputation :(
Your problem is with physicsBody's categoryBitMask and collisionTestBitMask.
Your bitwise declarations :
static const uint32_t redBallCategory = 0x1 << 0;
static const uint32_t playerCategory = 0x1 << 1;
This has actually set the following bit patterns (i've shortened to 8 bits for the example) :
redBallCategory - 00000001 and
playerCategory - 00000010
However in the following code, you tell the player to only collide with collision bit mask - 00000000;
self.playerSprite.physicsBody.collisionBitMask = 0;
So your first problem is here. The player will not collide with any category that you have defined.
Your second problem is you have not given the ground a categoryBitMask, or collisionBitMask. By default this means all bits are set, IE the ground's collisionBitMask is equal to 11111111;
There will be no collision between these two physics bodies.
Try this - i have simply added a third physics category, and edited your code slightly to set the ground categoryBitMask / collisionBitMask, and your player collisionBitMask.
static const uint32_t redBallCategory = 0x1 << 0;
static const uint32_t playerCategory = 0x1 << 1;
static const uint32_t groundCategory = 0x1 << 2;
#implementation MyScene
-(id)initWithSize:(CGSize)size
{
if (self = [super initWithSize:size])
{
/* Setup your scene here */
//Sets player location
playerLocX = 50;
playerLocY = 100;
//Sets player score
score = 0;
//Set Background
self.backgroundColor = [SKColor colorWithRed:0.53 green:0.81 blue:0.92 alpha:1.0];
//Set Ground
SKSpriteNode *ground = [SKSpriteNode spriteNodeWithImageNamed:#"ground"];
ground.position = CGPointMake(CGRectGetMidX(self.frame), 34);
ground.xScale = 0.5;
ground.yScale = 0.5;
ground.physicsBody = [SKPhysicsBody bodyWithRectangleOfSize:ground.size];
ground.physicsBody.categoryBitMask=groundCategory;
ground.physicsBody.collisionBitMask=playerCategory|redBallCategory;
ground.physicsBody.dynamic = NO;
//Player
self.playerSprite = [SKSpriteNode spriteNodeWithImageNamed:#"character"];
self.playerSprite.position = CGPointMake(playerLocX, playerLocY);
//Set Player Physics
self.playerSprite.physicsBody = [SKPhysicsBody bodyWithRectangleOfSize:self.playerSprite.size];
self.playerSprite.physicsBody.dynamic = YES;
self.playerSprite.physicsBody.categoryBitMask = playerCategory;
self.playerSprite.physicsBody.contactTestBitMask = redBallCategory;
self.playerSprite.physicsBody.collisionBitMask = groundCategory|redBallCategory;
self.playerSprite.physicsBody.usesPreciseCollisionDetection = YES;
//Score Label
self.scoreLabel = [SKLabelNode labelNodeWithFontNamed:#"Arial-BoldMT"];
self.scoreLabel.text = #"0";
self.scoreLabel.fontSize = 40;
self.scoreLabel.fontColor = [SKColor blackColor];
self.scoreLabel.position = CGPointMake(50, 260);
//Pause Button
self.pauseButton = [SKSpriteNode spriteNodeWithImageNamed:#"pauseButton"];
self.pauseButton.position = CGPointMake(self.frame.size.width / 2, self.frame.size.height - 40);
self.pauseButton.name = #"pauseButton";
//Add nodes
[self addChild:ground];
[self addChild:self.playerSprite];
[self addChild:self.scoreLabel];
//[self addChild:self.pauseButton];
//Sets gravity
self.physicsWorld.gravity = CGVectorMake(0,-2);
self.physicsWorld.contactDelegate = self;
}
return self;
}
Just:
self.playerSprite.physicsBody.dynamic = NO;
should work.
Your problem occurs due to scaling. For some reason in sprite kit scaling an image doesn't change it's size when used in following code. Judging by your pictures, your physics body rectangle for your ground is actually twice as big as you think and already engulfing the player, which is why there would by no collision detection. This is from recent experience with a very similar style game.
Do you have an edge loop physcis body around the scene? Collision flags and category flags set correctly so the player collides with the ground?
i had same problem and i soved it simply...
On the update method i've putted an if statement:
if(player.position.y<your_closest_value_near_ground){
player.position.y == your_Closest_value_near_ground
}
The comparition differs by the anchor point you have.. hope it helps someone

How can I stop walking of a man if there is a wall in Sprite Kit?

I am practicing with Sprite Kit in iOS 7.
Now I am getting a problem while a man walking from one point to another point by SKAction moveTo.
If there is any wall in the screen, I want to stop walking before the wall.
I used the code in skscen.h file like below:
typedef NS_ENUM(uint32_t, CollisionType)
{
CollisionTypePlayer = 0x1 << 0,
CollisionTypeWall = 0x1 << 1,
CollisionTypeRiver = 0x1 << 3,
CollisionTypeBush = 0x1 << 4
};
#import <SpriteKit/SpriteKit.h>
#interface THMyScene : SKScene <SKPhysicsContactDelegate>
#property (nonatomic) SKSpriteNode *manContainer;
//Wall sprite
#property (nonatomic) SKSpriteNode *wallNode1;
#end
and in the scene.m file given below:
#import "THMyScene.h"
#interface THMyScene()
#property (nonatomic) SKTextureAtlas *manAtlas;
#property (nonatomic) NSMutableArray *manImagesArray;
#end
#implementation THMyScene
-(id)initWithSize:(CGSize)size {
if (self = [super initWithSize:size]) {
/* Setup your scene here */
// Configure physics for the world.
self.physicsWorld.gravity = CGVectorMake(0.0f, 0.0f); // no gravity
self.physicsWorld.contactDelegate = self;
[self createWall];
[self createMan];
}
return self;
}
-(void) createMan
{
_manAtlas = [SKTextureAtlas atlasNamed:#"secondLevelMan"];
_manImagesArray = [[NSMutableArray alloc]init];
for(int i = 1; i <= _manAtlas.textureNames.count ; i++)
{
NSString *imageName = [NSString stringWithFormat:#"man%d.png",i];
SKTexture *tmpTexture = [SKTexture textureWithImageNamed:imageName];
[_manImagesArray addObject:tmpTexture];
}
_manContainer = [SKSpriteNode spriteNodeWithTexture:[_manImagesArray objectAtIndex:0]];
_manContainer.position = CGPointMake(50, 250);
[self addChild:_manContainer];
_manContainer.physicsBody = [SKPhysicsBody bodyWithRectangleOfSize:_manContainer.size];
_manContainer.physicsBody.allowsRotation = YES;
_manContainer.physicsBody.dynamic = YES;
_manContainer.physicsBody.usesPreciseCollisionDetection = YES;
_manContainer.physicsBody.categoryBitMask = CollisionTypePlayer;
_manContainer.physicsBody.contactTestBitMask = CollisionTypeWall;
_manContainer.physicsBody.collisionBitMask = CollisionTypeRiver;
}
-(void) createWall
{
//Create wall 1
_wallNode1 = [SKSpriteNode spriteNodeWithImageNamed:#"rock-1"];
_wallNode1.name = #"stonewall";
_wallNode1.position = CGPointMake(400 , 700);
[self addChild:_wallNode1];
_wallNode1.physicsBody = [SKPhysicsBody bodyWithRectangleOfSize:_wallNode1.size];
_wallNode1.physicsBody.dynamic = NO;
_wallNode1.physicsBody.categoryBitMask = CollisionTypeWall;
_wallNode1.physicsBody.contactTestBitMask = 0;
_wallNode1.physicsBody.collisionBitMask = CollisionTypePlayer;
}
-(void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event {
UITouch *touch = [touches anyObject];
CGPoint locationTouch = [touch locationInNode:self];
CGPoint manLocation = _manContainer.position;
CGFloat rotatingAngle = [self pointPairToBearingDegrees:manLocation secondPoint:locationTouch];
CGFloat distance = [self distanceBetweenTwoPoints:manLocation andSecondpoint:locationTouch];
SKAction *animationAction = [SKAction repeatActionForever:[SKAction animateWithTextures:_manImagesArray timePerFrame: 0.0833]];
float duration = distance / 300;
SKAction *movePoint = [SKAction moveTo:locationTouch duration:duration];
SKAction *rotate = [SKAction rotateToAngle:rotatingAngle duration:0.0f];
SKAction *sequenceAction = [SKAction sequence:#[rotate, movePoint]];
[_manContainer runAction:[SKAction group:#[sequenceAction, animationAction]] withKey:#"runAnimation"];
}
- (CGFloat) pointPairToBearingDegrees:(CGPoint)startingPoint secondPoint:(CGPoint) endingPoint
{
CGPoint originPoint = CGPointMake(endingPoint.x - startingPoint.x, endingPoint.y - startingPoint.y); // get origin point to origin by subtracting end from start
float bearingRadians = atan2f(originPoint.y, originPoint.x); // get bearing in radians
return bearingRadians;
}
-(CGFloat)distanceBetweenTwoPoints:(CGPoint)firstPoint andSecondpoint:(CGPoint)secondPoint
{
CGFloat dx = secondPoint.x - firstPoint.x;
CGFloat dy = secondPoint.y - firstPoint.y;
return sqrt(dx*dx + dy*dy );
}
- (void) didBeginContact:(SKPhysicsContact *)contact
{
SKPhysicsBody *firstBody, *secondBody;
if (contact.bodyA.categoryBitMask < contact.bodyB.categoryBitMask)
{
firstBody = contact.bodyA;
secondBody = contact.bodyB;
}
else
{
firstBody = contact.bodyB;
secondBody = contact.bodyA;
}
}
#end
try something like this:
-(void)update:(CFTimeInterval)currentTime {
int distance = 10; // distance to wall when to stop
if ((man.position.x > wall.position.x + wall.size.width/2 + distance) &&
(man.position.x < wall.position.x - wall.size.width/2 - distance)
if ((man.position.y > wall.position.y + wall.size.height/2 + distance) &&
(man.position.y < wall.position.y - wall.size.height/2 - distance)
//do what you want like remove some or all actions of man
} }
//change your code this would work
- (void) didBeginContact:(SKPhysicsContact *)contact
{
SKPhysicsBody *firstBody, *secondBody;
if (contact.bodyA.categoryBitMask < contact.bodyB.categoryBitMask)
{
firstBody = contact.bodyA;
secondBody = contact.bodyB;
[firstBody.node removeAllActions];
firstBody.node.speed=0;
}
else
{
firstBody = contact.bodyB;
secondBody = contact.bodyA;
[secondBody.node removeAllActions];
secondBody.node.speed=0;
}
}
//
remember every node in spritekit have speed property which control sprite animation speed

Resources