In SpriteKit, two spritenodes are not firing off contact event properly - sprite-kit

I have two SKSpriteNode-s(let's call them SKSPriteNode A and SKSpriteNode B), A is my hero sprite and B is a scene element, and sometimes, the didBeginContact fires off when they collide and sometimes it doesn't. A has physicsBody set based on texture and B has rectangle based physicsBody set for it. I can see their textures collide and intersect but I don't see the NSLog fire off in the didBeginContact. This is not totally consistent because another time they collide and the contact event fires off just fine. I need this to be consistent, naturally. Anyone has a suggestion on this issue? The scene is the only contact delegate for the physicsWorld, so only the scene can trigger and go in the event handler, so I know it is working - at least some of the time. Code in scene:
-(void)didBeginContact:(SKPhysicsContact *)contact {
NSLog(#"Contact happened!!!!!! :) ");
}
I have my custom class StaticLevelElement set so it extends SKSpriteNode:
#interface StaticLevelElement : SKSpriteNode
The B has been set as StaticLevelElement with a triangular image:
StaticLevelElement * B = [StaticLevelElement spriteNodeWithImageNamed:#"triangle"];
I tried to set the B's physicsBody to have texture based physicsBody:
B.physicsBody = [SKPhysicsBody bodyWithTexture:[SKTexture textureWithImageNamed:#"triangle"] size:B.size];
and I have the same result - intermittent contact, fires off occasionally, and not really consistent. Same happens if I set the B to have rectangle based physicsBody:
B.physicsBody = [SKPhysicsBody bodyWithRectangleOfSize:B.size];

It is my opinion and I can say that with a medium level of certainty, that the anchorPoint caused this. I had it set to (0.5, 0) and that seemed to had caused this issue by, apparently, moving the whole physicsBody, which I dont like at all. Once I deleted this manual setting of anchorPoint (and thus returning it to (0.5, 0.5)), the contact looks like happening more or less properly. Still not perfect - not triggering for every time the overlap happens, but I think much better. As I said, I am still not completely clear on when does contact happens and how the anchorPoint influences it, but looks like it is.

Related

SKEmitterNode tied to a moving SKNode

I am attempting to move a SKEmitterNode to follow a bullet in my game to give it a trailing effect however, no matter which way I attempt to implement this, it doesn't seem to work how I want it to and I'm at a loss for how to make this.
I have attempted to add the emitter to my main scene and manually moved the node a few times per second but it ends up not leaving a trail and keeping all the particles in one place like this:
Next I attempted to set the target node, however when I do this the trail goes for a bit then stops rather than following the bullet like it's supposed to. It also rotates and distorts from the rotation of the projectile like shown here:
For reference of the type of effect I'm looking for:
You should populate the targetNode property of your emitter with a node that is not moving like the scene.
emitterNode.targetNode = self // where self is the current scene

Instantaneous hit detection with SpriteKit

What is an effective method to perform instantaneous hit detection in SpriteKit? Using SpriteKit's built in physics engine I want to collect affected objects during a single frame for situations such as an explosion's blast radius.
I have considered adding an SKNode with SKPhysicsBody to the explosive object and accessing its allContactedBodies property when it explodes but this would add unnecessary collision detection to other frames.
I have also considered adding an SKNode with SKPhysicsBody to the scene or altering an existing one at the time of explosion and using an action to remove or restore the node after a short duration but this feels unnecessarily complex and may delay the hit detection until the next frame.
I have wondered if there is a means to extend SKPhysicsWorld to allow for enumerating bodies with a circle in addition to a rectangle - or to extend SKPhysicsBody to allow manual collision detection with another test body.
However, I do not see how to access an SKPhysicsBody's shape data to do either of these.
If I were to tackle this problem I would do it by creating a node with a physics body and then messing with its dynamic, categoryBitMask, and collisionBitMask properties. I would set dynamic to false since theoretically the collision/explosion doesn't move because it hits other objects. I would set its categoryBitMask so that every object generated a "didBeginContact" report when colliding with it. And I would set its collisionBitMask so that it never caused impulses because of collision. This way the physicsBody is more of a probe then an actual physics object, just being what is their.
At the end of that the physics body should cause collision reports but not repel any objects. Obviously you can create this body and just not have it added to the scene until it is actually time to detect things. Then after that frame is over and you know all the physics bodies it touched you can remove it again.
Then I would set the scene as a physicsContactDelegate and impliment the did begin contact method.
class GameScene: Scene, SKPhysicsContactDelegate {
func didBeginContact(contact: SKPhysicsContact)
{
var AName:NSString = NSString(string: contact.bodyA.node!.name!)
var BName:NSString = NSString(string: contact.bodyB.node!.name!)
}
}
Then I would have if statements checking if either AName or BName were the name of your "probing" node/physicBody. Then after that you can add code to do whatever you want as a result of that collision.
Good luck! If you are having troubles with the bit mask properties I would strongly suggest checking out this.

Q: How do I move objects down the y axis as my player moves up?

Creating a game like Doodle Jump where my player is constantly being moved up by bouncing off of obstacles. Ive tried every trick in the book but nothing seems to be working/doing exactly what I want. Can anyone give me some tips?
iOS 9 introduced the Camera Node. Use SKCameraNode, which is a subclass of SKNode, and can be translated and rotated in same way.
So, instead of moving all of your background elements in the opposite direction of your hero/player, you can simply attach your scene's camera node to your hero/player and the rest is taken care of.
PS. You can also do cool stuff like scaling the camera size.
EDIT.
Happy to include an example.
First, make a camera constant in your scene.
import SpriteKit
class myFirstScene: SKScene {
let myCamera: SKCameraNode = SKCameraNode()
...
}
Then in your didMoveToView() function, assign the scene's built-in camera variable to the camera constant we made earlier.
override func didMoveToView( view: SKView ) {
camera = myCamera
...
}
Now, there are a few different ways to "attach" your camera to your hero/player. The first is to attach your camera node to your hero.
hero.addChild( myCamera )
I don't even know if it works that easily because my game uses something different, a simpler version is below.
update(){
camera!.zRotation = hero.zRotation
camera!.position = hero.position
}

SpriteKit Physics Bodies Collisions in Multiple Layers

Okay, so I'm writing code for a space blaster game, and all seems to be going well, minus these crucial issues.
First, I added a spaceship (with PhysicsBody) as a child of self (GameScene).
Then I added a background layer as a node also as a child of self, behind the spaceship, and simulated movement by moving the background around behind the spaceship via joystick control.
I added a node with a boundary physics Edge Loop body so the background wouldn't move beyond the ship, and added that node as a child of the bgLayer.
I have objects for the spaceship to shoot at, but I want them to move around as the background does, so I added them as children of the bgLayer.
Of course, I needed a laser when the spaceship fires, so I added this as well as a child of the bgLayer.
_spaceship.physicsBody = (custom physicsBody code);
[self addChild:_spaceship];
_bgLayer = [SKNode node];
[self addChild:_bgLayer];
_bounds = [SKNode node];
_bounds.physicsBody = (physicsBody edgeLoop code);
[_bgLayer addChild:_bounds];
_otherObjects.physicsBody = (custom physicsBody code);
[_bgLayer addChild:_otherObjects];
_laser.physicsBody = (custom physicsBody code);
[_bgLayer addChild:_laser];
All is well, the background doesn't move beyond the spaceship,the other objects move as the background moves, and the laser fires when called upon.
My first big dilemma is when my laser fires, it does not move as the background moves, but the background moves behind it. I could probably make do but it looks funny if the laser always moves in parallel with the ship. This seems odd to me since I added the laser as a child of _bgLayer.
Second, my laser's physicsBody doesn't seem to recognize the EdgeLoop body, and sails right on through it. Also, my spaceship's physicsBody seems to recognize the EdgeLoop body, but it does not recognize the other objects that are children of _bgLayer.
Do Physics Bodies that are not children of the same layer recognize each other?
And why doesn't my laser behave similarly to other children of the same layer?
Moving the world by changing its position will affect
Children with physics bodies
Children without physics bodies
Moving the world by applying a force/impulse or by settings its velocity will affect
Children without physics bodies
Instead of moving the world by setting its velocity, you can add a camera (an SKNode with a physics body) to the world, move the camera by setting its velocity, and then update the position of the world to center the camera. You can center the camera in the didFinishUpdate method.
-(void)fireLaser
{
_laser = [SKSpriteNode spriteNodeWithImageNamed: [NSString stringWithFormat:#"blueLaser"]];;
_laser.zRotation = _spaceShip.zRotation;
CGVector rotationVector = RadiansToVector(_spaceShip.zRotation);
_laser.position = (Custom code for positioning the laser just in front of the spaceship);
_laser.physicsBody = [SKPhysicsBody bodyWithRectangleOfSize:_laser.size];
[_bgLayer addChild:_laser];
_laser.physicsBody.velocity = CGVectorMake(rotationVector.dx * LASER_SPEED, rotationVector.dy * LASER_SPEED);
}
[self fireLaser] is called in touchesBegan when a particular SpriteNode is touched.
The laser fires beautifully, but does not scroll with the background, but rather moves in relation to the spaceship. Background scrolls with a PhysicsBody and a setVelocity method is called when the joystick moves, thus simulating spaceship motion, when in reality the spaceship never leaves the center of the screen. Physics categories prevent the background physics body from colliding with anything. LASER_SPEED is simply a static const float.

SKPhysicsContact when object collides with more than 1 object, handle first, ignore all others.. how?

Okay I have some physics objects, and they are all nicely categorized, and my didBeginContact is properly fired, and does what it is supposed to do. The problem is this:
I have two categories of objects, say ball and paddle.... When the ball touches a paddle the ball should explode... simple enough.. The problem lies in that the ball could touch 2 paddles at the same time... So, more than one didBeginContact gets called, and as such more than 1 explosion happens (1 per paddle the ball contacted with).. So the problem I am trying to figure out is, how do I remove/ignore all subsequent contacts with paddles from being handled if the code has already handled a collision involving the original ball? Removing the SKSpriteNode from parent before starting the explosion does not nullify the other contacts, they still get handled... so how do I tell it.. HEY PHYSICS CONTACTS STUFF... that body is no longer in the picture... so just throw those contacts away and don't worry about them?
I suppose I could explicitly check that the parent still has the SKSpriteNode available in the contact code before doing the explosion etc, but that seems kludgy at best, though I suppose it would work... Is there another/better way to handle this? I have to believe there is.
The moment you get the contact, set the contactBitMask of the ball node's physicsBody as 0. I am assuming the node needs to be destroyed and does not need to be reused.
This should prevent multiple contacts from appearing for the same node.
If the above doesn't work, you can try the following methods:
Removing the node from parent within the contact delegate, and triggering the explosion animation subsequently.
Subclass SKSpriteNode or SKPhysicsBody for the ball nodes and add a property for eg. alreadyTouched. Then you can check and set the property from within the contact delegate.
If you have three physicsBodies (ball, paddle1 and paddle2) and ball is set to collide with paddles (but paddles do not collide with each other) and the ball collides with 2 paddles during the same update cycle, then the game engine will generate 2 separate SKPhysicsContact objects - ball & paddle1 and ball & paddle2.
didBegincontact (Swift2) or didBegin(contact:) (Swift 3) (dBC for brevity) will then be called twice during the same update() cycle i.e. there will be 2 calls to dBC between calls to update()- firstly for the SKPhysicsContact between ball & paddle1 and then for the contact between ball & paddle2. You can do ball.removeFromParent() during the first contact, and the ball will be removed from the node tree but this will not prevent the second call to dBC for the ball & paddle2 collision and you will likely perform the same actions against ball (exploding, removing the score etc).
The simplest thing to do is to create a subclass of SKSPriteNode for your ball (ballNode?) and add a single property isActive, initialised to true. This is very easy.
In dBC, when the ball hits the paddle, check its isActive property and if false, simply return. If it's true, perform the normal collision actions and then set ball.isActive to false. When dBC is called the second time, the check against isActive will prevent any duplicate actions.
You could also use the ball's userData (part of SKSpriteNode) to store the isActive values to prevent subclassing, if you'd prefer that.
There are other ways to handle this issue, but so far I've found this one to work and to be the simplest.