IOS Swift Spritekit Collision Detection - ios

I am a very new programmer currently using Swift to make a simple brick breaker like game. I am trying to create a label that dynamically shows the score depending on how many times the ball collides with the paddle. Here are two different parts of my code I have so far.
bottom.physicsBody!.categoryBitMask = BottomCategory
ball.physicsBody!.categoryBitMask = BallCategory
paddle.physicsBody!.categoryBitMask = PaddleCategory
ball.physicsBody!.contactTestBitMask = BottomCategory
I know this may not be much help but I am wondering what type of bitmask I will have to make. Here is a part of my code where I want it to create the label
func didBeginContact(contact: SKPhysicsContact) {
// Make variables for the two physics bodies
var score: Int
var firstBody: SKPhysicsBody
var secondBody: SKPhysicsBody
let label = SKLabelNode(fontNamed: "Chalkduster")
label.text = String(score)
label.fontSize = 40
label.fontColor = SKColor.whiteColor()
label.position = CGPoint (x: 1136, y: 600)
addChild(label)
}
Any help would be appreciated.

To detect collisions you have to set collisionBitMask and to test contact you have to set the contactTestBitMask. The Ball has to detect collision with the paddle and detect contact with the bottom and the paddle. So you have to set the contactTestBitMask as
ball.physicsBody?.contactTestBitMask = BottomCategory | PaddleCategory
And the collisionBitMask as
ball.physicsBody?.collissionBitMask = PaddleCategory
So your code should be
bottom.physicsBody?.categoryBitMask = BottomCategory
bottom.physicsBody?.contactTestBitMask = BallCategory
paddle.physicsBody?.categoryBitMask = PaddleCategory
paddle.physicsBody?.contactTestBitMask = BallCategory
paddle.physicsBody?.collissionBitMask = BallCategory
ball.physicsBody?.categoryBitMask = BallCategory
ball.physicsBody?.contactTestBitMask = BottomCategory | PaddleCategory
ball.physicsBody?.collissionBitMask = PaddleCategory

Related

Deleting SKNode on contact with SKPhysicsWorld Boundaries in Swift 3

