Has anyone ever experienced things behaving faster/slower in the final build? I have this coroutine that shakes a door when player interacts with it but in final build its (almost exactly) 2x slower than in the editor. I haven’t been able to figure out why so I’m wondering whether there is some kind of unity setting that would affect this?
public IEnumerator Shake(Param.Attack attack)
{
bool toggle = true, moving = true; float time = 0f, speed = 1f, distance = 0.1f;
Vector3 start = transform.position;
Vector3 current = transform.position;
Vector3 destination;
// figure out whether attacker is in front of or behind door
Vector3 forward = transform.TransformDirection(Vector3.forward);
Vector3 attacker = Vector3.Normalize(attack.attacker.position - transform.position);
if (Vector3.Dot(forward, attacker) < 0) {
destination = transform.position + transform.forward * distance;
} else {
destination = transform.position + ((transform.forward * -1f) * distance);
}
while(moving)
{
time += Time.deltaTime * speed;
if (toggle) {
current = Vector3.Lerp(current, destination, time);
if (time >= 1f) { time = 1f; }
if (current == destination) { toggle = false; time = 0f; }
} else {
current = Vector3.Lerp(current, start, time);
if (time >= 1f) { time = 1f; }
if (current == start) { moving = false; }
}
transform.position = current;
yield return new WaitForEndOfFrame();
}
shake = null;
}
Or is it because of this line here…
yield return new WaitForEndOfFrame();
And the final build is limited to 60 fps and the editor runs with no vsync? If I use Time.deltaTime then FPS shouldn’t matter though right?
You need to work out an interpolation value that is a calculation of the current time / end time.
This is generally how I’ve always lerp-ed based on time:
float start = 0f;
float end = 5f;
while (start < end)
{
start += Time.deltaTime;
float t = start / end;
Vector3 p = Vector3.Lerp(a, b, t);
yield return null;
}
Worth pointing out, if you have a coroutine you want happening on a per-frame basis, use yield return null instead as you’re just creating tons of allocations for no reason here.
Also you write code in a way that’s really hard to read.
Your code is not framerate independent, because you’re doing this:
current = Lerp(current, target, time);
That gives you an ease out, but the lower the framerate, the faster the ease approaches the end. This should be intuitive if you think about it - if you move 1/30 of the way to the destination (30fps), that moves you further than if you move 1/60 of the way twice (60fps).
A perhaps easier way to understand the same concept is that adding 10% twice is more than adding 20% once, as (1.1 * 1.1 * x) == 1.21 * x, which is not the same as 1.2 * x!
You should use a linear interpolation from start to end instead, and then add an ease function on the time variable. A nice repository of easing functions is easings.net. Here’s your code in a framerate independent manner. I also simplified your check for if the attacker is in front and your ping pong implementation, because both was kinda overly complex.
public IEnumerator Shake(Param.Attack attack)
{
float time = 0f;
float speed = 1f;
float distance = 0.1f;
Vector3 startPos = transform.position;
bool attackerInFront = transform.InverseTransformPoint(attack.attacker.position).z > 0;// same as the dot, just more intuitive
if (attackerInFront)
distance = -distance;
Vector3 destination = transform.position + transform.forward * distance;
var endOfFrame = new WaitForEndOfFrame();
do
{
yield return endOfFrame;
time += Time.deltaTime * speed;
var lerpTime = time < 1f ? time : 2f - time; // ping-pong
lerpTime = 1f - (1 - lerpTime) * (1 - lerpTime); // easeOutQuad
transform.position = Vector3.Lerp(startPos, destination, lerpTime); // note that Lerp is clamped, so there's no need to clamp time.
} while (time < 2f);
shake = null;
}
Note that this is still not completely framerate independent, as you will always overshoot the final time unless you hit the exact float value. But it’s a lot better than what you had, and as good as you can really get it for this case.