Search Unity

Best way to display visual damage effect?

Discussion in 'Game Design' started by CaptCanada, Feb 1, 2016.

  1. CaptCanada

    CaptCanada

    Joined:
    Feb 3, 2013
    Posts:
    272
    Hi all

    I am working on a project and have my stereotypical zombie creature attacking my character when the player gets too close.

    I am trying to create a visual attack effect similiar to that used in Amnesia to show when the player is hit by the creature.

    Should I use a Canvas with a raw image for this effect and then just turn the canvas on/off when I need to display the effect?

    Thanks!
     
  2. LaneFox

    LaneFox

    Joined:
    Jun 29, 2011
    Posts:
    7,532
    Sounds like a perfect place to start, to me.
     
  3. Martin_H

    Martin_H

    Joined:
    Jul 11, 2015
    Posts:
    4,436
    I think there are image overlay effects in the standard assets that you could fade in and out smoothly with a script.
     
    xXx_Binladen_xXx and Not_Sure like this.
  4. CaptCanada

    CaptCanada

    Joined:
    Feb 3, 2013
    Posts:
    272
    Thanks Martin_H, I'll look into that.
     
  5. Kiwasi

    Kiwasi

    Joined:
    Dec 5, 2013
    Posts:
    16,860
    Sometimes just popping up raw damage numbers is enough.
     
  6. CaptCanada

    CaptCanada

    Joined:
    Feb 3, 2013
    Posts:
    272
    I was thinking that as well, but I really like the visual aspect of the damage effect. I think it will add more to the gameplay than just having a health counter decrease each time the player is hit.
     
  7. TonyLi

    TonyLi

    Joined:
    Apr 10, 2012
    Posts:
    12,697
    I happened to be working on this very thing yesterday. It's a HUD-less first person game, so no health counters or other text onscreen. I ended up using three things:

    1. A heartbeat sound that starts playing at 30% damage. As damage increases to 100%, the volume and pitch (speed) increase.

    2. A vignette postprocess effect that darkens the edges of the screen. It increases as damage increases, causing the darkness to encroach toward the center of the screen.

    3. A shake effect and impact sound effect at the moment of impact. It only lasts 0.2 seconds.

    Together, these three things provide a fairly visceral feeling of taking damage.
     
    theANMATOR2b, Kiwasi and Ryiah like this.
  8. CaptCanada

    CaptCanada

    Joined:
    Feb 3, 2013
    Posts:
    272
    Thanks
    I forgot about those gameplay elements. They certainly would add to the immersiveness of my game. I just want to get this visual damage indicator effect working first before I add anything else.
     
  9. TonyLi

    TonyLi

    Joined:
    Apr 10, 2012
    Posts:
    12,697
    You might want four images on your canvas to indicate the direction from which the player got hit. In a first person game, you can't see behind you. But if the image on the bottom edge of the screen flashes, you know you got hit from behind.
     
  10. CaptCanada

    CaptCanada

    Joined:
    Feb 3, 2013
    Posts:
    272
    There is only one creature in my game and attacks the player from the front.

    LaneFox suggested I use Canvas, but I haven't really tried it out. I was thinking of making a plane and attaching that to my fps camera. I'd put a texture on it and via script set the plane active or not depending on if the player is hit.

    Does that sound like a workable idea? Or should I try something else?
     
  11. TonyLi

    TonyLi

    Joined:
    Apr 10, 2012
    Posts:
    12,697
    A Canvas is probably easier. The default Canvas setting is Screen Space - Overlay, which is perfect for this. Your script can set it active/inactive the same as you describe.
     
  12. CaptCanada

    CaptCanada

    Joined:
    Feb 3, 2013
    Posts:
    272
    Where should I attach the script in this case, to the fps controller?
     
  13. TonyLi

    TonyLi

    Joined:
    Apr 10, 2012
    Posts:
    12,697
    It probably depends on how your player takes damage. With any luck, it'll have something like an "On Damage" event that you can just hook into from anywhere.
     
  14. CaptCanada

    CaptCanada

    Joined:
    Feb 3, 2013
    Posts:
    272
    I got it figured out actually just after I posted the question! LOL.
    I have a script attached to my monster and I just put in the code to display the canvas in that script.
    It is working now, just have to make a timer so that the image isn't displayed just before the attack animation plays.
     
  15. TonyLi

    TonyLi

    Joined:
    Apr 10, 2012
    Posts:
    12,697
  16. CaptCanada

    CaptCanada

    Joined:
    Feb 3, 2013
    Posts:
    272
    I tried the waitforseconds but for some reason it didn't work.

    I had something like this:
    void Start()
    {
    StartCoroutine(Wait());
    }

    This is further down in the code:
    IEnumerate Wait()
    {
    yield return new waitforseconds(5);
    }

    Then I would call Wait() from within the Update() where I check to see if the monster is attacking the player
     
  17. TonyLi

    TonyLi

    Joined:
    Apr 10, 2012
    Posts:
    12,697
    I would've put the script on the player so it could handle damage from any source, but either way is fine.

    Perhaps in your script you could use Invoke instead of StartCoroutine. Something like:

    Code (csharp):
    1. public GameObject damageIndicator; //<--Assign in inspector.
    2. public float damageDuration = 0.5f; //<--Show canvas for this duration each hit.
    3.  
    4. void Start()
    5. {
    6.     HideDamageIndicator();
    7. }
    8.  
    9. public void HitPlayer()
    10. {
    11.     ShowDamageIndicator();
    12.     CancelInvoke("HideDamageIndicator"); //<--Resets timer if hit before indicator is hidden.
    13.     Invoke("HideDamageIndicator", damageDuration);
    14. }
    15.  
    16. public void ShowDamageIndicator()
    17. {
    18.     damageIndicator.SetActive(true);
    19. }
    20.  
    21. public void HideDamageIndicator()
    22. {
    23.     damageIndicator.SetActive(false);
    24. }
    I'm not sure how your combat works, but if you want the monster to try to hit the player every 5 seconds, you could change Start to something like this:

    Code (csharp):
    1. public float hitFrequency = 5;
    2.  
    3. IEnumerator Start()
    4. {
    5.     HideDamageIndicator();
    6.     while (true)
    7.     {
    8.         yield return new WaitForSeconds(hitFrequency);
    9.         if (CanHitPlayer()) HitPlayer();
    10.     }
    11. }
     
    Favour001 likes this.
  18. CaptCanada

    CaptCanada

    Joined:
    Feb 3, 2013
    Posts:
    272
    Thanks for the code snippets.

    Here is the code snippet that I am using to determine when to show the damage texture when the monster is in attack range.

    Code (CSharp):
    1. void Update ()
    2.     {
    3.        
    4.              if (agent.remainingDistance < 0.5f)
    5.             {
    6.                            
    7.                 GotoNextPoint();
    8.             }
    9.              float Distance = Vector3.Distance(agent.transform.position, Player.transform.position);
    10.              if (Distance < MinDistance) // true if monster's distance is less than the MinDistance
    11.              {
    12.                  print(Distance);
    13.                  agent.transform.LookAt(Player);
    14.                  agent.destination = Player.transform.position; // monster's destination equals player's position  
    15.                  if(Distance < AttackDistance)
    16.                  {
    17.                    
    18.                      agent.Stop();
    19.                      animator.SetInteger("Attack", 10);
    20.                    
    21.                      canvas.SetActive(true);                            
    22.                    
    23.                    
    24.                  }
    25.                  else
    26.                  {
    27.                      canvas.SetActive(false);
    28.                      animator.SetInteger("Attack", 0);
    29.                      agent.Resume();
    30.  
    31.                        
    32.                  }
    33.  
    34.               }
    35.            
    36.            
    37.          
    38.          
    39.     }
     
    Favour001 likes this.
  19. CaptCanada

    CaptCanada

    Joined:
    Feb 3, 2013
    Posts:
    272
    It's in the If (Distance<AttackDistance) code block where I have the canvas with the image and where I tried to use the waitforseconds coroutine without any success.
     
  20. TonyLi

    TonyLi

    Joined:
    Apr 10, 2012
    Posts:
    12,697
    What about replacing canvas.SetActive(true) with something like the HitPlayer(), ShowDamageIndicator() and HideDamageIndicator() methods in my previous post?

    Even better, change your attack to an animation trigger parameter. When you want the monster to attack, call animator.SetTrigger("Attack").

    In this case, your attack state should have an animation event that calls a function like HitPlayer() at the point in the monster's attack animation that it would inflict damage. HitPlayer() then takes care of the timing of showing and hiding the canvas.
     
  21. CaptCanada

    CaptCanada

    Joined:
    Feb 3, 2013
    Posts:
    272
    I'll try your methods in my code and let you know how it turns out. I am not familiar with animation event but I'll definitely look into it.

    I thought that an integer parameter was the best to use after initially trying a bool parameter. Is there an advantage to using a trigger parameter instead of integer?

    Thanks for all your help btw
     
  22. TonyLi

    TonyLi

    Joined:
    Apr 10, 2012
    Posts:
    12,697
    I like trigger parameters because they're easy. You just "set and forget." Assuming you've defined a transition that uses the trigger, the trigger will automatically clear itself as soon as it transitions.
     
  23. CaptCanada

    CaptCanada

    Joined:
    Feb 3, 2013
    Posts:
    272
    For now, should I just change my attack to an animation trigger parameter and keep the existing code that I have?
     
  24. TonyLi

    TonyLi

    Joined:
    Apr 10, 2012
    Posts:
    12,697
    Sure. The first rule is usually to just get it working. You can go back and rewrite it later if you find that it's not efficient or has bugs.
     
  25. CaptCanada

    CaptCanada

    Joined:
    Feb 3, 2013
    Posts:
    272
    With your help I now have it working. Thanks for the tips and the code segments.
    I tried to use a Plane as my damage indicator but it doesn't work with it. I was just wondering if you could give me some clues as to why it's not working with the Plane object.

    Thanks
     
  26. CaptCanada

    CaptCanada

    Joined:
    Feb 3, 2013
    Posts:
    272
    Okay, figured it out. I knew I should try it myself before asking!
    It's because where I put the plane with the damage decal is out of view of the fps controller camera.
     
  27. TonyLi

    TonyLi

    Joined:
    Apr 10, 2012
    Posts:
    12,697
    Glad you got it working!
     
  28. CaptCanada

    CaptCanada

    Joined:
    Feb 3, 2013
    Posts:
    272
    I do have one question though and haven't been able to figure it out since my last post.

    Currently I have the your first script attached to my monster and call the HitPlayer() method from the Update method in my NavMeshAgent.cs script and it works as advertised.

    I just wanted to see if it would work by attaching your script to the fps controller, but no joy. Could it be that it needs to be attached to the animation controller for the animation I am trying to play?

    Any clues as to why it doesn't work attached the to fps controller would be great
     
  29. TonyLi

    TonyLi

    Joined:
    Apr 10, 2012
    Posts:
    12,697
    It sounds like you're onto the issue. You need a reference to the monster's animator controller.

    Consider splitting your code into two scripts, one on the monster and one on the player.

    The monster script would determine when to strike and play the animation. When it hits the player, it can call a method on the player called, for example, TakeDamage.

    The player script can show the damage indicator in its TakeDamage method.
     
  30. KevinAnthony

    KevinAnthony

    Joined:
    Nov 7, 2016
    Posts:
    16
    This could be useful: https://www.assetstore.unity3d.com/en/#!/content/91436
     
  31. TonyLi

    TonyLi

    Joined:
    Apr 10, 2012
    Posts:
    12,697
    Despite necro posting ;), you add a really good point. We often obsess over visuals to the point where we neglect audio. Good audio can really convey a sense of damage.
     
  32. Ryan6900

    Ryan6900

    Joined:
    Dec 19, 2021
    Posts:
    1
    I am working on a hud-less game aswell and I have got to making the damage effect with just visual effects, However I am new to game dev so I am not sure where to start with this. Can you give me insight on how you implemented this code wise. I understand how to handle when these effects would happen and how to handle them from what I learned handling other events in the game, I just need insight on the visual effect aspect. Thanks for any help.
     
  33. TonyLi

    TonyLi

    Joined:
    Apr 10, 2012
    Posts:
    12,697
  34. zenaeastin

    zenaeastin

    Joined:
    Apr 3, 2022
    Posts:
    1
    Recently I have also played a game related to zombies attack, that was really pretty much scary. Best of luck for your zombie projects