Lesson 35 - Understanding the Lerp Method

Tutorial Series: Introduction to Unity with C# Series

Previous Article  |  Next Article


Transcript

The purpose of this lesson is going to be to introduce you to a very useful Unity concept called Lerp or Lerping. Lerp refers to a commonly used, but often poorly understood method in the Unity engine called Lerp(), which stands for Linear Interpolation. Actually there are several different versions of the Lerp method found in a few different classes that mainly differ in what kind of values they operate on, but otherwise they all function under the same premise of linear interpolation. If you understand one type of Lerp, you pretty much understand them all. Here is another funny sounding term which is actually really simple on its basis. It’s basically just transitioning a value from point A to point B or a time. One of the reasons why this is so useful in Unity is because of the difficulty of handling changes over time via an update loop. Now we saw how this could be accomplished with ordinary Controller-based movement, but the Lerp method mainly comes into play whenever you need a predictable change to occur and then presumably stop after a period of time. This could be anything from change in the color of the background to say day to night or it could be fading a GameObject until it disappears or moving a GameObject from here to there, such as with the enemy movement pattern.

Whatever the case, learning how to Lerp is crucial to any of these tasks. To start off with we’ll demonstrate how Lerp methods work by setting up a new test script called LerpTest and we’ll attach it to the Test GameObject and we’ll go from there. LerpTest, and attach it to the Test GameObject. Don’t need that outputting anymore. We’ll open it up. Just to start out with, we’ll just make it fairly simple and we’ll say float Accumulator, which will make sense shortly, and we’ll give it that value; and float output. For consistency's sake, it doesn’t matter, use PascalCase. In the update we’ll call the Lerp() method, so the one that handles floats and outputs a float is in the Mathf.Lerp().As you see there, it takes a three floats. From point A to point B over a percentage, which represents time as a percentage of a value between A and B. I’ll explain that all in a minute here. I’ll pass in -5 for the point A, 5 for point B, and I’ll reference the Accumulator right there. Since returns a float, we'll return it to our Output field. Here we’ll increment the Accumulator += a relatively small value, but not too small, .004f. Here is a primer as to what to expect from this. In this case we’ve setup the first input parameter as - 5 and the second as 5. As I said earlier, the from and to points respectively. To transition between them, we refer to the third parameter, which is what we create as the Accumulator.

Now the way that this transition occurs is it’s a percentage, it represents a percentage between those two values. For instance 50% of the way between - 5 and 5 is 0, right? If I just stick in 0.5 as in 50% in his third parameter and then just for now to the debug log, we’ll output that. What should you expect? I just told you, but there 0 as being output on every frame, so that’s not very optimal. We’ll do something about this in a minute. That 50% represents the midway point, pretty easy. That’s great, but what we really need is a gradual transitioning between the two values over time, so what we really need to do is change this third parameter over time, so that it continues to represent a progressive percentage point between minus 5 and 5. In another words we want to go from returning from almost 0% of the way between these two values, which would be -5, all the way up to 100% of the way, which is 5. So that would mean, for example, 25% of the way would be -2.5, 50% is zero, 75% will be 2.5 and so on. That’s why we use the Accumulator that I setup here to store the value that is constantly increasing with each update call. Let’s now reference the Accumulator here instead of that static or I should say unchanging percentage. Accumulator will accumulate a percentage over time, so to better see what’s being output than what we are seeing through the debug log, let’s output it to a GUI as we define here.

Let’s just define an onGUI() method, and here let’s just give it at a content color, so it stands out, say magenta. Let’s give it a Label, new Rect(105, 250, ) and here we’ll go String.Format() We’ll say Lerp Accumulator for this output where you want to see. We’ll also want to output the Output right here. We want to see how these both output, make this slightly different and make this Output. This wants the output. Let’s run that, let’s actually take out Debug.Log(), we don’t need that anymore. It’s going to be outputting through the OnGUI() call. There you go. As the Accumulator is accumulating, we are getting closer to 100% and past 100% as I mentioned earlier, we get 5. Not too difficult to wrap our head around. At this point it should be fairly clear as to what this Lerp() method does, but now let’s make it a little bit more obvious and more akin to what you’ll be using it for in actual projects by attaching a SpriteRenderer component and have the return result of a Lerp() method move the Sprite from one point to another. Let’s do this. I don’t want to make a whole new Sprite for this, so let’s just go to our images here and let’s put our Tabor on there for the SpriteRenderer. Let’s just arbitrarily put it on the layer there. It stands out and we know what we are dealing with here and we don’t get it mixed up with our other items, just change this a bit. I don't know, there we go. Make it like a little green sort of, I guess a zombie Tabor of sorts. We can pretend, I guess it’s going after, has insatiable thirst for brains and we’ll be Lerping from there to wherever the brains are. Let’s just imagine that being the case.

