2
votes

I'm building a game with SpriteKit and I'm using Xcode's Scene Editor to set up my GameScene.

The scene contains a Player which I've dragged into the scene as a Color Sprite and I've filled in the Custom Class field as mygame.Player.

I have a custom subclass of SKSpriteNode called Player and in this class, I set up physics properties in the initializer that the app calls.

class Player: SKSpriteNode {

    required init?(coder aDecoder: NSCoder) {
        super.init(coder: aDecoder)
        print(self)
        self.physicsBody = SKPhysicsBody(rectangleOf: self.size)
        self.physicsBody?.restitution = 0
        self.physicsBody?.isDynamic = true
        self.physicsBody?.affectedByGravity = true
        self.physicsBody?.velocity = CGVector(dx: 50, dy: 0)
        print(self.physicsBody)
    }

}

In my GameScene.swift file, I'm able to reference the Player, so this linkage works.

override func didMove(to view: SKView) {
    player = childNode(withName: "player") as! Player
    print(player)
    print(player.physicsBody)
}

This actually prints the Player and the physics body out perfectly fine.

<SKSpriteNode> name:'player' texture:['nil'] position:{0, 0} scale:{1.00, 1.00} size:{100, 100} anchor:{0.5, 0.5} rotation:0.00
Optional(<SKPhysicsBody> type:<Rectangle> representedObject:[<SKSpriteNode> name:'player' texture:['nil'] position:{0, 0} scale:{1.00, 1.00} size:{100, 100} anchor:{0.5, 0.5} rotation:0.00])

However, the Player never ends up moving on the scene even though I've set the scene to have gravity! In order to get the player to actually move, I need to add another line of code to the didMove(to:) function in GameScene that sets the PhysicsBody again:

player.physicsBody = SKPhysicsBody(rectangleOf: player.size)

What's going on here? Why doesn't setting the physics body of my Player in the initializer work?

1

1 Answers

3
votes

Because there is a bug that when the physicsbody gets created before the physicsworld exists, it does not properly get set in the world.

As a temporary solution, try:

class Player: SKSpriteNode {

    required init?(coder aDecoder: NSCoder) {
        super.init(coder: aDecoder)
        print(self)
        DispatchQueue.main.asyncAfter(deadline: .now() + 1)
        {
            self.physicsBody = SKPhysicsBody(rectangleOf: self.size)
            self.physicsBody?.restitution = 0
            self.physicsBody?.isDynamic = true
            self.physicsBody?.affectedByGravity = true
            self.physicsBody?.velocity = CGVector(dx: 50, dy: 0)
            print(self.physicsBody)
        }
    }

}

to delay creating the body till after the world exists.

If it works, then you need to come up with a more elegant way to create bodies after the world exists.