Why are only some nodes removed from parent in Sprite Kit? - sprite-kit

So I am using the didBeginContact method to detect the collision between the physics bodies of a bullet and other objects in my scene using the block of code below:
-(void)didBeginContact:(SKPhysicsContact *)contact
{
uint32_t collision = (contact.bodyA.categoryBitMask | contact.bodyB.categoryBitMask);
if (collision == (WWPhysicsCategoryOther | WWPhysicsCategoryBullet)) {
[_bullet removeFromParent]
}
}
When one bullet is on the screen, the bullet is removed. However, like many shooting games, the player is allowed to fire bullets repetitively. I find that when more than one bullet is on screen, not all of them are removed after contact. How can I ensure that every bullet is immediately removed right after a collision?

0x141E and ZeMoon comments are correct.
_bullet is probably a pointer to a specific node, therefore, on collision detection, the bullet node that collides is probably not always the one that has that pointer to (which means that could be that when collision happens, some other bullet on screen is being removed).
A better and more 'correct' way of doing that is, on didBeginContact: identify which contact body is the bullet, and using its node property, remove it from its parent.
Something along the following example should work-
-(void)didBeginContact:(SKPhysicsContact *)contact
{
uint32_t collision = (contact.bodyA.categoryBitMask | contact.bodyB.categoryBitMask);
if (collision == (WWPhysicsCategoryOther | WWPhysicsCategoryBullet)) {
SKNode *bulletNode = (contact.bodyA.categoryBitMask == WWPhysicsCategoryBullt) ? contact.bodyA.node : contact.bodyB.node;
[bulletNode removeFromParent]
}
}

Related

Two Gameobjects collides although they are not on the same place

I have a Gameobject called ball and another called floor. As can be seen in the code, when the ball hits the floor, a printout shall be made.
void OnCollisionEnter2D(Collision2D collision)
{
bg = GameObject.Find(bg_name);
if (collision.gameObject.tag == "floor")
{
//bg.GetComponent<ControlGame>().deleteBall();
//bg.GetComponent<ControlGame>().recreateBall();
print("Ball hit floor");
}
if (collision.gameObject.tag == "rope")
{
bg.GetComponent<ControlGame>().ToggleIsShot(false);
}
}
The problem is that I have moved the floor now, such that the ball and the floor has different depth. Still, the printout is made although the two Gameobjects are not touching. See the screenshots below:
The print is in the lower left corner. Why is that?
According to the comment below, the z-value does not have an effect. But I have also a collision detector for the rope, which is a Bezier-curve with an EdgeCollider2D attached to it. When the rope passes the floor, nothing is triggered in onCollisionEnter2D.
void OnCollisionEnter2D(Collision2D collision)
{
bg = GameObject.Find(bg_name);
if (collision.gameObject.tag == "floor")
{
print("floor collision");
bg.GetComponent<ControlGame>().ShootBall();
}
}
Well, these are Collider2D.
You only changed the Z (depth) position of the floor which basically is completely ignored in a 2D game / by 2D physics.
As you can also see in the GameView for the Physics 2D the objects are still aligned and colliding.
Why isn't it also working for collision between rope and floor?
Please checkout the Collision Action Matrix for which collisions/trigger can be detected.
&rightarrow; At least one of the objects has to be a Rigidbody2D.
If the rope is e.g. moved by script then it has to be a Kinematic &rightarrow; in this case also the floor would need to be a Rigidbody2D and not Kinematic.
The reason I guess it worked just fine for the ball is because it already is a non-kinematic Rigidbody2D so able to detect collisions with static colliders.

Compare tags in UnityScript

