1
\$\begingroup\$

How do I target only the gameObject hit by my Raycast? Do I need to pass in the RaycastHit2D to a custom Unity Event?

I'm shooting a raycast, if it hits a valid target it fires off a UnityEvent to deduct health. My code is currently removing health from all "Enemy" prefabs within the scene even if they aren't being hit by the raycast.

public GameEvent OnDamaged;

public void ConfirmHit()
    {
        var target = Physics2D.Raycast(transform.position, Vector3.right * damageRange, damageRange, layermask);

if (!target) return;

        OnDamaged.Raise();
    }

if a target is hit by the raycast it will trigger the OnDamaged Event. The Event Triggers a simple function to decrease health.

public void DecreaseHealth(float amount)
{
    currentHealth -= amount;
}
\$\endgroup\$
2
\$\begingroup\$

To answer the surface question "Can I pass a [value of some type] with a UnityEvent" the answer is yes, by inheriting from the generic UnityEvent

[System.Serializable]
public class RaycastHit2DEvent : UnityEvent<RaycastHit2D> {
}


public class RaycastBroadcaster : MonoBehaviour {

    public RaycastHit2DEvent OnHit;

    ...
}

You can send up to four arguments of varying types this way.


However, I would not recommend using this to filter your "take damage" message. That scales as O(n) for the number of damageable entities in your scene, compared to O(1) for sending the message directly to the one you hit.

You can still use an event to alert systems that care about damage happening somewhere/anywhere (eg. combat flashing in your minimap, an announcer system, or an AI combat director) - as you're likely to have fewer of those to scan through and fewer false positives to reject, since scripts don't have to subscribe to the firehose just to get damage directed at themselves.

For the separate message of "I am trying to do damage to you specifically," I'd recommend using GetComponent methods as Joza100 describes.

If you're concerned about coupling, you can implement this via an interface. That way the script dealing the damage doesn't need to know every concrete variety of damage reaction script, it just needs to know "some scripts care about taking damage" - the same knowledge it needs to fire an OnDamage event anyway.

public interface IDamageTaker {
    void TakeDamage(
        float damage,
        DamageType type,
        Vector2 hitPosition,
        GameObject source);
}

Your concrete Health or DamageTrigger components can then implement this interface to do the corresponding thing (decrementing a health pool / dying, firing their own local aggro event for their AI to respond to, triggering a scripted destruction or puzzle sequence, etc.)

In your damage dealer script, you'd call it a bit like this:

 var hit = Physics2D.Raycast(
             transform.position, 
             Vector3.right * damageRange,
             damageRange,
             layermask);

 if(!hit)
     return;

 var damageTaker = hit.transform.GetComponent<IDamageTaker>();

 // No need to broadcast any event if we hit something non-damageable.
 if(damageTaker == null)
     return; 

 // Notify the thing we hit so it can process its damage reactions.
 // This could even return a value to say "nuh-uh, I'm invulnerable" or such.
 damageTaker.TakeDamage(damage, damageType, hit.point, gameObject);

 // Notify any listeners who want to know about damage everywhere.
 OnDamageAnywhere.Raise(hit);

If your objects are complicated and have many different scripts on them that all want to individually react when the object takes damage, you can route this through an intermediary. Stick one component on the object that uses its TakeDamage method to fire an object-local "I personally have taken damage" event. Then all the scripts on that object can subscribe to their local damage event, rather than listening to everybody's when they only care about their own.

\$\endgroup\$
1
\$\begingroup\$

You simply can't use a GameEvent for that because you add all enemies to the event and that's why all their Healths decrease.

You can get the target as a GameObject.

Get the HealthComponent (or whichever component the health is stored in) by using the getComponent<>() method.

Instead of that being triggered in the event, trigger it here!

public void ConfirmHit()
{
    var target = Physics2D.Raycast(transform.position, Vector3.right * damageRange, damageRange, layermask);

    if (!target)
        return;

    HealthComponent hpComponent = target.gameObject.getComponent<HealthComponent>();

    hpComponent.DecreaseHealth (amount);
}

When I used HealthComponent, I meant the component where you put your health. Depends on you, but just retrieve that component where your health is.

\$\endgroup\$

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.