Kyle Herrmann
Kyle Herrmann

Reputation: 35

How To Use Physics Body In Swift 3.0

I'm writing a simple Space Invaders type game for school. I'd like to use the physics properties included in Swift for my collision detection, but I've run into some troubles. Although I've written and re-read the code multiple times, it simply does not work. There are no errors nor any results. Since the code is fairly spread throughout my file, I've included my whole GameScene page below. Any help is greatly appreciated, thank you in advance.

//  --------------------------------------------------------
//  | Space Invasion.                                      |
//  | Created by Kyle Herrmann on 2017-04-05.              |
//  | Copyright © 2017 Kyle Herrmann. All rights reserved. |
//  --------------------------------------------------------


//Import the essentials.
import SpriteKit
import GameplayKit
enum BodyType:UInt32 {
    case userShipBody = 1
    case alienBody = 2
}

class GameScene: SKScene, SKPhysicsContactDelegate {

    //Variables and Declarations.

    //UserShip Variables.
    let moveLeft: SKAction = SKAction.moveBy(x: -20, y: 0, duration: 0.1)
    let moveRight: SKAction = SKAction.moveBy(x: 20, y: 0, duration: 0.1)
    var userShipSprite: SKNode = SKNode()
    var tapDetect: Bool = false
    var posX = CGFloat()
    var posY = CGFloat()

    //Sprite Variables.
    var xSpeed = CGFloat(3.5)
    var ySpeed = CGFloat(3.5)
    let alienMoveLeft: SKAction = SKAction.moveBy(x: -3, y: 0, duration: 0.1)
    let alienMoveRight: SKAction = SKAction.moveBy(x: 3, y: 0, duration: 0.1)
    let alienMoveDown: SKAction = SKAction.moveBy(x: 0, y: -4.5, duration: 0.1)
    let classicAlien = SKSpriteNode(texture: SKTexture(imageNamed: "ClassicAlien"))
    var aliens = [SKSpriteNode]()
    var alienAmount = 6
    var displayAliens = true
    var xAliens = -300
    var yAliens = 520
    var rowCount = 5
    var indexMoving = 0
    //Life Variables.
    var lblLife = SKLabelNode()
    var lifeCounter = 3

    //Detect and Save User Tap Location.
    func touchDown(atPoint pos : CGPoint)   {
        tapDetect = true
        posX = pos.x
        posY = pos.y
    }

    //Grab our object(s).
    override func didMove(to view: SKView)  {

        //Life Count Declaration.
        lblLife = self.childNode(withName: "lblLife") as! SKLabelNode!

        //User Ship.
        for node in self.children           {
            if (node.name == "userShip")    {

                userShipSprite = node
    }
        //Set up user ship body for physics.
        self.physicsWorld.contactDelegate = self
        userShipSprite.physicsBody?.categoryBitMask = BodyType.userShipBody.rawValue

    }
        //Add and display given amount of aliens.
        for index in 0...alienAmount - 1 {

            aliens.append(SKSpriteNode(texture: SKTexture(imageNamed: "ClassicAlien")))
            //Location
            let newPosition = CGPoint(x: CGFloat(xAliens), y: CGFloat(yAliens))
            xAliens += 120
            aliens[index].position = newPosition
            //Display
            self.addChild(aliens[(index)])

            //Set up physics body
            aliens[index].physicsBody?.categoryBitMask = BodyType.alienBody.rawValue

            //New Row
            if index >= rowCount {
                xAliens = -300
                yAliens -= 100
                rowCount += 6
    }
    }
    }

    //Physics collision and so forth.
    func didBegin(_ contact: SKPhysicsContact) {

        if contact.bodyA.categoryBitMask == BodyType.userShipBody.rawValue && contact.bodyB.categoryBitMask == BodyType.alienBody.rawValue {
            print("Death Detected")
            //Change Life.
            lifeCounter -= 1
            lblLife.text = "Lives:\(lifeCounter)"
        }
        else if contact.bodyB.categoryBitMask == BodyType.userShipBody.rawValue && contact.bodyA.categoryBitMask == BodyType.alienBody.rawValue {
            print("Death Detected")
            //Change Life.
            lifeCounter -= 1
            lblLife.text = "Lives:\(lifeCounter)"
        }
        }

    //Detect Touch Release.
    override func touchesEnded(_ touches: Set<UITouch>, with event: UIEvent?) {
        tapDetect = false
        for t in touches { self.touchUp(atPoint: t.location(in: self))        }
                                                                        }

