我正在开发一个带平台,障碍物等的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)
}
}
答案 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()
}
现在有了子类化,您可以更进一步,并检查何时删除子项以生成新的子项,以便始终创建节点,但我将为您提供更高级的工作。这应该至少可以帮助您从概念上开始使用池