6

I have a SpriteKit game I am building and I am loading a level from a multidimensional array. The loadlevel function works the first time. It does fail if I do a println of the physicsBody above the physicsBody assignment (after the initialization of the physicBody). When I remove all the tiles with removeChildrenInArray the secondtime I run load level it throws an error saying fatal error: unexpectedly found nil while unwrapping an Optional and it points to line right below the println below. And the println indicates that it is the physicsBody that is nil. In my mind there is no reason a freshly initialize PhysicsBody should ever be nil. The println prints physicsBody nil. I have no idea why the physicsBody would be nil. I am just trying to reset the level by removing all block nodes and adding new ones in the original place according to the level map.

func loadLevel() {
    var levels = Levels().data
    var frameSize = view.frame.size
    var thisLevel = levels[currentLevel]
    println("running load level")
    for (rowIndex,row) in enumerate(thisLevel) {
        for (colIndex,col) in enumerate(row) {
            if col == 4 {
                continue
            }
            println("COL: \(col)")
            var tile = SKSpriteNode(texture: SKTexture(imageNamed: "brick_\(tileMap[col])"))
            tile.name = "tile_\(rowIndex)_\(colIndex)"
            tile.position.y = frameSize.height - (tile.size.height * CGFloat(rowIndex)) - (tile.size.height/2)
            tile.position.x = tile.size.width  * CGFloat(colIndex) + (tile.size.width/2)
            var physicsBody = SKPhysicsBody(rectangleOfSize: tile.size)
            tile.physicsBody = physicsBody
            tile.physicsBody.affectedByGravity = false
            tile.physicsBody.categoryBitMask = ColliderType.Block.toRaw()
            tile.physicsBody.contactTestBitMask = ColliderType.Ball.toRaw()
            tile.physicsBody.collisionBitMask = ColliderType.Ball.toRaw()
            scene.addChild(tile)
            tileCount++
        }
    }
}

Here is my ColliderType

enum ColliderType:UInt32 {
    case Paddle = 1
    case Block = 2
    case Wall = 3
    case Ball = 4
}

This is my reset function contents:

func reset() {
    tileCount = 0
    var removeTiles = [SKSpriteNode]()
    // remove all the tiles
    for child in scene.children {
        var a_tile = child as SKSpriteNode
        if a_tile.name.hasPrefix("tile_") {
            a_tile.removeFromParent()
            a_tile.name = ""
            removeTiles.append(a_tile)
        }
    }
    removeTiles.removeAll(keepCapacity: false)
    ball!.position = CGPoint(x: 200, y: 200)
    ballVel = CGPoint(x: 0, y: -5)
    currentLevel++
    loadLevel()
    lost = false
    won = false
}

Here is my Level structs

struct Tile {
    let map = ["blue","green","purple","red"]
}

struct Levels {
    let data = [
        [
            [4,4,0,4,0,4,0,4,0,4,0,4,0,0,4,4],
            [4,4,1,4,1,4,1,4,1,4,1,4,1,1,4,4],
            [4,4,2,4,2,4,2,4,2,4,2,4,2,2,4,4],
            [4,4,3,4,3,4,3,4,3,4,3,4,3,3,4,4]
        ],
        [
            [0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],
            [1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1],
            [2,2,2,2,2,2,2,4,2,2,2,2,2,2,2,2],
            [3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3]
        ]
    ]
}

If this is a bug in Swift I am trying to figure out a way around so I can just make this work.

Johnston
  • 20,196
  • 18
  • 72
  • 121
  • 1
    any reason why you assign the body to a var first rather than directly to tile.physicsBody? – CodeSmile Jul 18 '14 at 22:34
  • @LearnCocos2D Just trying things out from this error. I was doing as you say the first couple of times. – Johnston Jul 18 '14 at 22:37
  • Well actually, for starters, you should not be using the built in physics engine in swift for a platformer. It is too intensive to make a physics body for each tile and can cause glitches. Read more here: http://www.learn-cocos2d.com/2013/08/physics-engine-platformer-terrible-idea/h Second of all, your remove function can be simplied to scene.removeAllChildren() – half-potato Jul 27 '14 at 17:56
  • I tried your code and I can't reproduce the problem. Can you reduce it to a smaller example that still has the problem? By the way, using `enum` like that is wrong since `Wall == Block & Paddle` it will collide with both. You might want to use [`RawOptionSet`](http://stackoverflow.com/questions/24066170/swift-ns-options-style-bitmask-enumerations). – jtbandes Jul 28 '14 at 07:26

1 Answers1

2

Looks like SKPhysicsBody is instantiated with an empty size. Try to create the physics body object with an explicit size, like so:

var physicsBody = SKPhysicsBody(rectangleOfSize: CGSizeMake(100, 100))

Alternatively, you can set the size directly on the SKSpriteNode or use one of its constructors that take a CGSize construct like initWithTexture:color:size:

jverrijt
  • 696
  • 4
  • 10