We are building a 3D game and right now I am stuck on an issue where I need to compare multiple tags to activate or deactivate a trigger that sends the player back to the respawn position.
This is currently the code:
#pragma strict
var spawnPoint : GameObject;
function OnTriggerStay ( other : Collider )
{
if (other.tag == "Player" && other.tag != "Ball")
{
other.tag == "Player";
other.gameObject.transform.position = spawnPoint.transform.position;
}
else if ( other.tag == "Ball" && other.tag == "Player" )
{
}
}
I am uncertain how to fix this to do the following:
If the player touches the trigger without there being a ball colliding with it, the player respawns. This is to create the feeling that the particles kill you.
If the player touches the trigger when there is a ball colliding with it as well, nothing happens to the player and so the player can freely pass through.
What we want to do is that we want to push a ball over a geyser so it covers it and if the geyser is not covered and the player tries to pass over it, the player respawns.
We also tried with another code and while it allows the ball to pass, it does not allow the player to do so. Even after the ball has been placed.
#pragma strict
var spawnPoint : GameObject;
function OnTriggerStay ( other : Collider )
{
if (other.tag == "Ball")
{
other.enabled = false;
}
else if (other.tag == "Player")
{
other.gameObject.transform.position = spawnPoint.transform.position;
}
}
So, I believe your problem is, when you use that function, you are only checking the first collision. So whats happening is your getting a value of either the player or the ball, not both. You need to store all the collision so you can compare all of them. To do that you can follow the generals of this documentation. http://docs.unity3d.com/ScriptReference/Collision-contacts.html
It's talking about collisions, but the same general principle should apply.
Hope this helps!

Recommended Approach to multiple colliders

