How to Remove Node from Parent If Touched More Than Once

How to remove node from parent if touched more than once?

The best way to remove your collided nodes is using the method didFinishUpdate, if you remove or launch a method to remove your node from didBeginContact your game could crash searching a collided node that meanwhile is in the process of being removed..

class BadGuy: SKSpriteNode {
var badGuyBulletCollisionsCounter: Int = 0
init() {
let texture = SKTexture(imageNamed: "CircleBadGuy")
super.init(texture: texture, color: nil, size: texture.size())
...
// fill this part with your BadGuy code
}
required init(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
}

Declare a global var :

var nodesToRemove = [SKNode]()

In the didBeginContact method:

func didBeginContact(contact: SKPhysicsContact) {
let A : SKPhysicsBody = contact.bodyA
let B : SKPhysicsBody = contact.bodyB
if (A.categoryBitMask == Numbering.Badguy) && (B.categoryBitMask == Numbering.Laser) || (A.categoryBitMask == Numbering.Laser) && (B.categoryBitMask == Numbering.Badguy)
{
badGuy = A.node as! BadGuy
badGuy.badGuyBulletCollisionsCounter += 1
runAction(BadGuyLostSound)
bulletsTouchedBadGuy(badGuy, Laser: B.node as! SKSpriteNode)
}
}

In the bulletsTouchedBadGuy method :

func bulletsTouchedBadGuy(badGuy: BadGuy, laser: SKSpriteNode){
nodesToRemove.append(laser)
if badGuy.badGuyBulletCollisionsCounter == 2 {
nodesToRemove.append(badGuy)
}
}

Finally:

override func didFinishUpdate() 
{
nodesToRemove.forEach(){$0.removeFromParent()}
nodesToRemove = [SKNode]()
}

How to remove a node when hit more than once

Here is a sample solution to your issue (this is a macOS project just replace mouseDown with touchesBegan if you want to convert).

Click the screen to watch the villains health deplete, and when reaches 0 the villain will die and remove from scene:

let category1 = UInt32(1)
let category2 = UInt32(2)

class Villain: SKSpriteNode {

var lives = 2
var hitThisFrame = false

init(color: SKColor, size: CGSize) {
super.init(texture: nil, color: color, size: size)
let pb = SKPhysicsBody(rectangleOf: self.size)
pb.categoryBitMask = category1
pb.contactTestBitMask = category2
self.physicsBody = pb
}

required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
}

class Hero: SKSpriteNode {

init(color: SKColor, size: CGSize) {
super.init(texture: nil, color: color, size: size)
let pb = SKPhysicsBody(rectangleOf: self.size)
pb.categoryBitMask = category2
pb.contactTestBitMask = category1
self.physicsBody = pb
}
required init?(coder aDecoder: NSCoder) { fatalError() }
}

class GameScene: SKScene, SKPhysicsContactDelegate {

let villain = Villain(color: .blue, size: CGSize(width: 50, height: 50))
let hero = Hero (color: .green, size: CGSize(width: 50, height: 50))

override func didMove(to view: SKView) {
physicsWorld.contactDelegate = self
physicsWorld.gravity = CGVector.zero

hero.position.y -= 100
addChild(villain)
addChild(hero)
}

func didBegin(_ contact: SKPhysicsContact) {
let contactedBodies = contact.bodyA.categoryBitMask + contact.bodyB.categoryBitMask

if contactedBodies == category1 + category2 {

// Find which one of our contacted nodes is a villain:
var vil: Villain
if let foundVil = contact.bodyA.node as? Villain {
vil = foundVil
} else if let foundVil = contact.bodyB.node as? Villain {
vil = foundVil
} else {
fatalError("one of the two nodes must be a villain!!")
}

if vil.hitThisFrame {
// Ignore a second contact if already hit this frame:
return
} else {
// Damage villain:
vil.lives -= 1
print(" vil lives: \(vil.lives)")
vil.hitThisFrame = true
if vil.lives == 0 {
// Kill villain:
print("villain is dead!!!")
vil.physicsBody = nil
vil.removeFromParent()
}
}
}
}

override func didSimulatePhysics() {
// Reset hero position (so as to not trigger another didBegin()
hero.position = CGPoint(x: 0, y: -100)
// Allow villain to be hit again next frame:
villain.hitThisFrame = false
}
override func mouseDown(with event: NSEvent) {
// Trigger didBegin():
hero.position = villain.position
}
}

Removing Node From Parent (Sprite Kit) Objective C

Here is an example about how you can create a sprite, move it until it ends up off-screen and remove it right after that:

#import "GameScene.h"

@interface GameScene()

@property (nonatomic, strong)SKSpriteNode *sprite;

@end

@implementation GameScene

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

self.sprite = [SKSpriteNode spriteNodeWithColor:[SKColor purpleColor] size:CGSizeMake(50.0f,50.0f)];

//Place a sprite on right edge of the screen (I assume that your view and a scene have same size)
self.sprite.position = CGPointMake(self.frame.size.width, self.frame.size.height / 2.0f);

SKAction *moveSprite = [SKAction moveTo:CGPointMake(-self.sprite.size.width, self.frame.size.height / 2.0f) duration:5.0f];

SKAction *removeSprite = [SKAction removeFromParent];

SKAction *sequence = [SKAction sequence:@[moveSprite, removeSprite]];

[self addChild:self.sprite];

[self.sprite runAction:sequence withKey:@"moving"];
}

