I want to lerp the start color of two particle systems from one color to another in Unity if the player enters a triggerzone. I tried to do it with Color.Lerp but the result is that it lerps "laggy", meaning it has only 3 colors inbetween. My code:
public IEnumerator animateTriggerEnter(float duration = 0.1f)
{
float elapsedTime = 0f;
float lerp = 0f;
while (lerp <= 1f)
{
elapsedTime += Time.deltaTime;
lerp = elapsedTime / (float) duration;
topParticle.startColor = Color.Lerp(standardColor, triggerColor, lerp);
botParticle.startColor = Color.Lerp(standardColor, triggerColor, lerp);
yield return null;
}
}
For the value lerp, I always get the same 6 values, but should it not be more? It also remains laggy with a higher duration.
First of all I'd try isolating
Color.Lerp(standardColor, triggerColor, lerp);
and testing it's speed Stopwatch?
If I am right lerp is noting more than
result=startValue + (endValue - startValue) * lerpValue;
In that case + 1 constructor execution.
According to MSDN
math itself will should take less than
lerp = elapsedTime / (float) duration;
Are you sure it's lerp problem?
Related
So I was looking for ways to do smooth angle motion in Unity and I stumbled upon this clip of code:
IEnumerator SlideToPosition(Vector3 targetPos, float time)
{
// Use an animation curve to make it look sweet!
AnimationCurve smoothly = AnimationCurve.EaseInOut(0, 0, 1, 1);
Transform myTrans = transform; // cache the transform for extra efficiency!
float curTime = 0;
Vector3 startPosition = myTrans.position;
moving = true;
while (curTime < time)
{
myTrans.position = Vector3.Lerp(startPosition, targetPos, smoothly.Evaluate(curTime / time));
curTime += Time.deltaTime;
yield return null;
}
moving = false;
myTrans.position = targetPos;
}
This worked absolutely fantastically and I was looking for a way to do angled motion the same way as the poster of the original method (from 6 years ago) said it would be easy to do angular movement in the same way. I've tried everything and for the life of me can't seem to get it to rotate more than a degree or so. Can anyone help me out? Here is where I'm currently at:
IEnumerator rotateToPosition(Vector3 targetAngle, float time)
{
// Use an animation curve to make it look sweet!
AnimationCurve smoothly = AnimationCurve.EaseInOut(0, 0, 1, 1);
Transform myTrans = transform; // cache the transform for extra efficiency!
float curTime = 0;
Quaternion startAngle = myTrans.rotation;
moving = true;
while (curTime < time)
{
myTrans.rotation = Quaternion.Lerp(startAngle, Quaternion.Euler(targetAngle), smoothly.Evaluate(curTime / time));
curTime += Time.deltaTime;
yield return null;
}
moving = false;
myTrans.rotation = Quaternion.Euler(targetAngle);
}
Any help would be greatly appreciated. Thanks!
The last parameter of Lerp is the time component - this is a float value between 0 and 1 which you can think of as the percentage of movement between the start and finish. i.e. a value of 0.5f would be exactly half way between (in your example) startAngle and targetAngle.
So, just do curTimefor the last parameter, as you add to it each frame with curTime += Time.deltaTime this gradually moves between 0 and 1. If you want to vary the speed of the lerp then multiply this value e.g. to make it 50% faster use curTime += Time.deltaTime * 1.5f.
I have a object that moves at a variable speed. It can reach from 0 speed units up to N speed units.
I'm lerping the camera angle to rotate and look at the object smoothly and nice, like this:
camera.eulerAngles = Vector3.Lerp(initialAngle, finalAngle, speed / N);
The problem is when the object collides and the velocity decrease instantaneously to 0, the lerp happens very abruptly.
How can I make it handle this situation? I'd like a fast interpolation in this case, but no instantaneous like I'm seeing.
You can clamp the speed delta per iteration:
private const float MAX_DELTA = 0.1F;
private float prevSpeed;
private void UpdateRotation() {
float currentSpeed = Mathf.Clamp(speed, prevSpeed - MAX_DELTA, prevSpeed + MAX_DELTA);
camera.eulerAngles = Vector3.Lerp(initialAngle, finalAngle, currentSpeed / N);
prevSpeed = currentSpeed;
}
I have a simple board game where tokens get moved from square to square. I am using a coroutine to move the tokens. It does something like below
IEnumerator MoveTokenCoRoutine(int steps)
{
while (steps > 0)
{
transform.position = Vector3.Lerp(transform.position, newPos, Time.deltaTime * speed);
yield return null;
--steps;
}
}
The clip is a sliding sound for 2 seconds.
Steps, is how many squares a token should move using Lerp. The only time I could get it synced is with below parameters.
1) Set the lerp speed to 10 which is default speed i use to move
2) Step = 1 (move one square)
3) Call Play() just before while
With this scenario movement and sound effect stay pretty much in sync.
However, if steps get more than 1 and I put the Play() inside loop it just plays one time and stops. But the token keeps moving until while ends.
What approaches do I have to keep this in sync?
There is not much salvagable from your code, unfortunately.
A better working variant would look like this:
IEnumerator MoveTokenCoRoutine()
{
float currentTime=0.0f; // note: NOT in seconds, 1.0f = end of sound sample
Vector3 oldPosition = transform.position;
float startTime = Time.time;
while (currentTime < 1.0f) {
currentTime = (Time.time - startTime)/ DurationInSeconds ;
transform.position = Vector3.Lerp(oldPosition, newPos, currentTime );
yield return null;
}
}
Required variables:
Vector3 newPos Target position at animation end
float DurationInSeconds Length of the sound sample in seconds
I have a coroutine that moves my Camera upwards each time the player reaches a certain point in the game. I used a coroutine so that the camera will move smoothly over time.
Here's a snippet of my code:
private IEnumerator MoveCameraUpCoroutine(Vector3 startPos, Vector3 endPos, float duration)
{
float elapsedTime = 0;
while(elapsedTime < duration)
{
transform.position = Vector3.Lerp(startPos, endPos, (elapsedTime/duration));
elapsedTime += Time.deltaTime;
yield return null;
}
}
public void MoveCameraUp(Vector3 startPos, Vector3 endPos, float duration)
{
StartCoroutine(MoveCameraUpCoroutine(startPos, endPos, duration));
}
In my controller script, I just call my coroutine like this:
cam.GetComponent<CameraMovement>().MoveCameraUp(cam.transform.position,
new Vector3(cam.transform.position.x, cam.transform.position.y + setupLevel.heightOfBlock, cam.transform.position.z),
0.1f);
The problem with this is that the camera's movement is not always consistent in terms of where it's supposed to stop. I did some debugging. On the first run, the camera moved to the 0.7864508 yPos. On the second run, the camera moved to the 0.7789915 yPos. etc. It's not consistent.
But when I simply use Translate instead of my coroutine:
cam.transform.Translate(0, setupLevel.heightOfBlock, 0);
I get consistent end values for the camera's yPos at 0.7876318, which is what I need. But this code does not move the camera smoothly over time which is not what I want.
Does anyone know how to fix this coroutine issue? I don't know but I think there's something wrong with my coroutine code. Any help is greatly appreciated.
The result is actually quite consistent with what's expected. There's absolutely nothing wrong with your code, rather it's because of of differences in frame rates.
The reason you're seeing the minor differences is because there's no guarantee that two frames will take the exact amount of time to render.
Take a look at this line
elapsedTime += Time.deltaTime;
What you're doing here is adding an inconsistent value to your elapsed time. (i.e. Time.deltaTime is different every frame).
One partial fix could be to use Time.smoothDeltaTime instead, which is a smoothed out value for deltaTime over several frames. This, however is not going to be perfect.
A second approach, (not entirely an answer per se, but I'm leaving this here for others as well) is to use a tweening engine such as DoTweenor iTween.
These engines have methods that essentially do what you're trying to.
float elapsedTime = 0;
float ratio = elapsedTime / duration;
while(ratio < 1f)
{
elapsedTime += Time.deltaTime;
ratio = elapsedTime / duration;
transform.position = Vector3.Lerp(startPos, endPos, ratio);
yield return null;
}
With this setup, your loop will run until ratio is 1. When 1, endPos is returned and the loop exits next round.
I think the issue was that you compare elapsedTime to duration. So on the last run, you move then you increase and you compare. As a result, the last increase is not considered and you end up somewhere near the end but not at the end.
I'm want to rotate an object on an axis for specific angles over period of time.
And repeat that on the reverse direction once it reached the limit (of let's say 5 degree).
I could use Quaternion.Euler() to do the rotation towards 5 degree, but how do I check whether it has reached 5 degree and start reversing the direction towards -5 degree?
so in Update() I do:
int dir = 1; // somewhere global
Quaternion r = Quaternion.Euler(0, Timer.deltaTime * dir, 0);
transform.rotation *= r;
// I want to: if the "angle is >= 5f", i want to do dir *= -1 to reverse it
if (/* angle delta is >= 5f or <= -5f */)
{
dir *= -1;
}
Thanks
If you just want to rotate back and forth, you can use a sine wave to move back and forth smoothly.
public class rotator : MonoBehaviour {
public float _Angle;
public float _Period;
private float _Time;
// Update is called once per frame
void Update () {
_Time = _Time + Time.deltaTime;
float phase = Mathf.Sin(_Time / _Period);
transform.localRotation = Quaternion.Euler( new Vector3(0, phase * _Angle, 0));
}
}