Tarang Hirani
Tarang Hirani

Reputation: 590

iOS SpriteKit collision with screen edges not working

I am trying to create a collision between an SKShapeNode instance and the edges of the screen but for some reason the ShapeNode still goes beyond the limits of the screen. I have implemented code that I believe should take care of the collision but I am new to SpriteKit so I am not entirely sure. Here's a snippet of the code:

//
//  GameScene.swift
//  SpriteKitTest
//
//  Created by 580380 on 3/10/16.
//  Copyright (c) 2016 580380. All rights reserved.
//

import SpriteKit
import UIKit
import CoreMotion

class GameScene: SKScene {

    //MARK: - Global variables
    let motionManager = CMMotionManager()
    var circleNode = SKShapeNode()
    var destX : CGFloat = 0.0
    var destY : CGFloat = 0.0

    enum Collision :UInt32 {
        case ball = 1
        case wall = 2
    }
    //MARK: - Sprite kit functionality
    override func didMoveToView(view: SKView) {

        backgroundColor = UIColor.whiteColor()
        createCircleNode()
        moveCircleNode()
        self.addChild(circleNode)
    }

    //Setup and configuring the SKShapeNode object
    private func createCircleNode() {
        circleNode.path = UIBezierPath(arcCenter: CGPoint(x: CGRectGetMidX(self.frame), y: CGRectGetMidY(self.frame)), radius: 20, startAngle: 0, endAngle: CGFloat(2*M_PI), clockwise: true).CGPath
        circleNode.fillColor = UIColor.redColor()
        circleNode.strokeColor = UIColor.blueColor()
        circleNode.lineWidth = 1
    }

    private func moveCircleNode() {
        circleNode.physicsBody = SKPhysicsBody()
        circleNode.physicsBody?.dynamic = true
        circleNode.physicsBody?.affectedByGravity = false
        if motionManager.accelerometerAvailable {
            motionManager.accelerometerUpdateInterval = 0.1
            motionManager.startAccelerometerUpdatesToQueue(NSOperationQueue.mainQueue(), withHandler: { (data, error) -> Void in
                self.destX = self.circleNode.position.x + CGFloat(data!.acceleration.x*100)
                self.destY = self.circleNode.position.y + CGFloat(data!.acceleration.y*200)
            })
        }
        self.physicsBody = SKPhysicsBody(edgeLoopFromRect: self.frame)
        self.physicsBody!.dynamic = true
        self.physicsBody!.affectedByGravity = false
        self.physicsBody!.categoryBitMask = Collision.wall.rawValue
        self.physicsBody!.collisionBitMask = Collision.ball.rawValue
    }

    override func update(currentTime: NSTimeInterval) {
        let destXAction = SKAction.moveToX(destX, duration: 0.1)
        let destYAction = SKAction.moveToY(destY, duration: 0.1)
        self.circleNode.runAction(destXAction)
        self.circleNode.runAction(destYAction)
    }
}

Any idea where I could be going wrong?

Upvotes: 1

Views: 1228

Answers (1)

David Williames
David Williames

Reputation: 1109

The main issue here I will assume is that the Game Scene never actually gets its size set.

In your GameViewController add the line scene.size = skView.bounds.size under let skView = self.view as! SKView.

This will set the size of your scene correctly, so now your ball should collide with your screen edge.... however when you set your circleNode path, the way you implemented it with offset your circle by half the size of the screen.

If you want your circle to appear in the middle of the screen, a better solution would be to have circleNode = SKShapeNode(circleOfRadius: 20) instead of circleNode.path = UIBezierPath(arcCenter: CGPoint(x: CGRectGetMidX(self.frame), y: CGRectGetMidY(self.frame)), radius: 20, startAngle: 0, endAngle: CGFloat(2*M_PI), clockwise: true).CGPath. Then set it to be in the center with circleNode.position = CGPointMake(frame.width/2, frame.height/2).

This should solve your issue as a whole, however something you may notice is that the ball may disappear through the edge the screen. To fix this, change your update method to something like this

func clamp(value: CGFloat, min: CGFloat, max: CGFloat) -> CGFloat {
    if value > max {
        return max
    }
    if value < min {
        return min
    }
    return value
}


override func update(currentTime: NSTimeInterval) {
    let ballRadius: CGFloat = 10
    destX = clamp(destX, min: ballRadius, max: frame.width - ballRadius)
    destY = clamp(destY, min: ballRadius, max: frame.height - ballRadius)

    let destXAction = SKAction.moveToX(destX, duration: 0.1)
    let destYAction = SKAction.moveToY(destY, duration: 0.1)
    self.circleNode.runAction(destXAction)
    self.circleNode.runAction(destYAction)
}

This will make it so the ball should never try to go outside the bounds of the screen. I would also suggest adding circleNode.physicsBody?.usesPreciseCollisionDetection = true so your collisions are more accurate.

Upvotes: 1

Related Questions