Unity 2D - Showing health on the sprite as red when the health is dropping

78 Views Asked by At

I am trying to make the enemy/player become red temporarily after taking damage. I was thinking as one of them takes damage, a red effect moves up on either player/enemy to show how much health is left. How could I do this?

enter image description here

enter image description here

So I think I may have found a way with a sprite mask?

Sprite 1 - Sprite of the desired sprite, and it doesn't interact with a sprite map Sprite 2 - I make a new sprite with a modified version of sprite one to be red that is visible within a sprite mask Sprite Mask - I create a sprite mask with a rectangle shape

Then I somehow translate the position and scale of the sprite mask into a range that the health can interact with and then the mask will change the y of the scale and position as health is taken

So now I'm stuck on finding a way to take the pos and scale and convert it into some way that it interacts with the health correctly

For some reason the animation still plays with the other sprite being shown and idk why but it works

    IEnumerator TakeDamageCorountine(int damage){
    currentHealth = currentHealth - damage;
    float maskPosY = 3.25f/currentHealth;
    float maskScaleY = 6.9f/currentHealth;
    if(gameObject.activeInHierarchy == true){
        spriteMask.localPosition = new Vector3(0.051f, maskPosY, 0f);
        spriteMask.localScale = new Vector3(8.884337f, maskScaleY, 1);
        print("Position: " + spriteMask.position + "Scale: " + spriteMask.localScale);
    }
    yield return new WaitForSeconds(.75f);
}

enter image description here

2

There are 2 best solutions below

1
derHugo On BEST ANSWER

I think what you can do is indeed go for a SpriteMask and have an underlying second SpriteRenderer for the tint.

In order to simplify the calculations I would use the following

  • Your original sprite
  • Note down
    • the pixel size (in particular height!) of the texture
    • the import settings -> Pixels Per Unit
  • Create a new texture (e.g. paint) with the same pixel dimensions and completely white
  • Your original SpriteRenderer
    • without any color tint at all!

    • Then add children to it with the following hierarchy and settings

      YourMainSpriteRenderer
      |-- MaskAnchor
          |-- MaskVisuals
      

      where

      • YourMainSpriteRenderer:

        • SpriteRenderer: no color tint (fully white)
        • SpriteMask: use same sprite as mask and adjust cutoff etc

        enter image description here

      • MaskAnchor:

        • Transform: Y = -pixelSize / 2 / Pixels Per Unit (values from second step above)

          in my example the texture is pixels height is 395, pixels per unit is default at 100 -> 395 / 2 / 100 = 1.93

        enter image description here

      • MaskVisuals :

        • Transform: Y = pixelSize / 2 / Pixels Per Unit (same as above but positive)

        • SpriteRenderer: the fully blank texture, your desired color overlay (e.g. red and alpha < 1), Mask InteractionVisible Inside Mask, Order in Layer -> 1 (or at least higher then the parent)

        enter image description here

Now you should see your original sprite overlaid by the color but only within its mask.

And thanks to the added MaskAnchor all you need to do to adjust its amount of fill from 0 to 1 is setting the MaskAnchor.transform.localScale.y to a value of 0 to 1.

enter image description here

This you can easily do by script now (assuming e.g. the color tint shows the health and shrinks over time)

maskAnchor.localScale = new Vector3(1, currentHealth / maxHealth, 1);

otherwise simply invert to make the fill grow while the enemy looses health

maskAnchor.localScale = new Vector3(1, 1 - currentHealth / maxHealth, 1);
3
AudioBubble On

Your wording is slightly confusing, but if you want to make the player flash red when it takes damage in script, I would recommend using a coroutine. This is what I use currently, and it works fine:

private IEnumerator HitFlash() {
    Color originalColor = sprite.color;
    float elapsed = 0f;

    while (elapsed < HurtFlashDuration) {
        elapsed += Time.deltaTime;
        sprite.color = Color.Lerp(hurtColor, originalColor, elapsed / HurtFlashDuration);
        yield return null;
    }
}

Call it anywhere with StartCoroutine(HitFlash())

also you'll need to have some public variables:

public Color hurtColor;
public float HurtFlashDuration;
public SpriteRenderer sprite;