    override func update(_ currentTime: TimeInterval)   {      

            //Alien Movement.
            if userShipSprite.position.y < aliens[indexMoving].position.y { //Bottom of Map.
                aliens[indexMoving].run(alienMoveDown)
            }
            if userShipSprite.position.x > aliens[indexMoving].position.x { //Right Side of Map.
                aliens[indexMoving].run(alienMoveRight)
            }
            if userShipSprite.position.x < aliens[indexMoving].position.x { //Left Side of Map.
                aliens[indexMoving].run(alienMoveLeft)
            }
            //Check for next alien.
            if aliens[indexMoving].position.y < userShipSprite.position.y {
                aliens[indexMoving].removeFromParent()
                indexMoving += 1
                //Check for end game.
                if indexMoving > alienAmount {
                    xSpeed += 1
                    ySpeed += 1
                    alienAmount += 6
            }
            }

        //Ship Movement.
        if tapDetect == true                            {
            //Prevent off screen movement (Right side)
            if userShipSprite.position.x > 200          {
                userShipSprite.run(moveLeft)
            }
            //Prevent off screen movement (Left side)
            else if userShipSprite.position.x < -200    {
            userShipSprite.run(moveRight)  }
            //Ship movement.
            if posX > 0 && posY < -300                  {
                userShipSprite.run(moveRight)
            }
            else if posX < 0 && posY < -300             {
                userShipSprite.run(moveLeft)
            }
            }
            }


    // We ain't using this stuff right now.

    override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
        for t in touches { self.touchDown(atPoint: t.location(in: self)) }
    }
    func touchMoved(toPoint pos : CGPoint) {
    }
    func touchUp(atPoint pos : CGPoint) {
    }
    override func touchesMoved(_ touches: Set<UITouch>, with event: UIEvent?) {
        for t in touches { self.touchMoved(toPoint: t.location(in: self)) }
    }
    override func touchesCancelled(_ touches: Set<UITouch>, with event: UIEvent?) {
        for t in touches { self.touchUp(atPoint: t.location(in: self)) }
    }

} //End of Class.

Upvotes: 0

Views: 102

Answers (1)

Chipster Chops
Chipster Chops

Reputation: 86

I have skimmed thru the code. looks good. U just need to a small amount of code to give the contact bitmask and collision bitmask. So under

userShipSprite.physicsBody?.categoryBitMask = BodyType.userShipBody.rawValue

Add the code below

userShipSprite.physicsBody?.collisionBitMask = BodyType.alienBody.rawValue
userShipSprite.physicsBody?.contactTestBitMask = BodyType.alienBody.rawValue

AND for the Alien

aliens[index].physicsBody?.categoryBitMask = BodyType.alienBody.rawValue

Add the codes below because you want it to collide with and also detect contact.

aliens[index].physicsBody?.categoryBitMask = BodyType.userShipBody.rawValue
aliens[index].physicsBody?.categoryBitMask = BodyType.userShipBody.rawValue 

Hope this provides the answer to the question

EDIT: Here is a sample proj you can try on

import SpriteKit
import GameplayKit

class GameScene: SKScene, SKPhysicsContactDelegate {


let newDot1 = SKSpriteNode(imageNamed: "Spaceship")
let newDot2 = SKSpriteNode(imageNamed: "Spaceship")
var reset = false

enum categoryBit: UInt32 {
    case ball = 1
    case ball2 = 2

}



override func didMove(to view: SKView) {

    physicsWorld.contactDelegate = self

    physicsWorld.gravity = CGVector(dx: 0, dy: -0.3)

    newDot2.setScale(0.3)
    newDot2.name = "dot1"
    newDot2.position = CGPoint(x: frame.midX, y: frame.maxY)
    newDot2.physicsBody = SKPhysicsBody(circleOfRadius: newDot2.size.width/2)
    newDot2.physicsBody?.categoryBitMask = categoryBit.ball.rawValue
    newDot2.physicsBody?.collisionBitMask = categoryBit.ball2.rawValue
    newDot2.physicsBody?.contactTestBitMask = categoryBit.ball2.rawValue
    self.addChild(newDot2)


    newDot1.setScale(0.8)
    newDot1.position = CGPoint(x: frame.midX, y: frame.minY)
    newDot1.physicsBody = SKPhysicsBody(circleOfRadius: newDot1.size.width/2)
    newDot1.physicsBody?.isDynamic = false
    newDot1.physicsBody?.categoryBitMask = categoryBit.ball2.rawValue
    newDot1.physicsBody?.collisionBitMask = categoryBit.ball.rawValue
    newDot1.physicsBody?.contactTestBitMask = categoryBit.ball.rawValue
    newDot1.name = "dot2"
    self.addChild(newDot1)

}


override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {

    newDot2.position = CGPoint(x: frame.midX, y: frame.maxY)



}

func didBegin(_ contact: SKPhysicsContact) {
    print("contact made")


    let hitDot = contact.bodyB.node! as! SKSpriteNode
    let hitDot1 = contact.bodyA.node! as! SKSpriteNode
    if hitDot1.name == "dot1" || hitDot.name == "dot1" {
        reset = true
    }


}

override func update(_ currentTime: TimeInterval) {
    if reset {
        newDot1.position.x = 0
        reset = false
    }
}


}

DELETE everything in GameViewController view didload method and add this

 super.viewDidLoad()

    let skview = self.view as! SKView


    let scene = GameScene(size: view.bounds.size)

    scene.scaleMode = .resizeFill
    skview.ignoresSiblingOrder = true
    skview.showsFPS = true
    skview.showsNodeCount = true
    skview.showsPhysics = true
    skview.presentScene(scene)

Upvotes: 1

Related Questions