-(void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event{

if (self.sprite.parent) {
NSLog(@"Sprite has a parent");
}else{
NSLog(@"Sprite doesn't have a parent");
}
}

@end

Try this code with, and without [SKAction removeFromParent] (see what is printed after sprite is off screen in both cases).

Remove multiple elements with same name using removeChild?

Here's a solution that removes the first level children with the specified name for the parent with the specified id. If you want to go deeper, you can recursively call it on the child elements you get inside (you'll have to add a parent parameter as well).

function removeChildren (params){
var parentId = params.parentId;
var childName = params.childName;

var childNodes = document.getElementById(parentId).childNodes;
for(var i=childNodes.length-1;i >= 0;i--){
var childNode = childNodes[i];
if(childNode.name == 'foo'){
childNode.parentNode.removeChild(childNode);
}
}
}

And to call it:

removeChildren({parentId:'div1',childName:'foo'});

And a fiddle for testing:

Notes: You can only access the name element dependably in JavaScript when it supported on your element (e.g. NOT on DIVs!). See here for why.

UPDATE:

Here's a solution using className based on our conversation:

function removeChildren (params){
var parentId = params.parentId;
var childName = params.childName;

var childNodesToRemove = document.getElementById(parentId).getElementsByClassName('foo');
for(var i=childNodesToRemove.length-1;i >= 0;i--){
var childNode = childNodesToRemove[i];
childNode.parentNode.removeChild(childNode);
}
}

Remove all childNodes in 0(1) on DOM element

I really doubt children can be removed in O(1), even with node.innerHTML = '' as the underlying implementation may very well be a O(N) operation.

What you should consider to improve performance is to minimize the number of DOM reflows.

  1. You could try replacing the element with a clone.

const list = document.querySelector('ul');const listClone = list.cloneNode(false);list.parentNode.replaceChild(listClone, list);
<ul>  <li>First</li>  <li>Last</li></ul>

SpriteKit detect if node is removed from parent

There is no such method. But you can create one.

Create a subclass of (for example) SKSpriteNode and override all "remove" methods (or just those you are using). Within that method send a message to whichever object needs to receive the removal event, or send a notification and register other objects to receive that notification. Don't forget to call the super implementation of the overridden method.

Remove cclayer once all actions finished on its children

Make your top level container a CCNodeRGBA and set in the init :

self.cascadeColorEnabled=YES;
self.cascadeOpacityEnabled=YES;
self.opacity=255;