I am looking to create a unique opening screen for an IOS app, and I would like to have a background of balls dropping to create visual interest. I am creating the balls in the didMove() function, and they are working fine, but only some of the time. I believe the frame is too large for the display, which is making the random X and Y coordinates sometimes occur outside of the screen.
self.physicsWorld.gravity = CGVector.init(dx: 0, dy: -9.8)
let sceneBody = SKPhysicsBody(edgeLoopFrom: self.frame)
sceneBody.friction = 0
self.physicsBody = sceneBody
let minXValue: CGFloat = 0
let maxXValue: CGFloat = self.frame.size.width
let minYValue: CGFloat = 0
let maxYValue: CGFloat = self.frame.size.height
let wait = SKAction.wait(forDuration: 0.4)
let run = SKAction.run {
let randomXNumber = (CGFloat(arc4random()).truncatingRemainder(dividingBy: maxXValue)) + minXValue
let randomYNumber = (CGFloat(arc4random()).truncatingRemainder(dividingBy: maxYValue)) + minYValue
print("Random Y Number: \(randomYNumber)")
print("Random X Number: \(randomXNumber)")
let ball = SKShapeNode(circleOfRadius: 20)
ball.fillColor = SKColor(colorLiteralRed: 212.0, green: 217.0, blue: 128.0, alpha: 1)
ball.position = CGPoint.init(x: randomXNumber, y: randomYNumber)
ball.physicsBody = SKPhysicsBody(circleOfRadius: 20)
ball.physicsBody?.affectedByGravity = true
self.addChild(ball)
}
self.run(SKAction.repeatForever(SKAction.sequence([wait, run])))
Here is my code for the random balls.
I would also like to have these balls get removed from the view when it hits the bottom of the container, in order to increase performance on all devices and not have redundant nodes.
Which function or block of code would I need to implement this concept?
Setting the scene size
In GameViewController.swift find the function viewDidLoad and add the following line right above skView.presentScene(scene)
scene.size = skView.bounds.size
Remove balls from view
To remove balls from the scene you can detect when there is a collision between the edgeLoop around the scene and the balls. Then call removeFromParent on the ball. First, change the class declaration like so:
class GameScene: SKScene, SKPhysicsContactDelegate
and then add
physicsWorld.contactDelegate = self in didMoveToView.
Second, above the class declaration in GameScene.swift, create the physics categories:
enum PhysicsCategory:UInt32
{
case edge = 1
case ball = 2
}
Next, assign those categories to the edge and the balls. (Add the first two lines under sceneBody and insert the third line where you defined the other ball physics properties):
self.physicsBody?.categoryBitMask = PhysicsCategory.edge.rawValue
self.physicsBody?.contactTestBitMask = PhysicsCategory.ball.rawValue
ball.physicsBody?.categoryBitMask = PhysicsCategory.ball.rawValue
And finally, check for collisions:
func didBeginContact(contact: SKPhysicsContact) {
let firstBody = contact.bodyA
let secondBody = contact.bodyB
if firstBody.categoryBitMask == PhysicsCategory.edge.rawValue && secondBody.categoryBitMask == PhysicsCategory.ball.rawValue || firstBody.categoryBitMask == PhysicsCategory.ball.rawValue && secondBody.categoryBitMask == PhysicsCategory.edge.rawValue
{
if firstBody.categoryBitMask == PhysicsCategory.ball.rawValue {
firstBody.node?.removeFromParent()
} else {
secondBody.node?.removeFromParent()
}
}

Setting up a Ground node using SpriteKit?

I've been designing a game where I want the game to stop as soon as the ball makes contact with the ground. My function below is intended to set up the ground.
class GameScene: SKScene, SKPhysicsContactDelegate {
var ground = SKNode()
let groundCategory: UInt32 = 0x1 << 0
let ballCategory: UInt32 = 0x1 << 1
//Generic Anchor coordinate points
let anchorX: CGFloat = 0.5
let anchorY: CGFloat = 0.5
/*Sets the background and ball to be in the correct dimensions*/
override init(size: CGSize) {
super.init(size: size)
//Create the Phyiscs of the game
setUpPhysics()
setUpGround()
setUpBall()
}
func setUpPhysics() -> Void {
self.physicsWorld.gravity = CGVectorMake( 0.0, -5.0 )
self.physicsWorld.contactDelegate = self
}
func setUpGround() -> Void {
self.ground.position = CGPointMake(0, self.frame.size.height)
self.ground.physicsBody = SKPhysicsBody(rectangleOfSize: CGSizeMake(self.frame.size.width, self.frame.size.height)) //Experiment with this
self.ground.physicsBody?.dynamic = false
self.ground.physicsBody?.categoryBitMask = groundCategory //Assigns the bit mask category for ground
self.ball.physicsBody?.contactTestBitMask = ballCategory //Assigns the contacts that we care about for the ground
/*Added in*/
self.ground.physicsBody?.dynamic = true
self.ground.physicsBody?.affectedByGravity = false
self.ground.physicsBody?.allowsRotation = false
/**/
self.addChild(self.ground)
}
func setUpBall() -> Void {
ball.anchorPoint = CGPointMake(anchorX, anchorY)
self.ball.position = CGPointMake(self.frame.size.width/2, self.frame.size.height/2)
self.ball.name = "ball"
self.ball.userInteractionEnabled = false
ball.physicsBody?.usesPreciseCollisionDetection = true
self.ball.physicsBody?.categoryBitMask = ballCategory //Assigns the bit mask category for ball
self.ball.physicsBody?.collisionBitMask = wallCategory | ceilingCategory //Assigns the collisions that the ball can have
self.ball.physicsBody?.contactTestBitMask = groundCategory //Assigns the contacts that we care about for the ball
addChild(self.ball) //Add ball to the display list
}
The problem I am noticing is that when I run the iOS Simulator, the Ground node is not being detected. What am I doing wrong in this case?
Is your ground node not showing up at all? I believe your ground should be an SKSpriteNode. Add that SKSpriteNode as a child of the SKNode object you created.
The SKNode is an empty node and the SKSpriteNode has an actual visual representation. And instead of doing it in a separate function, it would be much simpler if you did it in the DidMoveToView itself.
var objects = SKNode()
addChild(objects)
let ground = SKNode()
ground.position = .......
let colorsprite1 = SKSpriteNode(imageNamed: "yourimageoftheground")
ground.addChild(colorsprite1)
ground.physicsBody?.dynamic = false
Yes you were right to set ground's physics definition as not dynamic. I don't know why people are telling you otherwise. You don't want it to move upon contact with your ball thingies or whatever it is you have moving around in your scene.
Add all other physics definitions you need. Consider using a ContactTestBitMask as you want SpriteKit to let you know when a collision with the ground occurs, instead of SpriteKit handling it by itself. Details about that mask is easily available in StackOverFlow with examples.
objects.addChild(ground)
Hope this helps
Your ballCategory should be assigned to self.ground.contactBitMask not the ball.
You need to set the dynamic property to true in order for contact detection to work.
At the same time, you can set the following properties.
ground.affectedByGravity = false
ground.allowsRotation = false

Stop Sprites from Pushing Each Other in SpriteKit

I'm coding a SpriteKit game and I'm trying to add the equivalent of a Cocos2D sensor. My problem is that my sensor keeps pushing the other sprites, but this isn't what I want. I want the sensor to be able to overlap with other sprites and not push them around. How do I fix this? Here is the code I've added for my sensor:
bubble.physicsBody = SKPhysicsBody(circleOfRadius: CGFloat(50))
bubble.physicsBody?.dynamic = false
bubble.physicsBody?.allowsRotation = false
bubble.physicsBody?.affectedByGravity = false
And here is my collision method:
func didBeginContact(contact: SKPhysicsContact) {
let collision:UInt32 = (contact.bodyA.categoryBitMask | contact.bodyB.categoryBitMask)
if collision == (playerCategory | crateCategory) {
NSLog("Game Over")
var myLabel = SKLabelNode(fontNamed: "Arial")
myLabel.text = "GAME OVER"
myLabel.fontSize = 50
myLabel.color = SKColor.blueColor()
myLabel.position = (CGPointMake(self.frame.size.width*0.5, self.frame.size.height*0.5))
self.addChild(myLabel)
fisherman.physicsBody?.dynamic = false
fisherman.removeFromParent()
fish.physicsBody?.dynamic = false
fish.removeFromParent()
crate.physicsBody?.dynamic = false
crate.removeFromParent()
crateDuplicate.physicsBody?.dynamic = false
crateDuplicate.removeFromParent()
}
if collision == (playerCategory | bubbleCategory) {
NSLog("Bubble Contact")
bubbleDuplicate.removeAllActions()
bubbleDuplicate.removeFromParent()
bubble.removeAllActions()
bubble.removeFromParent()
bubbleDuplicate.setScale(0)
bubble.setScale(0)
bubbleDuplicate.alpha = 0
}
}
Set a collisionBitMask to 0, so all collisions of this particular object to others will be ignored.

SKPhysics with Swift and SpriteKit

I am trying to create a iOS game using Swift Programming Language.
I want to use swipe gestures on the screen to make the character (player) move up, down, left and right
So far so good. But I want some physics. I want the player to collide with the edges of the screen. (and later I want it to collide with other stuff, but I need to know the code first ;)
QUESTION: How do I identify a collision between 2 objects (player and edge of screen)
import SpriteKit
class GameScene: SKScene, SKPhysicsContactDelegate {
var player:SKSpriteNode = SKSpriteNode()
let moveUp = SKAction.moveByX(0, y:2000, duration:10.0) //it is set to 2000 because I want to see if it collides with the edge of the screen
let moveDown = SKAction.moveByX(0, y: -200, duration: 2.0)
// Define the bitmasks identifying various physics objects
let worldCategory:UInt32 = 0x1 << 0
let playerCategory:UInt32 = 0x1 << 1
override func didMoveToView(view: SKView) {
}
override init(size:CGSize) {
super.init(size: size)
self.backgroundColor = SKColor.whiteColor()
player = SKSpriteNode(imageNamed: "Kundvagn1")
player.position = CGPointMake(self.frame.size.width/2, player.size.height/2 + 20)
self.addChild(player)
player.runAction(moveUp)
self.physicsWorld.gravity = CGVectorMake(0, 0)
self.physicsWorld.contactDelegate = self
player.physicsBody = SKPhysicsBody(rectangleOfSize: player.size)
player.physicsBody?.dynamic = true //I dont think the question marks should be there, but Xcode doesn´t accept the code if there is no question marks there
player.physicsBody?.categoryBitMask = playerCategory
player.physicsBody?.contactTestBitMask = worldCategory
player.physicsBody?.collisionBitMask = 0
player.physicsBody?.usesPreciseCollisionDetection = true
}
func didBeginContact(contact: SKPhysicsContact!) {
println("Collision")
}
Assuming that you want the left and right edges to block, simply add two SKShapeNode rectangular objects to your scene, set their heights to the screen height, and place them just beyond the left and right edges of the screen. Set up their physics bodies and you should be good to go.
If you want all the edges of the screen to be boundaries, you need to setup a line-based physics body for your scene, as detailed here: Add boundaries to an SKScene
I added this in case you need a code example to jonogilmour's instructions . Turn on the ShowsPhysics = true in your viewController to see physics. Both sides should be blue. If you want a contact notification of course you would add to this code with your bit mask stuff. Hope this helps!
var leftEdge = SKNode()
leftEdge.physicsBody = SKPhysicsBody(edgeFromPoint: CGPointZero, toPoint: CGPointMake(0.0, self.size.height))
leftEdge.position = CGPointZero;
self.addChild(leftEdge)
var rightEdge = SKNode()
rightEdge.physicsBody = SKPhysicsBody(edgeFromPoint: CGPointZero, toPoint: CGPointMake(0.0, self.size.height))
rightEdge.position = CGPointMake(self.size.width, 0.0);
self.addChild(rightEdge)

Circles not bouncing elastically in SpriteKit's physics engine

I've been messing around with Swift and SpriteKit's physics engine and wanted to make a simple billiards style game. I've got a simple ball/table setup made but when the balls collide with each other or the wall, they lose much of their velocity, even though their friction and linear damping are set to 0 and their restitution is set to 1. Obviously billiard balls have some friction and don't have max restitution, but for my game's sake, I would like to have completely elastic collisions. I've tinkered around with all of the physical properties, and can't find a combination that yields elastic collisions. I've also checked if the walls' physical properties have a say in the matter, and no, they don't. Changing the walls' friction or restitution did not change the results. Thanks for the help!
import SpriteKit
class GameScene: SKScene {
override func didMoveToView(view: SKView) {
for i in 0...5
{
addChild(newBall())
}
self.physicsBody = SKPhysicsBody(edgeLoopFromRect: self.frame)
}
func newBall() -> SKShapeNode
{
let radius = CGFloat(Int(arc4random_uniform(10)+10))
var ball = SKShapeNode(circleOfRadius: radius)
ball.lineWidth = 1
ball.strokeColor = SKColor.whiteColor()
ball.fillColor = UIColor.cyanColor()
ball.position = CGPointMake(CGFloat(Int(arc4random_uniform(400)+50)), CGFloat(Int(arc4random_uniform(220)+50)))
var body = SKPhysicsBody(circleOfRadius: radius)
ball.physicsBody = body
body.friction = 0
body.restitution = 1
body.linearDamping = 0
body.allowsRotation = true
body.velocity = CGVectorMake(CGFloat(Int(arc4random_uniform(50)+50)), CGFloat(Int(arc4random_uniform(50)+50)))
return ball
}
}

Resources