如何在 Swift 3.0 中使用物理体

How To Use Physics Body In Swift 3.0

我正在为学校编写一个简单的 Space Invaders 类型的游戏。我想使用 Swift 中包含的物理属性进行碰撞检测,但我 运行 遇到了一些麻烦。尽管我已经多次编写并重新阅读代码,但它根本不起作用。没有错误,也没有任何结果。由于代码在我的文件中相当分散,我在下面包含了我的整个 GameScene 页面。非常感谢任何帮助,在此先感谢您。

//  --------------------------------------------------------
//  | 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.

我已经浏览了代码。看起来不错。您只需要少量代码即可提供接触位掩码和碰撞位掩码。所以在

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

添加下面的代码

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

AND 对于外星人

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

添加下面的代码,因为你希望它碰撞并检测接触。

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

希望这能提供问题的答案

编辑: 这是您可以尝试的示例项目

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
    }
}


}

删除 GameViewController 视图 didload 方法中的所有内容并添加这个

 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)