SpriteKit& Swift,是否可以使用Array来汇集SKSpritenodes?

时间:2017-08-07 18:08:57

标签: arrays swift performance sprite-kit skspritenode

我正在开发一个带平台,障碍物等的2d siderunner游戏。平台正在创建然后从右向左移动然后再次移除。这就是我的问题,因为每次创建和删除节点都不是很有效我有时会看到fps下降1-3帧,特别是当创建的节点超过+15时。所以我认为池化节点对于良好的性能至关重要。

这是我到目前为止所尝试的内容:

创建全局数组变量标题:

var smallPlArray = [SKSpriteNode]() 
var midPlArray = [SKSpriteNode]() 
var bigPlArray = [SKSpriteNode]() 
var groundPlArray = [SKSpriteNode]()

// small, middle and big refers to the platforms size's not the array size

创建SKSpritenodes并将其添加到数组中的函数:

// Gets called in didMoveToView

func fillSpriteArrays() {

    var smallPlCount:Int = 0
    var groundPlCount:Int = 0

    for _ in 0..<10 {

        let smallPlatform = SKSpriteNode(texture: platform_Small_Texture)
        smallPlatform.zPosition = 4
        smallPlatform.size = CGSize(width: 509.5, height: 55.8)
        smallPlatform.isHidden = true

        smallPlatform.physicsBody = SKPhysicsBody(rectangleOf: smallPlatform.size)
        smallPlatform.physicsBody?.categoryBitMask = PhysicsCatagory.platforms
        smallPlatform.physicsBody?.contactTestBitMask = 0
        smallPlatform.physicsBody?.isDynamic = false
        smallPlatform.physicsBody?.affectedByGravity = false
        smallPlatform.physicsBody?.friction = 0.0
        smallPlatform.physicsBody?.restitution = 0.0

        smallPlArray.append(smallPlatform)
        groundPair.addChild(smallPlArray[smallPlCount])

        smallPlCount += 1
    }


    for _ in 0..<10 {

        let groundPlatform = SKSpriteNode(texture: middle_Ground_Platform_Texture)
        groundPlatform.zPosition = 4
        groundPlatform.size = CGSize(width: 898.4, height: 255.5)
        groundPlatform.isHidden = true

        groundPlatform.physicsBody = SKPhysicsBody(rectangleOf: groundPlatform.size)
        groundPlatform.physicsBody?.categoryBitMask = PhysicsCatagory.groundPlatform
        groundPlatform.physicsBody?.contactTestBitMask = 0
        groundPlatform.physicsBody?.isDynamic = false
        groundPlatform.physicsBody?.affectedByGravity = false
        groundPlatform.physicsBody?.friction = 0.0
        groundPlatform.physicsBody?.restitution = 0.0

        groundPlArray.append(groundPlatform)
        groundPair.addChild(groundPlArray[groundPlCount])

        groundPlCount += 1
    }

 // Same for the other array's...
}

尝试从阵列中获取SKSpritenodes并使用它们:

func platforms1() {

    let ground1 = groundPlArray[0]
    ground1.position = CGPoint(x: 3800, y: 375)
    ground1.isHidden = false

    let ground2 = groundPlArray[1]
    ground2.position = CGPoint(x: 7000, y: 375)
    ground2.isHidden = false

    let smallPl1 = smallPlArray[0]
    smallPl1.position = CGPoint(x: 4350, y: 720)
    smallPl1.isHidden = false

    let smallPl2 = smallPlArray[1]
    smallPl1.position = CGPoint(x: 5600, y: 720)
    smallPl2.isHidden = false

// Other nodes not in connection with the array's

    createSpring(position: CGPoint(x: 2500, y: 337))
    createCrate(position: CGPoint(x: 5000, y: 350))
    createCrate(position: CGPoint(x: 5600, y: 350))
    createJelly(position: CGPoint(x: 7000, y: 330))
    createRoundWoodSpike(position: CGPoint(x: 7300, y: 550))
    createPlatformEndNode(position: CGPoint(x: 8500, y: 788))
    createDiamond(position: CGPoint(x: 2800, y: 800))
    createDiamond(position: CGPoint(x: 2880, y: 800))
    createDiamond(position: CGPoint(x: 4070, y: 575))
 }

所以,这就是我失败的地方。不知何故,只显示了smallPlArray中的一个节点,但是在groundPlArray上显示了所有两个节点。这些节点的父节点(groundPair)被移动,当某个点被传递时(屏幕上没有节点),父位置被重置,并且平台1()再次被调用,节点再次出现。 这适用于所有其他节点,如弹簧,板条箱等,但阵列中的节点不会再次返回。仅当第一次调用platforms1()时,才会显示阵列中4个节点中的3个节点。显然我在这方面失败了。我究竟做错了什么?是否有另一种或更好的汇集方式?不幸的是,我对阵列没有那么有经验,但我可以了解是否有必要。

