0
\$\begingroup\$

I have 2 objects both with trigger colliders. The first object has only 1 trigger collider (imagine this to be a sword).

The second object (a person) has multiple colliders. One is a non-trigger collider (used for collision detection for walls, ground) and then multiple trigger colliders on different parts of the body (the purpose is for hurt box).

Now when the sword hits the person, I want to only process code for those trigger colliders and to be able to detect which of those trigger colliders got hit (arm, leg, etc.).

But the event only exposes a parameter hit for the other collider:

void OnTriggerEnter(Collider other)
{
    if (other.gameObject.layer == LayerMask.NameToLayer(Constants.LAYER_HITBOX_PLAYER))
    {
        // Run code here
    }
}

I want to run code only when certain trigger collider are hit, like for example, when the head is hit, or the body, etc. How do I get this info to be able to run conditional code on this method?

\$\endgroup\$
0
\$\begingroup\$

you can make other colliding GameObject to define its own Event Function. for example other Object hast Enemy Class Component that has Die() function. so you have to write:

Action myAction;
void OnTriggerEnter(Collider other)
{
    myAction= Other.GetComponent<Enemy>().Die;
myAction.Invoke();
}

if you want to check function of different classes you can use polymorphism and get component by interface and those classes should implement the same Die Function form interface. you can getComponent like this:

IAttribute attribute = GetComponent(typeof(IAttribute)) as IAttribute;

but best practices is to instead of making you OnTriggerEnter check for different types of triggers, make those triggers to implement their own OnTriggerEnter and Run their own function.

\$\endgroup\$
0
\$\begingroup\$

I haven't found any information related to this. So the best solutions I can think of are only workarounds and there probably isn't any built in solutions to do this. It is very inconvenient indeed.

[SerializeField] private GameObject _hammerHeadGameObject;

[SerializeField] private LayerMask _impulseLayerMask;
[SerializeField] private CinemachineImpulseSource _cinemachineImpulseSource;
[SerializeField] private GamepadHapticSettings _hapticSettings;

private void OnCollisionEnter(Collision other)
{
    if ((this._impulseLayerMask & (1 << other.collider.gameObject.layer)) != 0)
    {
        for (int a = 0; a < other.contactCount; a++)
        {
            if (other.contacts[a].thisCollider.gameObject == this._hammerHeadGameObject)
            {
                this._cinemachineImpulseSource.GenerateImpulse();

                InputSystem.GetDevice<Gamepad>().TriggerHaptic(this._hapticSettings);
            }
        }
    }
}

private void OnTriggerEnter(Collider other)
{
}

This is an example of how I get colliders from Collision. Not the most efficient solution, but it works. The problem with Collider - there is a high chance that it's not possible to get contacts from it, at least I haven't found how to.


1.

We can simulate our own collision to get information. The process is pretty straightforward:

  1. Trigger collision.
  2. Work out what type of collider it is.
  3. Simulate collision with same settings (bounds, size, offset...).
  4. Get colliders that were hit (those will be the ones you are looking for).

Small example:

private void OnTriggerEnter(Collider other)
{
    if ((other as BoxCollider) != null)
    {
        BoxCollider boxCollider = (other as BoxCollider);

        Collider[] colliders = Physics.OverlapBox(boxCollider.center, boxCollider.size / 2f, boxCollider.transform.rotation); // I am not sure about (/ 2f) - not tested.
    }
}

Basically we solve this from another perspective from original. We take the collision information from other colliders that hit the colliders we need information about.

Same approach is taken in second solution.

2.

In this approach we process collision in reverse. Instead of finding the collision in the original object. We have the script attached to other objects that can collide with it. And upon collision we notify that object that it has been hit and pass it a collider that was hit.

private void OnTriggerEnter(Collider other)
{
    if (other.TryGetComponent<ObjectThatCanBeHit>(out ObjectThatCanBeHit objectThatCanBeHit))
    {
        objectThatCanBeHit.CollisionHappened(other); // There we process that collision.
    }
}

All in all, the first solution is more flexible, but requires more processing power if there are lots of collisions. Although, the second one can be intense as well, it all depends on quantity of objects in the scene and number of collisions. So some kind of benchmark would be required, I am just saying that first one would require more processing power from the first look and intuition.

\$\endgroup\$
0
\$\begingroup\$

If you don't mind just create children to the object that would be hit. Let the children's names be indexes for your hit registry. In my example, I named the head hitbox as "0", chest as "1", arms as "2" and legs as "3".

The hierarchy now.

Attach a script for "Hit Registry" to them. Then you should have a public function in the parent object (the object that would be hit) to determine the damage, the bleed, the punch values (if you need). The function has to have an "int" input for the registry.

public void Hit(int registry, float damage, float punch)
{
    //Run the code here
}

Then you can have the damage and punch values from the collider if you want. In my example, I declared them as constants.

float damage = 10;
float punch = 10;


void OnTriggerEnter2D(Collider col)
{
    transform.parent.GetComponent<HitBox>().Hit(int.Parse(gameObject.name), damage, punch);
}

So you will send the parsed int data for your hit function to know where has been hit. And in the final you can add switch to your hit function to deliver the damage to the object.

public void Hit(int registry, float damage, float punch)
{
    switch (registry)
    {
        case 0:
            //Run the hit code for head.
            break;
        case 1:
            //Run the hit code for chest.
            break;
        case 2:
            //Run the hit code for arms.
            break;
        case 3:
            //Run the hit code for legs.
            break;

    }
}
\$\endgroup\$
  • \$\begingroup\$ I had the same problem as OP. And as far as I could remember my scripts attached to child objects didn't receive any OnTrigger or OnCollision messages because the parent had a rigidbody, so all of the messages went to scripts attached to a parent, I assume. Does it work for you in the case you have Rigidbody on your parent object? (Note that there were some problems in Unity with having Rigidbody being children of other Rigidbodies, maybe making them kinematic would save the day, but as a general rule of thumb - I don't have this hierarchy to avoid problems, maybe unless newer Unity ver) \$\endgroup\$ – Candid Moon _Max_ Nov 6 at 10:45
  • \$\begingroup\$ Those magic numbers cry out for an enum. \$\endgroup\$ – Draco18s Dec 5 at 15:32

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.