SKSpriteNode Collision Removal In Removing Wrong One With Xcode Swift SpriteKit - swift

I am making a game where when the character lands on the cloud, the cloud fades out. I put in code to determine when it is hit, there are multiple clouds registered under the same SKSpriteNode, but when it lands on the cloud, the wrong cloud is fading away, it is the most recently added SKSpriteNode that is being removed, not the one it is colliding with.
Is there any way to do it so it only removes the one that the character has collided with, not the earliest one that spawns? Here is the code:
func didBeginContact(contact: SKPhysicsContact)
{
let contactMask = contact.bodyA.categoryBitMask | contact.bodyB.categoryBitMask
switch(contactMask)
{
case BodyType.PersonCategory.rawValue | BodyType.CloudCategory.rawValue:
let CheckDelay = delay(0.02)
{
self.Cloud.runAction(self.FadeAway)
}
default:
return
Person.physicsBody?.usesPreciseCollisionDetection = true
Person.size = CGSizeMake(self.frame.size.width / 25, self.frame.size.height / 16.25)
Person.physicsBody = SKPhysicsBody(rectangleOfSize: Person.size)
Person.physicsBody?.restitution = 0
Person.physicsBody?.friction = 0
Person.physicsBody?.allowsRotation = false
Person.physicsBody?.affectedByGravity = true
Person.physicsBody?.dynamic = true
Person.physicsBody?.linearDamping = 0
Person.zPosition = 5
Person.physicsBody?.categoryBitMask = BodyType.PersonCategory.rawValue
Person.physicsBody?.contactTestBitMask = BodyType.CloudCategory.rawValue
Person.position = CGPoint(x: CGRectGetMidX(self.frame), y: CGRectGetMidY(self.frame) * 1.7)
self.addChild(Person)
Cloud = SKSpriteNode(texture: NormalCloudTexture)
Cloud.zPosition = 7
Cloud.physicsBody?.usesPreciseCollisionDetection = true
Cloud.physicsBody?.affectedByGravity = false
Cloud.physicsBody?.allowsRotation = false
Cloud.size = CGSizeMake(self.frame.size.width / 8.05, self.frame.size.height / 40)
Cloud.physicsBody = SKPhysicsBody(rectangleOfSize: Cloud.size)
Cloud.physicsBody?.friction = 0
Cloud.physicsBody?.restitution = 0
Cloud.physicsBody?.dynamic = false
Cloud.position = CGPointMake(CGRectGetMidX(self.frame), CGRectGetMidY(self.frame) / 7.60)
addChild(Cloud)

You need to remove the cloud represented by the physicsBody actually making the contact. so bodyA is either the player, or the cloud, and then bodyB is either the player or the cloud. Then get the actual SKSpriteNode from the physics body. It can be referenced via the node property on the SKPhysics body. So either bodyA.node, or bodyB.node.

Related

Collision tests not working in SpriteKit Swift 2

I have been trying to figure out why my didBeginContact method isn't working. I have a triangle falling from the top of the screen to the bottom and I have a line at the bottom which if it collides with it. My score should increment.
Here is the code responsible for this.
func createScene(){
physicsWorld.contactDelegate = self
slider = SKSpriteNode(imageNamed: "Slider")
slider.setScale(0.20)
slider.position = CGPoint(x: self.frame.width / 2, y: 0 + slider.frame.height / 2)
slider.physicsBody = SKPhysicsBody(rectangleOfSize: slider.size)
slider.physicsBody?.categoryBitMask = PhysicsCatergory.slider
slider.physicsBody?.collisionBitMask = PhysicsCatergory.coin | PhysicsCatergory.greenTriangle | PhysicsCatergory.orangeHexagon | PhysicsCatergory.purpleOctagon | PhysicsCatergory.redSquare
slider.physicsBody?.contactTestBitMask = PhysicsCatergory.coin | PhysicsCatergory.greenTriangle | PhysicsCatergory.orangeHexagon | PhysicsCatergory.purpleOctagon | PhysicsCatergory.redSquare
slider.physicsBody?.affectedByGravity = false
slider.physicsBody?.dynamic = false
var timer = NSTimer.scheduledTimerWithTimeInterval(1.0, target: self, selector: Selector("SpawnShapes"), userInfo: nil, repeats: true)
self.addChild(slider)
}
func didBeginContact(contact: SKPhysicsContact) {
let firstBody = contact.bodyA
let secondBody = contact.bodyB
if firstBody.categoryBitMask == PhysicsCatergory.greenTriangle && secondBody.categoryBitMask == PhysicsCatergory.Score{
score++
print(score)
}
else if firstBody.categoryBitMask == PhysicsCatergory.Score && secondBody.categoryBitMask == PhysicsCatergory.greenTriangle{
score++
print(score)
}
}
override func didMoveToView(view: SKView) {
createScene()
invisibleBounderies()
}
func invisibleBounderies(){
let scoreNode = SKSpriteNode()
scoreNode.size = CGSize(width: 5, height: 600)
scoreNode.position = CGPoint(x: self.frame.width / 2, y: self.frame.height / 10)
scoreNode.physicsBody = SKPhysicsBody(rectangleOfSize: scoreNode.size)
scoreNode.physicsBody?.affectedByGravity = false
scoreNode.physicsBody?.dynamic = false
scoreNode.physicsBody?.categoryBitMask = PhysicsCatergory.Score
scoreNode.physicsBody?.collisionBitMask = PhysicsCatergory.greenTriangle
scoreNode.physicsBody?.contactTestBitMask = PhysicsCatergory.greenTriangle
scoreNode.zRotation = CGFloat(M_PI/2.0)
scoreNode.color = SKColor.blueColor()
self.addChild(scoreNode)
}
func SpawnShapes(){
greenTriangle = SKSpriteNode(imageNamed:"greenTriangle")
purpleOctagon = SKSpriteNode(imageNamed: "purpleOctagon")
redSquare = SKSpriteNode(imageNamed: "redSquare")
coin = SKSpriteNode(imageNamed: "coin")
greenTriangle.physicsBody = SKPhysicsBody(rectangleOfSize: greenTriangle.size)
greenTriangle.physicsBody?.categoryBitMask = 0
greenTriangle.physicsBody?.collisionBitMask = PhysicsCatergory.Score
greenTriangle.physicsBody?.contactTestBitMask = PhysicsCatergory.Score
greenTriangle.physicsBody?.affectedByGravity = false
greenTriangle.physicsBody?.dynamic = false
var MinValue = self.size.width / 8
var MaxValue = self.size.width - 150
var SpawnPoint = UInt32(MaxValue - MinValue)
let action = SKAction.moveToY(-30, duration: 2.0)
slider.zPosition = 1
coin.zPosition = 2
greenTriangle.zPosition = 3
orangeHexagon.zPosition = 4
purpleOctagon.zPosition = 5
redSquare.zPosition = 6
greenTriangle.position = CGPoint(x: CGFloat(arc4random_uniform(SpawnPoint)), y: self.size.height)
self.addChild(greenTriangle)
greenTriangle.runAction(SKAction.repeatActionForever(action))
}
You need to set only greenTriangle dynamic property to true.
You set greenTriangle.physicsBody?.categoryBitMask = 0, that's why you are not detecting contact. greenTriangle.physicsBody?.categoryBitMask = PhysicsCatergory.greenTriangle might do the trick.
If you are still not detecting contact after these changes, check if your physicsCatergory is alright. It would be good to implement an enum of type UInt32 where the values are all single bits, or like this, if you prefer:
enum PhysicsCatergory : UInt32{
case slider = 1
case coin = 2
case greenTriangle = 4
case purpleOctagon = 8
case redSquare = 16
case orangeHexagon = 32
case Score = 64
}
Doing like this will require to add .rawValue in every reference to one of these enums. Example: greenTriangle.physicsBody?.categoryBitMask = PhysicsCatergory.greenTriangle.rawValue
I know it is not related to the question, but I would recommend you not to use SKAction for movement in this case, because it's going to ignore physics collision, since you are "forcing" its position. Use the physicsBody.velocity property instead to let the physics work. You will notice that when using the later the block is going to collide with the score line, since you also set its collisionBitMask. This way you can properly control with that it collides. Set physicsBody.linearDamping to zero if don't want the block to lose speed while it travels (air friction).
A physicsBody with a dynamic property of false cannot be involved in collisions; set dynamic to true.

Make a node that doesn't move after collision iOS 8

I have nodes (sand) falling downwards.
I have other nodes (walls) that are static.
How do I make the walls not move when the sand collides with it?
let wall = SKSpriteNode(texture: chosen)
wall.position = location
wall.physicsBody = SKPhysicsBody(circleOfRadius: sprite.frame.width)
wall.physicsBody?.affectedByGravity = false
wall.physicsBody!.categoryBitMask = BLOCK
self.addChild(wall)
//////////
let sand = SKSpriteNode (imageNamed: img)
var randLoc = arc4random_uniform(26)
sand.position = CGPointMake(location.x - CGFloat(10) + CGFloat(randLoc), location.y)
self.addChild(sand)
//gravity
sand.physicsBody = SKPhysicsBody(circleOfRadius: sand.frame.width)
sand.physicsBody?.affectedByGravity = true
//contact
sand.physicsBody!.categoryBitMask = self.PARTICLE
sand.physicsBody?.collisionBitMask = self.BLOCK | self.PARTICLE
if you have wall.physicsBody?.dynamic = false & sand.physicsBody?.dynamic = true then you should be able to detect the contact without the walls being pushed/moved by the sand. - Daniel Mihaila
https://stackoverflow.com/users/4963031/daniel-mihaila

Moving Node on top of a moving platform

I have a moving platform, but when the node is above the platform it doesnt move with the platform horizontally
In this article, the problem is explained: Moving Platform Hell
http://www.learn-cocos2d.com/2013/08/physics-engine-platformer-terrible-idea/
And in comment there is solutions for Box2D: Kinematic body
But what about SpriteKit ?
Update
I'm moving the platform using
let moveHPart1 = SKAction.moveByX(origW, y: 0, duration: moveDuration);
let moveHPart2 = SKAction.moveByX(-origW, y: 0, duration: moveDuration);
platform(SKAction.repeatActionForever(SKAction.sequence([moveHPart1, moveHPart2])));
Personally I am against of using physics for moving platforms because moving platform physics body has to be dynamic.
Static platforms
For static platforms setting physics body dynamic property to false is perfect solution. And this is how it is meant to be. Static bodies are not affected by forces but still give you a collision response. So, the problem is solved.
But you can't change position of static physics bodies by using forces. You can do this by using actions or manually setting its position. But, then you are removing a platform out of physics simulation.
In order to do all with physics, you have to keep the platform dynamic. But this can lead in other problems. For example when player lands on platform, he will push the platform down, because player has a mass.
Even if platform has big mass it will go down as time passing. Remember, we cant just update platforms x position manually, because this can make a mess with physics simulation.
"Moving platform hell" as stated in that nice article of LearnCocos2d is probably the best description what can happen when using physics for this task :-)
Moving platform example
To show you some possibilities I made an simple example on how you can move a platform with applying a force to it, and make a character to stay on it.There are few things I've done in order to make this to work:
Changed a mass of platform. This will prevent platform from moving when player bumps in it from below.
Made an edge based physics body to prevent platform falling when player lands on it.
Played with properties like allows rotation and friction to get desired effect.
Here is the code :
import SpriteKit
class GameScene: SKScene,SKPhysicsContactDelegate
{
let BodyCategory : UInt32 = 0x1 << 1
let PlatformCategory : UInt32 = 0x1 << 2
let WallCategory : UInt32 = 0x1 << 3
let EdgeCategory : UInt32 = 0x1 << 4 // This will prevent a platforom from falling down
let PlayerCategory : UInt32 = 0x1 << 5
let platformSpeed: CGFloat = 40.0
let body = SKShapeNode(circleOfRadius: 20.0)
let player = SKShapeNode(circleOfRadius: 20.0)
let platform = SKSpriteNode(color: SKColor.greenColor(), size: CGSize(width:100, height:20))
let notDynamicPlatform = SKSpriteNode(color: SKColor.greenColor(), size: CGSize(width:100, height:20))
override func didMoveToView(view: SKView)
{
//Setup contact delegate so we can use didBeginContact and didEndContact methods
physicsWorld.contactDelegate = self
//Setup borders
self.physicsBody = SKPhysicsBody(edgeLoopFromRect: self.frame)
self.physicsBody?.categoryBitMask = WallCategory
self.physicsBody?.collisionBitMask = BodyCategory | PlayerCategory
//Setup some physics body object
body.position = CGPoint(x: CGRectGetMidX(self.frame), y: CGRectGetMidY(self.frame))
body.fillColor = SKColor.greenColor()
body.physicsBody = SKPhysicsBody(circleOfRadius: 20)
body.physicsBody?.categoryBitMask = BodyCategory
body.physicsBody?.contactTestBitMask = PlatformCategory
body.physicsBody?.collisionBitMask = PlatformCategory | WallCategory
body.physicsBody?.allowsRotation = false
body.physicsBody?.dynamic = true
self.addChild(body)
//Setup player
player.position = CGPoint(x: CGRectGetMidX(self.frame), y:30)
player.fillColor = SKColor.greenColor()
player.physicsBody = SKPhysicsBody(circleOfRadius: 20)
player.physicsBody?.categoryBitMask = PlayerCategory
player.physicsBody?.contactTestBitMask = PlatformCategory
player.physicsBody?.collisionBitMask = PlatformCategory | WallCategory | BodyCategory
player.physicsBody?.allowsRotation = false
player.physicsBody?.friction = 1
player.physicsBody?.dynamic = true
self.addChild(player)
//Setup platform
platform.position = CGPoint(x: CGRectGetMidX(self.frame), y: CGRectGetMidY(self.frame) - 100)
platform.physicsBody = SKPhysicsBody(rectangleOfSize: platform.size)
platform.physicsBody?.categoryBitMask = PlatformCategory
platform.physicsBody?.contactTestBitMask = BodyCategory
platform.physicsBody?.collisionBitMask = BodyCategory | EdgeCategory | PlayerCategory
platform.physicsBody?.allowsRotation = false
platform.physicsBody?.affectedByGravity = false
platform.physicsBody?.dynamic = true
platform.physicsBody?.friction = 1.0
platform.physicsBody?.restitution = 0.0
platform.physicsBody?.mass = 20
//Setup edge
let edge = SKNode()
edge.physicsBody = SKPhysicsBody(edgeFromPoint: CGPoint(x: 0, y:-platform.size.height/2), toPoint: CGPoint(x: self.frame.size.width, y:-platform.size.height/2))
edge.position = CGPoint(x:0, y: CGRectGetMidY(self.frame) - 100)
edge.physicsBody?.categoryBitMask = EdgeCategory
edge.physicsBody?.collisionBitMask = PlatformCategory
self.addChild(edge)
self.addChild(platform)
}
override func update(currentTime: NSTimeInterval) {
if(platform.position.x <= platform.size.width/2.0 + 20.0 && platform.physicsBody?.velocity.dx < 0.0 ){
platform.physicsBody?.velocity = CGVectorMake(platformSpeed, 0.0)
}else if((platform.position.x >= self.frame.size.width - platform.size.width/2.0 - 20.0) && platform.physicsBody?.velocity.dx >= 0.0){
platform.physicsBody?.velocity = CGVectorMake(-platformSpeed, 0.0)
}else if(platform.physicsBody?.velocity.dx > 0.0){
platform.physicsBody?.velocity = CGVectorMake(platformSpeed, 0.0)
}else{
platform.physicsBody?.velocity = CGVectorMake(-platformSpeed, 0.0)
}
}
override func touchesBegan(touches: NSSet, withEvent event: UIEvent) {
let touch: AnyObject? = touches.anyObject()
let location = touch?.locationInNode(self)
if(location?.x > 187.5){
player.physicsBody?.applyImpulse(CGVector(dx: 3, dy: 50))
}else{
player.physicsBody?.applyImpulse(CGVector(dx: -3, dy: 50))
}
}
}
Here is the result :

Static bodies still moving

this may be a duplicate of
Static Physics body still moving with contact
but I have not the reputation to add a comment.
My problem is with collisions and static bodies. I'm working on a little game and I have a player, so configured (from sks file):
player = worldNode.childNodeWithName("player") as! SKSpriteNode
let playerBodyTexture = SKTexture(imageNamed: "player_physicBody_mask")
player.physicsBody = SKPhysicsBody(texture: playerBodyTexture, size: CGSizeMake(player.size.width, player.size.height))
player.physicsBody!.usesPreciseCollisionDetection = true
player.physicsBody!.categoryBitMask = PhysicsCategory.player
player.physicsBody!.collisionBitMask = PhysicsCategory.ground | PhysicsCategory.enemies
player.physicsBody!.contactTestBitMask = PhysicsCategory.enemies
player.physicsBody!.restitution = 0
player.physicsBody!.friction = 0
player.physicsBody!.allowsRotation = false}
an enemy:
worldNode.enumerateChildNodesWithName("enemy") { node, _ in
let enemy = node as! SKSpriteNode
enemy.name = "enemy"
enemy.physicsBody = SKPhysicsBody(circleOfRadius: 17)
enemy.physicsBody!.dynamic = true
enemy.physicsBody!.mass = 0.4
enemy.physicsBody!.restitution = 0
enemy.physicsBody!.categoryBitMask = PhysicsCategory.enemies
enemy.physicsBody!.collisionBitMask = PhysicsCategory.ground | PhysicsCategory.player
enemy.physicsBody!.contactTestBitMask = PhysicsCategory.player}
and I have the platfrom brick, so configured:
worldNode.enumerateChildNodesWithName("fixedBlock") { node, _ in
let fixedBlock = node as! SKSpriteNode
fixedBlock.name = "fixedBlock"
let fixedBlockBodyTexture = SKTexture(imageNamed: "tripleBlocks_physicBody_mask")
fixedBlock.physicsBody = SKPhysicsBody(texture: fixedBlockBodyTexture, size: CGSizeMake(fixedBlock.size.width, fixedBlock.size.height))
fixedBlock.physicsBody!.categoryBitMask = PhysicsCategory.ground
fixedBlock.physicsBody!.collisionBitMask = PhysicsCategory.player | PhysicsCategory.enemies
fixedBlock.physicsBody!.contactTestBitMask = 0
fixedBlock.physicsBody!.dynamic = false
fixedBlock.physicsBody!.affectedByGravity = false
fixedBlock.physicsBody!.allowsRotation = false
fixedBlock.physicsBody!.pinned = false
fixedBlock.physicsBody!.friction = 0.3
fixedBlock.physicsBody!.restitution = 0}
When the player jumps on the enemies, he has an impulse (like Super Mario :-)), so I have this:
func didBeginContact(contact: SKPhysicsContact) {
let collision: UInt32 = contact.bodyA.categoryBitMask | contact.bodyB.categoryBitMask
if collision == PhysicsCategory.player | PhysicsCategory.enemies {
self.player.physicsBody!.applyImpulse(CGVectorMake(0, 500))}}
Ok... sometimes, often, when the player jumps on enemy, the impulse appens correctly, but the ground tile moves down, also if its dynamic propriety is set on false. I tried to apply a JointFixed on the ground tile but when it is pushed down, its physic body "glitch"; I tried to print the physicsBody velocity propierty of the ground tile and when the problem occours, the velocity's dx vector is >100, but should static bodies not be affected by forces?
Any idea?
Many thanks

