Swift 物理致命错误:当两个字符发生碰撞时,在展开可选值时意外发现 nil

Posted

技术标签:

【中文标题】Swift 物理致命错误:当两个字符发生碰撞时,在展开可选值时意外发现 nil【英文标题】:Swift Physics-fatal error: unexpectedly found nil while unwrapping an Optional value when two characters collide 【发布时间】:2015-07-18 00:14:35 【问题描述】:

游戏背景:基本上你控制一个左右移动的角色并试图躲避掉落的方块。产生了三个玩家。一个在屏幕中间,两个正好 size.width 远离两边的中间播放器。

此错误仅在与 playerRight 或 playerLeft 或 Player 碰撞和掉落的方块的 10 次左右发生一次。正如您在下面的屏幕截图中看到的那样,玩家在坠落时似乎没有接触到坠落的方块。

错误代码和截图:

fatal error: unexpectedly found nil while unwrapping an Optional value
(lldb) 

Thread 1: EXC_BAD_INSTRUCTION (code=EXC_l386_INVOP, subcode=0x0)

游戏场景如何停止

我认为这与我如何构建“无限”水平滚动播放器有关。基本上我有三个单独的字符,一旦中间字符超过 size.width 或低于 0,他在屏幕上的位置就会改变到屏幕的另一侧,基本上使其无限。也许玩家被传送到一个块内,它给出了一个零错误。不太确定,但似乎与此有关。无论如何,这是来自 GameScene 的相关代码。

import SpriteKit
import Foundation
import UIKit

//Collisions
 struct PhysicsCategory 
static let Enemy : UInt32 = 1
static let Player : UInt32 = 2
static let PlayerRight : UInt32 = 3
static let PlayerLeft : UInt32 = 4
static let EnemyRight : UInt32 = 5



var transition:SKTransition = SKTransition.fadeWithDuration(0.5)



class GameScene: SKScene, SKPhysicsContactDelegate 


//Highscore Variable
var Highscore = Int()

//Score
var Score : Int = 0
var ScoreLabel = UILabel()


//Main Character
var Player = SKSpriteNode(imageNamed: "mainPlayer.png")

//Right-far character
var PlayerRight = SKSpriteNode(imageNamed: "mainPlayer.png")

//Left-far character
var PlayerLeft = SKSpriteNode(imageNamed: "mainPlayer.png")


//Holding vs Tapping Movement of Player
var isTouching = false

var touchXPosition:CGFloat = 0


override func didMoveToView(view: SKView) 
    /* Setup your scene here */

    //Highscore
    var HighscoreDefault = NSUserDefaults.standardUserDefaults()

    if (HighscoreDefault.valueForKey("Highscore") != nil) 
        Highscore = HighscoreDefault.valueForKey("Highscore") as! NSInteger
    
    else 
        Highscore = 0
    




    //Collisions/Physics
    physicsWorld.contactDelegate = self

    //Background Color
    scene?.backgroundColor = UIColor.blackColor()

    //Spawn timer for enemy blocks
    var timer = NSTimer.scheduledTimerWithTimeInterval(0.4, target: self, selector: Selector("spawnEnemies"), userInfo: nil, repeats: true)

    //Timer for keeping score
    var scoretimer = NSTimer.scheduledTimerWithTimeInterval(0.1, target: self, selector: Selector("scoreCounter"), userInfo: nil, repeats: true)

    //Player coordinates
    Player.position.x = size.width * 0.5
    Player.position.y = size.width * 0.11 / 2


    //Setting Player Sizes
    Player.size.width = size.width * 0.11
    Player.size.height = size.width * 0.11
    PlayerRight.size.width = size.width * 0.11
    PlayerRight.size.height = size.width * 0.11
    PlayerLeft.size.width = size.width * 0.11
    PlayerLeft.size.height = size.width * 0.11


    //Initial position of player
    Player.position = CGPoint(x: Player.position.x, y: Player.position.y)
    //Initial position of far-right player
    PlayerRight.position = CGPoint(x: Player.position.x + size.width, y: Player.position.y)
    //Initial position of far-left player
    PlayerLeft.position = CGPoint(x: Player.position.x - size.width, y: Player.position.y)


    //Adding Physics/Collisions to Player
    Player.physicsBody = SKPhysicsBody (rectangleOfSize: Player.size)
    Player.physicsBody?.affectedByGravity = false
    Player.physicsBody?.categoryBitMask = PhysicsCategory.Player
    Player.physicsBody?.contactTestBitMask = PhysicsCategory.Enemy
    Player.physicsBody?.dynamic = false

    //Adding Physics/Collisions to PlayerRight
    PlayerRight.physicsBody = SKPhysicsBody (rectangleOfSize: PlayerRight.size)
    PlayerRight.physicsBody?.affectedByGravity = false
    PlayerRight.physicsBody?.categoryBitMask = PhysicsCategory.PlayerRight
    PlayerRight.physicsBody?.contactTestBitMask = PhysicsCategory.Enemy
    PlayerRight.physicsBody?.dynamic = false

    //Adding Physics/Collisions to PlayerLeft
    PlayerLeft.physicsBody = SKPhysicsBody (rectangleOfSize: PlayerRight.size)
    PlayerLeft.physicsBody?.affectedByGravity = false
    PlayerLeft.physicsBody?.categoryBitMask = PhysicsCategory.PlayerLeft
    PlayerLeft.physicsBody?.contactTestBitMask = PhysicsCategory.Enemy
    PlayerLeft.physicsBody?.dynamic = false

    //Making Players visible
    self.addChild(Player)
    self.addChild(PlayerRight)
    self.addChild(PlayerLeft)

    //Making Score Visible
    ScoreLabel.text = "\(Score)"
    ScoreLabel = UILabel(frame: CGRect(x: 0, y: 0, width: 100, height: 20))
    ScoreLabel.font = UIFont(name: ScoreLabel.font.fontName, size:20)
    ScoreLabel.textColor = UIColor.whiteColor()
    self.view?.addSubview(ScoreLabel)


