2

I am creating a game in which balls spawn on top and after they fall down and bounce off the ground(this works),I am working on collisions now, my bullets hit the ball and kind of bounces off. If you need any additional code I will post it. (Edited)

import Foundation
import SpriteKit

Did move to view

struct PhysicsCategory {
    static let none: UInt32 = 0b0
    static let player: UInt32 = 0b1
    static let ball: UInt32 = 0b10
    static let bullet: UInt32 = 0b100
    static let ground: UInt32 = 0b1000
}

class GameScene: SKScene, SKPhysicsContactDelegate {


override func didMove(to view: SKView) {
    self.physicsWorld.contactDelegate = self
    let sceneBody = SKPhysicsBody(edgeLoopFrom: self.frame)
    sceneBody.friction = 0
    self.physicsBody = sceneBody
    player.position = CGPoint(x: self.size.width/2, y: self.size.height / 8)
    var timer = Timer.scheduledTimer(timeInterval: 0.2, target: self, selector: #selector(spawnBullet), userInfo: nil, repeats: true)
    player.size = CGSize(width: 100, height: 100)
    player.physicsBody = SKPhysicsBody(circleOfRadius: player.size.width/2)
    player.physicsBody?.affectedByGravity = false
    player.physicsBody?.isDynamic = true

    ball.physicsBody = SKPhysicsBody(circleOfRadius: ball.size.width/2)
    ball.physicsBody?.restitution = 1
    ball.physicsBody?.linearDamping = 0


    bullet.size = CGSize(width: 30, height: 30)
    bullet.zPosition = -5
    bullet.position = CGPoint(x: player.position.x, y: player.position.y)
    let action = SKAction.moveTo(y: self.size.height + 30, duration: 1.0)
    bullet.run(SKAction.repeatForever(action))
    bullet.physicsBody = SKPhysicsBody(circleOfRadius: bullet.size.width/2)
    bullet.name = "bullet"
    player.physicsBody?.categoryBitMask = PhysicsCategory.player
    player.physicsBody?.collisionBitMask = PhysicsCategory.none
    player.physicsBody?.contactTestBitMask = PhysicsCategory.ball

    bullet.physicsBody?.categoryBitMask = PhysicsCategory.bullet
    bullet.physicsBody?.collisionBitMask = PhysicsCategory.none
    bullet.physicsBody?.contactTestBitMask = PhysicsCategory.ball


    ball.physicsBody?.categoryBitMask = PhysicsCategory.ball
    ball.physicsBody?.contactTestBitMask = PhysicsCategory.bullet
    ball.physicsBody?.collisionBitMask = PhysicsCategory.ground

    ground.physicsBody?.categoryBitMask = PhysicsCategory.ground
    ground.physicsBody?.collisionBitMask = PhysicsCategory.ball
    ground.physicsBody?.contactTestBitMask = PhysicsCategory.none
    player.name = "player"
    addChild(player)
    ballSpawner(delay: 2.0)
    spawnGround()
}
//didBegin function
func didBegin(_ contact: SKPhysicsContact) {
   // print("didBeginContact entered for \(String(describing: contact.bodyA.node!.name)) and \(String(describing: contact.bodyB.node!.name))")
    let contactMask = contact.bodyA.categoryBitMask | contact.bodyB.categoryBitMask

I THINK THIS PART IS WRONG Is my case right? Because when I try to write a bullet and ball it gives me an error - " Binary operator '|' cannot be applied to two 'SKSpriteNode' operands". So only default case is called.

    switch contactMask {
  //This case never gets called, i can't see it in the console when i run it
    case PhysicsCategory.bullet | PhysicsCategory.ball:
        let bullet = contact.bodyA.categoryBitMask == 
PhysicsCategory.bullet ? contact.bodyA.node : contact.bodyB.node
        let ball = contact.bodyA.categoryBitMask == 
PhysicsCategory.ball ? contact.bodyA.node : contact.bodyB.node
        collisionBulletAndBall(ball: (ball)!, bullet: (bullet)!)
        print("bullet and ball have contacted.")
    case PhysicsCategory.bullet:
        print("bullet contact")
    case PhysicsCategory.ball & PhysicsCategory.bullet:
        print("collision ball with bullet")
    default:
        print("Some other contact occurred")
    }
}

func gameOver() {
    print("game over")
}

func collisionBulletAndBall(ball: SKNode, bullet: SKNode) {

    ball.removeFromParent()
    bullet.removeFromParent()
    print("collision")
}
 @objc func spawnBullet() {
    var bullet = SKSpriteNode(imageNamed: "bullet")
    bullet.size = CGSize(width: 30, height: 30)
    bullet.zPosition = -5
    bullet.position = CGPoint(x: player.position.x, y: player.position.y)
    let action = SKAction.moveTo(y: self.size.height + 30, duration: 1.0)
    bullet.run(SKAction.repeatForever(action))
    bullet.name = "bullet"
    self.addChild(bullet)
    bullets.append(bullet)
}
 func spawnScoreBall() {
    var ballSize = CGSize(width: 30, height: 30)
    //Random Size
    let randomSize = arc4random() % 3
    switch randomSize {
    case 1:
        ballSize.width *= 1.2
        ballSize.height *= 1.2
    case 2:
        ballSize.width *= 1.5
        ballSize.height *= 1.5
    default:
        break
    }
    var ball = SKSpriteNode(imageNamed: "ball")
    ball.size = ballSize
    let y = size.height-ballSize.height/2
    //Random x
    var randomX = CGFloat(arc4random() % UInt32(size.width - ballSize.width))
    randomX -= size.width/2-ballSize.width*4
    ball.position = CGPoint(x: randomX, y: y)
    //Moving logic
    //        let moveDownAction = SKAction.moveBy(x: 0, y: -size.height + ball.size.height, duration: 2.0)
    //        let destroyAction = SKAction.removeFromParent()
    //        let sequenceAction = SKAction.sequence([moveDownAction, destroyAction])
    //        ball.run(sequenceAction)
    //Rotation
    var rotateAction = SKAction.rotate(byAngle: 1, duration: 1)
    let randomRotation = arc4random() % 2
    if randomRotation == 1 {
        rotateAction = SKAction.rotate(byAngle: -1, duration: 1)
    }
    let repeatForeverAction = SKAction.repeatForever(rotateAction)
    ball.run(repeatForeverAction)
    ball.physicsBody = SKPhysicsBody(circleOfRadius: ball.size.width/2)
    ball.physicsBody?.affectedByGravity = true
    ball.physicsBody?.restitution = 1
    ball.physicsBody?.linearDamping = 0
    ball.zPosition = 3
    blocks.append(ball)
    self.addChild(ball)
}

Spawn ground action

func spawnGround() {
    ground = SKSpriteNode(color: UIColor.white, size: CGSize(width: self.frame.size.width, height: 50))
    ground.position = CGPoint(x: self.size.width/2, y: 0)
 }
  • That's way too much code for you to throw up and expect someone to be able to spot the bug. What have you done to debug this? How do collisions "not work"? – divibisan Aug 01 '18 at 00:29
  • I am just trying to set them up correctly, I am new to swift and spritkit.When a bullet collides with the ball it just pushes it and goes up and the ball changes direction. I think i have something wrong with my didbegin func – Daniel Krupenin Aug 01 '18 at 00:41
  • I hate to tell you this, but I don't think StackOverflow is likely to be able to help you right now. This site is intended to be an encyclopedic resource for specific programming questions. Right now, your question is too big and too vague. Spend some time debugging it and reading tutorials for `swift` and `sprite-kit`, then come back when you know a bit more about your problem and can formulate **specific** questions about it. Good luck! – divibisan Aug 01 '18 at 00:45

1 Answers1

2

player and bullet don't appear to have physics bodies (there is no player.physicsBody = SKPhysicsBody... and bullet.physicsBody = SKPhysicsBody...

ball's physicsbody is assigned way after you assign the physics body's attributes:

ball.physicsBody?.categoryBitMask = PhysicsCategory.ball
    ball.physicsBody?.contactTestBitMask = PhysicsCategory.bullet | PhysicsCategory.ground | PhysicsCategory.player
    ball.physicsBody?.collisionBitMask = PhysicsCategory.bullet | PhysicsCategory.ground | PhysicsCategory.player

and then later

ball.physicsBody = SKPhysicsBody...

so all those ball.physicsBody assignments did nothing, as physicsBody? was 'nil'

Edit : Your didBegin looks a bit messy. Try this:

func didBegin(_ contact: SKPhysicsContact) {
   print("didBeginContact entered for \(String(describing: contact.bodyA.node!.name)) and \(String(describing: contact.bodyB.node!.name))")
   let contactMask = contact.bodyA.categoryBitMask | contact.bodyB.categoryBitMask

   switch contactMask {
   case PhysicsCategory.bullet | PhysicsCategory.ball:
      let bullet = contact.bodyA.categoryBitMask == bullet ? contact.bodyA.node : contact.bodyB.node
      let ball = contact.bodyA.categoryBitMask == ball ? contact.bodyA.node : contact.bodyB.node
      ball.removeFromParent()
      bullet.removeFromParent()
      print("bullet and ball have contacted.")
   default:
      print("Some other contact occurred")
 }

I removed the setting of the ball's physics body's isDynamic and affectedByGravity properties as you are removing it.

Steve Ives
  • 7,894
  • 3
  • 24
  • 55
  • It is a shame that XCode defaults to inserting`?` instead of `!` when giving the errors that the optional needs to be addressed – Knight0fDragon Aug 01 '18 at 13:38
  • @DanielKrupenin "Because when I try to write a bullet and ball" what do you mean by that? "Binary operator '|' cannot be applied to two 'SKSpriteNode' operands" are you getting that against the line `case PhysicsCategory.bullet | PhysicsCategory.ball:`? I can't see any code where you use `\` with 2 SKSpriteNodes or is this code you are *trying* to write? – Steve Ives Aug 02 '18 at 08:55
  • You have 2 cases for a contract between `bullet` and `ball` - You only need the first one: `case PhysicsCategory.bullet | PhysicsCategory.ball:`. . This covers the bullet and the ball making contact. Remove the second one `case PhysicsCategory.ball & PhysicsCategory.bullet:`. Also check your comments/print statements - contacts ARE NOT collisions. It might seem pedantic but it's the wrong terminology. Also what is `case PhysicsCategory.bullet:` checking for - this will only fire if a bullet contacts a bullet. – Steve Ives Aug 02 '18 at 09:01
  • Here's a guide to how to use the various bit masks ; https://stackoverflow.com/a/40596890/1430420 – Steve Ives Aug 02 '18 at 09:08
  • "Binary operator '|' cannot be applied to two 'SKSpriteNode' operands" - this error is given to me when I write just ball | bullet but not PhysicsCategory.ball | PhysicsCategory.bullet – Daniel Krupenin Aug 02 '18 at 21:22
  • @DanielKrupenin Sorry - my bad. Those should have been the values from the `PhysicsCategory` struct. See my edit. Apologies for that. – Steve Ives Aug 03 '18 at 07:23