Swift SpriteKit SKPhysicsJointPin

I'm trying to implement a rope in Swift SpriteKit and to add physics to it, the position is good for all, but they won't attach, when I hit play they all fall except the first one which is the "holder". Here is my code:
// create rope holder
let chainHolder = SKSpriteNode(imageNamed: "chainHolder")
chainHolder.position.y = self.frame.maxY - chainHolder.size.height
chainHolder.physicsBody = SKPhysicsBody(circleOfRadius: chainHolder.size.width / 2)
chainHolder.physicsBody?.dynamic = false
//chainHolder.physicsBody?.allowsRotation = true
chains.append(chainHolder)
addChild(chainHolder)
// add each of the rope parts
for i in 0...5 {
let chainRing = SKSpriteNode(imageNamed: "chainRing")
let offset = chainRing.size.height * CGFloat(i + 1)
chainRing.position = CGPointMake(chainHolder.position.x, chainHolder.position.y - offset)
chainRing.name = String(i)
chainRing.physicsBody = SKPhysicsBody(rectangleOfSize: chainRing.size)
//chainRing.physicsBody?.allowsRotation = true
chains.append(chainRing)
addChild(chainRing)
}
// set up joints between rope parts
for i in 1...5 {
var nodeA = chains[i - 1]
var nodeB = chains[i]
var joint = SKPhysicsJointPin.jointWithBodyA(nodeA.physicsBody, bodyB: nodeB.physicsBody,
anchor: CGPointMake(CGRectGetMidX(nodeA.frame), CGRectGetMinY(nodeA.frame)))
physicsWorld.addJoint(joint)
}
I found out that the problem was because I was setting the anchor point for the scene in (0.5, 0.5). If I leave it in (0, 0) everything is ok.