I am have some difficulty with what is probably a very silly thing. I have an enemy gameobject that depending on where it is hit (collision) - either it, or the play dies. I think the simplest way to describe this is by using the classic Super Mario Bros. game as an example.
As you all know, if the player runs into the enemy - the player will lose - UNLESS he jumps on top of the enemy's head, in which case the enemy should die.
My initial idea was to create two colliders on the gameobject:
Blue border represents a BoxCollider2D - that if collided with - will cause player to lose (notice it is slightly lower from the top)
Green border represents a BoxCollider2D on a child gameobject - that if collided with - will cause the gameobject to die.
The following is a simplified version of the code I used:
// Collider #1
public void OnCollisionEnter2D(Collision2D collision)
{
// Trigger 'Game-Over' logic
}
// Collider #2
public void OnCollisionEnter2D(Collision2D collision)
{
if (collision.gameObject.tag == "Player")
{
Destroy(this.gameObject);
}
}
This kind-of works, however momentarily after colliding with Collider #1, Collider #2 is also trigger - and while the enemy is destroyed, the player also loses.
I have been playing with the RigidBody2D values to prevent the player from entering the 2nd collider when hitting the enemy from the top - but apparently with that force / speed, the colliders may be slightly inaccurate (or maybe I'm just doing it wrong?).
I have looked into RayCasts but this seems too complex for something that me appears rather trivial (casting rays on all four sides and four vertices of the player - assuming that the player has a box collider).
What I have resorted to 'for the moment' is a a single collider with a simple piece of code that I am unhappy with, and doesn't always work:
public void OnCollisionEnter2D(Collision2D collision)
{
if (collision.gameObject.tag == "Player")
{
float yVelocity = collision.gameObject.transform.rigidbody2D.velocity.y;
if (yVelocity < 0)
{
Debug.Log("Enemy will lose..." + yVelocity);
Destroy(this.gameObject);
}
else
{
// Trigger 'Game-Over' logic
}
}
}
I'm currently working in 2D mode, but solutions for 3D mode (while maybe more complicated than necessary for my question) will also be considered.
Thanks guys.
as a game developer you always have many ways to solve a problem or make a gameplay.
first of all i have to say you should make a polygon collider 2d fo you objects and chracters. just colliding pictures is not very good as i see you used box cilliders in your game.
a good choice can be that you attach and empty object ot you player and set its position under foots of you player and check of enemy hit that enemy dies else if enemy hit main character object, player dies.
another choice can be when o objects collide check y position of 2 objects. of player was higher he kiils, else enemy kills the player.
if you think more you will find more answers.
you have to examin diffrent ways and find most efficient.
I think it will be easy to disable the other collider when one is triggered. You can easily enable/disable colliders with collider.enabled = true; or collider.enabled = false;
// Collider #1
public void OnCollisionEnter2D(Collision2D collision)
{
// Trigger 'Game-Over' logic
// collider2.enabled = false;
}
// Collider #2
public void OnCollisionEnter2D(Collision2D collision)
{
// collider1.enabled = false;
if (collision.gameObject.tag == "Player")
{
Destroy(this.gameObject);
}
}
This way it will be pretty lightweight and easy to implement.
One way of implementing what you want is to put each collider in its child own game object and use the IsTouching() method.
void OnTriggerEnter2D(Collider2D other){
if(GameObject.Find("Top Trigger").GetComponent<BoxCollider2D>().IsTouching(other)){
Destroy(transform.gameObject)
}
if(GameObject.Find("Bottom Trigger").GetComponent<BoxCollider2D>().IsTouching(other)){
Destroy(other.gameObject)
}
}

Creating a sprite during didBeginContact

I've been working on some SpriteKit tutorials. I understand the whole collision thing and have verified with NSLog that a collision is being registered between my two objects. However for some really strange reason my sprite is not being created (or rather shown) when it's done during didBeginContact.
- (void)didBeginContact:(SKPhysicsContact *)contact
{
uint32_t collision = (contact.bodyA.categoryBitMask | contact.bodyB.categoryBitMask);
if (collision == (CNPhysicsCategoryPlayer | CNPhysicsCategoryRock))
{
NSLog(#"ouch");
SKSpriteNode *bigOuch = [SKSpriteNode spriteNodeWithImageNamed:#"star"];
bigOuch.position = CGPointMake(200, 200);
[self addChild:bigOuch];
}
}
I get the ouch log message but no sprite appears.
I have tried the same (sprite creation) code in other parts of my program and have no issues. What am I doing wrong?
I was stuck on the exact same issue a while back. You can create a SKSpriteNode and add it to the view but it does not get displayed. The short of it is that I ended up creating an array and adding any sprites I needed to create during the didBeginContact phase. During the update phase I checked the array and added them to my view. Just remember to empty the array after you are done. Otherwise you will end up with the same sprite being added over and over again.

How to Make Raycast Effect Named GameObject Only

In my current(Unity 3.3 IOS)project I have a character walking across a bridge. If the character falls off
the bridge he is to fall into a fiery river and explode. The problem is when he's on the bridge the Raycast reads the rigidbody on the bridge and he immediately explodes. If I reposition him in the scene window to an open air position, the gravity causes him to fall into the river and explode as planned. I added the following line of code to designate only destroy if the raycast hits the "plane". It doesn't work.
if(hit.collider.gameObject.name == "plane");
The character does not get destroyed on the bridge or when he hits the plane/fiery river.
There are several bridges and buildings he will be walking into, so I only want him to be destroyed/explode if the raycast hits the plane.
Can anyone tell me why my code isn't working or how to correct it?
Here is my complete Raycast code.
var explosion : Transform;
var point : Vector3;
var explosionRotation : Quaternion;
function Update()
{
var hit :RaycastHit;
var dwn = transform.TransformDirection(Vector3.down);
if (Physics.Raycast(this.transform.position,dwn,hit,3))
if (hit.collider.gameObject.name == "plane")
{
point = hit.point;
explosionRotation = Quaternion.FromToRotation(Vector3.up, hit.normal);
Explode();
}
}
function Explode()
{
Destroy(this.gameObject);
var instanExplosion = Instantiate(explosion, point, explosionRotation);
}
Can you share your scene with the basic objects that are involved?
There are some problems that might occur.
The bridge might be named "plane", too
The ray is too long
Have you considered using layers? It sound like you use that ray only for falling/dying purpose for the character. You might want to add your plane to a separate layer and let the ray only check against that layer.