当玩家与其他 SKSpriteNode 碰撞时摆脱“反弹”

Posted

技术标签:

【中文标题】当玩家与其他 SKSpriteNode 碰撞时摆脱“反弹”【英文标题】:Getting rid of the "bounce" when the player collides with other SKSpriteNodes 【发布时间】:2017-06-29 11:11:23 【问题描述】:

我正在制作一个游戏,假设玩家要避开障碍物并收集积分。不过有个小问题。 当玩家与点(另一个 SKSpriteNode)发生碰撞时,玩家会反弹一点。我希望它只是“通过”而不受到碰撞的影响。

这是播放器类:

import SpriteKit

struct ColliderType 
static let Player: UInt32 = 1
static let Swan: UInt32 = 2
static let Branch: UInt32 = 3
static let Score: UInt32 = 4
static let Wall1: UInt32 = 5
static let Wall2: UInt32 = 6


class Player: SKSpriteNode 

func initialize() 
    self.name = "Player"
    self.zPosition = 4
    self.setScale(0.3)
    self.anchorPoint = CGPoint(x: 0.5, y: 0.5)
    self.physicsBody = SKPhysicsBody(circleOfRadius: self.size.height / 
2 - 5)
    self.physicsBody?.affectedByGravity = false
    self.physicsBody?.restitution = 0
    self.physicsBody?.categoryBitMask = ColliderType.Player
    self.physicsBody?.collisionBitMask = ColliderType.Swan | 
ColliderType.Branch | ColliderType.Wall1 | ColliderType.Wall2
    self.physicsBody?.contactTestBitMask = ColliderType.Swan | 
ColliderType.Score | ColliderType.Branch | ColliderType.Wall1 | 
ColliderType.Wall2





这是游戏场景:

import SpriteKit

class GameplayScene: SKScene, SKPhysicsContactDelegate 

var player = Player()

var swan = SKSpriteNode()

var frog = SKSpriteNode()

var egg = SKSpriteNode()

var branch = SKSpriteNode()

var scoreLabel = SKLabelNode()
var score = 0

var gameStarted = false
var isAlive = false

var press = SKSpriteNode()

var touched: Bool = false

var location = CGPoint.zero

override func didMove(to view: SKView) 
    initialize()


override func update(_ currentTime: TimeInterval) 

    if isAlive 
        moveBackgrounds()
    

    if (touched) 
        moveNodeToLocation()
    


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

    if gameStarted == false 
        isAlive = true
        gameStarted = true
        press.removeFromParent()
        spawnSwans()
        spawnEggs()
        spawnBranches()
    

    touched = true
    for touch in touches 
        location = touch.location(in:self)
    

    for touch in touches 

        let location = touch.location(in: self)

        if atPoint(location).name == "Retry" 
            self.removeAllActions()
            self.removeAllChildren()
            initialize()
        

        if atPoint(location).name == "Quit" 
            let mainMenu = MainMenuScene(fileNamed: "MainMenuScene")
            mainMenu!.scaleMode = .aspectFill
            self.view?.presentScene(mainMenu!, transition: 
SKTransition.fade(withDuration: TimeInterval(1)))
        

    



override func touchesEnded(_ touches: Set<UITouch>, with event: 
UIEvent?) 
    touched = false


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

    for touch in touches 
        location = touch.location(in: self)
    


func didBegin(_ contact: SKPhysicsContact) 

    var firstBody = SKPhysicsBody()
    var secondBody = SKPhysicsBody()

    if contact.bodyA.node?.name == "Player" 
        firstBody = contact.bodyA
        secondBody = contact.bodyB
     else 
        firstBody = contact.bodyB
        secondBody = contact.bodyA
    

    if firstBody.node?.name == "Player" && secondBody.node?.name == 
"Egg" 
        incrementScore()
        secondBody.node?.removeFromParent()
     else if firstBody.node?.name == "Player" && secondBody.node?.name 
