微信公众号搜"智元新知"关注
微信扫一扫可直接关注哦!

Swift / SpriteKit Collision产生不同的结果

如何解决Swift / SpriteKit Collision产生不同的结果

我有一个小测试项目(我的第一个使用Swift / XCode)是为了使我脱离HTML5和Canvas进行游戏制作。

代码可以编译并正常运行。我将iPhone用作测试设备,而不是内置的模拟器。

问题的症状是

  1. 从玩家的船上反复发射的激光偶尔会绕着外星人弯曲
  2. 从节点中拔出的名称显示为其名称,而不是我在创建时为其分配的名称

在某些情况下,碰撞可以正常进行,并产生外星爆炸,并从场景中移除外星精灵节点。

我将外星节点命名为“ alien”,将激光节点命名为“ laser”。 两者的contactTestBitMask设置为相同的值。

这是我的GameScene.swift代码

import SpriteKit

class GameScene: SKScene,SKPhysicsContactDelegate {
    
 
    var lastUpdateTime: TimeInterval = 0
    var delta: TimeInterval = 0
    
    var sp_player: SKSpriteNode!
    var stars: SKSpriteNode!
    var deeperstars: SKSpriteNode!
    var laser: SKSpriteNode!
    var alien: SKSpriteNode!
    var explosionSplat1: SKSpriteNode!
    
    var playerscore: UInt32!
    
    struct PhysicsCategory {
        static let base:UInt32 = 0x1 << 0
        static let alien:UInt32 = 0x1 << 1
        static let laser:UInt32 = 0x1 << 2
        static let player:UInt32 = 0x1 << 3
    }
    
    
    override func didMove(to view: SKView) { // called when the scene is presented into view (happens only once)

        playerscore = 0
        
        physicsWorld.contactDelegate = self
        physicsWorld.gravity = .zero
        
        // BACKGROUND
        backgroundColor = UIColor(red: 0/255,green: 0/255,blue: 48/255,alpha: 1.0)
        print("Background color is set")
        
        // WRAP THE STARFIELDS
        // Front most layer of stars
        let starsTexture = SKTexture(imageNamed: "stars.png")
        let bgAnimation = SKAction.move(by: CGVector(dx: 0,dy: -starsTexture.size().height),duration: 5)
        let bgReset = SKAction.move(by: CGVector(dx: 0,dy: starsTexture.size().height),duration: 0)
        let bgConstantMotion = SKAction.repeatForever(SKAction.sequence([bgAnimation,bgReset]))
        
        // Back layer of slower stars
        let deeperStarsTexture = SKTexture(imageNamed: "stars-deeper.png")
        let deeperStarsbgAnimation = SKAction.move(by: CGVector(dx: 0,dy: -deeperStarsTexture.size().height),duration: 8)
        let deeperStarsbgReset = SKAction.move(by: CGVector(dx: 0,dy: deeperStarsTexture.size().height),duration: 0)
        let deeperStarsbgConstantMotion = SKAction.repeatForever(SKAction.sequence([deeperStarsbgAnimation,deeperStarsbgReset]))
        
        var i: CGFloat = 0
        while i < 3
        {
            stars = SKSpriteNode(texture: starsTexture)
            stars.position = CGPoint(x: frame.midX,y: starsTexture.size().height * i)
            stars.size.height = frame.height
            stars.run(bgConstantMotion)
            stars.zPosition = -1
            addChild(stars)

            deeperstars = SKSpriteNode(texture: deeperStarsTexture)
            deeperstars.position = CGPoint(x: frame.midX,y: deeperStarsTexture.size().height * i)
            deeperstars.size.height = frame.height
            deeperstars.run(deeperStarsbgConstantMotion)
            deeperstars.zPosition = -1
            addChild(deeperstars)

            i += 1
        }
        

        // PLAYER
        let playerTexture1 = SKTexture(imageNamed: "player-1.png")
        let playerTexture2 = SKTexture(imageNamed: "player-2.png")
        let playerAnimation = SKAction.animate(with: [playerTexture1,playerTexture2],timePerFrame: 0.2)
        let constantAnimation = SKAction.repeatForever(playerAnimation)

        sp_player = SKSpriteNode(texture: playerTexture1)
        sp_player.position = CGPoint(x: frame.midX,y: (sp_player.size.height * 2))
        sp_player.physicsBody = SKPhysicsBody(rectangleOf: sp_player.size)
        sp_player.physicsBody!.isDynamic = false
        sp_player.name = "player"
        sp_player.run(constantAnimation)
        addChild(sp_player)
        
        // PLACE ALIENS
        let alienTexture1 = SKTexture(imageNamed: "alien-1a.png")
        let alienTexture2 = SKTexture(imageNamed: "alien-1b.png")
        let alienAnimation = SKAction.animate(with: [alienTexture1,alienTexture2],timePerFrame: 0.4)
        let constantAlienAnimation = SKAction.repeatForever(alienAnimation)

        var x: CGFloat = 0,y: CGFloat = 0
        
        while y < 6
        {
            while x < 6
            {
                alien = SKSpriteNode(texture: alienTexture1)

                alien.position = CGPoint(x: 32 + (x * alien.size.width),y: (frame.size.height - (alien.size.height * 1.5) - (alien.size.height * y)))
                print("Setting y to \(frame.size.height - (alien.size.height * y))")
                alien.physicsBody = SKPhysicsBody(rectangleOf: CGSize(width: alien.size.width,height: alien.size.height))
                alien.physicsBody!.isDynamic = false
                alien.name = "alien"
                alien.physicsBody!.contactTestBitMask = PhysicsCategory.laser
                alien.run(constantAlienAnimation)
                addChild(alien)
                x += 1
            }
            y += 1
            x = 0
        }
        
        print("Sprites added to scene")

        spawnLasers()
        
    }
    
