Easing functions that run in a update function ???

Ok so I am currently lerping my camera to the destination in order to get the smooth movement I want.
At the moment it starts out going fast and then slows down because thats how lerp works
{x+= (target-x) * 0.1f }
The problem is that I want to have the camera start out slow and move faster as the distance gets bigger and then slow down as it gets closer to the character.

Ive looked into easing functions (http://sol.gfxile.net/interpolation/) but they all seem to have a fixed start and end point and I cant figure out how to adapt the functions to work continuously within a update function like lerp does.

So does someone have any ideas ?
Thanks for your time.

*Edit* I should add that I am interested in using smoothstep.

Comments

  • Why don't you calculate the amount you need to move using a Lerp (so it starts out fast and then slows down to the end)...

    And then instead of just using that value to move the camera, Lerp a movement variable from zero (or whatever value) to that value (and then use the new, twice lerped, value to move the camera).

    Depending on how you implement it, it might feel a bit wobbly. But it really depends on how much tweaking you do and what you use it for.

    In any case, hope that helps.
  • edited
    [deleted[
    Thanked by 1Kobusvdwalt9
  • edited
    There are a few ways. The question is do you want constant time for the duration of the tween (the camera always gets to the character in the same period of time. eg If it's far way it moves really quickly) or constant speed (if it's really far away it needs to move at the same speed until it gets near the camera?

    Constant time is easy: Just lerp the end point for your tweening equation.

    Constant speed is a bit harder. You need to work work with speed and acceleration. Smooth Damp kinda does this but is a bit fiddly to work so I tend to just roll my own. The hardest part is knowing how much to slow down by so that you don't overshoot the target. Without invoking Calculus, I'd probably blend between this acceleration and switch over to your lerp as you get closer to the target. This might wobble like evan said and would take some tweaking.

    Edit: Didn't know about DOTween. Glad to see they've fixed lots of the stuff I didn't' like about HOTween.
    Thanked by 1Kobusvdwalt9
  • Not exactly an answer to your question, but something I found useful on some projects was: https://xnatweener.codeplex.com
    It's basically just the standard tween math structured nicely. Fairly trivial to grab the source and rebuild for Unity.
  • Use AnimationCurve

    Then you can draw your own easing function in the inspector.
  • Thanks for the reply's guys.
    @BlackShipsFilltheSky I am going to try this out and see what happens, thanks.

    @SUGBOERIE I kinda tried this but I was unsure how it would work as both the characters and the camera is moving every frame. So how would I get a 0..1 range ?

    I get static tweening which works in something like a for loop, but what I am struggling to wrap my head around is ways of doing "dynaming tweening" where the start point and the end point is constantly moving. Also most tweening functions seem to work with a time variable t that tells the function how far along the tween it is. But because the tweening functions start and end point is constantly changing the t variable is constantly being reset because its effectively a new curve.

    I also tried some stuff with acceleration and velocity but I could not think of a way to prevent overshooting the target.

    Thinking about it more Evans solution will probably be the best in terms of producing actual results easily, but it would be cool if I could learn a bit more about tweening and that sort of thing.
  • edited
    Camera behaviour is hard. :P

    Personally, I'd keep track of the camera's normalised progress along the path as a separate variable between 0 and 1, which I'd increment each tick according to whatever acceleration curve I wanted (a (1+sin(x))/2 curve might be pretty good here, for example). I'd then recalculate the vector between the start and end points every frame, then calculate the camera's new position by using the new start point as a base, and adding the new inter-point vector multiplied by the progress variable. In pseudocode: newCameraPoint = startPointThisFrame + (startToEndVector * tweenProgress). As @BlackShipsFilltheSky says above, this would very likely result in some really weird motion, depending on how much the start and end points move during the interpolation, and you'll have to implement some sort of acceleration scaling when you increment tweenProgress to ensure that short distances are traversed faster than longer distances. You'll just have to feel it out and tweak as you go. :P

    EDIT: This, of course, assumes a single predefined, uninterrupted movement between two points (for instance, once-off camera switches between two characters). If you're looking for some sort of continuously moving camera with variable acceleration (for instance, a tracking camera for one character in a platformer that lags when they start moving and decelerates when they stop) then things will have to be done quite a bit differently. It'll help to know the context of the camera behaviour.
  • edited
    @Gazza_N This is exactly what I want >
    Gazza_N said:
    If you're looking for some sort of continuously moving camera with variable acceleration (for instance, a tracking camera for one character in a platformer that lags when they start moving and decelerates when they stop).
    I did try @BlackShipsFillTheSky 's method and it seems to work like it should. Stuff is nice and smooth now.
    *Edit* after testing it some more it doesn't exactly work like I wanted it too work because what happens is if the character telleports 100 x units forward and the camera is on 0 x the first lerp will take it a tenth of the way there on the first frame so thats a x of 10 then the second one will also take it a tenth of the way there which is 1. So on the 1st frame it moves one unit forward.

    If the characters teleported say 500 units forward it would then move 5 units on the 1st frame. This is not what I had in mind. I would like it to start out smooth and accelerate and then decelerate.
    Thanked by 1Gazza_N
  • You could try using your current lerping thing, and do it another time, lerping toward a target that is lerping toward a teleported character.
  • @Elyaradine I am not sure this would work. Because of the way that function works. x+= (target-x) * 0.1f is the same as x = lerp(x, target, 0.1f) if I am not mistaken. So it means that if your lerping towrads something thats lerping you could have just done a smaller step instead.

    IE
    x = lerp(x, target, 0.01f)

    would be the same as
    lerpVar = lerp(lerpVar, target, 0.1f) ; 
    x= lerp(x, lerpVar, 0.1f)


    If someone knows a way to do smoothstep in a update loop it would be great. I also found this :https://chicounity3d.wordpress.com/2014/05/23/how-to-lerp-like-a-pro/ But its intended for a static end point and start point.
  • Eh, what I meant when I was thinking through it was actually lerping an intermediate value, which is similar to what Evan suggested.

    i.e. Instead of jumping position, we're actually lerping a velocity. And instead of the velocity jumping, we lerp an acceleration. At which point I believe it's about solving what the acceleration should be, which afaik is a spring damping problem for what the "ideal" damping is at any time for the spring to come comfortably to rest.

    ...which is something I last did 8 years ago and can't remember. :D
  • Maybe you could do some really ugly coding and constantly change the step according to the distance between the objects. I know that's basically what keeping is but you seem to need more control over the step.
  • Keeping = lerping in predictive text btw...
  • Yeah, this is a fun double-part problem - you'd be looking to set your camera velocity proportionally to its distance from the character, with a sweet spot where it matches the character velocity (assuming Kobus has a constant following distance in mind once the character is moving), but also following a distinct acceleration curve when adjusting to that velocity. As Johno says above, solving for acceleration is the interesting bit here.
  • @Elyaradine @Gazza_N Thanks for the help guys but I am not sure what your talking about here. If you could write code or pseudo code that would help a lot cause I am missing something.
  • edited
    [deleted[
  • edited
    @bischonator: That approach "locks" the end position into the coroutine. That's exactly what @Kobusvdwalt9 doesn't want...

    A velocity-based system would probably look a bit like the following pseudo-code:

    Update(deltaTime)
    {
      distanceToTarget = targetPosition - myPosition;
      if (distanceToTarget.length > currentVelocity * deltaTime * someFactor)
      {
        currentVelocity += acceleration * deltaTime;
      }
      else
      {
        currentVelocity -= acceleration * deltaTime;
      }
      
      if (currentVelocity < minimumVelocity) currentVelocity = minimumVelocity;
      if (currentVelocity * deltaTime > distanceToTarget) currentVelocity = distanceToTarget;
    
      myPosition = myPosition + currentVelocity * normalize(targetDirection) * deltaTime;
    }


    Where you can mess with someFactor, acceleration and minimumVelocity to change how it works. Good starting values might be:
    someFactor = 5
    acceleration = 3 (?? Not sure about this, experiment with it)
    minimumVelocity = 0.2

    With those settings, if something is less than 5 times the distance it'd cover in the current frame from your target, it accelerates. If it's closer than that, it decelerates. You could make acceleration and deceleration relative to specific distances from the target and "smooth them out" that way, but that's left up to you... Also, this was written super-fast so it could very easily be full of derp.

    You can easily "reset" the camera to move slowly by setting currentVelocity to 0 :)
    Thanked by 1Kobusvdwalt9
  • edited
    [deleted[
  • dislekcia said:
    @bischonator: That approach "locks" the end position into the coroutine. That's exactly what @Kobusvdwalt9 doesn't want...

    A velocity-based system would probably look a bit like the following pseudo-code:

    Update(deltaTime)
    {
      distanceToTarget = targetPosition - myPosition;
      if (distanceToTarget.length > currentVelocity * deltaTime * someFactor)
      {
        currentVelocity += acceleration * deltaTime;
      }
      else
      {
        currentVelocity -= acceleration * deltaTime;
      }
      
      if (currentVelocity < minimumVelocity) currentVelocity = minimumVelocity;
      if (currentVelocity * deltaTime > distanceToTarget) currentVelocity = distanceToTarget;
    
      myPosition = myPosition + currentVelocity * normalize(targetDirection) * deltaTime;
    }


    Where you can mess with someFactor, acceleration and minimumVelocity to change how it works. Good starting values might be:
    someFactor = 5
    acceleration = 3 (?? Not sure about this, experiment with it)
    minimumVelocity = 0.2

    With those settings, if something is less than 5 times the distance it'd cover in the current frame from your target, it accelerates. If it's closer than that, it decelerates. You could make acceleration and deceleration relative to specific distances from the target and "smooth them out" that way, but that's left up to you... Also, this was written super-fast so it could very easily be full of derp.

    You can easily "reset" the camera to move slowly by setting currentVelocity to 0 :)
    Kinda what I meant but explained much better :p
  • edited
    This has been nagging at me, so here's some code that I wrote and tested that illustrates the principle along the x axis only. May need refinement, but it Does The Thing.
    public class CameraLaggoMatic : MonoBehaviour {
    
    	public float maxSpeed = 5;
    	public float thresholdDistance = 2.5;
    	public Transform playerChar;	
    	
    	void Update () 
    	{
    
    		//Find the distance of the camera from the character on x axis
    
    		Vector3 cameraGapVector = playerChar.position - transform.position;
    		float cameraGapDistance = cameraGapVector.x;
    
    		//Normalize to threshold distance (distance of maximum velocity)
    
    		float velocityMagnitude = cameraGapDistance / thresholdDistance;
    
    		//If we're beyond 1 on the threshold, we've teleported. Double the speed for a quicker snap.
    		if (velocityMagnitude > 1)
    				velocityMagnitude *= 2;
    
    		//Move the camera
    		transform.position += new Vector3 (velocityMagnitude * maxSpeed, 0, 0) * Time.deltaTime;
    	}
    }

  • Ok so I tested various stuff and @dislekcia 's method got the behavior I desired.
    @Gazza_N 's method started fast and then slowed down like a lerp (not sure if I did something wrong here ?)

    After doing @dislekcia 's method it felt to weird to me. Like it feels too delayed. In my head the camera behaved differently than reality. Nothing wrong with the code but the start slow, speed up, slow down method isn't great for platformer camera's;

    I am also disapointed that there is no easy way to use those easing functions in a dynamic way, but thinking about it more the acceleration/velocity way is better suited to moving end points.

    I think I am going to settle on smoothdamp (http://docs.unity3d.com/ScriptReference/Mathf.SmoothDamp.html). Its what feels best to me.

    Thanks again for the code and time.
  • @Kobusvdwalt9: If your levels are designed instead of randomly generated, you could always add invisible node objects that defined a camera position/orientation. Then as your player moves through the level, calculate the two closest nodes and lerp the camera position between the two node values accordingly. You could do neat stuff that way :)
    Thanked by 1Kobusvdwalt9
  • edited
    @Gazza_N 's method started fast and then slowed down like a lerp (not sure if I did something wrong here ?)
    Nah, I'm likely just not grokking exactly what sort of camera behaviour you're looking for. :P My camera fulfils the "lag when the player starts, accelerate to chase, and decelerate on stop" requirements, but it's a linear progression (so pretty much a lerp). I haven't implemented any sort of fancy acceleration curves to twiddle how the camera accelerates or decelerates. That's something you could do by feeding a sinusoidal or exponential function into the velocityMagnitude variable. Anyway, as long as it's helped you narrow down what you need, that's cool. :)

    Thanked by 1Kobusvdwalt9
  • Interesting discussion involving easing ;) For anyone interested I found a rather useful method to use for any interpolation curve was like this :

    I like to treat the time property as a normalized value , then have your start and end values be 0 and 1 respectively and your duration be 1.

    The result been you plug in a percentage and get back an interpolated percentage. This can be used to feed the "t" of a Unity Lerp function or as a Scalar in some or other multiplication logic etc..

    I have used this before on some camera coding where the "time value" was derived from a distance calculation.
Sign In or Register to comment.