Control lerps with if-statemnts in Update or with Coroutines?
I created a project a while back where buttons in the UI of the app started programmed animations. I had a landscape model that I scaled in the Y axis to morph it from a 2D map into a 3D map and vise versa. I also blended between textures. I did this by triggering coroutines when a UI button was presses. But the problem was that each coroutine had to finish running before the opposite one could run. So when the player choose to morph the map into 3D and then clicked again to morph it back into 2D before the first morphing was complete I ran into problem. The first coroutine simply kept playing until it was finished, and then the other one kicked in, but that made a jump in time and everything became very jerky. I tried using StopSoroutine() but that didn't work. In the end I worked around it by only showing the reverse-button after the coroutine had finished.
But that's not what I want this time in my new project. I want all my lerps to be able to change direction at any time. So if the lerp has reached 75% (goring from 0.0 to 1.0) when a new button is pressed I simply want to update the lerp values so that it changes direction but uses the current value as start value, so it now goes from 0.75 down to 0.0 again.
Can this be done with coroutines, or is it better to just do it inside the Update method and control it with if statements and changing the values on the fly?
The reason I looked into coroutines was because I thought it would give better performance. If the lerp is in the Update method then Unity has to check every frame if the lerp is currently active or not. I'm not sure how expensive an if statement check is though? But morphing the landscape isn't something that the player does very often, hence my idea was simply that rather then checking every frame if the lerp is running, simply off-load it to a separate coroutine and trigger that only when it's needed.
Any advice on how to make this work and be performance gently for mobile devices? :)
Cheers!
Answer by UnityCoach · Dec 04, 2016 at 02:33 PM
I wouldn't use Coroutines for this. You can simply disable the component whenever the value isn't changing.
Here's some code, using a property that detects if the value changes, so that you only trigger the mechanics when it does change.
I've added a UI Text and a UI Slider for testing purposes.
using UnityEngine;
using System.Collections;
using UnityEngine.UI;
public class LerpBackAndForth : MonoBehaviour
{
public Text uiText;
public Slider uiSlider;
public float minValue;
public float maxValue = 1f;
public float speed = 1f;
private float _lerpValue;
private bool _forward;
private float _value ;
public float Value
{
get { return _value ; }
set
{
if (value != _value)
{
_value = value ;
// TODO : add your mechanics here
uiText.text = _value.ToString();
uiSlider.value = _value;
}
}
}
void Start ()
{
uiSlider.maxValue = maxValue;
enabled = false; // component disabled upon start
}
void Update ()
{
_lerpValue += (_forward ? speed : -speed) * Time.deltaTime;
Value = Mathf.Lerp(minValue, maxValue, _lerpValue);
if (_lerpValue <= 0)
{
_lerpValue = 0;
enabled = false;
}
else if (_lerpValue >=1)
{
_lerpValue = 1;
enabled = false;
}
}
public void Animate (bool forward = true)
{
_forward = forward;
enabled = true;
}
public void Reverse ()
{
Animate (!_forward);
}
}
And if you want more control over animation ti$$anonymous$$g, you can drop the Lerp and use Animation Curves ins$$anonymous$$d..
using UnityEngine;
using System.Collections;
using UnityEngine.UI;
public class AnimateBackAndForth : $$anonymous$$onoBehaviour
{
public Text uiText;
public Slider uiSlider;
public float speed = 1f;
public AnimationCurve forth;
public AnimationCurve back;
private bool _forward;
private float _time;
private float _value ;
public float Value
{
get { return _value ; }
set
{
if (value != _value)
{
_value = value ;
// TODO : add your mechanics here
uiText.text = _value.ToString();
uiSlider.value = _value;
}
}
}
void Start ()
{
enabled = false; // component disabled upon start
}
void Update ()
{
_time += Time.deltaTime;
Value = _forward ? forth.Evaluate(_time) : back.Evaluate(_time);
if (_time >= forth.keys[forth.keys.Length-1].time)
{
enabled = false;
_time = 0;
}
}
public void Animate (bool forward = true)
{
_forward = forward;
_time = 0;
enabled = true;
}
public void Reverse ()
{
Animate (!_forward);
}
}
Wow, nice! Thank you for such a detailed respons! I will start on this project on $$anonymous$$onday, so I will get back to you later in the week when I have tried this out :)
Follow-up question: could one use Events or Delegates for this? To register when a button has been pressed I mean and then enable/disable the component?
yes, you could totally use an event that you raise whenever the value changes. I do this very often, so I can split things up and have several behaviours subscribing to on event.
Something like this :
public delegate void LerpChange(float lerp) ;
public event LerpChange lerpChanged;
private float _lerpWithEvent;
public float LerpWithEvent
{
get { return _lerpWithEvent; }
set
{
if (value != _lerpWithEvent)
{
_lerpWithEvent = value;
// TODO : add your mechanics here
if (lerpChanged != null)
lerpChanged(_lerpWithEvent);
}
}
}
Your answer
Follow this Question
Related Questions
Performance Issue with Coroutines 2 Answers
Lerping Vectorisity spline 0 Answers
Best way (performance wise) to move multiple objects in certain time 0 Answers
Object lerping forwards a set distance every 3 seconds. 1 Answer
Coroutine: delay transform rotation but start transform movement immediately 2 Answers