    func spawnLasers()
    {
        let delay1 = SKAction.wait(forDuration: 0.5)
        let spawn = SKAction.run {
            let laserTexture = SKTexture(imageNamed: "laser-1.png")
            self.laser = SKSpriteNode(texture: laserTexture)

            self.laser.position = CGPoint(x: self.sp_player.position.x,y: self.sp_player.position.y + self.sp_player.size.height)
            self.laser.physicsBody = SKPhysicsBody(rectangleOf: CGSize(width: self.laser.size.width,height: self.laser.size.height))
            self.laser.physicsBody!.isDynamic = true
            self.laser.physicsBody!.lineardamping = 0
            self.laser.physicsBody!.allowsRotation = false
            self.laser.physicsBody!.contactTestBitMask = PhysicsCategory.laser
            self.laser.name = "laser"

            self.addChild(self.laser)
            
            let shoot = SKAction.moveto(y: self.frame.size.height,duration: 1)
            let killLaser = SKAction.removeFromParent()
            let handleLaser = SKAction.sequence([shoot,killLaser])
            self.laser.run(handleLaser)
        }

        let action = SKAction.sequence([delay1,spawn])
        let constantLasers = SKAction.repeatForever(action)
        self.run(constantLasers)
    }
    
    func didBegin(_ contact: SKPhysicsContact) {
        var check: UInt32 = 0

        if contact.bodyA.node != nil
        {
            check += 1
        }

        if contact.bodyB.node != nil
        {
            check += 1
        }

        if check == 2
        {
            if contact.bodyA.node!.name == "alien" && contact.bodyB.node!.name == "laser"
            {
                // EXPLOSION
                let explosionSplatTexture1 = SKTexture(imageNamed: "explosion-1a.png")
                let explosionSplatTexture2 = SKTexture(imageNamed: "explosion-1b.png")
                let explosionSplatTexture3 = SKTexture(imageNamed: "explosion-1c.png")
                let explosionSplatTexture4 = SKTexture(imageNamed: "explosion-1d.png")
                let explosionSplatanimation = SKAction.animate(with: [explosionSplatTexture1,explosionSplatTexture2,explosionSplatTexture3,explosionSplatTexture4],timePerFrame: 0.1)
                let killExplosion = SKAction.removeFromParent()
                let explosionSequence = SKAction.sequence([explosionSplatanimation,killExplosion])
                explosionSplat1 = SKSpriteNode(texture: explosionSplatTexture1)
                explosionSplat1.name = "explosion"
                explosionSplat1.position = CGPoint(x: contact.bodyA.node!.position.x,y: contact.bodyA.node!.position.y)
                addChild(explosionSplat1)
                explosionSplat1.run(explosionSequence)
                
                self.playerscore += 1
                print("score: \(self.playerscore!)")

                contact.bodyA.node?.removeFromParent()
                print("Alien named \(contact.bodyA.node?.name ?? "defaultAlienName") from scene")
                contact.bodyB.node?.removeFromParent()
                print("Laser named \(contact.bodyB.node?.name ?? "defaultLaserName") from scene")

            }
        }
    }
    