修改

以下是调用代码的方式:

    func diceRollPlatforms() {

    platformSpawnCount = 0

    let shuffled = GKRandomDistribution(lowestValue: 1, highestValue: 15)
    var number = shuffled.nextInt()

// Repeat one platform sequence over and over instead of a random int
    if number != 1 || number == 1 {
        platforms1() 
}

然后:

func platforms_1() {

    shouldMovePlatforms = true // Just triggers the moving of groundPair in update

    createSpring(position: CGPoint(x: 2500, y: 337))
    createCrate(position: CGPoint(x: 5000, y: 350))
    createCrate(position: CGPoint(x: 5600, y: 350))
    createJelly(position: CGPoint(x: 7000, y: 330))
    createRoundWoodSpike(position: CGPoint(x: 7300, y: 550))
    createPlatformEndNode(position: CGPoint(x: 8500, y: 788)) // On contact with the player,groundPair(parent) gets reseted and diceRollPlatforms() is called again. 
//BUT the children are not removed. Children like spring etc are removed with the physics engine when they contact a node
//on the near outside left of the screen which does not affect the platforms, only obstacles like said before spring, crate...


    let ground1 = groundPlArray[0]
    ground1.position = CGPoint(x: 3800, y: 375)
    ground1.isHidden = false


    let ground2 = groundPlArray[1]
    ground2.position = CGPoint(x: 7000, y: 375)
    ground2.isHidden = false

    let smallPl1 = smallPlArray[0]
    smallPl1.position = CGPoint(x: 4350, y: 720)
    smallPl1.isHidden = false

    let smallPl2 = smallPlArray[1]
    smallPl2.position = CGPoint(x: 5600, y: 720)
    smallPl2.isHidden = false

// How the array is being filled is already explained above
    }

在函数createSpring和其他函数中,它们基本上完全相同:

func createSpring(position: CGPoint) {
    let spring = SKSpriteNode(texture: spring_Texture3)
    spring.size = CGSize(width: 230.9, height: 151.0)
    spring.zPosition = 4
    spring.position = position

    spring.physicsBody = SKPhysicsBody(rectangleOf: spring.size)
    spring.physicsBody?.categoryBitMask = PhysicsCatagory.spring
    spring.physicsBody?.contactTestBitMask = 0
    spring.physicsBody?.isDynamic = false
    spring.physicsBody?.affectedByGravity = false
    spring.physicsBody?.friction = 0.0
    spring.physicsBody?.restitution = 0.0

    groundPair.addChild(spring)
}

@ Knight0fDragon如果有什么不清楚的地方请告诉我。

第二次编辑

    func createPlatformEndNode(position: CGPoint) {
    let platformEndNode = SKSpriteNode()
    platformEndNode.position = position
    platformEndNode.zPosition = 4
    platformEndNode.size = CGSize(width: 30, height: 1536)

    platformEndNode.physicsBody = SKPhysicsBody(rectangleOf: platformEndNode.size)
    platformEndNode.physicsBody?.categoryBitMask = PhysicsCatagory.platformEndNode
    platformEndNode.physicsBody?.contactTestBitMask = PhysicsCatagory.platforms | PhysicsCatagory.obstacle | PhysicsCatagory.obstacleDeathObject | PhysicsCatagory.enemy | PhysicsCatagory.collidablePlatforms | PhysicsCatagory.spring | PhysicsCatagory.breakAbleObstacle | PhysicsCatagory.triggerEnemyNode | PhysicsCatagory.platformEndNode | PhysicsCatagory.groundPlatform | PhysicsCatagory.collidableGroundPlatform | PhysicsCatagory.breakAblePlatformsNotcollidable | PhysicsCatagory.breakAblePlatforms | PhysicsCatagory.cherry
    platformEndNode.physicsBody?.isDynamic = false
    platformEndNode.physicsBody?.affectedByGravity = false

    groundPair.addChild(platformEndNode)
}

func didEnd(_ contact: SKPhysicsContact) {
        // Check if contact ended with platformEndNode
    if firstBody.categoryBitMask == PhysicsCatagory.platformEndNode && secondBody.categoryBitMask == PhysicsCatagory.dragon || firstBody.categoryBitMask == PhysicsCatagory.dragon && secondBody.categoryBitMask == PhysicsCatagory.platformEndNode {
        spawnPlatformsAgain()

        if firstBody.categoryBitMask == PhysicsCatagory.platformEndNode {
            firstBody.node!.removeFromParent()
        } else {
            secondBody.node!.removeFromParent()
        }
    }
        // Check if contact ended with platformStartNode
    if firstBody.categoryBitMask == PhysicsCatagory.triggerPlatformStartNode && secondBody.categoryBitMask == PhysicsCatagory.dragon || firstBody.categoryBitMask == PhysicsCatagory.dragon && secondBody.categoryBitMask == PhysicsCatagory.triggerPlatformStartNode {
        if firstBody.categoryBitMask == PhysicsCatagory.triggerPlatformStartNode {
            firstBody.node!.removeFromParent()
        } else {
            secondBody.node!.removeFromParent()
        }
        if platformSpawnCount == 0 { platformSpawnCount = 1; prepareGroundPair(); diceRollPlatforms() }
    }
}
     func spawnPlatformsAgain() {

    if !wasWaterHere {

        timeToIncreaseGameSpeed = true; platformSpawnCount = 0
        prepareForNextPlatforms(); increaseGameSpeed()
        if addRollingWoodEnemy == true { spawnRollingWoodEnemyAfterPlatforms() }
        isCollidingWithBreakAblePlatforms = false

        createPlatformStartNode(position: CGPoint(x: 1024, y: 768))

    } else {

        timeToIncreaseGameSpeed = true; platformSpawnCount = 0
        prepareForNextPlatforms(); increaseGameSpeed()
        if addRollingWoodEnemy == true { spawnRollingWoodEnemyAfterPlatforms() }
        isCollidingWithBreakAblePlatforms = false

        wasWaterHere = false
        bigGroundAndWaterTileNode.removeAllChildren()
        bigGroundAndWaterTileNode.removeAllActions()
        bigGroundAndWaterTileNode.position = CGPoint(x: 0, y: 0)
        bigGroundAndWaterTileNode.isHidden = true

        createPlatformStartNode(position: CGPoint(x: 1536, y: 768))
    }
}
    func prepareGroundPair() {

    shouldMovePlatforms = false
    groundPair.position.x = 0
}

func prepareForNextPlatforms() {

    groundPair.position.x = 0

    enemyParent.removeAllChildren(); enemyParent.removeAllActions()
    enemyParent.position.x = 0

}

第三次编辑

移动groundPair:

    // Runs in Update
func moveWorldObjects() {
            if shouldMovePlatforms == true {
                groundPair.position = CGPoint(x: groundPair.position.x - CGFloat(xPosition), y: groundPair.position.y)
            }

}

1 个答案:

答案 0 :(得分:2)

是的,有可能,您的问题是复制和粘贴失败。你永远不会设置smallPl2的位置,而是偶然设置smallPl1两次。

我会尽量避免这种方法。只有当我遇到严重的性能限制时,我才推荐像这样做一个池(你不是这样,你的代码效率不高)。创建一个新对象每次都会给你一个干净的平板,所以你不必担心重置节点的属性。

相反,回到你正在做的事情,每次都创建一个新的平台。

这一次,创建一系列在后台线程上排队的保留节点以实现您的目标。然后,您可以在更新过程中将它们添加到场景中,或者在更新阶段将didFinish部分添加到场景中,以避免任何冲突。

以下是如何创建一些保留节点的示例

var reservedNodes = SKNode()  //This SKNode is going to house all of our reserves,  I prefer it over [SKNode]().

func spawnReserves()
{
       if reservedNodes.children.count < 5
       {
          DispatchQueue.global(qos: .background).async 
          { 
            [weak self] in 
            //before this point if self does not exist then safely exit
            guard let strongSelf = self else {return}
           //beyond this point we do not want self to die                
            let node = strongSelf.createNode()
            DispatchQueue.main.async 
            {
              [weak self] in 
              //before this point if self does not exist then safely exit
              //now because we use `node`,  strongSelf should not even be needed, since the background thread should be retained, but I threw it in just in case
              guard let strongSelf = self else {return}
              //beyond this point we do not want self to die
              //As long as reservedNodes is not added to the scene, this should not cause problems, never add it to the scene.
              strongSelf.reservedNodes.addChild(node)
            }
          }
       }

}


func update(currentTime: TimeInterval)
{
    //If I need nodes grab from reserve
    //what is nice about .first is the where feature, so you can say first(where:{$0.isKind(of:PlatformNode)})
    if let node = reserveNodes.children.first
    {
      node.moveToParent(self) //this will remove them from the array for us
    }
}

func didFinishUpdate()
{
   spawnReserves()
}

现在有了子类化,您可以更进一步,并检查何时删除子项以生成新的子项,以便始终创建节点,但我将为您提供更高级的工作。这应该至少可以帮助您从概念上开始使用池