xcode 超出屏幕边界时如何正确删除节点?

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/24169882/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-09-15 05:03:38  来源:igfitidea点击:

How to properly remove node when out of screen bounds?

iosobjective-cxcodesprite-kit

提问by user3576196

I'm working on a sprite-kit game where nodes spawn below the lowest point on the screen and gravity is set to have them float to the top of the screen. Everything works perfectly, but it quickly starts to slow down the FPS, and eventually lags and glitches becoming very slowly. I thought the way to solve this would have been to remove the nodes from the parent after they'd past a point, this was the code I used in the update:

我正在开发一个精灵套件游戏,其中节点在屏幕最低点下方生成,重力设置为让它们漂浮到屏幕顶部。一切正常,但它很快开始减慢 FPS,最终滞后和故障变得非常缓慢。我认为解决这个问题的方法是在节点超过某个点后从父节点中删除节点,这是我在更新中使用的代码:

-(void)update:(CFTimeInterval)currentTime {

    if (_bubble1.position.y > CGRectGetMaxX(self.frame)+40) {
        [self removeFromParent];
    }

}

And in case it is needed, this is how I spawned said bubble below the initWithSizemethod:

如果需要,这就是我在initWithSize方法下方生成气泡的方式:

-(void)didMoveToView:(SKView *)view {

    [self performSelector:@selector(spawnBubbles) withObject:nil afterDelay:1.0];
    [self performSelector:@selector(spawnBubbles1) withObject:nil afterDelay:1.5];

}

-(void)spawnBubbles {
    randomPosition = arc4random() %260*DoubleIfIpad;
    randomPosition = randomPosition + 20*DoubleIfIpad;

    randomNumber = arc4random() %7;
    randomNumber = randomNumber + 1;

    myColorArray = [[NSArray alloc] initWithObjects:colorCombo1, colorCombo2, colorCombo3, colorCombo4, colorCombo5, colorCombo6, colorCombo7, colorCombo8, nil];
    myRandomColor = [myColorArray objectAtIndex:randomNumber];

    _bubble1 = [SKShapeNode node];
    [_bubble1 setPath:CGPathCreateWithEllipseInRect(CGRectMake(-25*DoubleIfIpad, -25*DoubleIfIpad, 50*DoubleIfIpad, 50*DoubleIfIpad), nil)];
    _bubble1.strokeColor = _bubble1.fillColor = myRandomColor;
    _bubble1.position = CGPointMake(randomPosition, CGRectGetMinY(self.frame)-60);
    _bubble1.physicsBody = [SKPhysicsBody bodyWithCircleOfRadius:20];
    _bubble1.physicsBody.categoryBitMask = CollisionBubble;

    [self addChild:_bubble1];

    [self runAction:[SKAction sequence:@[
                                         [SKAction waitForDuration:1.0],
                                         [SKAction performSelector:@selector(spawnBubbles) onTarget:self],
                                         ]]];

}

How can I make it so that the nodes are properly disposed of when they leave the screen? And how can I keep the FPS at a constant rate of 60 FPS?

我怎样才能让节点在离开屏幕时被正确处理?我怎样才能将 FPS 保持在 60 FPS 的恒定速率?

Thanks in advance!!

提前致谢!!

回答by meisenman

I would recommend using the built in contact detection in spritekit. Create a skspritenode mimicking a roof that has a physics body set to detect contact with bubbles. Create an event on contact between the roof node and bubble node that will simply remove the bubbles. This will ensure that bubbles are removed and you maintain a constant FPS.

我建议在 spritekit 中使用内置的接触检测。创建一个模仿屋顶的 skspritenode,该屋顶的物理主体设置为检测与气泡的接触。在屋顶节点和气泡节点之间的接触上创建一个事件,该事件将简单地去除气泡。这将确保去除气泡并保持恒定的 FPS。

Example of event called on contact:

调用联系人的事件示例:

- (void)bubble:(SKSpritenode*)bubble didCollideWithRoof:(SKSpriteNode*)roof{
[bubble removeFromParent];}

Contact detection example:

接触检测示例:

- (void)didBeginContact:(SKPhysicsContact *)contact
{
SKPhysicsBody *firstBody, *secondBody;
if (contact.bodyA.categoryBitMask < contact.bodyB.categoryBitMask)
{
    firstBody = contact.bodyA;
    secondBody = contact.bodyB;
}
else
{
    firstBody = contact.bodyB;
    secondBody = contact.bodyA;
}

if (firstBody.categoryBitMask==bubbleCategory && secondBody.categoryBitMask == roofCategory)
{
    [self bubble:(SKSpriteNode*)firstBody.node didCollideWithRoof:(SKSpriteNode*)secondBody.node];
}}