    func didEnd(_ contact: SKPhysicsContact) {
        //print("Contact ended between \(contact.bodyA) and \(contact.bodyB)")
    }
    
    override func touchesBegan(_ touches: Set<UITouch>,with event: UIEvent?)
    {
//        if let touch = touches.first {
//            let position = touch.location(in: view)
//            storedTouch = position
//        }

    }
    
    override func touchesMoved(_ touches: Set<UITouch>,with event: UIEvent?)
    {
        if let touch = touches.first {
            let position = touch.location(in: view)

            var playerpos: CGPoint!
            playerpos = sp_player.position

            let pl_move = SKAction.move(to: CGPoint(x: position.x,y: playerpos.y),duration: 0.1)
            sp_player.run(pl_move)
        }
    }

    override func touchesEnded(_ touches: Set<UITouch>,with event: UIEvent?)
    {
        /*
            Need to figure out how to use storedTouch properly
            to move player relative to the screen touch co-ordinates
         */
//        if let touch = touches.first {
//            let position = touch.location(in: view)
//        }
    }
    
    override func update(_ currentTime: TimeInterval) {
        // Called before each frame is rendered

        if (lastUpdateTime > 0)
        {
            delta = currentTime - lastUpdateTime
        } else {
            delta = 0
        }
        lastUpdateTime = currentTime
        
    }
}

游戏运行时会显示以下屏幕:

enter image description here

您可以在此处看到意外的激光行为: {{3}}

在诊断中,我从碰撞函数获得以下输出

score: 1
Alien named defaultAlienName removed from scene
Laser named laser removed from scene
score: 2
Alien named defaultAlienName removed from scene
Laser named laser removed from scene
score: 3
Alien named defaultAlienName removed from scene
Laser named laser removed from scene
score: 4
Alien named defaultAlienName removed from scene
Laser named defaultLaserName removed from scene
score: 5
Alien named defaultAlienName removed from scene
Laser named defaultLaserName removed from scene
score: 6
Alien named defaultAlienName removed from scene
Laser named defaultLaserName removed from scene

这很可能是我完全不了解可选选项以及碰撞实际上是如何工作的。如有任何见解,我将不胜感激。

解决方法

在您的外来循环以及spawnLasers()中,您没有为子画面节点提供实际的PhysicsBody类别。为了让子画面能够检测彼此之间的联系,它们需要一个类别名称和一个联系人名称。

因此,在您的while循环(构建外星人)中,您需要具备以下条件:

alien.physicsBody!.categoryBitMask = PhysicsCategory.alien
alien.physicsBody!.contactTestBitMask = PhysicsCategory.laser

在spawnLasers()中,您希望添加此内容:

self.laser.physicsBody!.categoryBitMask = PhysicsCategory.laser

,然后将contactTestBitMask更改为Alien,而不是Laser:

self.laser.physicsBody!.contactTestBitMask = PhysicsCategory.alien

希望您能看到外星人类别想知道何时激光触摸,而激光类别想知道外星人何时触摸。

要在视觉上帮助您,请打开“显示物理”选项,这样您可以查看要处理的实际物理物体。 为此,请在您的GameViewController(或类似版本)中找到:

showsFPS = true
showsNodeCount = true

您要添加以下内容:

showsPhysics = true

这将有助于在屏幕上查看实际的物理物体。

在:

func didBegin(_ contact: SKPhysicsContact)

您仅测试BodyA是外星人,而BodyB是激光。 这是接触测试:

if contact.bodyA.node!.name == "alien" && contact.bodyB.node!.name == "laser"

我相信BodyA可以是激光,而BodyB可以是外星人。基本上,物理引擎的接触事件可以是“ A击中B”或“ B击中A”。

因此,一种快速而肮脏的解决方案是在当前语句的下面创建另一个if语句,但是更改主体名称,因此:

if contact.bodyA.node!.name == "laser" && contact.bodyB.node!.name == "alien" {

并从现有的if语句复制代码,并更改两个print语句。 这不是理想的方法,但是希望当您整理它时,您将了解物理接触的作用。

我希望一旦您实现了以上所述,您的状况就会好得多。

版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。