SpriteKit didBeginContact not being called - ios

I hope you can help, have a block moving across and it jumps to avoid obstacles and collect coins.
It's collision with the obstacles is working correctly and the below gameOver is working correctly.
-(void)didBeginContact:(SKPhysicsContact *)contact
{
if ([contact.bodyA.node.name isEqualToString:#"coins"] || [contact.bodyB.node.name isEqualToString:#"coins"]) {
[self coinCollected]; //THIS IS NOT WORKING
NSLog(#"contacted"); //THIS IS NOT WORKING
}
else if ([contact.bodyA.node.name isEqualToString:#"ground"] || [contact.bodyB.node.name isEqualToString:#"ground"]) {
[hero land];
}
else {
NSLog (#"dead");
[self gameOver];
[self runAction:[SKAction playSoundFileNamed:#"gameover.wav" waitForCompletion:NO]];
}
}
My PMWorldGenenerator file looks as below:
#import "PMWorldGenerator.h"
#interface PMWorldGenerator ()
#property double currentGroundX;
#property double currentObstacleX;
#property double coinX;
#property SKNode *world;
#end
#implementation PMWorldGenerator
static const uint32_t obstacleCategory = 0x1 << 1;
static const uint32_t groundCategory = 0x1 << 2;
static const uint32_t coinCategory = 0x1 << 3;
+ (id)generatorWithWorld:(SKNode *)world {
PMWorldGenerator *generator = [PMWorldGenerator node];
generator.currentGroundX = 0;
generator.currentObstacleX = 400;
generator.coinX = 50;
generator.world = world;
return generator;
}
-(void)populate
{
for (int i = 0; i <3; i++)
[self generate];
}
-(void)generate
{
SKSpriteNode *ground = [SKSpriteNode spriteNodeWithColor:[UIColor greenColor] size:CGSizeMake(self.scene.frame.size.width, self.scene.frame.size.height/2.7)];
ground.name = #"ground";
ground.position = CGPointMake(self.currentGroundX, -self.scene.frame.size.height/2 + ground.frame.size.height/2);
ground.physicsBody = [SKPhysicsBody bodyWithRectangleOfSize:ground.size];
ground.physicsBody.categoryBitMask = groundCategory;
ground.physicsBody.dynamic = NO;
[self.world addChild:ground];
self.currentGroundX += ground.frame.size.width;
SKSpriteNode *obstacle = [SKSpriteNode spriteNodeWithColor:[UIColor redColor] size:CGSizeMake(10,10)];
obstacle.name = #"obstacle";
obstacle.position = CGPointMake(self.currentObstacleX/5, ground.position.y + ground.frame.size.height/2 + obstacle.frame.size.height/2 + 5);
obstacle.physicsBody = [SKPhysicsBody bodyWithRectangleOfSize:obstacle.size];
obstacle.physicsBody.dynamic = NO;
obstacle.physicsBody.categoryBitMask = obstacleCategory;
[self.world addChild:obstacle];
self.currentObstacleX += 550 ;
SKSpriteNode *coins = [SKSpriteNode spriteNodeWithColor:[UIColor yellowColor] size:CGSizeMake(4, 4)];
coins.name = #"coins";
coins.position = CGPointMake(self.coinX+80, ground.position.y + ground.frame.size.height/2 + obstacle.frame.size.height/2 + 25);
coins.physicsBody.categoryBitMask = coinCategory;
coins.physicsBody.dynamic = YES;
SKAction *revolution = [SKAction rotateByAngle:M_PI_4*10 duration:3];
SKAction *repeatRotate = [SKAction repeatActionForever:revolution];
[coins runAction:repeatRotate];
[self.world addChild:coins];
self.coinX += 550;
}
and lastly my PMHero file:
#import "PMHero.h"
#interface PMHero ()
#end
#implementation PMHero
static const uint32_t heroCategory = 0x1 << 0;
static const uint32_t obstacleCategory = 0x1 << 1;
static const uint32_t groundCategory = 0x1 << 2;
static const uint32_t coinCategory = 0x1 << 3;
+(id)hero
{
PMHero *hero = [PMHero spriteNodeWithColor:[UIColor redColor] size:CGSizeMake(12,12)];
hero.name = #"hero";
hero.physicsBody = [SKPhysicsBody bodyWithRectangleOfSize:hero.size];
hero.physicsBody.categoryBitMask = heroCategory;
hero.physicsBody.contactTestBitMask = obstacleCategory | groundCategory | coinCategory;
return hero;
}
I have done exactly what I had done for my obstacles and ground for the "coins", but it doesn't detect any collisions with them in my didBeginContact

You haven't added your physics body for coins
SKSpriteNode *coins = [SKSpriteNode spriteNodeWithColor:[UIColor yellowColor] size:CGSizeMake(4, 4)];
coins.name = #"coins";
coins.position = CGPointMake(self.coinX+80, ground.position.y + ground.frame.size.height/2 + obstacle.frame.size.height/2 + 25);
coins.physicsBody = [SKPhysicsBody bodyWith...//need code here
coins.physicsBody.categoryBitMask = coinCategory;
coins.physicsBody.dynamic = NO;
coins.physicsBody.collisionBitMask = 0;
SKAction *revolution = [SKAction rotateByAngle:M_PI_4*10 duration:3];
SKAction *repeatRotate = [SKAction repeatActionForever:revolution];
[coins runAction:repeatRotate];
[self.world addChild:coins];

Related

collision detection in didbegincontact does not work

I want every spritenode the user creates come with a left and right edge bitmask. Other bitmasks work, but this one don't.
Below the method for creating a new spritenode, which is called in TouchesBegin
-(void)spawnBrick
{
brick = [[BrickNodeOne alloc] init];
brick.name = #"Brick";
NSArray *array = [self objectForKeyedSubscript:#"Brick"];
BrickNodeOne *oldBrick;
if ([array count] > 0) {
oldBrick = [array objectAtIndex:([array count]-1)];
}
brick.position = CGPointMake(brick.size.width / 2, (brick.size.height + oldBrick.position.y)-15);
brick.physicsBody = [SKPhysicsBody bodyWithRectangleOfSize:CGSizeMake(brick.size.width, brick.size.height)];
brick.physicsBody.categoryBitMask = kBrickCategory;
brick.physicsBody.contactTestBitMask = kCarCategory;
brick.physicsBody.collisionBitMask = 0;
[self addChild:brick];
SKAction *moveTo = [SKAction moveByX:0.0 y:-125.0 duration:1.0];
[brick runAction:[SKAction repeatActionForever:moveTo]withKey:#"moveDown"];
_brickRightEdge = [[SKNode alloc] init];
_brickRightEdge.physicsBody = [SKPhysicsBody bodyWithEdgeFromPoint:CGPointMake(brick.size.width, 0.0) toPoint:CGPointMake(brick.size.width, brick.size.height)];
_brickRightEdge.physicsBody.dynamic = NO;
_brickRightEdge.physicsBody.categoryBitMask = brickRightEdgeCategory;
_brickRightEdge.physicsBody.contactTestBitMask = kCarCategory;
_brickRightEdge.position = brick.position;
[brick addChild:_brickRightEdge];
_brickLeftEdge = [[SKNode alloc] init];
_brickLeftEdge.physicsBody = [SKPhysicsBody bodyWithEdgeFromPoint:CGPointMake(0.0, 0.0) toPoint:CGPointMake(0.0, brick.size.height)];
_brickLeftEdge.physicsBody.dynamic = NO;
_brickLeftEdge.position = brick.position;
_brickLeftEdge.physicsBody.categoryBitMask = brickLeftEdgeCategory;
_brickLeftEdge.physicsBody.contactTestBitMask = kCarCategory;
[brick addChild:_brickLeftEdge];
}
The car spritenode which I want collision detection with the brick right and left edge is declared in didMoveToView
SKSpriteNode *car = [SKSpriteNode spriteNodeWithImageNamed:#"HighWayCar_Red"];
car.position = CGPointMake(self.size.width / 2, 120);
car.physicsBody = [SKPhysicsBody bodyWithRectangleOfSize:car.size];
car.physicsBody.categoryBitMask = kCarCategory;
car.physicsBody.contactTestBitMask = kWaterCategory | brickLeftEdgeCategory | brickRightEdgeCategory;
car.physicsBody.collisionBitMask = 0;
car.physicsBody.dynamic = NO;
car.zPosition = 2;
[self addChild:car];
Code DidBeginContact
if (firstBody.categoryBitMask == kCarCategory && secondBody.categoryBitMask == brickLeftEdgeCategory) {
NSLog(#"HIT");
}
if (firstBody.categoryBitMask == kCarCategory && secondBody.categoryBitMask == brickRightEdgeCategory) {
NSLog(#"HIT");
}
Update for comment
I declared `SKPhysicsContactDelegate` in the header file. Set the delegate in `didMoveToView`
Bitmasks:
static const uint32_t kBrickCategory = 0x1 << 0;
static const uint32_t leftEdgeCategory = 0x1 << 1;
static const uint32_t rightEdgeCategory = 0x1 << 2;
static const uint32_t brickLeftEdgeCategory = 0x1 << 3;
static const uint32_t brickRightEdgeCategory = 0x1 << 4;
static const uint32_t kCarCategory = 0x1 << 5;
static const uint32_t kWaterCategory = 0x1 << 6;

How to rotate by using accelerometer? ios

I want to rotate my character by tilting the phone left and right, but it only rotate once when DidMoveToView is called. What do I need to change in my code?
self.manager = [[CMMotionManager alloc] init];
if ([self.manager isAccelerometerAvailable] == YES) {
[self.manager startAccelerometerUpdatesToQueue:[[NSOperationQueue alloc] init]
withHandler:^(CMAccelerometerData *data, NSError *error)
{
float destX = 0.0, destY = 0.0;
float currentX = _character.position.x;
float currentY = _character.position.y;
BOOL shouldMoveToRight = NO;
BOOL shouldMoveToLeft = NO;
if(data.acceleration.y < -0.25) { // tilting the device to the right
destX = currentX + (data.acceleration.x * kPlayerSpeed);
destY = currentY;
shouldMoveToRight = YES;
shouldMoveToLeft = NO;
} else if (data.acceleration.y > 0.25) { // tilting the device to the left
destX = currentX + (data.acceleration.x * kPlayerSpeed);
destY = currentY;
shouldMoveToRight = NO;
shouldMoveToLeft = YES;
}
if(shouldMoveToRight) {
SKAction *moveAction = [SKAction moveTo:CGPointMake(destX, destY) duration:1];
[_character runAction:moveAction];
SKAction *rotateAction = [SKAction rotateToAngle:M_PI duration:1];
[_character runAction:rotateAction];
}
if (shouldMoveToLeft) {
SKAction *moveAction = [SKAction moveTo:CGPointMake(destX, destY) duration:1];
[_character runAction:moveAction];
SKAction *rotateAction = [SKAction rotateToAngle:-M_PI duration:1];
[_character runAction:rotateAction];
}
}];
}

How to detect SKSpriteNode Contact but do not react?

I have two SKSpriteNode first Hero
+(id)hero
{
NSMutableArray *walkFrames = [NSMutableArray array];
SKTextureAtlas *heroAnimatedAtlas = [SKTextureAtlas atlasNamed:#"HeroImages"];
int numImages = (int)heroAnimatedAtlas.textureNames.count;
for (int i=1; i <= numImages; i++) {
NSString *textureName = [NSString stringWithFormat:#"hero%d", i];
SKTexture *temp = [heroAnimatedAtlas textureNamed:textureName];
[walkFrames addObject:temp];
}
Hero *hero = [Hero spriteNodeWithTexture:walkFrames[0]];
hero.heroWalkingFrames = walkFrames;
hero.name =#"Hero";
hero.physicsBody = [SKPhysicsBody bodyWithRectangleOfSize:hero.size];
hero.physicsBody.categoryBitMask = heroCategory;
hero.physicsBody.categoryBitMask = obstacleCategory | groundCategory | homeCategory | ~goodiesCategory;
return hero;
}
and second is Coin
SKSpriteNode *coin = [SKSpriteNode spriteNodeWithImageNamed:#"coin"];
coin.size = CGSizeMake(10,10);
coin.position = CGPointMake(100,100);
coin.physicsBody = [SKPhysicsBody bodyWithRectangleOfSize:coin.size];
coin.physicsBody.contactTestBitMask = coinCategory;
coin.physicsBody.dynamic=NO;
coin.name = #"coin";
[self.world addChild:coin];
And I am able to get collision detection by
if([contact.bodyA.node.name isEqual: #"coin"] || [contact.bodyB.node.name isEqual: #"coin"])
{
//[self LevelComplete];
SKNode* coinNode ;
if ([contact.bodyA.node.name isEqual: #"coin"]) {
coinNode=contact.bodyA.node;
}
else{
coinNode=contact.bodyB.node;
}
[coinNode removeFromParent];
NSLog(#"Coin touched");
}
Now my problem is every time hero jump and touch the coin it will go down to ground instead to continue jump and reach the height that it should, I know I am missing something here but don't know what it is, So anyone can please show me the right direction to correct this effect .
Create an extra "nilCategory" and set the collisionBitMask of your coin..
coin.physicsBody.collisionBitMask = nilCategory;

adding second to timer iOS game sprite kit

I am trying to add a few seconds to countdown timer each time when enemy and player node contacts.
Also when the player reach to some certain point I want player to pass to another level. I am sending part of the code . Could anybody help me to solve this problem. By the way because I am new to programming my code is a little hard to read . Sorry for that.
Thanks
-(void)update:(CFTimeInterval)currentTime{
if (startGamePlay){
startTime = currentTime;
startGamePlay = NO;
}
countDownInt = 10.0 - (int)(currentTime-startTime);
if(countDownInt > 0 ){ //if counting down to 0 show counter
countDown.text = [NSString stringWithFormat:#"%i", countDownInt];
}
else if (countDownInt == 0){
countDown.text =#"0";
Level2 *level2 = [Level2 sceneWithSize:self.frame.size];
SKTransition *transition = [SKTransition fadeWithDuration:0.5];
[self.view presentScene:level2 transition:transition];
}
}
- (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 == CollisionCategoryBrick && secondBody.categoryBitMask == CollisionCategoryShark ) {
NSLog(#"BRICK");
[contact.bodyB.node removeFromParent];
} else if ( firstBody.categoryBitMask == CollisionCategoryWaterBall && secondBody.categoryBitMask == CollisionCategoryShark ) {
NSLog(#"BALL");
countDownInt = [countDown.text intValue];
[contact.bodyA.node removeFromParent];
[self addPoints:PointsPerHit];
if (![ self childNodeWithName:#"WATERBALLTL"]) {
[self addWaterBallTopLeft];
}
if (![ self childNodeWithName:#"WATERBALLTR"]) {
[self addWaterBallTopRight];
}
if (![ self childNodeWithName:#"WATERBALLBL"]) {
[self addWaterBallBottomLeft];
}
if (![ self childNodeWithName:#"WATERBALLBR"]) {
[self addWaterBallBottomRight];
}
}
NSLog(#"%lu", (unsigned long)[self.children count]);
}
#import "HudNode.h"
#implementation HudNode
{
SKLabelNode *countDown;
BOOL startGamePlay;
NSTimeInterval startTime;
}
+ (instancetype) hudAtPosition:(CGPoint)position inFrame:(CGRect)frame {
HudNode *hud = [self node];
hud.position = position;
hud.zPosition = 10;
hud.name = #"HUD";
SKLabelNode *scoreLabel = [SKLabelNode labelNodeWithFontNamed:#"Futura-CondensedExtraBold"];
scoreLabel.name = #"Score";
scoreLabel.text = #"0";
scoreLabel.fontSize = 24;
scoreLabel.horizontalAlignmentMode = SKLabelHorizontalAlignmentModeRight;
scoreLabel.position = CGPointMake(frame.size.width-20, -10);
scoreLabel.zPosition = 12;
[hud addChild:scoreLabel];
SKLabelNode *countDown = [SKLabelNode labelNodeWithFontNamed:#"Futura-Medium"];
countDown.fontSize = 50;
countDown.position = CGPointMake(30, -10);
countDown.verticalAlignmentMode = SKLabelVerticalAlignmentModeCenter; //good for positioning with other sprites
countDown.fontColor = [SKColor whiteColor ];
countDown.name = #"countDown";
countDown.zPosition = 100;
[hud addChild:countDown];
return hud;
}
- (void) addPoints:(NSInteger)points {
self.score += points;
SKLabelNode *scoreLabel = (SKLabelNode *) [self childNodeWithName:#"Score"];
scoreLabel.text = [NSString stringWithFormat:#"Score:%li", (long)self.score];
}
Create a boolean flag that the update method checks to see if it should add seconds.
Add the following variable and set self.addSeconds = NO; initially:
#property (nonatomic) BOOL addSeconds;
Set self.addSeconds =YES; in the method where your player hits your enemy:
Add the following to the very beginning of the Update:
if(self.addSeconds == YES)
{
CountDownInt+= 5 //Change this to add as many seconds as you want
self.addSeconds=NO;
}
This should do the trick.

Sprite Kit - cant get custom class data from object

I am trying to get the value from my custom class SKNode object. The problem is when I touch on the object no matter what is gives me the same value regardless of the object I touch on.
(blue button) and I cannot get the buttonID,buttonType that I set earlier.
Everything works well, except when I need to get the buttonType, buttonID of the object I touch or drag over.
I am not sure where I am going wrong, any help or push in the right direction would be great. Thanks.
here is my custom class .h file.
#import <SpriteKit/SpriteKit.h>
#interface ButtonNode : SKNode {
SKNode *buttonCustom;
}
#property int buttonType, buttonColumn, buttonID, xPos, yPos;
#property NSString *buttonName;
-(id)initWithButtonType:(int)buttonType;
#end
Here is my custom class .m file
#import "ButtonNode.h"
#define kBoxSize CGSizeMake(40, 40)
#implementation ButtonNode
#synthesize buttonColumn,buttonType,buttonID,xPos,yPos,buttonName;
static const uint32_t blueCategory = 1 << 0;
static const uint32_t redCategory = 1 << 1;
static const uint32_t greenCategory = 1 << 2;
static const uint32_t yellowCategory = 1 << 3;
-(id)initWithButtonType:(int)buttonType {
self = [super init];
if (buttonType == 1) {
NSLog(#"BLUE BUTTON CREATE");
[self addButtonBlue];
}
if (buttonType == 2) {
NSLog(#"RED BUTTON CREATE");
[self addButtonRed];
}
if (buttonType == 3) {
NSLog(#"Green BUTTON CREATE");
[self addButtonGreen];
}
if (buttonType == 4) {
NSLog(#"Yellow BUTTON CREATE");
[self addButtonYellow];
}
return self;
}
- (void) addButtonBlue {
SKSpriteNode *rect;
//button type 1
rect = [SKSpriteNode spriteNodeWithColor:[UIColor blueColor] size:kBoxSize];
int tmpInt = [[NSDate date] timeIntervalSince1970];
NSString *tmpName = [NSString stringWithFormat:#"%i", tmpInt];
rect.name = tmpName; //unique name.
rect.name = #"1";
rect.physicsBody.categoryBitMask = blueCategory;
rect.physicsBody.contactTestBitMask = blueCategory;
rect.physicsBody.collisionBitMask = blueCategory | redCategory | yellowCategory | greenCategory;
rect.position = CGPointMake(xPos , yPos );
[self addChild:rect];
}
- (void) addButtonRed {
SKSpriteNode *rect;
//button type 2
rect = [SKSpriteNode spriteNodeWithColor:[UIColor redColor] size:kBoxSize];
int tmpInt = [[NSDate date] timeIntervalSince1970];
NSString *tmpName = [NSString stringWithFormat:#"%i", tmpInt];
rect.name = tmpName; //unique name.
rect.name = #"2";
rect.physicsBody.categoryBitMask = redCategory;
rect.physicsBody.contactTestBitMask = redCategory;
rect.physicsBody.collisionBitMask = blueCategory | redCategory | yellowCategory | greenCategory;
rect.position = CGPointMake(xPos , yPos );
[self addChild:rect];
}
- (void) addButtonGreen {
SKSpriteNode *rect;
//button type 3
rect = [SKSpriteNode spriteNodeWithColor:[UIColor greenColor] size:kBoxSize];
int tmpInt = [[NSDate date] timeIntervalSince1970];
NSString *tmpName = [NSString stringWithFormat:#"%i", tmpInt];
rect.name = tmpName; //unique name.
rect.name = #"3";
rect.physicsBody.categoryBitMask = greenCategory;
rect.physicsBody.contactTestBitMask = greenCategory;
rect.physicsBody.collisionBitMask = blueCategory | redCategory | yellowCategory | greenCategory;
rect.position = CGPointMake(xPos , yPos );
[self addChild:rect];
}
- (void) addButtonYellow {
SKSpriteNode *rect;
//button type 4
rect = [SKSpriteNode spriteNodeWithColor:[UIColor yellowColor] size:kBoxSize];
int tmpInt = [[NSDate date] timeIntervalSince1970];
NSString *tmpName = [NSString stringWithFormat:#"%i", tmpInt];
rect.name = tmpName; //unique name.
rect.name = #"4";
rect.physicsBody.mass = 1;
rect.physicsBody.categoryBitMask = yellowCategory;
rect.physicsBody.contactTestBitMask = yellowCategory;
rect.physicsBody.collisionBitMask = blueCategory | redCategory | yellowCategory | greenCategory;
rect.position = CGPointMake(xPos , yPos );
[self addChild:rect];
}
#end
Here is where I create the buttons.
(at top of file with rest of global ivar )
ButtonNode * newButton;
for (int i = 1; i <= 6; i++) {
//create random Int
int tmpInt = arc4random() %3;
NSLog(#"tmp %i" ,tmpInt);
column1.position = CGPointMake(100, self.frame.size.height - 40);
if (tmpInt == 0) {
//button type 1
newButton = [[ButtonNode alloc] initWithButtonType:1];
newButton.xPos = column1.position.x;
newButton.yPos = column1.position.y *i;
newButton.buttonID = 344224351; //unique name
newButton.buttonColumn = 2;
newButton.buttonType = 1;
[column1 addChild:newButton];
blueTotal++;
totalButtons++;
column1Total++;
}
if (tmpInt == 1) {
//button type 2
newButton = [[ButtonNode alloc] initWithButtonType:2];
newButton.xPos = column1.position.x;
newButton.yPos = column1.position.y *i;
newButton.buttonID = 344224351; //unique name
newButton.buttonColumn = 2;
newButton.buttonType = 1;
[column1 addChild:newButton];
redTotal++;
totalButtons++;
column1Total++;
}
}
Here is the Part that is not working correctly.
- (void) touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
{
for (UITouch *touch in touches) {
UITouch* touch = [touches anyObject];
CGPoint loc = [touch locationInNode:self];
NSArray *nodes = [self nodesAtPoint:loc];
for (SKNode *nod in nodes) {
NSString *tmp = nod.name;
if (tmp.length !=0) {
NSString * tmpType = nod.name;
if ([tmpType isEqualToString:#"1"]) {
NSLog(#"Node Type: %#", nod.name);
previousButton = #"1";
NSLog (#"%d",newButton.buttonType);
}
if ([tmpType isEqualToString:#"2"]) {
NSLog(#"Node Type: %#", nod.name);
previousButton = #"2";
NSLog (#"%d",newButton.buttonType);
}
if ([tmpType isEqualToString:#"3"]) {
NSLog(#"Node Type: %#", nod.name);
previousButton = #"3";
NSLog (#"%d",newButton.buttonType);
}
if ([tmpType isEqualToString:#"4"]) {
NSLog(#"Node Type: %#", nod.name);
previousButton = #"4";
NSLog (#"%d",newButton.buttonType);
}
}
}
}
}
A SKNode does not have those properties.
Try this just inside your for loop :
ButtonNode *myButton = (ButtonNode *)nod;
That will cast nod correctly as a ButtonNode, and you can use myButton like this :
NSLog(#"%d",myButton.buttonType);
Then you should be able to access the properties you have defined in the ButtonNode class.
You might only want to do that cast if you are sure it's a ButtonNode, but was just trying to help you understand why those properties would NEVER be accessible given your current code.
Also, your usage of newButton in that loop in touchesBegan is not what I think you 'think' it is. It's going to be the last button created, not the current node being stored in nod in the loop.

Resources