Bubble needs:

泡泡需求:

 _bubble.physicsBody.contactTestBitMask = roofCategory;

Roof:

屋顶:

SKSpriteNode *roof = [SKSpriteNode spriteNodeWithColor:[SKColor blackColor] size:CGSizeMake(self.scene.size.width, 1)];   
roof.physicsBody = [SKPhysicsBody bodyWithRectangleOfSize:CGSizeMake(self.scene.size.width, 1)];

    roof.position = CGPointMake(self.scene.size.width/2,self.scene.size.height)
    roof.physicsBody.dynamic = NO;
    roof.physicsBody.categoryBitMask = floorCategory;
    roof.physicsBody.contactTestBitMask = bubbleCategory;
    [self addChild:roof];

回答by Imanou Petit

With Swift 1.1 / iOS 8, you can use the following pattern in order to solve your problem.

使用 Swift 1.1 / iOS 8,您可以使用以下模式来解决您的问题。



1. Init your scene's physicsBodyproperty with the following code:

1.physicsBody使用以下代码初始化场景的属性:

physicsBody = SKPhysicsBody(edgeLoopFromRect: frame) // or CGRectInset(frame, -10, -10) if you need insets 

2. Create category masks for your scene and your sprite nodes:

2. 为场景和精灵节点创建类别遮罩:

let boundaryCategoryMask: UInt32 =  0x1 << 1
let someNodeCategoryMask: UInt32 =  0x1 << 2

3. Set the proper categoryBitMask, collisionBitMaskand contactTestBitMaskfor your scene and sprite nodes physicsBodyproperties:

3.为您的场景和精灵节点属性设置适当的categoryBitMask,collisionBitMask和:contactTestBitMaskphysicsBody

// Scene
physicsBody!.categoryBitMask = boundaryCategoryMask

// Sprite node
/* ... */
someNode.physicsBody!.categoryBitMask = someNodeCategoryMask
someNode.physicsBody!.contactTestBitMask = boundaryCategoryMask 


As an example, the following Swift SKScene implementation shows you how to remove all sprite nodes that go beyond your scene's frame:

例如,以下 Swift SKScene 实现向您展示了如何删除超出场景框架的所有精灵节点:

class GameScene: SKScene, SKPhysicsContactDelegate {

    let boundaryCategoryMask: UInt32 =  0x1 << 1
    let squareCategoryMask: UInt32 =  0x1 << 2


    override func didMoveToView(view: SKView) {
        // Scene
        backgroundColor = SKColor.whiteColor()
        physicsWorld.contactDelegate = self
        physicsBody = SKPhysicsBody(edgeLoopFromRect: frame)
        physicsBody!.categoryBitMask = boundaryCategoryMask

        // Square
        let square = SKSpriteNode(color: SKColor.redColor(), size: CGSize(width: 80, height: 80))
        square.zPosition = 0.1
        square.position = CGPoint(x: size.width / 2.0 - square.size.width / 2, y: size.height - square.size.height)
        square.physicsBody = SKPhysicsBody(rectangleOfSize: square.frame.size)
        square.physicsBody!.dynamic = true
        square.physicsBody!.affectedByGravity = true
        square.physicsBody!.categoryBitMask = squareCategoryMask
        // square.physicsBody!.collisionBitMask = 0 // don't set collisions (you don't want any collision)
        square.physicsBody!.contactTestBitMask = boundaryCategoryMask // this will trigger -didBeginContact: and -didEndContact: 
        addChild(square)
    }

    override func touchesBegan(touches: NSSet, withEvent event: UIEvent) {
        // Check if the array containing the scene's children is empty
        println("children: \(children)")
    }

    func didBeginContact(contact: SKPhysicsContact) {
        if contact.bodyA.categoryBitMask == squareCategoryMask {
            contact.bodyA.node?.removeFromParent()
            println("square removed")
        }

        if contact.bodyB.categoryBitMask == squareCategoryMask {
            contact.bodyB.node?.removeFromParent()
            println("square removed")
        }
    }

    func didEndContact(contact: SKPhysicsContact) {
        /* ... */
    }

    override func update(currentTime: CFTimeInterval) {
        /* Called before each frame is rendered */
    }

}