M. Gallant M. Gallant - 5 months ago 55
Swift Question

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.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 = 0
    case ball = 1

Next, assign those categories to the edge and the balls. (Add the first line under sceneBody and the second line where you define the other ball physics properties):

self.physicsBody?.categoryBitMask = PhysicsCategory.edge.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 {
         } else {