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.
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.
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.
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++;
}
}
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;
}
}
}
Related
Using yield return new WaitForSeconds(waitTime);
within an IEnumerator, what is the shortest wait time other than 0? I have tried using a float number and have tried as low as 0.00001f for the waitTime, however i'm not sure if there is a limit or not?
The purpose is that I am having a player's coins added one unit at a time, so the meter is 'filling up' rather than instant. I have tried searching but I cannot seem to find the answer to what the shortest limit is for WaitForSeconds, so if anyone knows I'd greatly appreciate the answer.
Additionally, my code is as follows if anyone has any input on how I can speed up the process without making it instant, as it's just not quite quick enough and the player is having to sit for a while waiting for the coins to be added to the meter at the end of the game.
IEnumerator AddCoins()
{
yield return new WaitForSeconds(1);
while (userGainedCoins > 0)
{
if (addingSoundPlaying == false)
{
addingSound.Play();
addingSoundPlaying = true;
}
if (userGainedCoins == 1)
{
addingSound.Stop();
}
userCoins += 1;
userGainedCoins -= 1;
PlayerPrefs.SetInt("User Coins", userCoins);
yield return new WaitForSeconds(waitTime);
}
addingSoundPlaying = false;
}
I think it doesn't have a minimum time, it just waits until it reaches a certain amount before waiting. If you want, you can just add multiple coins at a time. I usually try not to mess with these kinds of things, and instead of messing with the coins, I would just mess with the value it displays. You could have a script that holds the number of coins and increases/decreases it based on the timestep. Something like this:
public int coins = 100;
public int displayedCoins = 0;
private void Update() {
int change = Mathf.CeilToInt(300 * Time.deltaTime);
if (displayedCoins < coins)
displayedCoins = Math.Min(displayedCoins + change, coins);
else
displayedCoins = Math.Max(displayedCoins - change, coins);
}
and with this you can just set the amount and forget about it, and it works with gaining and losing coins. You can also change the change value to something like int change = Mathf.CeilToInt(Mathf.Abs(coins - displayCoins) * Time.deltaTime) to make it have an ease-out effect, or whatever you want. (I didn't test this code.)
I don't know if this is an easy question or not.
I'm trying to write a single function that slows down the player and returns them to normal speed after a certain amount of time. I founds some answers online but they all use a second function to set a timer, but I'm trying to contain the whole thing in one function.
I've already tried this:
public void slowDown(float slowAmount, float durationSlowed)
{
var playerInfo = player.GetComponent<FPSMovement>();
playerInfo.speed *= slowAmount;
float lengthSlowed = Time.time + durationSlowed;
if(Time.time > lengthSlowed)
{
playerInfo.speed /= slowAmount;
}
}
and calling it with:
slowDown (0.5f, 2f)
It manages to slow down the player, but doesn't return their speed to normal.
What am I missing here?
I assume this is unity.
You probably want to do it with IEnumerators and Coroutines. They distribute code to be executed over multiple frames. Something like this:
IEnumerator SpeedUp() {
speed *= 2;
yield return new WaitForSeconds(2.0f);
speed /= 2;
}
void Update (){
if(Input.GetKeyDown(***YOUR KEY HERE***)) {
StartCoroutine(SpeedUp());
}
}
This will double the speed of your character, wait 2 seconds, and halve it. If you want to add a cooldown to the amount of times you can add speed, just drop a boolean check.
I have a little question. I would like to increase the Light.spotAngle property over period of time. The written code works but I want this increase with speed, or something like that. I want to increase the value at spot angle at some speed, not to be directly 100, but from 30 slowly to grow by 10 to 100.
Transform thisLight = lightOB.transform.GetChild(0);
Light spotA = thisLight.GetComponent<Light>();
spotA.spotAngle = 100f;
I tried with Time.DeltaTime, but is not work.
Help!!!
Use Mathf.Lerp to lerp from value a to b. The a value is 10 and the b value is 100 according to your question. Do that in a coroutine function. that gives you control over how long you want that slow tansistion to happen.
When you click an Object, start the coroutine function.
The coroutine function:
IEnumerator increaseSpotAngle(Light lightToFade, float a, float b, float duration)
{
float counter = 0f;
while (counter < duration)
{
counter += Time.deltaTime;
lightToFade.spotAngle = Mathf.Lerp(a, b, counter / duration);
yield return null;
}
}
Will change Light's SpotAngle from 10 to 100 within 5 seconds.
public Light targetLight;
void Start()
{
StartCoroutine(increaseSpotAngle(targetLight, 30, 100, 5));
}
See this post for how to detect click on any GameObject.
I am trying to make a game which "kind of" simulates the shooting of the "worms" game. The player can choose the position (circular) of an object and then, the force that is applied to the object should move in the direction its pointing towards. I tried using the AddForce(transform.right) code, but it would just go to the right. (2D BoxCollider and RigidBody2D)
Then comes the hard part, making the player choose the force by charging the power. When the player holds down the "f" key, I want the power to go up to a certain point. Once it reaches that point, I want it to go down again and then up again, so the player can choose the power he wants. I have no idea how to go about this.
It's been awhile since I've did Unity coding, so there may be some minor errors with my syntax but it should give you an idea of how to accomplish this. Your best bet for the loop is to use a coroutine to not block the main thread.
in Update() check for 'on key down' for F and start this coroutine:
IEnumerator Cycle()
{
float max = 10.0f;
float min = 1.0f;
float interval = 0.5f;
do
{
for(int i=min;i<max;i++)
{
PowerValue = i;
yield return new waitforseconds(interval);
if(!input.getkey(f))
break;
}
for(int i=max;i>min;i--)
{
PowerValue = i;
yield return new waitforseconds(interval);
if(!input.getkey(f))
break;
}
} while(input.getkey(f));
}
And back in update() use that powerValue with getKeyUp(f)
And here is PowerValue setup as a parameter that prevents code from setting the max and min outside of a 1 to 10 range (configurable)
private float powerValue = 1.0f;
public float PowerValue
{
get { return powerValue; }
set {
if(value>10f)
powerValue=10f;
else if (value<1f)
powerValue=1f;
else
powerValue=value;
}
}
I am trying to figure out how to count 10 times each second and display it.
I have an int called countpoints which is what the user starts with points. Lets say 800.
I would like to drop 10 points each second but showing each point falling instead of every 10 points like my script below does.
here is how I have done so far:
if(miliseconds <= 0){
if(seconds <= 0){
minutes--;
seconds = 59;
}
else if(seconds >= 0){
seconds--;
countpoints = countpoints-10;
}
miliseconds = 100;
}
miliseconds -= Time.deltaTime * 100;
This runs in void update and here countpoints falls by 10 each second. but i would like to be able to show the numbers drop down like a stopwatch each second. How do i do that?
Any help is appreciated and thanks in advance :-)
You should use coroutine for that calculation instead of in Update(). It can be very easy with the coroutine. You just need to start croutine and then wait for 0.1 seconds and decrease counterpoints by 1. Again call that coroutine inside that to keep it running. Just add condition whenever you want to keep on calling it.
private int countpoints=800;
private float t=0.1f;
private int noOfSeconds=90;
private int min;
private int sec;
private int temp=0;
void Start ()
{
StartCoroutine(StartTimer());
}
IEnumerator StartTimer ()
{
yield return new WaitForSeconds(t);
countpoints--;
temp++;
if(temp==10)
{
temp=0;
noOfSeconds--;
}
min = noOfSeconds/60;
sec = noOfSeconds%60;
if(noOfSeconds>0)
{
StartCoroutine(StartTimer());
}
}
void OnGUI ()
{
GUI.Label(new Rect(100f,100f,100f,50f),countpoints.ToString());
GUI.Label(new Rect(100f,160f,100f,50f),"Time : "+min.ToString("00")+":"+sec.ToString("00"));
}
You could just do it in the Update method and decrement the points once every 100ms, you need to take care of not rounding things up or down as the error would be systematic and you'll get choppy results.
Using coroutines won't work as expected as the interval is not guaranteed.
private float _milliseconds = 0;
private int points = 800;
void Update()
{
_milliseconds += Time.delta * 1000;
if( _milliseconds > 100 )
{
points--;
//add updating GUI code here for points
_milliseconds -= 100;
}
}
You won't get choppy decrements as the _milliseconds are decremented by 100, so even if there are differences in the duration of the frames in the long run you'll get a proper handling.
One problem with the script is if frames take a lot more than 100ms consistenly , but if it takes that long you probably have bigger problems:D