func scoreCounter() 
    //Setting score
    Score += 1
    ScoreLabel.text = "\(Score)"



func didBeginContact(contact: SKPhysicsContact) 
    var firstBody : SKPhysicsBody = contact.bodyA
    var secondBody : SKPhysicsBody = contact.bodyB

    //Checking for Player to enemy collisions
    if ((firstBody.categoryBitMask == PhysicsCategory.Enemy) && (secondBody.categoryBitMask == PhysicsCategory.Player))
        CollisionWithEnemy(firstBody.node as! SKShapeNode, Player: secondBody.node as! SKSpriteNode)

    
    else if (firstBody.categoryBitMask == PhysicsCategory.Player) && (secondBody.categoryBitMask == PhysicsCategory.Enemy) 
        CollisionWithEnemy2(firstBody.node as! SKSpriteNode, Enemy: secondBody.node as! SKShapeNode)
    
    //Checking for PlayerRight to enemy collisions
    if ((firstBody.categoryBitMask == PhysicsCategory.Enemy) && (secondBody.categoryBitMask == PhysicsCategory.PlayerRight))
        CollisionWithEnemy(firstBody.node as! SKShapeNode, Player: secondBody.node as! SKSpriteNode)

    
    else if (firstBody.categoryBitMask == PhysicsCategory.PlayerRight) && (secondBody.categoryBitMask == PhysicsCategory.Enemy) 
        CollisionWithEnemy2(firstBody.node as! SKSpriteNode, Enemy: secondBody.node as! SKShapeNode)
    
    //Checking for PlayerLeft to enemy collisions
    if ((firstBody.categoryBitMask == PhysicsCategory.Enemy) && (secondBody.categoryBitMask == PhysicsCategory.PlayerLeft))
        CollisionWithEnemy(firstBody.node as! SKShapeNode, Player: secondBody.node as! SKSpriteNode)

    
    else if (firstBody.categoryBitMask == PhysicsCategory.PlayerLeft) && (secondBody.categoryBitMask == PhysicsCategory.Enemy) 
        CollisionWithEnemy2(firstBody.node as! SKSpriteNode, Enemy: secondBody.node as! SKShapeNode)
    



func CollisionWithEnemy(Enemy: SKShapeNode, Player: SKSpriteNode) 

    //Highscore
    var ScoreDefault = NSUserDefaults.standardUserDefaults()
    ScoreDefault.setValue(Score, forKey: "Score")
    ScoreDefault.synchronize()


    if (Score > Highscore) 
        var HighscoreDefault = NSUserDefaults.standardUserDefaults()
        HighscoreDefault.setValue(Score, forKey: "Highscore")
    

    //var gameOver:SKScene = GameOverScene(size: self.size)
    //ScoreLabel.removeFromSuperview()
    Enemy.removeFromParent()
    //Player.removeFromParent()
    //self.view?.presentScene(gameOver, transition: transition)




func CollisionWithEnemy2(Player: SKSpriteNode, Enemy: SKShapeNode) 

    //Highscore
    var ScoreDefault = NSUserDefaults.standardUserDefaults()
    ScoreDefault.setValue(Score, forKey: "Score")
    ScoreDefault.synchronize()

    if (Score > Highscore) 
        var HighscoreDefault = NSUserDefaults.standardUserDefaults()
        HighscoreDefault.setValue(Score, forKey: "Highscore")
    

    //var gameOver:SKScene = GameOverScene(size: self.size)
    //ScoreLabel.removeFromSuperview()
    Enemy.removeFromParent()
    //Player.removeFromParent()
    //self.view?.presentScene(gameOver, transition: transition)



