Home > front end >  How to destroy one Game Object out of two having same script
How to destroy one Game Object out of two having same script

Time:09-22

Problem:-

I have a Traffic system in my game I want to destroy a car that gets hit by another car. But the problem is both cars have the same script with the name TrafficCar. Here is the function inside the script which is responsible for the collision.

void OnCollisionEnter (Collision col)
{
  // I want to destroy car after 1 sec due to some reason
  if (col.gameObject.tag == gameObject.tag) 
   Destroy (col.gameObject,1f); // or let suppose I'm using traffic pooling 
}

In this case, both cars are destroyed. But I want to destroy the car which gets hits

Question:-

How I can destroy cars that get hits?

For example:- Car A hits Car B then Car B should be destroyed

Thanks In Advance

CodePudding user response:

I think what you could do to tackle this is store a list of already known => already handled collisions and handle each collision pair only once

Something like e.g.

// struct that holds references to the two colliding gameobjects and implements 
// GetHashCode and IEquatable regardless of the order of the two GameObjects
private readonly struct CollisionPair : IEquatable<CollisionPair>
{
    private readonly GameObject _a;
    private readonly GameObject _b;

    public CollisionPair(GameObject a, GameObject b)
    {
        _a = a;
        _b = b;
    }

    public bool Equals(CollisionPair other)
    {
        // return true regardless of the order of a and b
        return Equals(_a, other._a) && Equals(_b, other._b) || Equals(_b, other._a) && Equals(_a, other._b);
    }

    public override bool Equals(object obj)
    {
        return obj is CollisionPair other && Equals(other);
    }

    // return same HashCode regardless of the order of a and b
    public override int GetHashCode()
    {
        var hashA = _a.GetHashCode();
        var hashB = _b.GetHashCode();

        return HashCode.Combine(Math.Min(hashA, hashB), Mathf.Max(hashA, hashB));
    }

    public static bool operator ==(CollisionPair left, CollisionPair right)
    {
        return left.Equals(right);
    }

    public static bool operator !=(CollisionPair left, CollisionPair right)
    {
        return !left.Equals(right);
    }
}

and then have collections and do e.g.

// stores the already handled collision pairs => if already know this collision => ignore
private static readonly HashSet<CollisionPair> _alreadyHandledCollisions = new();

// [OPTIONAL] stores objects that are already marked for destroy
// if collide with such thing or we are such thing => ignore
private static HashSet<GameObject> _alreadyMarkedForDestroy = new();

private void OnCollisionEnter(Collision col)
{
    // Tag doesn't match => ignore
    if (!col.gameObject.CompareTag(gameObject.tag)) return;

    // [OPTIONAL] This object will be destroyed within a second already => ignore
    if(_alreadyMarkedForDestroy.Contains(gameObject)) return;

    // [OPTIONAL] The other object will be destroyed within a second already => ignore
    if(_alreadyMarkedForDestroy.Contains(col.gameObject)) return;

    
    var collisionPair = new CollisionPair(gameObject, col.gameObject);

    // This colliso pair has already been handled (by the other object) => ignore
    // but also remove this collision pair since it has already been handled by the other collision partner
    if (_alreadyHandledCollisions.Contains(collisionPair))
    {
        _alreadyHandledCollisions.RemoveWhere(c => c == collisionPair);
        return;
    }
    
    // Otherwise you handle the collision and store it so he other one doesn't
    _alreadyHandledCollisions.Add(collisionPair);
    Destroy(col.gameObject, 1f);

    // [OPTIONAL]
    _alreadyMarkedForDestroy.Add(col.gameObject);
}

the additional things marked with [OPTIONAL] are to prevent that an object that will be destroyed within the next second causes new collisions with other objects. If this is desired anyway get rid of those lines.


As a little demo I additionally mark the object that is going to be destroyed in red so you can see it is always only one and once it is red all further collisions with this object are ignored

enter image description here

CodePudding user response:

You can use Rigidbody to find out which car gets hits.

void OnCollisionEnter (Collision col)
{
  // I want to destroy car after 1 sec due to some reason
  if (col.gameObject.tag == gameObject.tag && IsFirstCar(col.gameObject)) Destroy (col.gameObject,1f); 
}
bool IsFirstCar(GameObject other)
{
  var rb = GetComponent<Rigidbody>();
  var otherRb = other.GetComponent<Rigidbody>();
  return rb.velocity.magnitude > otherRb.velocity.magnitude;
}

if car A hits car B. that means car A's velocity greater than car B's. Then we can destroy car B

  • Related