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.
Related
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'm using SmoothDamp in a co-routine to gradually resize a character collider as the character plays a crouch animation:
void OnRifleIdleToCrouchStart(){
float idleToCrouchVelocity = -0.01f;
IEnumerator idleToCrouchColl = smoothHeightChange(idleToCrouchVelocity, 1.45f, 1.2f, 0.38f); //smoothTime is .38 since speed for idle to crouch is 2
StartCoroutine(idleToCrouchColl);
}
IEnumerator smoothHeightChange(float idleToCrouchVelocity, float targetHeight, float targetCenterY, float smoothTime){
Debug.Log("smoothHeightChange START " + Time.time);
while(Math.Round(controller.height, 2) > targetHeight)
{
float idleToCrouchVelocityCenter = idleToCrouchVelocity * -1f;
float newHeight = Mathf.SmoothDamp(controller.height, targetHeight, ref idleToCrouchVelocity, smoothTime);
float newCenterY = Mathf.SmoothDamp(controller.center.y, targetCenterY, ref idleToCrouchVelocityCenter, smoothTime);
controller.height = newHeight;
controller.center = new Vector3(controller.center.x, newCenterY, controller.center.z);
yield return new WaitForEndOfFrame();
}
Debug.Log("smoothHeightChange FINISH " + Time.time);
}
I expected the crouch to take the length of smoothTime (.38 seconds) but comparing the start and end times shows it actually takes around 2 seconds. I suspect this is because I'm waiting for the end of each frame in the coroutine, and SmoothDamp assumes its being done every frame. So my smoothDamp value needs to actually be smaller than .38 proportional to the time left in each frame over the course of the animation. But I'm not sure what value I should modify smoothDamp by, as the frame timing is likely different each frame over the course of the animation (~40 frames). How do I refactor the coroutine takes the length of smoothDamp to complete?
Thanks!
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;
}
}
}
When I start up my game it stays around 95-101 rapidly changing, in between all of those numbers.. but when I open up the stats bar I'm getting upper 200's low 300's
so wondering why that is still new to c# so be easy on me lol. heres the code
thanks in advance as always ^_^.
float deltaTime = 0.0f;
void Update()
{
deltaTime += (Time.deltaTime - deltaTime) * 0.1f;
}
void OnGUI()
{
int w = Screen.width, h = Screen.height;
GUIStyle style = new GUIStyle ();
Rect rect = new Rect (0, 0, w, h * 2 / 100);
style.alignment = TextAnchor.UpperRight;
style.fontSize = h * 2 / 100;
style.normal.textColor = new Color (255.0f, 255.0f, 255.0f, 1.0f);
float msec = deltaTime * 1000.0f;
float fps = 1f / deltaTime;
string text = string.Format ("({1:0.} fps)", msec, fps);
GUI.Label (rect, text, style);
}
}
In order to display a meaningful FPS rate you need to measure how many frames were rendered over a constant period of time, for example one second. Then only after that period do you display the calculated value on screen. This will provide for an average frames per second as opposed to an instantaneous frames per second, the latter of which is not particularly useful in most cases as it leads to widely fluctuating values.
Code
First define some fields:
DateTime _lastTime; // marks the beginning the measurement began
int _framesRendered; // an increasing count
int _fps; // the FPS calculated from the last measurement
Then in your render method you increment the _framesRendered. You also check to see if one second has elapsed since the start of the period:
void Update()
{
_framesRendered++;
if ((DateTime.Now - _lastTime).TotalSeconds >= 1)
{
// one second has elapsed
_fps = _framesRendered;
_framesRendered = 0;
_lastTime = DateTime.Now;
}
// draw FPS on screen here using current value of _fps
}
Cross-technology
It should be pointed out that the above code makes no particular use of Unity whilst still being reasonably accurate and is compatible with many frameworks and APIs such as DirectX; OpenGL; XNA; WPF or even WinForms.
When I start up my game it stays around 95-101 rapidly changing, in between all of those numbers.. but when I open up the stats bar I'm getting upper 200's low 300's
The ASUS VG248QE is 1ms and the max it can do is 144Hz so it is unlikely you are getting "upper 200's low 300's". FPS is meaningless when VSYNC is turned off on a non-GSYNC monitor. Is your VSYNC turned on?
In Unity, FPS is equivalent to number of Updates that occur in 1 second. This is because Update() is called every Time.deltaTime seconds.
InvokeRepeating method
You can also use InvokeRepeating to implement your own FPS counter while using only integers, like this:
private int FrameCounter = 0;
private int Fps = 0;
void Start()
{
InvokeRepeating("CountFps", 0f, 1f);
}
void Update()
{
FrameCounter++;
}
private void CountFps()
{
Fps = FrameCounter;
FrameCounter = 0;
}
Then just display the Fps variable in the OnGUI() method. Using this method, your Fps value will get updated every second; if you want more frequent updates, change the last argument of InvokeRepeating call and then adjust the Fps calculation accordingly.
Note, however, that InvokeRepeating takes Time.timeScale into account, so e.g. if you pause the game with Time.timeScale = 0f; the counter will stop updating until you unpause the game.
FixedUpdate method
Another approach is to count the FPS in FixedUpdate() method instead of OnGUI() or Update(). This gets called every Time.fixedDeltaTime seconds which is always the same, no matter what. The value of Time.fixedDeltaTime can be set globally for the project via menu Edit->Project Settings->Time, item Fixed Timestep.
In this case, you would count frames the same way (in Update), but update your FPS counter in FixedUpdate - which is basically the same as calling you own method with InvokeRepeating("CountFps", 0f, 0.02f) (0.02f being a typical Time.fixedDeltaTime value, but this depends on your project settings as per above).
Conclusion
Most of the time, you won't need to update the displayed FPS that often, so I personally like to use the InvokeRepeating method and 1 second intervals.
OnGUI function is called at las twice per frame (sometimes more). You are calculating your "FPS" inside OnGUI so it will almost never be accurate.
Defining :
deltaTime += (Time.deltaTime - deltaTime) * 0.05f;
will bring your FPS values closest to real but it will not be accurate if you calc it on OnGUI method.
I guess (not sure) that you should use FixedUpdate() instead of OnGUI() to calc your FPS. (also you don't need to change your deltaTime to multiply by 0.05f if you use FixedUpdate)
Im creating a speedometer in Unity and I want my speedo arrow to smoothly lerp up to it's max angle based on the wither or not a button is pressed on. When the button is let go, I'd like the arrow to fall back to it's original rotation value.
However, I'm having issues trying to work out how to make my object lerp from its original position, to it's new position and back again. I've got it so when I press a button the objects jumps to a position, however, I need it to be smoother. Could someone please look over my code and point out what I need to do please?
float maxAngle = 100.0f;
float maxVel = 200.0f;
Quaternion rot0;
public float rotateValue;
// Use this for initialization
void Start ()
{
rot0 = transform.localRotation;
}
// Update is called once per frame
void Update ()
{
if(Input.GetKey(KeyCode.Space))
{
SetNeedle(rotateValue);
}
}
void SetNeedle(float vel)
{
var newAngle = vel * maxAngle / maxVel;
transform.localRotation = Quaternion.Euler(newAngle,0,0) * rot0;
float angle = Mathf.LerpAngle(minAngle, maxAngle, Time.time);
transform.eulerAngles = new Vector3(0, angle, 0);
}
From the documentation:
static float Lerp(float from, float to, float t);
Interpolates between a and b by t. t is clamped between 0 and 1.
Which means t goes from 0% to 100%. When t == 0.5f the result will be the middle angle between from to to.
Hence, if the needle needs to be moved in a smooth and constant speed, you should do:
time += Time.deltaTime; // time is a private float defined out of this method
if(time >= 1.5f) // in this example the needle takes 1.5 seconds to get to its maximum position.
time = 1.5f // this is just to avoid time growing to infinity.
float angle = Mathf.LerpAngle(from, to, time/1.5f); // time/1.5f will map the variation from 0% to 100%.
But that requires the from and to to be constant during the lerp fase.
So what you need to do is the following:
when you press Space, set time = 0, from = currentAngle and to = maxAngle; Obviously this must be done only once during the key down event.
when you release Space, set time = 0, from = currentAngle and to = 0. Also do this only once.
All right?
You should use Time.deltaTime, not Time.time. That should make it move correctly.
EDIT: deltaTime won't solve the problem alone, however. Sorry I didn't fully read your code at first.
In order to smoothly move the needle, you need to know where the needle is currently, where it needs to go, and how much time has elapsed since the last time it moved.
We know how much time has elapsed with Time.deltaTime. But your code currently doesn't take into consideration where the needle is. float angle = Mathf.LerpAngle(minAngle, maxAngle, Time.deltaTime); will always evaluate to roughly the same value, so your needle will never move beyond a tiny bit.
What you should do instead is lerp from the needle's current position towards the maxAngle.
float angle = Mathf.LerpAngle(currentAngle, maxAngle, Time.deltaTime);