When you run a CCFadeAction on that, the node will do all the forklifting of cascading down to children and grand-children. At the end of the fade action,

id fade = [CCFadeTo actionWithDuration:2.5 opacity:0];
id done = [CCCallBlock actionWithBlock:^{
[self removeFromParentAndCleanup:YES];
// plus whatever else you see fit
}];
id seq = [CCSequence actions:fade, done, nil];
[self runAction:seq];

ob cit : from memory, not compiled nor tested, YMMV

SKEmitterNode isn't removing itself from parent?

particleLifetime determines the average lifetime of a particle, in seconds. That doesn't affect on removal of SKEmitterNode from parent.

numOfParticlesToEmit which refers to Maximum field in Particles area of Particle Editor determines the number of particles that emitter should emit before stopping. That doesn't affect on removal of SKEmitterNode from parent too. Also note that you've set 0 in this field which will enable infinitely emitting.

So, if you want to remove node from parent when emitter is done with emitting, you can set the number of particles to emit (field called Maximum in Particles area inside editor) and run an SKAction sequence which will:

  • start an emitter
  • wait for some duration of time
  • and remove the emitter from parent (at this point emitter should finish with emitting)

Here is an simple example to show you how to do this with SKAction sequence:

class GameScene: SKScene {

let emitter : SKEmitterNode = NSKeyedUnarchiver.unarchiveObjectWithFile(NSBundle.mainBundle().pathForResource("MyParticle", ofType: "sks")!) as SKEmitterNode
override func didMoveToView(view: SKView) {

self.backgroundColor = SKColor.blackColor()

}

func addEmitter(position:CGPoint){

var emitterToAdd = emitter.copy() as SKEmitterNode

emitterToAdd.position = position

let addEmitterAction = SKAction.runBlock({self.addChild(emitterToAdd)})

var emitterDuration = CGFloat(emitter.numParticlesToEmit) * emitter.particleLifetime

let wait = SKAction.waitForDuration(NSTimeInterval(emitterDuration))

let remove = SKAction.runBlock({emitterToAdd.removeFromParent(); println("Emitter removed")})

let sequence = SKAction.sequence([addEmitterAction, wait, remove])

self.runAction(sequence)

}

override func touchesBegan(touches: NSSet, withEvent event: UIEvent) {

let touch: AnyObject? = touches.anyObject()

let location = touch?.locationInNode(self)

self.addEmitter(location!)

}

}

And here is the result (note how node's count is changing after emitting is done) :

Removing emitters from scene

Hope this helps

EDIT:

For those interested in how to make similar effect like from the video above, try with something like this:

emitter setup

The point is to use Color Ramp, and to choose Add for a blend mode.

Here is the dropbox link to the .sks file : Effect.sks

Touch detection of SKSpriteNode with child nodes

You can do this inside the node subclass:

class PlanListItem:SKSpriteNode {

var isSelected: Bool = false
override init(texture size etc) {
//your init
self.userInteractionEnabled = true
}

override func touchesBegan(touches: Set<UITouch>, withEvent event: UIEvent?) {
print("PlanListItem touched")
isSelected = !isSelected
}
}

Increase Touchable Area of an SKSpriteNode

In you code you have two nodes:

  • original
  • invisible

Who is the parent, who is the child?

According to your question "If I touch the invisible SKNode, then both the invisible and original SKNodes disappear..however, for whatever reason, the original SKNode always appears infront of the invisible SKNode" seems that you want invisible inside original, in other words original should be the parent and invisible should be the child.

But your code says the exact opposite:

invisible.addChild(original)
self.addChild(invisible)

So , we make an example:

Sample Image

Suppose that your original node is green, this how appear your elements follow your code: invisible node (red) do an addChild to the original (green) , and then you add to self the invisible. Invisible is parent, original is child.

Finally, if you touch the red rectangle to remove it, both red and childs (green) will be removed, if you touch the green rectangle to remove it, only the green rectangle disappear.

Hope you can help you to understand what happen to your code.



Related Topics



Leave a reply



Submit