2
\$\begingroup\$

Currently, when the player jumps on top of an enemy, the player and the enemy take damage.

I'm trying to figure out a way that only the enemy gets damaged when the player jumps on top of the enemy and so the player does not get damaged. I have two colliders on the enemy (image below). One collider under the Enemy gameObject (wider, bottom collider) and then there's a child gameObject under Enemy called Receive Damage Check that has a collider (top, smaller collider player jumps on). There is a hazard Layer that the enemy gameObject is on. The child gameObject, Receive Damage Check, underneath the enemy is not on the hazard Layer.

The goal is to have the Receive Damage Check collider register the player collided with it and only damage the Enemy.

enter image description here

enter image description here

Base class:

public class Character : MonoBehaviour {

    public int maxHealth;
    public int currentHealth;
    public HealthBar healthBar;

    [SerializeField] protected float damageInterval = 2f; //in seconds
    protected float currentDamageInterval;

    protected SpriteRenderer sr;
    private Rigidbody2D rb;

    protected virtual void Start() {
        sr = GetComponent<SpriteRenderer>();
        rb = GetComponent<Rigidbody2D>();
        currentHealth = maxHealth;
        healthBar.SetMaxHealth(maxHealth);
    }

    protected virtual void Update() {
        //subtract 1 real life second
        if (currentDamageInterval >= 0)
            currentDamageInterval -= Time.deltaTime;
    }

    public bool CanTakeDamage() {
        return (currentDamageInterval < 0);
    }

    public virtual void TakeDamage(int damage) {
        currentHealth -= damage;
        healthBar.SetHealth(currentHealth);

        int yDamageVelocity = 15;
        rb.velocity = new Vector2(rb.velocity.x, yDamageVelocity);

        //set currentDamageInterval to start a new cooldown/delay period (2 seconds)
        currentDamageInterval = damageInterval;
    }
}

Children classes:

public class Player : Character {

    [SerializeField] private Transform groundCheck;
    [SerializeField] private LayerMask hazardLayer;

    private Color[] colors = { Color.red, Color.white };
    private Coroutine damageFlash;

    protected override void Start() {
        base.Start();
        maxHealth = 10;
    }

    protected override void Update() {
        base.Update();
        //if the current number in the currentDamageInterval is <= 0 or in other words if
        //2 seconds of delay has passed, take damage
        if (CanTakeDamage())
            if (Physics2D.OverlapCircle(groundCheck.position, 0.5f, hazardLayer)) {
                TakeDamage(1);
            }

        //for testing purposes
        if (Input.GetKeyDown(KeyCode.H))
            TakeDamage(1);
    }

    public override void TakeDamage(int damage) {
        base.TakeDamage(damage);
        //guarantee at most, one coroutine runs at a time
        if (damageFlash != null)
            StopCoroutine(damageFlash);
        damageFlash = StartCoroutine(DamageFlashing(1f, .1f));
    }

    IEnumerator DamageFlashing(float duration, float interval) {
        int index = 0;
        //var is a replacement for WaitForSeconds bc it would be redundant
        var wait = new WaitForSeconds(interval);

        for (float elapsedTime = 0; elapsedTime < duration; elapsedTime += interval) {
            //divides the index by 2 and returns the remainder
            sr.color = colors[index % 2];
            index++;
            //waits the interval time and then continues the next color in the flashing duration
            yield return wait;
        }
        damageFlash = null;
    }
}
public class Enemy : Character {

    protected override void Start() {
        maxHealth = 3;
        base.Start();
    }

    private void OnCollisionEnter2D(Collision2D collision) {
        if (collision.gameObject == GameObject.FindWithTag("Player"))
            if (CanTakeDamage())
                TakeDamage(1);
    }
}
\$\endgroup\$
7
  • \$\begingroup\$ There is no code in your Player class that would specifically cause it to take damage when colliding with an enemy. Is the enemy on the hazard layer? \$\endgroup\$
    – Kevin
    Mar 17, 2023 at 1:25
  • \$\begingroup\$ @Kevin it's the OnCollisionEnter2D in Enemy that causes player to take damage \$\endgroup\$ Mar 17, 2023 at 11:19
  • \$\begingroup\$ What should happen when you jump on the enemy? Does it die right away? Do you stay on top? Are you for a second invincible? Is it acting like a spring board? \$\endgroup\$
    – Zibelas
    Mar 17, 2023 at 12:37
  • \$\begingroup\$ @Kevin there is a hazard Layer that the enemy gameObject is on. The child gameObject underneath the enemy is not on the hazard Layer. \$\endgroup\$ Mar 17, 2023 at 16:43
  • 2
    \$\begingroup\$ @ratchetfreak In Enemy, the OnCollisionEnter2D causes the Enemy to take damage, not the player. \$\endgroup\$ Mar 17, 2023 at 16:43

2 Answers 2

1
\$\begingroup\$

When you detect the top collider colliding with the player you can try disabling the enemy bottom collider or flag it as unable to inflict damage for a short while using a coroutine. If both colliders are triggered at the same time you are still in trouble. In which case you might do as Egor suggests and test if the player is on above the enemy and block it from inflicting damage on the player.

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

I would do this with only one collider and then analyze the contact points to decide who gets hurt.

private void OnCollisionEnter2D(Collision2D collision) {
    if (collision.gameObject == GameObject.FindWithTag("Player"))
       var contact = collision.GetContact(0);
       Vector2 collisonPoint = contact.point;

Now you have one of the points in world space where the colliders of player and enemy touched. When its y-component is on the upper edge of the enemy collider, that means the player hit form above. When it's further down, the player hit from the side or from below.

      if(collisionPoint.y >= GetComponent<Collider2D>().bounds.max.y) {
         // Player hits from above
      } else {
        // Player hits from side or below
      }

Note that this particular code sample uses the axis-aligned bounding box of the collider. This box should be equal to the collider if it is a non-rotated box collider. When you use more complex colliders, then the bonds might be larger than you would expect, so you might have subtract a bit from it or access the type-specific properties of the particular type of collider you are using.

\$\endgroup\$
2
  • \$\begingroup\$ This is definitely possible! How would I go about knowing what region/exact points to set the Enemy's collider to have the enemy receive damage and have the enemy deal damage to the player? \$\endgroup\$ Mar 17, 2023 at 16:58
  • \$\begingroup\$ @nataliadalomba Answer updated. \$\endgroup\$
    – Philipp
    Mar 17, 2023 at 19:28

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .