1
votes

I have problem with physics body. I have about 20 nodes spawning randomly to view. Every time the node spawns I set the object's physics, like this:

func showObject(){
    let texture =  SKTexture(imageNamed: "A.png")
    object = SKSpriteNode(texture: texture)
        object.name = "A"
    object.position = CGPoint(x: 0, y: self.frame.width)
    object.setScale(0.7)
    object.zPosition = 2
    object.run(moveAndRemove)

    object.physicsBody = SKPhysicsBody(texture: texture, size: texture.size())
    object.physicsBody?.categoryBitMask = PhysicsCategory.Object
    object.physicsBody?.collisionBitMask = PhysicsCategory.Object2
    object.physicsBody?.contactTestBitMask = PhysicsCategory.Object2
    object.physicsBody?.affectedByGravity = false
    object.physicsBody?.isDynamic = true

    addChild(object)
}

I think this is not optimal, because physicsBody is set everytime when node is spawned. Because of this I have sometimes little flickering - lower fps when app running. When is physicsBody turned off, everything is ok. So I have a simple question. How to set physics body for all 20 nodes after games started and than just spawn them without creating physicsBody again. I try to set physics body with texture straight to SKView but after this, app crash with nil error.

Thanks for any tip.

There is my spawn:

let SpawnObject = SKAction.run({
    () in

    let randomFunc = [self.showObject, self.showObject1.......]
        let randomResult = Int(arc4random_uniform(UInt32(randomFunc.count)))
        randomFunc[randomResult]()
})

let delay1 = SKAction.wait(forDuration: 0.9)
let SpawnDelay1 = SKAction.sequence([SpawnObject,delay1])
let SpawnDelayForever1 = SKAction.repeatForever(SpawnDelay1)
self.run(SpawnDelayForever1)

let distance = CGFloat(self.frame.height + 200)
let moveObject = SKAction.moveBy(x: -distance, y: 0, duration: TimeInterval(0.004 * distance))
let removeObject = SKAction.removeFromParent()
moveAndRemove = SKAction.sequence([moveObject,removeObject])
1
When spawning, the physicBody is redrawn, even for the ones that you created before. One way to do this is to NOT remove them from the scene, but move them off the screen, so that the user cannot see them. when you need them again, move them back into the screen.f_qi

1 Answers

2
votes

There is many different ways you could go about this, what I like to do if I am spawning multiple of the same object is preload the objects into an array and the pull them from the array as I need them, when you are done with the object you can remove them from the scene but keep them in the array. You can then use that object again without having to recreate it and without having to recreate the physics body causing the lag

private var moveAndRemove = SKAction()
private var objects = [SKSpriteNode]()
private let objectCount = 20

override func didMove(to view: SKView) {

    for x in 0..<objectCount {
        let texture =  SKTexture(imageNamed: "A")
        let object = SKSpriteNode(texture: texture)
        object.name = "A"
        object.isHidden = true
        object.setScale(0.7)
        object.zPosition = 2

        object.physicsBody = SKPhysicsBody(texture: texture, size: texture.size())
        object.physicsBody?.categoryBitMask = 0
        object.physicsBody?.collisionBitMask = 0
        object.physicsBody?.contactTestBitMask = 0
        object.physicsBody?.affectedByGravity = false
        object.physicsBody?.isDynamic = true

        objects.append(object)
    }

    let distance = CGFloat(self.frame.height + 200)
    let moveObject = SKAction.moveBy(x:0 - distance, y: 0, duration: Double(0.004 * distance))
    let removeObject = SKAction.removeFromParent()
    let hideObject = SKAction.hide()
    moveAndRemove = SKAction.sequence([moveObject, hideObject, removeObject])

    let SpawnObject = SKAction.run( {

        let randomResult = Int(arc4random_uniform(UInt32(self.objects.count)))
        self.showObject(objectIndex: randomResult)
    })

    let delay1 = SKAction.wait(forDuration: 0.9)
    let SpawnDelay1 = SKAction.sequence([SpawnObject, delay1])
    let SpawnDelayForever1 = SKAction.repeatForever(SpawnDelay1)
    self.run(SpawnDelayForever1)
}

func showObject(objectIndex: Int) {

    let object = objects[objectIndex]

    guard object.isHidden == true else { return }

    object.isHidden = false
    object.position = CGPoint(x: 0, y: self.frame.width)
    object.zPosition = 10000
    addChild(object)
    object.run(moveAndRemove)
}

I've edited the answer to reflect your spawn code. Please note that I am only checking if the object in the array has already been used by checking the hidden status of the object. There are many other ways to check if the object has been used, but you'll have to figure out what works best for you.