Now let’s go ahead and move the Lerp-specific code into its own method called LerpTypes(), just to make it a little bit easier to keep separate. We’ll be returning the Output of the float because that’s returning a float. In update let’s just say, we've got to put it into a temporary Vector3 that will assign them to the transform for this zombie Tabor. In that Vector3 constructor, we’ll pass in for the X because let’s say we want to move it on the X coordinate. We’ll just reference LerpTypes() as the X input argument, and then just 1 and 1 for the second and third input, and then assign that to the transform.position. So, it actually moves it on every frame. Don’t get too confused here by using the return keyword, having return type and output being a field that survives between calls to LerpTypes() method. I’m simply doing this so that I could reference LerpTypes() as a method directly within the constructor as you saw here or otherwise I’d have to call LerpTypes() on an another line of code and then reference Output, the field where I’m referencing Lerp types. That’s the only reason I’m doing that, otherwise you don’t have to use the return in this case. I’ll run this now, so you can see the results of Lerp() a little bit more explicitly, there's our zombie Tabor here. Here you go, it’s going from -5 to, we’ll expect it at 100%, to stop at 5.

This pretty much demonstrates the general use of Lerping, but you’ll see a bunch of different ways of using Lerping in different cases. This particular usage is a very constant motion from one point to another. That’s because of the fixed incrementing Accumulator, and the fixed point A to point B input parameters. We could make Lerping also speed up faster and faster, simply by using an exponentially increasing Accumulator simply by doing the following. Instead of a constant, we can simply have plus equal itself and I’ll add a little multiplier just so it’s little slower, it’s not going too fast. I’ll make a little comment here. I’ll say Accumulator Type1 and this one is Exponential, which you’ll see running in a second here. Doubling, so it’s going to double every time it’s called, this method is called. For the other type of Accumulator which is linear, I guess you could say, so I’ll say, "Accumulator Type2: Linear Fixed Accumulator." I’ll say Accumulator plus equals what it was before, so 0.004f, and that’s the linear one, but right now we are going to just comment that out and stick with this one, which will be the exponentially increasing one. This is going to be like going 2 + 2 = 4, 4 + 4 = 8, 8 + 8 = 16, and so on. Rather than the previous Accumulator calculation, which was more like 1 + 1 = 2, 2 + 1 = 3, 3 + 1 = 4, and so on.

Now let’s just run this and you’ll see the difference. That’s also a very different type of Lerp motion and it’s also useful. Just the really simple change and you have a very different type of motion. Now as were commenting here, I’ll show a new little thing here that I didn’t show you before called Regions. All you do is to create a region is use the #region. There you go, it’s already as you can see in IntelliSense, it's already a recognized keyword. I’m going to say ACCUMULATOR TYPES, so you can look at this at a glance and know what the region has to do with. Regions are basically just for organization and I’ll show you here, #endregion, for the closing tag of that. Basically this is its use is you can then roll up the entire block here and get out of the way, but have just the note that’s useful, so that you can then open it up and then go back to it. Now I’ll show you a different Lerp(). In this case what I’m going to do is I’m going to have the 'from' input parameter gradually change over time rather than remain static, staying as -5 and then the Accumulator or the third parameter representing percentage of a way between point A to point B. I’m going to have that -5 change an increase, and so the percentage will represent a different value as that point A starting point changes over time.

Why not feed that return value back in as the first input, and in doing so, we can keep the percentage, the third input fixed. This will result in a motion that has the effect of smoothing when it reaches its destination since the relative percentage decreases the closer we get from point A to point B. It’ll be all pretty obvious here when I’m doing it. Let’s start off with another region as I show you this other kind of Lerp behavior. Here let’s say... let’s call this the fixed type, the fixed "from" Accumulator. "Lerp Type1: Constant transition, Fixed "from," and comment that out immediately. I’ll fix this up. That’s just so if you uncomment, the actual comment stays as a comment if you uncomment the entire block there. Now the other type that I just mentioned we’ll call "Lerp Type2: Smooth Transition, "No Accumulator Required." For this we will feed the output back in as the evolving point A. For this, we don’t even need the Accumulator anymore, we can use the static percentage. Boy, I’m making a mess out of this, kind of getting out of my control here. The only reason I did this, so you can just roll those up really quickly and come back to them because I think you will come back to this when you employ Lerping later on and see how you did it. Let’s see what this Lerp is like. It slowly finds way to its destination point. Almost gets to 5, not quite, but basically it will be interpreted as 5, which would be 100% of the way.