== "Swan" 
        if isAlive 
            playerDied()
            firstBody.node?.removeFromParent()
        
     else if firstBody.node?.name == "Player" && secondBody.node?.name 
== "Branch" 
        if isAlive 
            playerDied()
            firstBody.node?.removeFromParent()
        
     else if firstBody.node?.name == "Player" && secondBody.node?.name 
== "Wall1" 
        if isAlive 
            playerDied()
            firstBody.node?.removeFromParent()
        
     else if firstBody.node?.name == "Player" && secondBody.node?.name 
== "Wall2" 
        if isAlive 
            playerDied()
            firstBody.node?.removeFromParent()
        
    




func initialize() 

    gameStarted = false
    isAlive = false
    score = 0

    physicsWorld.contactDelegate = self

    createInstructions()
    createPlayer()
    createBackgrounds()
    createWall1()
    createWall2()
    createLabel()


func createInstructions() 
    press = SKSpriteNode(imageNamed: "Press")
    press.anchorPoint = CGPoint(x: 0.5, y: 0.5)
    press.position = CGPoint(x: 0, y: 100)
    press.setScale(0.4)
    press.zPosition = 10
    self.addChild(press)


func createPlayer() 
    player = Player(imageNamed: "Player")
    player.initialize()
    player.position = CGPoint(x: 0, y: 0)
    self.addChild(player)


func createBackgrounds() 
    for i in 0...2 
        let bg = SKSpriteNode(imageNamed: "BG")
        bg.name = "BG"
        bg.anchorPoint = CGPoint(x: 0.5, y: 0.5 )
        bg.position = CGPoint(x: 0, y: CGFloat(i) * bg.size.height)
        self.addChild(bg)
    


func createWall1() 
    for i in 0...2 
        let wall = SKSpriteNode(imageNamed: "Wall1")
        wall.name = "Wall1"
        wall.zPosition = 4
        wall.anchorPoint = CGPoint(x: 0.5, y: 0.5)
        wall.position = CGPoint(x: -(self.frame.size.width / 2) + 23, 
y: CGFloat(i) * wall.size.height)
        wall.physicsBody = SKPhysicsBody(rectangleOf: CGSize(width: 
wall.size.width - 30, height: wall.size.height))
        wall.physicsBody?.affectedByGravity = false
        wall.physicsBody?.isDynamic = false
        wall.physicsBody?.categoryBitMask = ColliderType.Wall1
        self.addChild(wall)
    


func createWall2() 
    for i in 0...2 
        let wall = SKSpriteNode(imageNamed: "Wall2")
        wall.name = "Wall2"
        wall.zPosition = 4
        wall.anchorPoint = CGPoint(x: 0.5, y: 0.5)
        wall.position = CGPoint(x: (self.frame.size.width / 2) - 23, y: 
CGFloat(i) * wall.size.height)
        wall.physicsBody = SKPhysicsBody(rectangleOf: CGSize(width: 
wall.size.width - 30, height: wall.size.height))
        wall.physicsBody?.affectedByGravity = false
        wall.physicsBody?.isDynamic = false
        wall.physicsBody?.categoryBitMask = ColliderType.Wall2
        self.addChild(wall)
    


func moveBackgrounds() 

    enumerateChildNodes(withName: "BG", using: (
        (node, error) in

        node.position.y -= 5

        if node.position.y < -(self.frame.height) 
            node.position.y += self.frame.height * 3
        

    ))

    enumerateChildNodes(withName: "Wall1", using: (
        (node, error) in

        node.position.y -= 5

        if node.position.y < -(self.frame.height) 
            node.position.y += self.frame.height * 3
        

    ))

    enumerateChildNodes(withName: "Wall2", using: (
        (node, error) in

        node.position.y -= 5

        if node.position.y < -(self.frame.height) 
            node.position.y += self.frame.height * 3
        

    ))