override func touchesBegan(touches: Set<NSObject>, withEvent event: UIEvent) 
    /* Called when a touch begins */

    isTouching = true

    for touch in (touches as! Set<UITouch>) 
        let location = touch.locationInNode(self)

        touchXPosition = location.x

    



override func touchesEnded(touches: Set<NSObject>, withEvent event: UIEvent) 
    isTouching = false



func spawnEnemies() 
    //Randomizing width of blocks
    var blockWidth = Int(arc4random_uniform(UInt32(size.width / 3)) + UInt32(size.width / 5))

    //Min and Max position of blocks
    var minPosition : UInt32 = UInt32(blockWidth / 2)
    var maxPosition : UInt32 = UInt32(size.width - CGFloat(blockWidth / 2))

    //Randomizing Block Position
    var blockXPosition = arc4random_uniform(maxPosition - minPosition) + minPosition

    //Making Blocks
    var Enemy = SKShapeNode(rectOfSize: CGSize(width: blockWidth, height: 5))
    Enemy.position = CGPointMake (CGFloat(blockXPosition),  CGFloat(size.height+50))

    //Coloring Blocks
    Enemy.fillColor = SKColor.whiteColor()

    //Moving Blocks
    let action = SKAction.moveToY(-50, duration: 2.5)

    //Removing blocks once off screen
    let actionDone = SKAction.removeFromParent()

    //Running the above actions
    Enemy.runAction(SKAction.sequence([action, actionDone]))


    //Physics/Collisions
    Enemy.physicsBody = SKPhysicsBody(rectangleOfSize: CGSize (width: blockWidth, height: 1))
    Enemy.physicsBody?.categoryBitMask = PhysicsCategory.Enemy
    Enemy.physicsBody?.affectedByGravity = false
    Enemy.physicsBody?.dynamic = true

    //Adding enemy to scene
    self.addChild(Enemy)



override func update(currentTime: CFTimeInterval) 
    /* Called before each frame is rendered */

    var offsetLeft = 0 - (Player.position.x - 25)


    Player.position = CGPoint(x: Player.position.x, y: Player.position.y)

    PlayerRight.position = CGPoint(x: Player.position.x + size.width, y: Player.position.y)

    PlayerLeft.position = CGPoint(x: Player.position.x - size.width, y: Player.position.y)

    if isTouching 
        if touchXPosition > self.size.width / 2 
            // move character to the right.
            Player.position.x += 10

        
        if touchXPosition < self.size.width / 2 
            // move character to the left.
            Player.position.x -= 10

        
    


    if Player.position.x < 0 
        Player.position.x = size.width
    
    if Player.position.x > size.width 
        Player.position.x = 0
    


所有内容都得到了很好的评论,但如果您对什么有任何疑问,请告诉我。对此的任何帮助将不胜感激!

【问题讨论】:

请不要粘贴代码、错误消息等的屏幕截图。粘贴代码。粘贴错误信息。解释问题。不要让人们处理代码图像。 我的坏人我会相应地更新它。 请不要“删除”您的问题 - 如果您找到解决方案,您可以回答自己的问题 【参考方案1】:
static let PlayerRight : UInt32 = 3 //00000000000000000000000000000100
static let PlayerLeft : UInt32 = 4  //00000000000000000000000000001000
static let EnemyRight : UInt32 = 5  //00000000000000000000000000010000

真的相信 3 是二进制 100 并且 4 是二进制 1000(等等)?因为如果你这样做了,如果你需要它是真实的,那么如果你尝试使用这些值作为实际的位掩码,你以后会遇到很大的麻烦。

【讨论】:

老实说,我不知道,但我认为这对我的代码无关紧要。我正在看一个教程,这就是那个人说的,如果他们不是,他们会很抱歉。我会修复代码,尽管这并不重要,因为它们是 cmets。感谢您指出这一点。 @Darkstar 我猜你错过了1 &lt;&lt; 3 1 &lt;&lt; 4 等等,那么它(几乎)是正确的 是的,理解你一起复制的代码并不重要。只需将它置换,直到它“有点工作”,然后我们将完成剩下的工作。如果没有,只需将其发布在商店中即可。 :)

以上是关于Swift 物理致命错误:当两个字符发生碰撞时,在展开可选值时意外发现 nil的主要内容,如果未能解决你的问题,请参考以下文章

玩家和敌人之间的碰撞错误

Swift 致命错误:数组索引超出范围

视图的图像在 iOS Swift 中不会相互碰撞?

未检测到 spritekit 物理碰撞

来自纹理 swift SKphysicsbody 的碰撞问题

游戏开发中的物理介绍