Why didn't the SpriteKit PhysicContactDelegate work? - ios

I am implementing a simple game for iOS.
I am trying to use Sprite Kit for development.
However, I don't know why the detection of contact did not happen.
Can anyone help me fix this problem?
Here is the code that I cannot get the expected results with:
#import "TesttingScene.h"
#interface TesttingScene()<SKPhysicsContactDelegate>
#property (nonatomic) SKTexture *ballText;
#end
#implementation TesttingScene
-(id)initWithSize:(CGSize)size{
self = [super initWithSize:size];
if (self) {
self.physicsWorld.gravity = CGVectorMake(0, 0);
self.physicsWorld.contactDelegate = self;
SKSpriteNode *hitBoxx = [[SKSpriteNode alloc] initWithColor:[UIColor clearColor] size:CGSizeMake(self.frame.size.width/3, self.frame.size.height/3)];
hitBoxx.anchorPoint = CGPointMake(0, 0);
hitBoxx.position = CGPointMake(self.size.width/2, self.size.height/2);
hitBoxx.physicsBody = [SKPhysicsBody bodyWithRectangleOfSize:hitBoxx.frame.size];
hitBoxx.physicsBody.dynamic = YES;
hitBoxx.physicsBody.categoryBitMask = abPlayerHitBoxCategory;
hitBoxx.physicsBody.contactTestBitMask = adsViewCategory;
hitBoxx.physicsBody.collisionBitMask = 0;
hitBoxx.physicsBody.usesPreciseCollisionDetection = YES;
self.ballText = [SKTexture textureWithImageNamed:#"FinalBossSkill1SS"];
}
return self;
}
-(void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event{
SKTexture *t1 = [SKTexture textureWithRect:CGRectMake(0, 0, 0.5, 1) inTexture:self.ballText];
SKTexture *t2 = [SKTexture textureWithRect:CGRectMake(0.5, 0, 0.5, 1) inTexture:self.ballText];
SKSpriteNode *ball = [SKSpriteNode spriteNodeWithTexture:t1];
ball.position = CGPointMake(self.size.width, self.size.height/2);
ball.name = #"FinalBossSkill1Ball";
ball.physicsBody = [SKPhysicsBody bodyWithRectangleOfSize:ball.size];
ball.physicsBody.dynamic = YES;
ball.physicsBody.categoryBitMask = adsViewCategory;
ball.physicsBody.contactTestBitMask = abPlayerAttackBoxCategory | abPlayerHitBoxCategory;
ball.physicsBody.collisionBitMask = 0;
ball.physicsBody.usesPreciseCollisionDetection = YES;
SKAction *moveTo = [SKAction moveToX:-ball.size.width duration:1.0];
SKAction *flash = [SKAction animateWithTextures:#[t1,t2] timePerFrame:0.1];
SKAction *moveBall = [SKAction repeatAction:flash count:moveTo.duration/flash.duration];
SKAction *group = [SKAction group:#[moveTo, moveBall]];
[self addChild:ball];
[ball runAction:[SKAction sequence:#[group, [SKAction removeFromParent]]]];
}
-(void)didBeginContact:(SKPhysicsContact *)contact{
NSLog(#"Contact"); // <~~~~this msg doesn't appear in console when the ball pass the hitboxx.
}

You will need to define bit mask category, right below your imports.
static const uint32_t abPlayerHitBoxCategory = 0x1 << 0;
static const uint32_t adsViewCategory = 0x1 << 1;

Related

Adding a start and end screen in ios game

I am having trouble having my game begin paused so that the player has to press a button to start the game. I also have collision detection working, so when the two objects collide they just fall off of the screen. Here is my current code:
- (instancetype)initWithSize:(CGSize)size {
if (self = [super initWithSize:size]) {
self.physicsWorld.contactDelegate = self;
[self buildBackground];
[self startScrolling];
_firstPosition = CGPointMake(self.frame.size.width * 0.817f, self.frame.size.height * .40f);
_squirrelSprite = [SKSpriteNode spriteNodeWithImageNamed:#"squirrel"];
_squirrelSprite.position = _firstPosition;
_atFirstPosition = YES;
_squirrelSprite.physicsBody = [SKPhysicsBody bodyWithCircleOfRadius:10];
_squirrelSprite.physicsBody.categoryBitMask = squirrelHitCategory;
_squirrelSprite.physicsBody.contactTestBitMask = nutHitCategory;
_squirrelSprite.physicsBody.collisionBitMask = nutHitCategory;
_squirrelSprite.physicsBody.affectedByGravity = NO;
self.physicsWorld.contactDelegate = self;
[self addChild:_squirrelSprite];
// Declare SKAction that waits 2 seconds
SKAction *wait = [SKAction waitForDuration:3.0];
// Declare SKAction block to generate the sprites
SKAction *createSpriteBlock = [SKAction runBlock:^{
const BOOL isHeads = arc4random_uniform(100) < 50;
NSString* spriteName = isHeads ? #"lightnut.png" : #"darknut.png";
SKSpriteNode *nut = [SKSpriteNode spriteNodeWithImageNamed:spriteName];
BOOL heads = arc4random_uniform(100) < 50;
nut.position = (heads)? CGPointMake(257,600) : CGPointMake(50,600);
nut.physicsBody = [SKPhysicsBody bodyWithRectangleOfSize:CGSizeMake(200,160)];
nut.physicsBody.categoryBitMask = nutHitCategory;
nut.physicsBody.contactTestBitMask = squirrelHitCategory;
nut.physicsBody.collisionBitMask = squirrelHitCategory;
nut.physicsBody.affectedByGravity = NO;
self.physicsWorld.contactDelegate = self;
[self addChild: nut];
SKAction *moveNodeUp = [SKAction moveByX:0.0 y:-700.0 duration:1.3];
[nut runAction: moveNodeUp];
}];
// Combine the actions
SKAction *waitThenRunBlock = [SKAction sequence:#[wait,createSpriteBlock]];
// Lather, rinse, repeat
[self runAction:[SKAction repeatActionForever:waitThenRunBlock]];
}
return self;
}
My touchesBegan:
- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event {
if (_atFirstPosition)
{
SKAction *moveNodeLeft = [SKAction moveByX:-207.8 y:0.0 duration:0.85];
[_squirrelSprite runAction: moveNodeLeft withKey:#"moveleft"];
} else {
SKAction *moveNodeRight = [SKAction moveByX:207.8 y:0.0 duration:0.85];
[_squirrelSprite runAction: moveNodeRight withKey:#"moveright"];
}
_atFirstPosition = !_atFirstPosition;
_squirrelSprite.xScale *= -1.0;
}
Finally, my didBeginContact:
-(void)didBeginContact:(SKPhysicsContact *)contact
{
SKPhysicsBody *firstBody, *secondBody;
firstBody = contact.bodyA;
secondBody = contact.bodyB;
if(firstBody.categoryBitMask == squirrelHitCategory || secondBody.categoryBitMask == nutHitCategory)
{
}
}
I've been trying to figure this out for the last 2 weeks and I have read many tutorials and code to start the game with a simple start button and end screen, but nothing has worked. Any help is greatly appreciated!
I would use a navigation controller and have your start button on the root view and have that button push a viewcontroller that presents your game.

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!

Why does my simulator still show node count and FPS? [SpriteKit]

I have prepared my game to be published to the app store. Before I do that, I wanted to turn off the node count and FPS and did not want those two things to be displayed. I have a GameScene.m and a TitleScene.m. I tried view.showsFPS = NO; and view.showsNodeCount = NO; in my GameScene.m and it works fine. In my TitleScene.m I tried self.view.showsNodeCount = NO; and self.view.showsFPS = NO;, but it still shows the NodeCount and FPS. Here is my code for TitleScene.m:
#import "TitleScene.h"
#import "GameScene.h"
#implementation TitleScene
-(id)initWithSize:(CGSize)size {
if (self = [super initWithSize:size]) {
/* Setup your scene here */
self.view.showsNodeCount = NO;
self.view.showsFPS = NO;
SKTexture *YellowLabelTexture = [SKTexture textureWithImageNamed:#"YellowLabel.png"];
SKTexture *BlueLabelTexture = [SKTexture textureWithImageNamed:#"BlueLabel.png"];
SKTexture *GreenLabelTexture = [SKTexture textureWithImageNamed:#"GreenLabel.png"];
SKTexture *RedLabelTexture = [SKTexture textureWithImageNamed:#"RedLabel.png"];
SKTexture *WhiteLabelTexture = [SKTexture textureWithImageNamed:#"WhiteLabel.png"];
SKSpriteNode *background = [SKSpriteNode spriteNodeWithImageNamed:#"awsome.png"];
background.size = CGSizeMake(640, 1136);
background.position = CGPointMake(0,0);
NSArray *anim = [NSArray arrayWithObjects:YellowLabelTexture, BlueLabelTexture, GreenLabelTexture, RedLabelTexture, WhiteLabelTexture, nil];
SKSpriteNode *labelNode = [SKSpriteNode spriteNodeWithImageNamed:#"WhiteLabel.png"];
labelNode.position = CGPointMake(self.size.width / 2, self.size.height / 2 * 1.5);
SKSpriteNode *startButtonNode = [SKSpriteNode spriteNodeWithImageNamed:#"playButton.png"];
startButtonNode.position = CGPointMake(self.size.width / 2, self.size.height / 3);
SKAction *actionAnimate = [SKAction animateWithTextures:anim timePerFrame:.3 resize:YES restore:NO];
SKAction *actionRepeat = [SKAction repeatActionForever:actionAnimate];
[labelNode runAction:actionRepeat];
[self addChild:background];
[self addChild:labelNode];
[self addChild:startButtonNode];
}
return self;
}
- (void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event
{
GameScene* gameScene = [[GameScene alloc] initWithSize:self.size];
gameScene.scaleMode = SKSceneScaleModeAspectFill;
[self.view presentScene:gameScene transition:[SKTransition doorsOpenHorizontalWithDuration:1.5]];
}
Is there something that I am doing wrong? Thanks!
The default SpriteKit template sets node count and fps after the initialization of the scene in ViewController.m, you have to remove these lines.
Comment showsFPS and ShowsNodeCount in your ViewController.m file;
//skView.showsFPS = YES;
//skView.showsNodeCount = YES;

SKScene transition, memory deallocation

Ok, i've read the iOS Games book and i've searched for my question in a number of sites and although I do find that a number of people had this problem, I haven't found a solution as such.
I am building a game where I transition a few times from a SKScene to another SKScene. What happens is that even when I transition from a simple SKScene, as the example bellow, to an empty SKScene the memory does not get deallocated. I've heard that I need to remove any strong references to my SKScene, but I do not believe that my code bellow has any:
#import "LaunchScene.h"
#import "EmptyScene.h"
#implementation LaunchScene
{
float _scaleForDevice;
}
-(id)initWithSize:(CGSize)size {
if (self = [super initWithSize:size]) {
_scaleForDevice = 0.5;
if (UI_USER_INTERFACE_IDIOM() == UIUserInterfaceIdiomPhone) {
_scaleForDevice = 0.208335;
}
SKSpriteNode *bg;
if (UI_USER_INTERFACE_IDIOM() == UIUserInterfaceIdiomPhone && IS_WIDESCREEN) {
bg = [SKSpriteNode spriteNodeWithImageNamed:#"launchBackground-568h"];
} else {
bg = [SKSpriteNode spriteNodeWithImageNamed:#"launchBackground"];
}
bg.position = CGPointMake(CGRectGetMidX(self.frame),
CGRectGetMidY(self.frame));
bg.zPosition = 10;
bg.name = #"launchBackground";
[self addChild:bg];
for (int i = 0; i < 5; i++) {
SKSpriteNode *launch = [SKSpriteNode spriteNodeWithImageNamed:[NSString stringWithFormat:#"launch%d",i]];
launch.position = CGPointMake(self.size.width, 0);
launch.anchorPoint = CGPointMake(0.5, 0.5);
launch.zPosition = 1000+i;
launch.name = [NSString stringWithFormat:#"launch%d",i];
[launch setScale:_scaleForDevice];
[self addChild:launch];
if (i == 0 || i == 2) {
SKAction* rotate = [SKAction rotateByAngle:-RadiansToDegrees(360) duration:10000*(i+1)];
[launch runAction:rotate];
} else if (i == 1) {
SKAction* rotate = [SKAction rotateByAngle:RadiansToDegrees(360) duration:10000*(i+1)];
[launch runAction:rotate];
}
}
SKSpriteNode *mainMenuBackground;
if (UI_USER_INTERFACE_IDIOM() == UIUserInterfaceIdiomPhone && IS_WIDESCREEN) {
mainMenuBackground = [SKSpriteNode spriteNodeWithImageNamed:#"mainMenuBackground-568h"];
} else {
mainMenuBackground = [SKSpriteNode spriteNodeWithImageNamed:#"mainMenuBackground"];
}
mainMenuBackground.position = CGPointMake(CGRectGetMidX(self.frame),
CGRectGetMidY(self.frame));
mainMenuBackground.zPosition = 5;
mainMenuBackground.name = #"mainMenuBackground";
[self addChild:mainMenuBackground];
[SKActionEffects fadeOutAndRemove:bg duration:2];
SKSpriteNode *mainMenuGround;
if (UI_USER_INTERFACE_IDIOM() == UIUserInterfaceIdiomPhone && IS_WIDESCREEN) {
mainMenuGround = [SKSpriteNode spriteNodeWithImageNamed:#"mainMenuGround-568h"];
} else {
mainMenuGround = [SKSpriteNode spriteNodeWithImageNamed:#"mainMenuGround"];
}
mainMenuGround.position = CGPointMake(self.size.width, 0);
mainMenuGround.zPosition = 500;
mainMenuGround.anchorPoint = CGPointMake(1, 1);
mainMenuGround.name = #"mainMenuGround";
[self addChild:mainMenuGround];
SKAction *waitMainMenuGround = [SKAction waitForDuration:1];
SKAction *moveMainMenuGround = [SKAction moveToY:(self.size.height)/3 duration:0.3];
moveMainMenuGround.timingMode = SKActionTimingEaseInEaseOut;
SKAction *shakeMainMenuGround = [SKAction runBlock:^{
[SKActionEffects shakeSprite:mainMenuGround toDirection:1];
}];
SKAction *shrinkLaunch = [SKAction runBlock:^{
for (int i = 0; i < 5; i++) {
SKSpriteNode *launch = (SKSpriteNode*)[self childNodeWithName:[NSString stringWithFormat:#"launch%d",i]];
[SKActionEffects disappearAndRemove:launch];
}
}];
SKAction *presentMainMenu = [SKAction runBlock:^{
//Create and configure the scene.
EmptyScene * scene = [[EmptyScene alloc] initWithSize:self.size];
scene.scaleMode = SKSceneScaleModeAspectFill;
// Present the scene.
[self.view presentScene:scene];
}];
SKAction *group = [SKAction sequence:#[waitMainMenuGround, moveMainMenuGround, shakeMainMenuGround, shrinkLaunch, waitMainMenuGround, presentMainMenu]];
[mainMenuGround runAction:group];
}
return self;
}
#end
I have created an EmptyScene that is just that. It doesn't have anything in its init.
#import "EmptyScene.h"
#implementation EmptyScene
-(id)initWithSize:(CGSize)size {
if (self = [super initWithSize:size]) {
}
return self;
}
#end
When I run just the EmptyScene from the start my memory is around 25mb. When I run the LaunchScene first, the memory goes to 95mb, and when it transitions to the EmptyScene, where one would expect the memory to go down to 25mb, it remains at 95mb. Any idea why and what I can do to solve this?

I want to make a soccer ball by IOS SpriteKit

I want to make a soccer ball by IOS SpriteKit
I was able to create a ball in the code such as the following.
However, it does not bounce at all.
To make rebound rate, How can I do this?
(UITouch *touch in touches) {
SKSpriteNode *sprite = [SKSpriteNode spriteNodeWithImageNamed:#"Ball"];
sprite.position = location;
const CGFloat radius = 20.0f;
SKPhysicsBody *pbody = [SKPhysicsBody bodyWithCircleOfRadius:radius];
sprite.physicsBody = pbody;
sprite.size = (CGSize){radius * 2, radius * 2};
sprite.physicsBody.linearDamping=0.12f;
[self addChild:sprite];
}
I have declared like this.
  
-(id)initWithSize:(CGSize)size {
if (self = [super initWithSize:size]) {
self.backgroundColor = [SKColor colorWithRed:0.15 green:0.15 blue:0.3 alpha:1.0];
self.physicsBody = [SKPhysicsBody bodyWithEdgeLoopFromRect:(CGRect){CGPointZero, size}];
self.physicsWorld.gravity=CGVectorMake(0, -100);
}
return self;
}
This looks like a ball to me
- (void) soccerBallExample
{
// the world bounds
self.physicsBody = [SKPhysicsBody bodyWithEdgeLoopFromRect:CGRectMake(0, 0, self.size.width, self.size.height)];
// the ball
SKShapeNode *ball;
ball = [[SKShapeNode alloc] init];
CGMutablePathRef myPath = CGPathCreateMutable();
CGPathAddArc(myPath, NULL, 0,0, 30, 0, M_PI*2, YES);
ball.path = myPath;
CGPathRelease(myPath);
ball.fillColor = [SKColor blueColor];
ball.position = CGPointMake(200, 200);
[self addChild:ball];
// set the physics
ball.physicsBody = [SKPhysicsBody bodyWithCircleOfRadius:30];
ball.physicsBody.restitution = 0.8;
}
In your example, you use a sprite so for radius use
// ball.size.width * 0.5;

Resources