func createSwans() 

    swan = SKSpriteNode(imageNamed: "Swan")
    swan.name = "Swan"

    swan.anchorPoint = CGPoint(x: 0.5, y: 0.5)
    swan.position = CGPoint(x: 0, y: 300)
    swan.zPosition = 5
    swan.physicsBody = SKPhysicsBody(rectangleOf: CGSize(width: 
swan.size.width - 50, height: swan.size.height - 50))
    swan.physicsBody?.categoryBitMask = ColliderType.Swan
    swan.physicsBody?.affectedByGravity = false
    swan.physicsBody?.isDynamic = false

    swan.position.y = self.size.height + 100
    swan.position.x = CGFloat.randomBetweenNumbers(firstNum: -255, 
secondNum: 255)

    self.addChild(swan)

    let destination = self.frame.height * 2
    let move = SKAction.moveTo(y: -destination, duration: 
TimeInterval(10))
    let remove = SKAction.removeFromParent()

    swan.run(SKAction.sequence([move, remove]), withKey: "MoveSwans")




func spawnSwans() 

    let spawn = SKAction.run( () -> Void in
        self.createSwans()
    )

    let delay = SKAction.wait(forDuration: TimeInterval(0.8))
    let sequence = SKAction.sequence([spawn, delay])

    self.run(SKAction.repeatForever(sequence), withKey: "SpawnSwans")


func createBranches() 
    let branch = SKSpriteNode(imageNamed: "Branch")
    branch.name = "Branch"

    branch.anchorPoint = CGPoint(x: 0.5, y: 0.5)
    branch.position = CGPoint(x: 0, y: 500)
    branch.zPosition = 4
    branch.physicsBody = SKPhysicsBody(rectangleOf: CGSize(width: 
branch.size.width - 30, height: branch.size.height - 30))
    branch.physicsBody?.categoryBitMask = ColliderType.Branch
    branch.physicsBody?.affectedByGravity = false
    branch.physicsBody?.isDynamic = false
    branch.position.y = self.frame.height + 500
    branch.position.x = CGFloat.randomBetweenNumbers(firstNum: -228, 
secondNum: 228)

    self.addChild(branch)

    let destination = self.size.height / 1.5
    let move = SKAction.moveTo(y: -destination, duration: 
TimeInterval(10))
    let remove = SKAction.removeFromParent()

    branch.run(SKAction.sequence([move, remove]), withKey: 
"MoveBranches")


func spawnBranches() 
    let spawn = SKAction.run( () -> Void in
        self.createBranches()
    )

    let delay = SKAction.wait(forDuration: TimeInterval(1.5))
    let sequence = SKAction.sequence([spawn, delay])

    self.run(SKAction.repeatForever(sequence), withKey: 
"SpawnBranches")


func createEggs() 

    let egg = SKSpriteNode(imageNamed: "Egg")
    egg.name = "Egg"

    egg.anchorPoint = CGPoint(x: 0.5, y: 0.5)
    egg.position = CGPoint(x: 0, y: 500)
    egg.zPosition = 3
    egg.setScale(0.3)
    egg.physicsBody = SKPhysicsBody(circleOfRadius: egg.size.height / 2 
- 5)
    egg.physicsBody?.categoryBitMask = ColliderType.Score
    egg.physicsBody?.collisionBitMask = 0
    egg.physicsBody?.affectedByGravity = false
    egg.physicsBody?.restitution = 0
    egg.physicsBody?.isDynamic = false

    egg.position.y = self.frame.height + 500
    egg.position.x = CGFloat.randomBetweenNumbers(firstNum: -250, 
secondNum: 250)

    self.addChild(egg)

    let destination = self.size.height / 2
    let move = SKAction.moveTo(y: -destination, duration: 
TimeInterval(10))
    let remove = SKAction.removeFromParent()

    egg.run(SKAction.sequence([move, remove]), withKey: "MoveEggs")