The way that this works is if you look at what output first holds, it starts off with zero. The first time this Lerp() method runs, it returns … 0.02 was our percentage for the third parameter, so it returns 2% of the way between 0 and 5, which is 0.1.Then it returns 2% of the way between 0.1 and 5, which is a slightly smaller increment, 0.098. This process continues all the way up until you’re close to 5, so for instance 2% between 4.9 and 5 will be an increment of only 0.002, but since it still hasn’t reached 5, the endpoint point B, it’ll still increment, however small, as you saw here towards the end there and produces a smoothing effect as it reaches its end destination. For this reason this Lerp is sometimes called a "Smooth Lerp." Often gives a more appealing looking result for gameplay purposes. A common one would be using the GameObjects current transform.position as input for the first point A position, and in Lerp from that position to wherever you want to end up, and outputting the value return back to the GameObjects transform.position in order to create that movement. Since every call to this Lerp() would make the transform.position a little bit closer to the desired endpoint, you get this kind of smoothing effect. You could do that simply by making a little modification to this, you’ll have the exact same effect but you’ll just type in transform.position.x.

It’s for the same basic idea, we are taking it from where it currently is and just increasing until it gets to the final destination. We are putting it back into the transform.position with the smoothing behavior. Basically you’ll have to pick and choose which Lerping approach is best for your particular mechanic that you’re trying to create. Bear in mind that there are a lot of different potential formulas you can apply to generate a particular Lerp motion. For instance, if you want to create say a wave pattern, you could use a smooth lerp on the Y-axis that loops up and down, and then have maybe a steady Lerp on the X-axis or any variation between to produce a wavelike pattern. In this case I purposefully chose very simple math formulas using just simple addition and multiplication because you can get pretty much any behavior you want that way. Honestly I’m not much for math and this is my hand-fisted way of approaching things, but in the end it works and you can achieve just about anything you really want with simple formulations. Before we leave this lesson and move on, I want to do something really quick and pretty simple, which is not going to make a lot of sense right away. I want to attach a SpriteRenderer to this GameObject, this test GameObject and I want to keep it all in code, so I’ll be attaching all this stuff in code rather than in the Unity Editor. This isn’t going to make a lot of sense, but it’ll be a bit of a preview to what you’ll learn later on. I’ll explain why I’m doing this in a second.

In Start() we are going to setup our GameObject and here I’ll put a comment, SpriteRenderer. I’ll say SpriteRenderer renderer, so that’s a local variable, equals GameObject. I’m referencing this GameObject and I’m calling the add component method, and then something you probably haven’t seen before, at least not in this course is, now I’m going to reference a type here in these angle brackets. I’ll go all into this later, it’s called generic type, actually in this case a generic method, because I’m calling method. It’s that line, I’m setting the renderer here in code, and then I’m accessing that renderer and feeding it a Sprite. Actually I have to hold off on that in a second, I’ll come back to it. renderer.sortingLayerName so it’s exactly as we set it up, Player1 and what did we do, we made a bit of a zombie-like color. color, new Color() and what was it here? here. These are, it’s not float values but colors here in the constructor taken floats. I’ll have to change what I had to a percentage as a percentage between those points. It’s just awkward, so 0%, 1%, 1 would be 255, 0 would be 0. I don't know, add about 60 I think so let’s just say 0.3. It was 255. This is about 30% of the way I think. I know my math is probably bad. I should make a green Tabor as we had before. Before we can get this working, we are going to have to … I’ll talk about this more later, but this is how you load assets dynamically in code. You have to do it through a resources folder. I’m going to create a Resources folder and any assets you add here you can reference in code and load it from that folder. In this folder what I’m going to do is I'm going to make an Images folder. Once again I’ll talk more about this later, this is more of a prelude to that, all these topics, I’ll mention why I’m doing this in the second.

