-1

So when my character gets hit by the enemies fire breath, I want to create the feel of the character being set on fire. So while the character is on fire I want him to lose a specific amount of health for a specific amount of time.

For example; lets say he is on fire for 3 seconds and I want to make him lose 30 health for being on fire, how would I evenly distribute losing 30 health for 3 seconds? I dont want the 30 damage to be applied instantly to the health, I want it to slowly tick away at the players health so that at the 3 second mark 30 damage has been dealt.

The game is being made with c#.

Thanks.

Programmer
  • 121,791
  • 22
  • 236
  • 328
fourthking
  • 321
  • 1
  • 4
  • 19
  • Check out coroutines: https://docs.unity3d.com/Manual/Coroutines.html – Rafiwui Aug 17 '17 at 13:56
  • [Mathf.Lerp()](https://docs.unity3d.com/ScriptReference/Mathf.Lerp.html) may also be handy. This can just as easily be accomplished without coroutines, by using a bool in Update() to enter a "decreaseHealth" method. – ryeMoss Aug 17 '17 at 14:00
  • So ive tried having a timer variable start at 3 and then subtracting Time.deltaTime from it and until the timer gets to 0, subtract 1 health from the character. But that obviously didnt work cause if I remember correctly Time.delta time is based on frame rate. So what ended up happening was my player would lose a huge amount of health within those 3 seconds. – fourthking Aug 17 '17 at 14:12
  • Stuff like this one should be done in a coroutine not in a void/Update function. When I say stuff, I mean the *"do something within x time"* questions. Check the answer I left. That should solve your issue. – Programmer Aug 17 '17 at 14:27
  • I ended up using a couroutine – fourthking Aug 17 '17 at 15:26

4 Answers4

5

This is just like moving Gameobject over time or doing something over time. The only difference is that you have to use Mathf.Lerp instead of Vector3.Lerp. You also need to calculate the end value by subtracting the value you want to lose over time from the current value of the player's life. You pass this into the b or second parameter of the Mathf.Lerp function.

bool isRunning = false;

IEnumerator loseLifeOvertime(float currentLife, float lifeToLose, float duration)
{
    //Make sure there is only one instance of this function running
    if (isRunning)
    {
        yield break; ///exit if this is still running
    }
    isRunning = true;

    float counter = 0;

    //Get the current life of the player
    float startLife = currentLife;

    //Calculate how much to lose
    float endLife = currentLife - lifeToLose;

    //Stores the new player life
    float newPlayerLife = currentLife;

    while (counter < duration)
    {
        counter += Time.deltaTime;
        newPlayerLife = Mathf.Lerp(startLife, endLife, counter / duration);
        Debug.Log("Current Life: " + newPlayerLife);
        yield return null;
    }

    //The latest life is stored in newPlayerLife variable
    //yourLife = newPlayerLife; //????

    isRunning = false;
}

Usage:

Let's say that player's life is 50 and we want to remove 2 from it within 3 seconds. The new player's life should be 48 after 3 seconds.

StartCoroutine(loseLifeOvertime(50, 2, 3));

Note that the player's life is stored in the newPlayerLife variable. At the end of the coroutine function, you will have to manually assign your player's life with the value from the newPlayerLife variable.

Programmer
  • 121,791
  • 22
  • 236
  • 328
  • In my case, the while loop is only running once. `counter` is definitely less than `duration` (as I've checked using `Debug.Log()`). Any reason why this may be happening? Just to clarify, every iteration this script should write `newPlayerLife` to the log shouldn't it (only writes to the log once in my case, leading me to believe the while loop is only being executed once)? – Anya Oct 27 '18 at 16:10
  • Add edit to your [question](https://stackoverflow.com/q/53023297/3785314) followed by the code your're currently using and how you're using it. I will be more willing to help. – Programmer Oct 27 '18 at 16:15
  • This doesn't work if you try to decrease life again while the coroutine is still running. So if you take damage multiple times, it will decrease it only once. – HamsterWithPitchfork Sep 27 '20 at 05:18
0

I suppose, what you are looking for is a Coroutine. Check out here and here for the documentation. It will allow you to do your custom health reducing actions separately from update function. Using coroutines you can make something happening by ticks, and you can determine how much time the tick is.

vmchar
  • 1,314
  • 11
  • 15
0

You could use couroutines. Something like this:

void OnHitByFire()
{
    StartCoroutine(DoFireDamage(5f, 4, 10f));
}

IEnumerator DoFireDamage(float damageDuration, int damageCount, float damageAmount)
{
    int currentCount = 0;
    while (currentCount < damageCount)
    {
        HP -= damageAmount;
        yield return new WaitForSeconds(damageDuration);
        currentCount++;
    }
}
  • Thanks for all the answers everyone, really appreciated. I ended up implementing something similar to this to get the desired result. – fourthking Aug 17 '17 at 14:42
  • 1
    Why is this even up-voted or accepted as an answer? 1.This will not work. It will decrease life once at the duration time not within duration. It will simply do it in one loop due to `WaitForSeconds(damageDuration)`. damageCount and damageAmount parameter....Don't they mean the-same thing? Don't blindly up-vote or accept answer. Read the code and make sure it does what its suppose to do. This answer will not help people with this same problem. – PassetCronUs Aug 17 '17 at 14:52
  • 1
    WaitForSeconds(damageDuration) wont make it run once, itll make it wait 5 seconds before running again according to his example but itll still run 4 times. All I really had to do was use my own values to get the desired result I wanted – fourthking Aug 17 '17 at 15:25
  • 1
    `WaitForSeconds(damageDuration)` will wait for the duration passed to it and is not doing this over-time. *"It doesnt specifically answer the question"* Programmers's [answer](https://stackoverflow.com/a/45737410/8407180) did answer your question with a correct code that functions properly and likely to help others. I am not trying to tell you which answer to accept. Just trying to say that accepting non working answer is not helpful for the community. If this works for you then fine but it doesn't because I tested both answers and programmer's answer is the only working one. – PassetCronUs Aug 17 '17 at 15:34
  • 1
    Alright, thats fair. So then ill unaccept this one and post what I used as an answer. – fourthking Aug 17 '17 at 15:37
  • 1
    Well, that's why my answers starts with the words *something like this*. As I said, it is jsut an example how coroutines work, not the final piece of code. Calm down, @PassetCronUs – user3699959 Aug 18 '17 at 11:43
  • @user3699959 I am sorry if you took that as on offense. I know you were trying to help but I didn't mean any harm. I thought this is not useful since this is not a question about [how coroutine work](https://stackoverflow.com/questions/12932306/how-does-startcoroutine-yield-return-pattern-really-work-in-unity).If you put your answer in a pseudo-code form that get the translated to C# **that would be fine**. Also, your answer is just a code. *It just does not explain anything*. – PassetCronUs Aug 18 '17 at 16:08
  • To me, it looks like you took Programmer's answer, quickly modified it, then replaced `Time.deltaTime` with `WaitForSeconds` and then added some random parameter+code that does not make sense and won't even work. The **most** important part of this question is not waiting, it is **that something should be done within x amount of time** which means a variable should be decremented to something within x amount of time. You missed that part too. If you had a pseudo-code that did that, that would be excellent.I would up-vote that. I hope I explained this well to you. Thanks – PassetCronUs Aug 18 '17 at 16:16
0

So this is what I ended up doing. It causes the character on fire to lose 30 health and you can see the health ticking down instead of it happening over intervals.

  IEnumerator OnFire()
    {
        bool burning = true;
        float timer = 0;
        while (burning)
        {
            yield return new WaitForSeconds(0.1f);
            hp -= 1;
            timer += 0.1f;
            if (timer >= 3)
            {
                burning = false;
            }
        }
    }
fourthking
  • 321
  • 1
  • 4
  • 19
  • This timer should work. I don't know why you chose to hard-code every value instead of using the solution I provided which does this automatically for you. You will basically have to change most of the values if you want to change the 30 health, the decrement duration or the value it decrements with. Happy coding! – Programmer Aug 17 '17 at 15:59
  • 1
    I hard coded it just to show it here but I wont hard code it in my code. Thanks for your answer – fourthking Aug 17 '17 at 16:01