//testa göra likadant med createCrocodiles och spawnCrocodiles som du 
gör med createScore och spawnScore
func spawnEggs() 

    let spawn = SKAction.run( () -> Void in
        self.createEggs()
    )

    let delay = SKAction.wait(forDuration: TimeInterval(3))
    let sequence = SKAction.sequence([spawn, delay])

    self.run(SKAction.repeatForever(sequence), withKey: "SpawnEggs")


func createLabel() 
    scoreLabel.zPosition = 7
    scoreLabel.position = CGPoint(x: 0, y: -300)
    scoreLabel.fontName = "Bradley Hand"
    scoreLabel.fontSize = 120
    scoreLabel.text = "0"
    self.addChild(scoreLabel)


func incrementScore() 
    score += 1
    scoreLabel.text = String(score)


func playerDied() 

    self.removeAction(forKey: "SpawnSwans")
    self.removeAction(forKey: "SpawnBranches")
    self.removeAction(forKey: "SpawnEggs")

    for child in children 
        if child.name == "Swan" 
            child.removeAction(forKey: "MoveSwans")
         else if child.name == "Branch" 
            child.removeAction(forKey: "MoveBranches")
         else if child.name == "Egg" 
            child.removeAction(forKey: "MoveEggs")
        
    

    isAlive = false

    let highscore = GameManager.instance.getHighscore()

    if highscore < score 
        GameManager.instance.setHighscore(highscore: score)
    

    let retry = SKSpriteNode(imageNamed: "Retry")
    let quit = SKSpriteNode(imageNamed: "Quit")

    retry.name = "Retry"
    retry.anchorPoint = CGPoint(x: 0.5, y: 0.5)
    retry.position = CGPoint(x: -150, y: 0)
    retry.zPosition = 8
    retry.setScale(0)

    quit.name = "Quit"
    quit.anchorPoint = CGPoint(x: 0.5, y: 0.5)
    quit.position = CGPoint(x: 150, y: 0)
    quit.zPosition = 8
    quit.setScale(0)

    let scaleUp = SKAction.scale(to: 1, duration: TimeInterval(0.5))

    retry.run(scaleUp)
    quit.run(scaleUp)

    self.addChild(retry)
    self.addChild(quit)




func moveNodeToLocation() 
    // Compute vector components in direction of the touch
    var dx = location.x - player.position.x
    // How fast to move the node. Adjust this as needed
    let speed:CGFloat = 0.08
    // Scale vector
    dx = dx * speed
    player.position = CGPoint(x:player.position.x+dx, y: 0)



【问题讨论】:

【参考方案1】:

我在这里看到了您的问题,所以如果我理解正确,您希望能够在不与您的播放器发生碰撞的情况下拾取电源或硬币。

在这种情况下,您只需要从玩家的碰撞位掩码中删除您不想碰撞的项目,反之亦然,因为这决定了哪些项目与哪些项目发生了碰撞。您仍然可以使用 contactBitMask 检查与它的联系。这里还有一个详细解释collisionBitMasks的链接How does collisionBitMask work? Swift/SpriteKit!

【讨论】:

是的,这正是我想要的。唯一的问题是我的玩家collisionBitMask中甚至没有“硬币”,只有在contactBitMask中。我还尝试在玩家和硬币中放入“.physicsbody.collisionBitMask = 0”以匹配它们。但是我仍然会遇到那个小碰撞,即使它不是那么糟糕它仍然很烦人。

以上是关于当玩家与其他 SKSpriteNode 碰撞时摆脱“反弹”的主要内容,如果未能解决你的问题,请参考以下文章

当玩家节点与另一个节点碰撞时游戏结束,当玩家与边界碰撞时游戏才应该结束

SKSpriteNode 无法检测到与两个 bodyWithEdgeLoopF​​romPath 主体的碰撞

碰撞检测导致颜色检测? [复制]

skemitternode 与 skspritenode 碰撞

单个 SKSpriteNode 上的多个碰撞矩形

我在 SKSpriteNode 中使用碰撞检测时遇到问题