What I want to do is I want to take my Tabor here and put it in my, I can't copy and paste, so I’ll have to import as a new asset. Now I can reference this folder on this line of code to import that Sprite. I access the... uncomment that first, I access the Resources class and the Load() method, which again, we use these angle brackets. In that method we’ll pass in the string reference to that image to that Sprite, so it loads that Sprite image. It’s not a spite, it’s a Sprite. That’s the property for, the renderer has a Sprite property. That looks, it’s all good. Now here I can just remove the SpriteRenderer here entirely, and it will show up. As long as the script is enabled, it’ll run the start method and it’ll create the SpriteRenderer dynamically in code. Now when we press play, it should create our SpriteRenderer and have it just as it was setup before. Great. That was a little quick diversion. Again, I’ll go back to this topic later about how to attach components in code and manage them a little bit more in code. That was more of a prelude to that, mainly so that I could get rid of this SpriteRenderer in the inspector. It kind of was ugly sitting around there with all these other scripts. I didn’t want you to worry about enabling and disabling and remembering that when you come back to this Lerp example. That’s quite a bit of work for today. I’ll see you in the next video.


Related Articles in this Tutorial:

Lesson 1 - Who This Course is For

Lesson 2 - What to Expect from this Course

Lesson 3 - Installation and Getting Started

Lesson 4 - Starting the First Project

Lesson 5 - Prototype Workflow

Lesson 6 - Basic Code Review

Lesson 7 - Game Loop Primer

Lesson 8 - Prototyping Continued

Lesson 9 - C# Fundamentals and Hello World

Lesson 10 - Variables and Operations

Lesson 11 - Variables and Operations Continued

Lesson 12 - Floats, Bools and Casting

Lesson 13 - If Statement Conditionals

Lesson 14 - If Statements Continued

Lesson 15 - Complex Evaluations and States

Lesson 16 - Code Syntax vs. Style

Lesson 17 - Variable Scope

Lesson 18 - Object-Oriented Programming Intro

Lesson 19 - OOP, Access Modifiers, Instantiation

Lesson 20 - Object Containment and Method Returns

Lesson 21 - "Has-A" Object Containment

Lesson 22 - "Is-A" Inheritance Containment

Lesson 23 - Static Fields and Methods

Lesson 24 - Method Inputs and Returns

Lesson 25 - Reference vs. Value Types

Lesson 26 - Introduction to Polymorphism

Lesson 27 - Navigating the Unity API

Lesson 28 - Applying What You've Learned and Refactoring

Lesson 29 - Constructors, Local Variables in the Update Method

Lesson 30 - Collecting Collectibles, Items and Powerups

Lesson 31 - Spawning and Managing Prefab Powerups

Lesson 32 - Implementing Powerup State Logic

Lesson 33 - Displaying Text, OnGUI, Method Overloading

Lesson 34 - Referencing Instantiated GameObjects, Parenting

Lesson 35 - Understanding the Lerp Method

Lesson 36 - Creating Pseudo Animations in Code

Lesson 37 - Understanding Generic Classes and Methods

Lesson 38 - Animations Using SpriteSheets and Animator

Lesson 39 - Working with Arrays and Loops

Lesson 40 - Debugging Unity Projects with Visual Studio

Lesson 41 - Camera Movement and LateUpdate

Lesson 42 - Playing Audio Clips

Lesson 43 - Routing Audio, Mixers and Effects

Lesson 44 - Adding Scoring Mechanics and Enhancements

Lesson 45 - Scene Loading and Game Over Manager

Lesson 46 - Understanding Properties

Lesson 47 - Controller Mapping and Input Manager

Lesson 48 - Understanding Enums

Lesson 49 - Dealing with Null References

Lesson 50 - Handling Variable Framerates with time.DeltaTime

Lesson 51 - Preparing the Project for Final Build

Lesson 52 - Final Build and Project Settings

Lesson 53 - Introduction to the Unity Physics Engine

Lesson 54 - Understanding FixedUpdate vs. Update

Lesson 55 - Movement Using Physics

Lesson 56 - Attack Script and Collision Events with OnCollisionEnter2D

Lesson 57 - Projectiles and Stomping Attack

Lesson 58 - Parallax Background and Scrolling Camera

Lesson 59 - Infinitely Tiling Background Sprites

Lesson 60 - OOP Enemy Classes

Lesson 61 - OOP Enemy Classes Continued

Lesson 62 - Trigger Colliders and Causing Damage

Lesson 63 - Multi-Dimensional Arrays and Procedural Platforms

Lesson 64 - Finishing Touches

Lesson 65 - Series Wrap


Comments

Please login or register to add a comment