Frodgers
Frodgers

Reputation: 95

Issues with SKPhysicsBody from Texture

I have searched around the internet / StackOverflow for the past few days without much success in finding an answer to a few questions that I believe are intertwined in some way.

  1. How do I prevent my game from freezing when it is rendering an SKPhysicsBody from a texture?
  2. Why are my physics bodies sometimes not created at all, and why are they sometimes the wrong shapes?

Below is the code I am using to try and generate these pillars (image attached at the bottom of the post. There are roughly 6-8 at any point in time before they are removed. When I change the shape to just a rectangle, I get no lag / freezing at all and the physics object is created properly every time.

Is this SKPhysicsBody rendering too complex, or is there a way that I can have my game run smoothly while still generating a correctly shaped physics object? Below is the function I use to set up the walls as they are created in my game.

func createWalls() {

    let scoreNode = SKSpriteNode()

    wallPair = SKNode()
    wallPair.name = "wallPair"

    //MARK: - Top Wall Setup
    let topWall = SKSpriteNode(imageNamed: "Pillar")
    topWall.size = CGSize(width: 100, height: 700)
    topWall.physicsBody = SKPhysicsBody(texture: topWall.texture!, size: topWall.size)
    topWall.position = CGPoint(x: self.frame.maxX+50, y: 0 + 400)
    topWall.physicsBody?.categoryBitMask = PhysicsCategory.Wall
    topWall.physicsBody?.collisionBitMask = PhysicsCategory.Character
    topWall.physicsBody?.contactTestBitMask = PhysicsCategory.Character
    topWall.physicsBody?.isDynamic = false
    topWall.physicsBody?.affectedByGravity = false
    topWall.zRotation = .pi



    //MARK: - Bot Wall Setup
    let botWall = SKSpriteNode(imageNamed: "Pillar")
    botWall.size = CGSize(width: 100, height: 700)
    botWall.physicsBody = SKPhysicsBody(texture: botWall.texture!, size: botWall.size)
    botWall.position = CGPoint(x: self.frame.maxX+50, y: 0 - 400)
    botWall.physicsBody?.categoryBitMask = PhysicsCategory.Wall
    botWall.physicsBody?.collisionBitMask = PhysicsCategory.Character
    botWall.physicsBody?.contactTestBitMask = PhysicsCategory.Character
    botWall.physicsBody?.isDynamic = false
    botWall.physicsBody?.affectedByGravity = false


    scoreNode.size = CGSize(width: 1, height: 600)
    scoreNode.position = CGPoint(x: topWall.position.x+15, y: 0)
    scoreNode.physicsBody = SKPhysicsBody(rectangleOf: scoreNode.size)
    scoreNode.physicsBody?.affectedByGravity = false
    scoreNode.physicsBody?.isDynamic = false
    scoreNode.physicsBody?.categoryBitMask = PhysicsCategory.Score
    scoreNode.physicsBody?.collisionBitMask = 0
    scoreNode.physicsBody?.contactTestBitMask = PhysicsCategory.Character
    scoreNode.color = SKColor.blue

    wallPair.addChild(topWall)
    wallPair.addChild(botWall)
    wallPair.zPosition = 1

    let randomPosition = CGFloat.random(min: -200, max: 200)
    wallPair.position.y = wallPair.position.y + randomPosition
    wallPair.addChild(scoreNode)
    wallPair.run(moveAndRemove)
    self.addChild(wallPair)

}

Pillar Image

Upvotes: 0

Views: 363

Answers (2)

Ben Anthony Donnelly
Ben Anthony Donnelly

Reputation: 11

I found that creating the physics body separately from the node when you're first loading your scene works.

Each time you create a new node, just assign the node the variable containing your preset physics body. Obviously this only works if the sizing of these nodes are always the same

var presetPhysicsBody:SKPhysicsBody?

func createPhysicsBody() {
    let texture = SKTexture(imageNamed: "your-image")
    presetPhysicsBody = SKPhysicsBody(texture: texture, size: CGSize(width: 205, height: 130))
}

spriteNode?.physicsBody = presetPhysicsBody?.copy() as! SKPhysicsBody
spriteNode2?.physicsBody = presetPhysicsBody?.copy() as! SKPhysicsBody

Upvotes: 0

Knight0fDragon
Knight0fDragon

Reputation: 16827

you can use this extension:

extension SKTexture{
  func pristineCopy() -> SKTexture{
      return SKTexture(cgImage:self.cgImage)
  }
}

This should temporarily create a new texture for your physics body to use without getting bogged down by the atlas.

Once the SKPhysicsBody creates the CGPath out of this texture, the memory should get released, so try not to retain it with a variable.

example:

func createWalls()
    let scoreNode = SKSpriteNode()

    wallPair = SKNode()
    wallPair.name = "wallPair"

    //MARK: - Top Wall Setup
    let topWall = SKSpriteNode(imageNamed: "Pillar")
    topWall.size = CGSize(width: 100, height: 700)
    topWall.physicsBody = SKPhysicsBody(texture: topWall.texture!.pristineCopy(), size: topWall.size)
    topWall.position = CGPoint(x: self.frame.maxX+50, y: 0 + 400)
    topWall.physicsBody?.categoryBitMask = PhysicsCategory.Wall
    topWall.physicsBody?.collisionBitMask = PhysicsCategory.Character
    topWall.physicsBody?.contactTestBitMask = PhysicsCategory.Character
    topWall.physicsBody?.isDynamic = false
    topWall.physicsBody?.affectedByGravity = false
    topWall.zRotation = .pi



    //MARK: - Bot Wall Setup
    let botWall = SKSpriteNode(imageNamed: "Pillar")
    botWall.size = CGSize(width: 100, height: 700)
    botWall.physicsBody = SKPhysicsBody(texture: botWall.texture!.pristineCopy(), size: botWall.size)
    botWall.position = CGPoint(x: self.frame.maxX+50, y: 0 - 400)
    botWall.physicsBody?.categoryBitMask = PhysicsCategory.Wall
    botWall.physicsBody?.collisionBitMask = PhysicsCategory.Character
    botWall.physicsBody?.contactTestBitMask = PhysicsCategory.Character
    botWall.physicsBody?.isDynamic = false
    botWall.physicsBody?.affectedByGravity = false


    scoreNode.size = CGSize(width: 1, height: 600)
    scoreNode.position = CGPoint(x: topWall.position.x+15, y: 0)
    scoreNode.physicsBody = SKPhysicsBody(rectangleOf: scoreNode.size)
    scoreNode.physicsBody?.affectedByGravity = false
    scoreNode.physicsBody?.isDynamic = false
    scoreNode.physicsBody?.categoryBitMask = PhysicsCategory.Score
    scoreNode.physicsBody?.collisionBitMask = 0
    scoreNode.physicsBody?.contactTestBitMask = PhysicsCategory.Character
    scoreNode.color = SKColor.blue

    wallPair.addChild(topWall)
    wallPair.addChild(botWall)
    wallPair.zPosition = 1

    let randomPosition = CGFloat.random(min: -200, max: 200)
    wallPair.position.y = wallPair.position.y + randomPosition
    wallPair.addChild(scoreNode)
    wallPair.run(moveAndRemove)
    self.addChild(wallPair)
}

Upvotes: 0

Related Questions