I have a game where a contact with the enemy ends the game but when I use removeFromParent on the enemy node it only removes the most recent one. The enemies are spawned using a switch statement to determine what side they enter from and once a contact happens and the game ends the nodes still present mess up the game by causing more contacts. If the user doesn't press anything and the enemies finish their trajectory during the game over screen then the problem doesn't occur.
Could I kill all the nodes from the screen any way once the game ends? Could I suspend input for like 3 seconds during the end screen to let the actions finish? Do I need to enumerate the enemies as they spawn to kill them off one by one once the game ends?
I've been stuck on this for like 3 days and would love some help. I think I explained it fairly well but if I need to show any code please ask and I will. I'm very new to swift by the way.
In your scene class you can write
// For all children
self.removeAllChildren()
// Removing Specific Children
for child in self.children {
//Determine Details
If child.name == "bob" {
child.removeFromParent
}
}
You can use the SKNode's method removeAllChildren(). Call it in the enemies' parent node. If it's the scene, the call should be scene.removeAllChildren().
Enumerating is probably the best way. I would give all your enemies a name(s)
let enemyName = "Enemy" // make it a property to avoid typos
myEnemySprite.name = enemyName
and than if you want to delete a particular set of enemies you can enumerate the scene and remove them
enumerateChildNodesWithName(enemyName) { (node, _) in
node.removeFromParent()
}
Note: If your enemies are children of another node (e.g enemyNode) instead of the scene you will have to enumerate like this
enemyNode.enumerateChildNodesWithName(enemyName) { (node, _) in
node.removeFromParent()
}
Alternatively you could also put all your enemies into an array after you added them to the scene.
Create the array
var enemiesArray = [SKSpriteNode]()
and than for each enemy you add to the scene you add them to the array as well
addChild(yourEnemy1Sprite)
enemiesArray.append(yourEnemy1Sprite)
Now if you want to delete them you can do this
for enemy in enemiesArray {
enemy.removeFromParent()
// clear array as well if needed
}
Hope this helps
Related
im trying to track collsion on a unity project and i dont get whats wrong
thats the code i'm working with.
im trying to destroy an object on collision but it doesnt work for some reason.
void OnCollisionEnter(Collision col)
{
if (col.collider.gameObject.tag == "Enemy")
{
Destroy(col.gameObject);
Debug.Log("collided");
}
}
it hits the object but nothing happends i dont get a message and the object doesnt get destroyed
Assuming that the script you posted is a component of some kind of a character (which it should be since you are looking for an Enemy using its tag) and you have proper Rigidbody components with proper settings and Colliders, you should use:
void OnCollisionEnter(Collision col)
{
// You can also use col.gameObject.tag == "Enemy" since it does the same thing as CompareTag().
// Although using col.gameObject.tag == "Enemy" is less performant.
if (col.gameObject.CompareTag("Enemy"))
{
// Using print to get proper information on what is happening.
print("destroyed " + col.gameObject.name);
// Destroying the gameObject with the tag "Enemy"
Destroy(col.gameObject);
}
}
If you wanted to destroy the player object then you should change the tag accordingly and have this script as a component of the enemy.
The most common issues if nothing is showing up can be:
the object has no RigidBody (or RigidBody2D if you are in a 2D project) component
the 2 objects are in 2 layers which don't collide with others (or the same layer which don't collide with itself). Check both objects' layers, and go to Edit -> Project Settings -> Physics (or Physics2D if the project is in 2D) and go at the bottom and check that the layers collide with each other (the check box is on)
one of the 2 colliders is in 2D and the other in 3D
I'm building an engineer Augmented reality app,.
Basically I want 1 game object which is the screw, detect another game object which is the screw nut, then if the two detected each other they will connect. (create animation that it is connecting like the screw is rotates through the hole).
So the questions are:
1) how will the one object (active) will detect the another object (passive)?
2) how can i set it to real time size detection like if this screw is not fitted then it will not connect to each other.
Add a proper tags to your objects, e.g. tag diameter_5mm. Add a collider with isTrigger enabled to one kind of items and attach a script with something like that:
void OnTriggerEnter(Collider other){
if (other.tag == transform.tag){
//let the magic begin!
}
}
AppImage I have a wall of 4 rectangles that are different colors, to pass through the wall the color of the ball has to match that of the rectangle on the wall. The ball will pass through the wall, and a new wall will appear. However, when I detect this collision I get multiple collision readings. I have tested this by printing dead or alive, and it prints both or more many times.
func didBegin(_ contact: SKPhysicsContact) {
if let nodeA = contact.bodyA.node as? SKShapeNode, let nodeB = contact.bodyB.node as? SKShapeNode {
if nodeA.fillColor != nodeB.fillColor {
print("DEAD")
}
else {
print("Alive")
}
}
}
please help!!!
Yep - this happens. The way to handle it (you can't get sprite-kit to NOT call didBegin multiple times in some circumstances) is to make sure that your contact code accommodates this and that handling the contract multiple times does not cause a problem (such as adding to the score multiple times, removing multiple lives, trying to access a node or physicsBody that has been removed etc).
There is a discussion here: Sprite-Kit registering multiple collisions for single contact
Some things you can do include:
If you remove a node that is contacted, check for it being nil before
you remove it (for the duplicate contacts)
Add the node to a set and then remove all the nodes in the set in
didFinishUpdate
Add an 'inactive' flag' to the node's userData
Make the node a subclass of SKSpriteNode and add an inactive property
Etc etc.
I'm trying to pool my particle emitter nodes. I re-use them when they are needed by removing them from their old parent node and adding them as a child of a SKSpriteNode at a new location. I leave the emitter node position set to 0,0 so the emitter should appear in the center of its new parent sprite node.
The emitters display correctly the first time they are added as a child to a sprite node, but simply do not show up on subsequent attempts. This all worked great in iOS8 and is only broken in iOS9 (seems like lots of particle emitter bugs in iOS9?)
Here's a basic example of my code when I need to place the particle effect:
if emitter.parent != nil {
emitter.removeFromParent()
}
newLocationSpriteNode.addChild(emitter)
emitter.resetSimulation()
This worked perfectly in iOS8 - I could re-use my emitter nodes at new locations. In iOS9 the nodes only appear the first time this code runs and never show up again after. Do you have any insight into how I might work around this issue? Thanks!
I experienced the exact same problem as you described. Emitters were not visible when re-attached for the second time. Everything worked fine on ios8 though. After several hours of experimenting with different settings I almost gave up.. However, I found a solution that works now. First of all I have a pool of SKEmitterNodes which I re-use during gameplay. This method grabs an emitter from the pool (array) and adds it to the gameplay-layer (SKNode):
func createExplosion(position: CGPoint) {
let emitter = _pool.borrowDirtEmitter()
emitter.resetSimulation() //Important
emitter.position = position
emitter.targetNode = self //Important
if emitter.parent == nil {
self.addChild(emitter)
}
}
So "self" here is the actual node that I attach the emitter to. When the node is offscreen I clean up emitters (and other objects):
if let dirtEmitter = childNode as? SKEmitterNode {
if dirtEmitter.parent != nil {
dirtEmitter.removeFromParent()
}
dirtEmitter.targetNode = nil //Important!
_pool.returnDirtEmitter(dirtEmitter)
}
I haven´t had the time to go into more detail yet, but the "Important" comments should give you some pointers. I will try testing out an approach using an action to remove from parent as well (after x seconds), but since I´m making a side scroller I can get away with cleaning up when emitters are offscreen for now.
Hope this helps..
I am building a game where the player runs on a path. When the player triggers a collider, 2 enemy objects will spawn.
What I want is when the first collider trigger has been entered, I want the second collider, which is at a certain distance from the first collider, to get disabled for a certain time. How to achieve this?
If you'd like to disable the colliders so they won't hit or rebound off the wall, for example, then you can change your collider's "isTrigger" variable to true, to change it into a trigger volume instead of a solid collider. This has the effect of disabling it - in that it won't cause other objects to stop or rebound if they hit it.
For example:
function Update() {
if (Input.GetKeyDown(KeyCode.X)) {
collider.isTrigger = true;
}
}
Note that things like MouseOver still work.
If you want to disable that completely, you can try collider.enabled = false. I'm not sure if that works or not. If it doesn't, you can always scale down your collider:
var myOldSize:Vector3;
function DisableBoxCollider(myCollider:BoxCollider)
{
//actually just resizes it
myOldSize=myCollider.size;
myCollider.size=Vector3(0,0,0);
}
function EnableBoxCollider(myCollider:BoxCollider)
{
if(myOldSize!=Vector3(0,0,0))
myCollider.size=myOldSize;
}
You can use the above code to integrate it in your own project. I'm not going to spill out all of the code for you because else we'd miss the point of learning to program and post on Stackoverflow in general. But it should help you to get on your way. Try and play some with the code, and if you have questions, get back here and ask them, providing the question with some code to show what you have tried.