Not detecting collision between boundary frame and SKNode in SpriteKit - ios

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.

Related

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

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

spritekit didBeginContact not called

didBeginContact doesnt get called for some reason. How can I fix this? Thanks! I set the category bitmasks and the skphysicsContactDelegate, yet it is still not registering contacts. I've been stuck at this for some time now.
#import "MyScene.h"
#import "FuelNode.h"
#import "SKSpriteNode+DebugDraw.h"
typedef NS_OPTIONS(uint32_t, CollisionCategory) {
CollisionCategoryPlayer = 1 << 0,
CollisionCategoryFuel = 1 << 1,
};
#interface MyScene() <SKPhysicsContactDelegate>
#end
#implementation MyScene
{
SKNode *_playerNode;
SKNode *_backgroundNode;
SKNode *_foreGround;
}
-(id)initWithSize:(CGSize)size
{
if (self = [super initWithSize:size]) {
self.physicsWorld.contactDelegate = self;
_backgroundNode = [self createBackground];
[self addChild:_backgroundNode];
_foreGround = [SKNode node];
[self addChild:_foreGround];
//add a fuelNode
FuelNode *fuel = [self createFuelAtPosition:CGPointMake(160, 440)];
[_foreGround addChild:fuel];
_playerNode = [self createPlayer];
[_foreGround addChild:_playerNode];
SKAction *actionMove = [SKAction moveToY:-100 duration:3.0];
[fuel runAction:actionMove];
NSLog(#"yea");
}
return self;
}
-(SKNode *)createPlayer
{
CGSize playerPhysicsBody;
//Create player
SKNode *playerNode = [SKNode node];
SKSpriteNode *player = [SKSpriteNode spriteNodeWithImageNamed:#"wship-3.png"];
player.position = CGPointMake(self.size.width/2, 120);
[playerNode addChild:player];
//Add physics
playerPhysicsBody = CGSizeMake(player.size.width/2, player.size.height/2);
playerNode.physicsBody = [SKPhysicsBody bodyWithRectangleOfSize:playerPhysicsBody];
playerNode.physicsBody.dynamic = NO;
//Setup collision settings
playerNode.physicsBody.usesPreciseCollisionDetection = YES;
playerNode.physicsBody.categoryBitMask = CollisionCategoryPlayer;
playerNode.physicsBody.collisionBitMask = 0;
playerNode.physicsBody.contactTestBitMask = CollisionCategoryFuel;
[player attachDebugRectWithSize:playerPhysicsBody];
return playerNode;
}
-(SKNode *)createBackground
{
//Create background
SKNode *bgNode = [SKNode node];
SKSpriteNode *bg = [SKSpriteNode spriteNodeWithImageNamed:#"purple"];
bg.anchorPoint = CGPointZero;
[bgNode addChild:bg];
return bgNode;
}
- (FuelNode *)createFuelAtPosition:(CGPoint)position
{
// 1
FuelNode *node = [FuelNode node];
[node setPosition:position];
[node setName:#"NODE_FUEL"];
// 2
SKSpriteNode *sprite;
sprite = [SKSpriteNode spriteNodeWithImageNamed:#"fuelBlue"];
[node addChild:sprite];
// 3
CGSize contactSize = CGSizeMake(sprite.size.width/2, sprite.size.height/2);
node.physicsBody = [SKPhysicsBody bodyWithRectangleOfSize:contactSize];
// 4
node.physicsBody.dynamic = NO;
//Setup collision settings
node.physicsBody.categoryBitMask = CollisionCategoryFuel;
node.physicsBody.collisionBitMask = 0;
//node.physicsBody.contactTestBitMask = CollisionCategoryPlayer;
[sprite attachDebugRectWithSize:contactSize];
//SKAction *actionMove = [SKAction moveToY:-100 duration:3.0];
//[node runAction:actionMove];
return node;
}
-(void)didBeginContact:(SKPhysicsContact *)contact
{
BOOL fuelCollision = NO;
SKNode *other = (contact.bodyA.node != _playerNode) ? contact.bodyA.node : contact.bodyB.node;
NSLog(#"collision");
fuelCollision = [(GameObjectNode *)other collisionWithPlayer:_playerNode];
}
#end
In order to enable contact detection, you need to set
node.physicsBody.dynamic = YES;
Have a look at the documentation as well.
Fixed! In the create player method I have mistakenly set the position of the player sprite instead of setting the position of the player node!

Change sprite image on touch

I am making a game which purpose it is to catch multiple objects that are falling from the top of the screen. In the bottom there is a basket to catch the objects. i managed to randomly spawn objects from the top dropping to the bottom using raywenderlich's tutorial : http://www.raywenderlich.com/42699/spritekit-tutorial-for-beginners
But what i want is that when i tap on that random object, the image of that object changes into another image , so just for imagination if the random objects are cats, after i tap them they have to become dogs, how do i have to program this?
edit this is what i got so far :
#import "MyScene.h"
static NSString* basketCategoryName = #"basket";
static NSString* monsterCategoryName= #"monster";
static const uint32_t projectileCategory = 0x1 << 0;
static const uint32_t monsterCategory = 0x1 << 1;
#interface MyScene() <SKPhysicsContactDelegate>
#property (nonatomic) SKLabelNode * scoreLabelNode;
#property int score;
#property (nonatomic) SKSpriteNode * basket;
#property (nonatomic) SKSpriteNode * monster;
#property (nonatomic) BOOL isFingerOnBasket;
#property (nonatomic) BOOL isFingerOnMonster;
#property (nonatomic) BOOL isTouching;
#property (nonatomic) NSTimeInterval lastSpawnTimeInterval;
#property (nonatomic) NSTimeInterval lastUpdateTimeInterval;
//#property (nonatomic, strong) SKSpriteNode *selectedNode;
#end
#implementation MyScene
-(id)initWithSize:(CGSize)size {
if (self = [super initWithSize:size]) {
// Initialize label and create a label which holds the score
_score = 0;
_scoreLabelNode = [SKLabelNode labelNodeWithFontNamed:#"MarkerFelt-Wide"];
_scoreLabelNode.position = CGPointMake( CGRectGetMidX( self.frame ), 3 * self.frame.size.height / 4 );
_scoreLabelNode.zPosition = 100;
_scoreLabelNode.text = [NSString stringWithFormat:#"%d", _score];
[self addChild:_scoreLabelNode];
// Set the background
SKTexture* groundTexture = [SKTexture textureWithImageNamed:#"AcornFlipTestBackground1136x640.png"];
groundTexture.filteringMode = SKTextureFilteringNearest;
for( int i = 0; i < 2 + self.frame.size.width / ( groundTexture.size.width * 2 ); ++i ) {
SKSpriteNode* sprite = [SKSpriteNode spriteNodeWithTexture:groundTexture];
[sprite setScale:1.0];
sprite.size = CGSizeMake(self.frame.size.width,self.frame.size.height);
sprite.position = CGPointMake(CGRectGetMidX(self.frame),
CGRectGetMidY(self.frame));
[self addChild:sprite];
}
// Make grafity for sprite
self.physicsWorld.gravity = CGVectorMake(0.0f, 0.0f);
self.physicsWorld.contactDelegate = self;
// Make catching object sprite
self.basket = [SKSpriteNode spriteNodeWithImageNamed:#"bedTest.png"];
self.basket.position = CGPointMake(CGRectGetMidX(self.frame), _basket.frame.size.height * 0.5f);
self.basket.name = basketCategoryName;
[self addChild:self.basket];
// For default this is set to no until user touches the basket and the game begins.
self.isTouching = NO;
}
return self;
}
-(void)addAcorn{
if(_isTouching == YES) {
self.monster= [SKSpriteNode spriteNodeWithImageNamed:#"AcornFinal.png"];
// Determine where to spawn the monster along the X axis
int minX = self.monster.size.width;
int maxX = self.frame.size.width - self.monster.size.width;
int rangeX = maxX - minX;
int actualX = (arc4random() % rangeX)+minX;
// Random position along the X axis as calculated above
// This describe from which way the acorns move
// - means moving from top to the right and + means moving from the top to the left
self.monster.position = CGPointMake(actualX ,self.frame.size.height+ self.monster.size.height);
self.monster.name = monsterCategoryName;
[self addChild:self.monster];
CGSize contactSize = CGSizeMake(self.monster.size.width - 5.0, self.monster.size.height - 10.0);
self.monster.physicsBody = [SKPhysicsBody bodyWithRectangleOfSize:contactSize]; // 1
self.monster.physicsBody.dynamic = YES; // 2
self.monster.physicsBody.categoryBitMask = monsterCategory; // 3
self.monster.physicsBody.contactTestBitMask = projectileCategory; // 4
self.monster.physicsBody.collisionBitMask = 0; // 5
// Determine speed of the monster
int minDuration = 8.0;
int maxDuration = 10.0;
int rangeDuration = maxDuration - minDuration;
int actualDuration = (arc4random() % rangeDuration) + minDuration;
// Create the actions
SKAction * actionMove = [SKAction moveTo:CGPointMake(actualX,-self.monster.size.height) duration:actualDuration];
SKAction * actionMoveDone = [SKAction removeFromParent];
[self.monster runAction:[SKAction sequence:#[actionMove, actionMoveDone]]];
}
}
- (void)updateWithTimeSinceLastUpdate:(CFTimeInterval)timeSinceLast {
self.lastSpawnTimeInterval += timeSinceLast;
if (self.lastSpawnTimeInterval > 0.5) {
self.lastSpawnTimeInterval = 0;
[self addAcorn];
}
}
- (void)update:(NSTimeInterval)currentTime {
// Handle time delta.
// If we drop below 60fps, we still want everything to move the same distance.
CFTimeInterval timeSinceLast = currentTime - self.lastUpdateTimeInterval;
self.lastUpdateTimeInterval = currentTime;
if (timeSinceLast > 1) { // more than a second since last update
timeSinceLast = 1.0 / 60.0;
self.lastUpdateTimeInterval = currentTime;
}
[self updateWithTimeSinceLastUpdate:timeSinceLast];
}
-(void)touchesBegan:(NSSet*)touches withEvent:(UIEvent*)event {
self.isTouching = YES;
UITouch *touch = [touches anyObject];
CGPoint location = [touch locationInNode:self];
SKNode* body = [self nodeAtPoint:location];
if ([body.name isEqualToString:basketCategoryName])
{
NSLog(#"Began touch on basket");
self.isFingerOnBasket = YES;
}
else if ([body.name isEqualToString:monsterCategoryName])
{
NSLog(#"Began touch on MONSTER");
self.isFingerOnMonster = YES;
}
}
-(void)touchesMoved:(NSSet*)touches withEvent:(UIEvent*)event {
if (self.isFingerOnMonster) {
// 2 Get touch location
UITouch* touch = [touches anyObject];
CGPoint location = [touch locationInNode:self];
CGPoint previousLocation = [touch previousLocationInNode:self];
// 3 Get node for paddle
SKSpriteNode* monster = (SKSpriteNode*)[self childNodeWithName: monsterCategoryName];
int oldPosition = monster.position.x + (location.x - previousLocation.x);
self.monster = [SKSpriteNode spriteNodeWithImageNamed:#"AcornFinal.png"];
monster.position = CGPointMake(oldPosition, monster.position.y);
NSLog(#"reached the touch though");
}
// 1 Check whether user tapped paddle
if (self.isFingerOnBasket) {
// 2 Get touch location
UITouch* touch = [touches anyObject];
CGPoint location = [touch locationInNode:self];
CGPoint previousLocation = [touch previousLocationInNode:self];
// 3 Get node for paddle
SKSpriteNode* basket = (SKSpriteNode*)[self childNodeWithName: basketCategoryName];
// 4 Calculate new position along x for paddle
int basketX = basket.position.x + (location.x - previousLocation.x);
// 5 Limit x so that the paddle will not leave the screen to left or right
basketX = MAX(basketX, basket.size.width/2);
basketX = MIN(basketX, self.size.width - basket.size.width/2);
// 6 Update position of paddle
basket.position = CGPointMake(basketX, basket.position.y);
CGSize contactSize = CGSizeMake(basket.size.width - 8.0, basket.size.height - 8.0);
basket.physicsBody = [SKPhysicsBody bodyWithRectangleOfSize:contactSize];
basket.physicsBody.dynamic = YES;
basket.physicsBody.categoryBitMask = projectileCategory;
basket.physicsBody.contactTestBitMask = monsterCategory;
basket.physicsBody.collisionBitMask = 0;
basket.physicsBody.usesPreciseCollisionDetection = YES;
}
}
- (void)projectile:(SKSpriteNode *)basket didCollideWithMonster:(SKSpriteNode *)monster {
NSLog(#"Hit");
[monster removeFromParent];
}
- (void)didBeginContact:(SKPhysicsContact *)contact
{
// 1
SKPhysicsBody *firstBody, *secondBody;
if (contact.bodyA.categoryBitMask < contact.bodyB.categoryBitMask)
{
firstBody = contact.bodyA;
secondBody = contact.bodyB;
}
else
{
firstBody = contact.bodyB;
secondBody = contact.bodyA;
}
// 2
if ((firstBody.categoryBitMask & projectileCategory) != 0 &&
(secondBody.categoryBitMask & monsterCategory) != 0)
{
[self projectile:(SKSpriteNode *) firstBody.node didCollideWithMonster:(SKSpriteNode *) secondBody.node];
NSLog(#"test");
_score++;
_scoreLabelNode.text = [NSString stringWithFormat:#"%d", _score];
}
}
// Removing this void will result in being able to drag the basket accross the screen without touching the basket itself.
-(void)touchesEnded:(NSSet*)touches withEvent:(UIEvent*)event {
self.isFingerOnBasket = NO;
self.isFingerOnMonster = NO;
}
#end
Once a touch is detected on a sprite (I assume you already got that working out) you can either create the sprite again with spriteNodeWithImageNamed. Make sure you save the previous node's position and set it again on the new sprite so it will match the position of the old sprite.
CGPoint oldPosition = touchedSprite.position;
touchedSprite = [SKSpritNode spriteWithImageNamed:#"imgge.png"];
touchedSprite.position = oldPosition;
// If you have any other sprite properties you will have to save them as well
You can also set the texture with setTexture method which will not require you to change anything else (e.g. position) :
[touchedSprite setTexture:[SKTexture textureWithImageNamed:#"image.png"]];
EDIT :
Answering your question in the comments you implement this in the touchesEnded method of the parent node of the sprites :
-(void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event {
UITouch *touch = [touches anyObject];
CGPoint touchLocation = [touch locationInNode:self];
for (SKSpriteNode *sprite in fallingSprites) { // Assuming fallingSprite is an array containing the cat sprites you want to detect touches for
if (CGRectContainsPoint(sprite.frame, touchLocation)) {
[sprite setTexture:[SKTexture textureWithImageNamed:#"dog.png"]];
}
}
}
Another approach (Haven't tried it yet) is to subclass SKSpriteNode and implement the same method but without the touch detection in rect since if this method is called the sprite has been touched .
Each part of the skeleton model is a spritekit node. Find the node that you wish to change and update its texture property like this:
spriteKitNode.texture = // Updated SKTexture
In your TouchesEnded event:
Add a small skspritenode positioned at the touch location with a small physics body that last for a short duration. ("touch spritenode)
Set up contact between all possible transformed objects and the "touch spritenode"
Create a subclass of SKSpritenode for the falling objects that has a variable to stores their type.
In the method called from contact between the touch spritenode and the falling objects:
Remove the touchspritenode first
Create if statements to check which falling object was touched.
Update the image of the contacted falling object spritenode according to its type.
If you are following Raywenderlich you have access to the actual syntax

sprite kit collision detection with child sprite

I'm trying to detect collisions between two sprites but I'm unable to do this with a child sprite.
self.player = [[Player alloc] initWithImageNamed:#"player"];
self.player.position = CGPointMake(150, 75);
[self addChild:self.player];
_object = [SKSpriteNode spriteNodeWithImageNamed:#"object"];
_object.position = CGPointMake(-40, 27);
[self.player addChild:_object];
then I have collision detetion like this
- (void)checkCollisions {
[self.map enumerateChildNodesWithName:#"enemy"
usingBlock:^(SKNode *node, BOOL *stop){SKSpriteNode *enemy = (SKSpriteNode *)node;
if (CGRectIntersectsRect(enemy.frame, _object.frame)) {
[enemy removeFromParent];
}
}]; }
*This does not work!!! but if I use :
CGRectIntersectsRect(enemy.frame, self.player.frame)
I can detect a collision with the main body. how do I do collision detection for a child of another sprite?
The child node's position and frame property are relative to it's parent, which you need to account for in your code.
SKNode has two methods that can help you with converting the position of a given SKNode to/from the coordinate spaces of another node:
convertPoint:fromNode:
convertPoint:toNode:
You can use those to convert a SKNode's position property to another coordinate space. What you want to do is convert the _object's position to the coordinate space of the enemy or visa versa and then use a temporary CGRect with that new position for your CGRectIntersectsRect check.
Here's an example :
CGPoint globalPosition = [self.player convertPoint:_object.position toNode:self.player.parent];
CGRect tempRect = CGRectMake(globalPosition.x, globalPosition.y, _object.frame.size.width, _object.frame.size.height);
if (CGRectIntersectsRect(enemy.frame, _object.frame))
{
// collision occurred
}
This code is assuming that your enemy and your player are in the same coordinate space. (have the same parent)
This happens because _object is a child node for self.player, and enemy node is a child node for self.map node. Both nodes should have same parent if you want to compare their frames.
I suggest you to use Sprite Kit's built-in collision handling mechanism:
// Object:
_object = [SKSpriteNode spriteNodeWithImageNamed:#"object"];
_object.position = CGPointMake(-40, 27);
_object.physicsBody = [SKPhysicsBody bodyWithRectangleOfSize:_object.frame.size;
_object.physicsBody.categoryBitMask = OBJECT_CATEGORY;
_object.physicsBody.contactTestBitMask = ENEMY_CATEGORY;
_object.physicsBody.collisionBitMask = 0;
[self.player addChild:_object];
// Enemy:
// init enemy: SKSpriteNode *enemy = [SKSpriteNode spriteNodeWithImageNamed:#"enemy"];
enemy.physicsBody = [SKPhysicsBody bodyWithRectangleOfSize:enemy.frame.size;
enemy.physicsBody.categoryBitMask = ENEMY_CATEGORY;
enemy.physicsBody.contactTestBitMask = OBJECT_CATEGORY;
enemy.physicsBody.collisionBitMask = 0;
// add enemy to the scene: [self addChild:enemy];
// Define that your SKScene conforms to SKPhysicsContactDelegate protocol:
.....
#interface MyScene : SKScene <SKPhysicsContactDelegate>
....
// SKScene.m:
static uint32_t const OBJECT_CATEGORY = 0x1 << 0;
static uint32_t const ENEMY_CATEGORY = 0x1 << 1;
#implementation MyScene
- (id)initWithSize:(CGSize)size {
if (self = [super initWithSize:size]) {
.......
self.physicsWorld.contactDelegate = self;
........
}
return self;
}
.......
- (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;
}
if (((firstBody.categoryBitMask & OBJECT_CATEGORY) != 0) &&
(secondBody.categoryBitMask & ENEMY_CATEGORY) != 0) {
NSLog(#"Collision detected!");
}
#end

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