简体   繁体   中英

Struggling with collision detection in Swift/SpriteKit

I'm having a play with game programming in Swift/Xcode and am struggling getting collision to work.

I'm using Xcode 8 Beta 3 (because I couldn't use Xcode 7 on MacOS Sierra) so its using the new Swift 3.0 Syntax. The game has a little puppy (defined in a SK Scene file) that jumps and collects thrown items from the right. The thrown item is created in a function and removed when a new item is thrown.

I need contact with the thrown item comes into contact with the puppy. I've got physics turned on and the item bounces off the puppy but no contact is registered.

Heres my code. Appologies for the mess of code but its very first attempt at Swift and everything just thrown in for now. I can't see what I'm doing wrong :(

UPDATE: As cocoajoe suggested, I've tried setting the masks to 1 and have simplified the code to remove references to the gamescene.sks file and created everything programmatically but its still not working. I've updated the code below to reflect the latest version of the code...

import SpriteKit
import AVFoundation

class GameScene: SKScene, SKPhysicsContactDelegate {
    required init?(coder aDecoder: NSCoder) {
    fatalError("init(coder) is not used in this app")
}

override init(size: CGSize) {
    super.init(size: size)

    anchorPoint = CGPoint(x: 0.0, y: 0.0)
}

var isJumping = false
var gamePaused = false
var gameRunning = false
var playerScore = 0
var lastSpawnTime = 0
var lastTick = 0
var spawnDelay = 4

let PuppyCategory       : UInt32 = 0x1 << 0
let ThrowableCategory   : UInt32 = 0x1 << 1
let GroundCategory      : UInt32 = 0x1 << 2

override func didMove(to view: SKView) {

    physicsWorld.gravity = CGVector(dx: 0.0, dy: -9.8)
    physicsWorld.contactDelegate = self

    let background = SKSpriteNode(imageNamed: "bg")
    background.anchorPoint = CGPoint(x: 0.0, y:0.0)
    background.size = size
    addChild(background)

    // Set up Ground Object
    let Ground = SKSpriteNode()
    Ground.name = "Ground"
    Ground.size = CGSize(width:frame.width, height: frame.height / 10)
    Ground.position = CGPoint(x: frame.midX, y: Ground.frame.height / 2 )
    Ground.zPosition = -20
    Ground.color = SKColor.white()

    // Set up the Physics
    Ground.physicsBody = SKPhysicsBody(rectangleOf: Ground.size)
    Ground.physicsBody!.categoryBitMask = GroundCategory
    Ground.physicsBody!.contactTestBitMask = PuppyCategory | ThrowableCategory
    Ground.physicsBody!.collisionBitMask = PuppyCategory | ThrowableCategory
    Ground.physicsBody?.affectedByGravity = false
    Ground.physicsBody?.allowsRotation = false
    Ground.physicsBody?.isDynamic = false
    Ground.physicsBody?.mass = 1.99999

    // Initialise the Node
    addChild(Ground)

    // Create a Puppy
    let Puppy = SKSpriteNode(imageNamed: "puppy1")
    Puppy.name = "Puppy"
    Puppy.position = CGPoint(x: frame.width / 10, y: frame.height / 2)
    Puppy.size = CGSize(width: frame.width / 7, height: frame.height / 5)
    Puppy.zPosition = 3

    // Set up the Physics
    Puppy.physicsBody = SKPhysicsBody(rectangleOf: Puppy.size)
    Puppy.physicsBody!.categoryBitMask = PuppyCategory
    Puppy.physicsBody!.contactTestBitMask = ThrowableCategory | GroundCategory
    Puppy.physicsBody!.collisionBitMask = ThrowableCategory | GroundCategory
    Puppy.physicsBody?.affectedByGravity = true
    Puppy.physicsBody?.allowsRotation = false
    Puppy.physicsBody?.isDynamic = true
    Puppy.physicsBody?.mass = 1.99999

    // Set up the Textures/Animation Frames
    var TextureAtlas = SKTextureAtlas()
    var TextureArray = [SKTexture]()
    TextureAtlas = SKTextureAtlas(named: "puppy")

    for i in 1...TextureAtlas.textureNames.count {
        let Name = "puppy\(i).png"
        TextureArray.append(SKTexture(imageNamed: Name))
    }

    Puppy.run(SKAction.repeatForever(SKAction.animate(with:TextureArray, timePerFrame: 0.1)))

    // Add the Sprite
    addChild(Puppy)


    let gameMessage = SKSpriteNode(imageNamed: "TapToPlay")
    gameMessage.name = "GameMessage"
    gameMessage.position = CGPoint(x: self.frame.midX, y: self.frame.midY)
    gameMessage.zPosition = 30
    gameMessage.setScale(0.0)
    addChild(gameMessage)

    let gameLogo = SKSpriteNode(imageNamed: "pjlogo")
    gameLogo.name = "GameLogo"
    gameLogo.size = CGSize(width: frame.width / 3, height: frame.height / 5)
    gameLogo.position = CGPoint(x: frame.midX, y: (frame.maxY / 100) * 80)
    gameLogo.zPosition = 30
    addChild(gameLogo)

    let scale = SKAction.scale(to: 1.0, duration: 1)
    scene?.childNode(withName: "GameMessage")!.run(scale)

    if let musicURL = Bundle.main.urlForResource("bgmusic", withExtension: "mp3") {
        var backgroundMusic: SKAudioNode!
        backgroundMusic = SKAudioNode(url: musicURL)
        addChild(backgroundMusic)
    }

    startNewGame()
}

func puppyJump() {
    if gameRunning {
        let Puppy = childNode(withName: "Puppy")
        if !isJumping {
            Puppy?.physicsBody?.applyImpulse(CGVector(dx: 0,dy: 1400))
            isJumping = true
        }
    }
}

func generateThrowable() {
    print("new enemy")
    let oldItem = childNode(withName: "throwableItem")
    oldItem?.removeFromParent()
    let throwableItem = SKSpriteNode(imageNamed: "puppy1")
    throwableItem.name = "throwableItem"
    throwableItem.position = CGPoint(x: frame.maxX, y: frame.midY)
    throwableItem.zPosition = 3
    throwableItem.setScale(0.1)
    throwableItem.physicsBody = SKPhysicsBody(rectangleOf: throwableItem.size)
    throwableItem.physicsBody!.categoryBitMask = ThrowableCategory
    throwableItem.physicsBody!.contactTestBitMask = PuppyCategory | GroundCategory
    throwableItem.physicsBody!.collisionBitMask = PuppyCategory | GroundCategory

    throwableItem.physicsBody?.friction = 0.1
    throwableItem.physicsBody?.restitution = 0.1
    throwableItem.physicsBody?.mass = 0.01
    throwableItem.physicsBody?.affectedByGravity = true
    throwableItem.physicsBody?.allowsRotation = true
    throwableItem.physicsBody?.isDynamic = true

    addChild(throwableItem)
    throwableItem.physicsBody?.applyImpulse(CGVector(dx: -7,dy: 4))
    let throwableTrail = SKEmitterNode(fileNamed: "particleTrail.sks")
    throwableTrail?.name = "throwableTrail"
    throwableTrail?.targetNode = scene
    throwableItem.addChild(throwableTrail!)
}

func startNewGame() {
    let logo = childNode(withName: "GameLogo")
    let gameMessage = childNode(withName: "GameMessage")
    logo?.removeFromParent()
    gameMessage?.removeFromParent()

    let gameScore = SKLabelNode(fontNamed: "Arial")
    gameScore.name = "GameScore"
    gameScore.text = "Score:0"
    gameScore.horizontalAlignmentMode = SKLabelHorizontalAlignmentMode.right
    gameScore.fontSize = 20
    gameScore.position = CGPoint(x: frame.maxX - 20, y: frame.maxY - 26)
    gameScore.zPosition = 30
    addChild(gameScore)

    let pauseButton = SKLabelNode(fontNamed: "Arial")
    pauseButton.name = "pauseButton"
    pauseButton.text = "PAUSE"
    pauseButton.horizontalAlignmentMode = SKLabelHorizontalAlignmentMode.left
    pauseButton.fontSize = 20
    pauseButton.position = CGPoint(x: 20, y: frame.maxY - 26)
    pauseButton.zPosition = 30
    addChild(pauseButton)

    gameRunning = true
}

func didBeginContact(contact: SKPhysicsContact) {
    print("contact")
    var firstBody: SKPhysicsBody
    var secondBody: SKPhysicsBody
    // 2
    if contact.bodyA.categoryBitMask < contact.bodyB.categoryBitMask {
        firstBody = contact.bodyA
        secondBody = contact.bodyB
    } else {
        firstBody = contact.bodyB
        secondBody = contact.bodyA
    }
    // 3
    if firstBody.categoryBitMask == PuppyCategory && secondBody.categoryBitMask == GroundCategory {
        print("Hit bottom. First contact has been made.")
    }
}

override func update(_ currentTime: TimeInterval) {
    // Called before each frame is rendered

    if gameRunning {
        let gameScore = childNode(withName: "GameScore") as? SKLabelNode
        gameScore?.text = "Score:\(playerScore)"

        let currentTick:Int = Int(ceil(currentTime))
        if lastTick < currentTick {
            lastTick = currentTick
        }

        if (lastSpawnTime + spawnDelay) < currentTick {
            // Time to Spawn new Bad Guy
            generateThrowable()
            lastSpawnTime = currentTick
        }

    }

}
}

I haven't checked other parts. (Your code still needs plenty of resources...)

But this line:

func didBeginContact(contact: SKPhysicsContact) {

needs to be changed as:

func didBegin(_ contact: SKPhysicsContact) {

in Swift 3. (Which is suggested by Xcode, with just typing didBegin .)

Please try.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM