Lesson 36 - Creating Pseudo Animations in Code

Tutorial Series: Introduction to Unity with C# Series

Previous Article  |  Next Article


Transcript

Alright. Now that we understand how to Lerp, we can put it to good use by creating what I call "pseudo animations." Basically manipulating the transform or the SpriteRenderer and so on in order to produce some sort of visual The first pseudo animation I want to do is pretty simple. I want to just fade out the render for the Cube GameObject whenever we engage Teleport(). Actually, I want to immediately make the cube disappear and then gradually fade back in. We can do this by accessing the transparency or alpha property through the SpriteRenderer's color property. Let's go to the cube here, and looking at the cube on the screen there, that's how we can access the alpha channel We'll need to Lerp that out at some point We can access this property in code by first retrieving a reference to the SpriteRenderer instance that's attached to this GameObject. We do that with the built-in Unity method called, GetComponent(). In the Scripts Characters Folder let's create a script called, CubeAnimate. We can just click and drag and put it right on the Cube, just like that. We can get that referenced to the SpriteRenderer by using GetComponent(), as I just mentioned. Let's put that in a temporary local variable called, cubeRenderer = Like that. Alright. Once again, we see these angled right here where we pass in the type that relates to the particular component we want return in code. We want a SpriteRenderer. That's why we're specifying that as the type Again, this is called a generic method, which I don't want to go into just yet, and I'll explain how this works under the hood For now what I want is just to, let's just get a reference to the SpriteRenderer and take the current color property and put it in a temporary local color variable.

Under here, make a temporary color variable called, "color" and we'll reference the cube renderer temporary variable, and we'll get its color property. A bit of a quick side note on these temporary You may have been noticing that we've been doing this frequently, storing things like Vector3s and colors into temporary variables and then assigning right back into the actual properties we want to actually change. We do this because these properties are structs, value types; right? When structs are properties, such as a transform or the color property, you can't just modify just the alpha channel or just the red channel, or for transform, just the X, Y or Z float properties. You have to modify the entire color or transform the entire struct when referring to, in this case, the SpriteRenderer's color property. We have to modify the entire color. What we're doing here is we're making a temporary color that then we can set its individual properties for the alpha channel, the RBG, red, blue and green color channels as well. Then we'll assign it back into the actual color property. We also do this with the transform positions, as I mentioned, where you have to modify the entire Vector3; right? The easiest way to do this is by creating the temporary variable, or you can actually use a constructor.

You can access the constructor after the new keyword, as you've seen before, and assign that to the property. I don't want to do that here, invoking the constructor. It's going to look a little bit messy I'll show you back in the Lerp example how you can do that with the Right here we made a temporary variable and then immediately stored it into the Because it's a struct it has to, we need to put an entire Vector3 into this position property. Instead, we can just use the constructor and We can just do that. A little simpler but otherwise get used to creating temporary variables to handle these issues with struct properties. Alright? we can now modify the alpha channel, the A float property of this color variable here with a simple conditional before we assign it back to the actual SpriteRenderer's color We won't determine what the condition is yet. We'll just write, true, if (true) and we'll put a little note here for us, "If Teleport is engaged, write in some logic If Teleport is engaged, whatever that means, take the color variable, the alpha channel, the alpha property, and immediately set to 0. Remember, I want to immediately disappear and then later fade in. Else, the alpha will be Lerped from the current color alpha property value.

If it's 0, it will Lerp() from 0 to 1F, 1, in other words, which is full transparency, which is to say no transparency at all. I'll give it a little percentage here value for it to change over time towards 100% I already tested this out. That's why I chose that number. I think it says, "Unreachable code" here because it's always going to evaluate true until we actually put a proper condition here that could possibly evaluate false, so we'll deal with that later. Just to make it clear, remember 1 represents 100% out of a value range of 0 to 255. 100% would be 255. The alpha channel for the render here, 255, would be 100%. We want to Lerp() from wherever it is. If it's at 0 because we've engaged Teleport, it will start Lerping all the way to 100%, which would be 255. We represent that as 1 as the float argument You might also notice that this else clause, it would execute under normal conditions. In other words, when not engaging Teleport, but since under normal circumstances the alpha channel is already at 100% or 1 in this case in the Lerp(), it will Lerp() from 1 to 1, which means you won't see any change. Even though this will always be returning 100% under normal conditions, it's essentially not a visible change. That's how this logic is working.

To make this all work, we can specify a state for when engaging Teleport() versus not engaging Teleport(), so why not do that with a public bool in the Let's go to the CubeController and let's say, it doesn't matter where. Let's just put it underneath the Teleport(), just keeps some organization to it. Say, public bool IsDisappear = false to start off with. Here we'll say, we'll reference that, and if we engage the Teleport(), in that instance when we hit the key down and those conditions are satisfied, for that very instance, that one frame. Else, we'll say IsDisappear = false. Now to reference this IsDisappear state from the CubeController script we can make it static just like we've been thus far when referencing fields from outside However, I want to show you how you can use GetComponent() to access a non-static field. Considering that the CubeController script when attached to this GameObject is a component just like any other. We can use the GetComponent() method to reflect back to the GameObject that whatever current script we're writing in and reference GetComponent(), it will point to that very same GameObject and then we can use that to retrieve the to whatever component name we pass into those angle brackets, in this case the cube It's important to realize it's not the script for the CubeController that we're retrieving. It's the actual instance attached to this GameObject as a component. Remember, once again, that instances are handled by Unity in the background, but this is one of the ways that Unity lets us get references to those instances even if we don't know them by name, by their identifier that Unity sets for them in the We can then reference this IsDisappear field in the particular instance of the cube by simply doing this.

We want a cubeController instance, and it will be referencing the instance that's already been attached as a component to this GameObject, so we just run the GetComponent() method and pass in the hat component instance we actually want. We want the CubeController. Again, the component, not just the script It's important to realize that. Then here we can say. if (CubeController.IsDisappear) so it's a bool, if it's true, then execute that. Otherwise, if it's false, execute that. We'll get that from the attached instance Let's save this and try that, see if we get that Lerping fading in and out as I don't know when I missed that, but I missed it, so put that there. We're also going to need to of course take that temporary color variable and reassign it to the renderer, the color property of the render is going to or we're going to, in other words, assign the temporary color that we're Lerping here to the color property, the actual renderer's Let's save that and see if we get this teleporting behavior fading out when we Yep. There we go. Pretty good. It fades back in. There's actually a shorthand for this right because we know that GetComponent(), it's going to take in the type CubeController and return to us a cubeController; right? Because it returns a CubeController, we can just use the dot accessor to then access that component's constituent its public members, in other words, simply by doing this. You can get rid of this entirely and just put the whole GetComponent() return, what's returned from the GetComponent() right here.

Again, CubeController, and we're returning a CubeController and one of its public is Is Disappear, just a handy shorthand. You'll see it often used in Unity, just this shorthand, so it makes your code a little less cluttered up. It may take a while to get used to it. I think everything is working pretty good. What I want to do next is add a simple pseudo animation to our Sphere enemies. How about we just randomly rotate them. That should be simple enough. Let's make another script for this once again. Call it, SphereAnimate. I'm just going to move this up just so it looks I'll re-enable that. We might want that working again, the SphereController script. This is going to be a lot simpler. Let's do this. Let's make a field an Int field called, as in Random Rotate Direction. We'll set that later, and a float field, RandRotateSpeed. We'll have a direction and a speed for Now we'll actually create the randomized rotation direction. Reference the rotate direction field, and we'll use the Random class, again for spitting out randoms, very useful I'll explain what I'm doing here in a second. It's going to look at little funky. Two, take away one, and I want to cast that to an int of course, because we declared that field as an Int.

The reason I'm doing this funny looking formula is I want to, what I want to do is I want to set the eyeball's rotation, the Sphere enemy, in other words, its rotation as soon as it gets substantiated, so that's why I'm doing the Start()method. I want to set the direction as either a counterclockwise or clockwise direction. I want to set a multiplier as being either -1 or Whatever random value we generate, multiply it by -1 or 1. I'm getting that -1 or 1 through this kind of funky looking formula right here where I'm employing the Random.Range() like we did before, which generates a number between 0 and 2 but excluding 2. It says, "Exclusive" there for the max number. It's basically zero or 1. Zero times 2 is zero. One times 2 is 2. Either we get zero or 2. If we get a zero and we minus by 1 we get If we get 2 and we minus 1 we get 1. That's why I'm doing it that way. Of course I cast it to an Int and make sure I thought this returned to float. It doesn't. I don't even need to cast. Actually I can get rid I'm sure there's an easier way of doing this. Again, this is just how I bash my head against math problems and solve them. I'm sure some of you out there can figure out If you do, let me know.

Thankfully, setting the rotation speed factor is going to be a lot more forgiving than doing Let's just set the random rotation speed here say, "Random," reference that float. Random.Range() Now, this will return floats because we're We want to actually have some sort of variable amount rather than just whole numbers returned. Basically, between 3 and 9. That's as fast as we want the rotation speed Finally, we want to set the rotate over so of course we'll have to do that in the setting the rotation by calling the Rotate() from the transform property. It's the easiest way of doing it that I've found. transform.Rotate() is that method. We want this one, where it takes in two floats. Is it that one? I don't know which one it is actually, to be Let's just pass that in. We do the rotation through the third or argument, which is Z, the angle. That's how the rotate is accessed. If we go here, rotation, you can't really see it because I have to manipulate it quite a bit for you to see it. Anyways, it goes through that property, Z value or the Z property, I should say. Let's just do this. Rotate speed multiplied by random direction, Let's run this and let's see how that affects the rotation of the enemies. It should be randomized in direction and It's taking a while for another one to pop up There we go. Yep. The other one is going in a different direction, certainly a different speed. Yep. Alright. Good.

Now we'll turn to the power-up items, which will allow us to create a simple by this time using, we'll use Vector3.Lerp(). Another Lerp but this time will be Lerping Let's actually just do the animation stuff this time as a method in the There's not a whole lot else going on in the so let's just do it there rather than creating Where do we have that? Items. Here again it doesn't matter where. Let's make another method just for animation call it, private void animatePowerUp() and transform.localScale. We want to change the scale. The transform has a scale property. Scale here makes it larger or smaller on the X That's what we'll be interested in here. We'll simply say, Vector3 whatever return value from Vector3.Lerp(), whatever we return from this method will get stuffed right into the local scale. Say, transform... Well, it returns a Vector3. That's why this is possible; right? We'll feed in the local scale, and then for Point B we'll make it go to the normalized which will make sense in a second I think of an un-scaled, in other words, an un-scaled 1 for each X, Y and Z value, and we'll give it that percentage value over Now we'll originally set it as scaled up, so it's going to go from scaled up to the un- That's how it's going to Lerp. It's going to Lerp scaled to un-scaled basically.

For that, let's go actually to the prefab, because that's what this script is attached to, and let's edit it right here. Let's scale this to 2 and this to zero. You'll see what I mean when we Lerp it. Let's watch those ... I know, I didn't call that method. I knew I was missing something. Let's go here and say ... It doesn't matter. I'm going to put it above it, though. AnimatePowerUp(), call that method. Now let's watch the items as they pop into They should scale with that Vector3 Lerp It should actually go from a scale state to an That's what the Lerping will do. I'll try to pause that handy little feature and put it on Pause, and I'll try to pause it, un-pause it when there's a item. I didn't do it in time. Oh, yes, I did. There you go. It's some catching, sort of mid-Lerp, where the X value is going from ... What was it 2 to 1 and Y is going gradually from 0 to 1. Once it reaches 1, 100% of the way, it will effectively be 100% of the way through That's why we did it that way.

I want to finish off this lesson with a bit of an interesting pseudo animation just to give the What I want to do is add eyes to our little Cheesehead character and then have the eyes follow the main Sphere enemy. Let's start off by adding a left and right eye for our Cube as child GameObjects. Call this EyeL and this one EyeR Both of these are going to need a spray We will want to import a new image that I made, nothing too fancy. (Cough) Excuse me. Import new asset. Import this asset, and as usual we'll set its, we'll set this to point, no filter, apply that. We'll put that in the SpriteRendererer for each I already figured out these settings, where to put it just right. For the left eye, go minus .21 for X, and here, .24 for Y. We'll scale it. I made it a little too small so just scale it. Put on Player1, same layer. Then put it, just to It's popping up in front of our Cube here, but just to make sure, let's put it on the order in Layer 1 because Cube is on 0. Always going to be on top of the Cube so we don't have any rendering issues. I may have done this slightly wrong.

Let's actually make this ... Set this to minus .21 and .24 for Y. I made the eye a little small, so let's scale it For this one make that .06, and there you go. Yep. Got our little CheeseHead with eyes now. In our scripts let's create a new script called, CubeEyesAnimate. We actually have to do it in its own script here. You'll see why, because we'll be attaching it to so each eye will have its own script. It will actually be quite important to getting this so open up the cube eye. Actually, let's do it this way. It doesn't really matter, but ... We're going to need to reference the Sphere. The eyes are going to have to look at the It's going to need to know what the position, so go to Start() and establish the SphereReference as GameObject.Find(), and then the main Sphere GameObject. I'm just going to copy and paste this code because we're not really going to try to make In the inspector we'll want to put that script on each eye, so that one and that one. Let's run it and let's see if it follows our Yep. Here, I'll try to maximize it. It's a little more conspicuous. It's following around the sphere, apprehensively looking at the Sphere enemy Alright. Pretty good.

Honestly, I didn't come up with this code on I really don't know how these calculations but that's the beauty of coding in the 21st There's a lot of stuff you don't really need to how it works or even how to solve a problem. You just need to be able to go on the internet, and as long as you know how to ask the right chances are there's somebody out there who solves your problem and shared it on the Why reinvent the wheel; right? In this case I found this on a forum. I'll try to post the Q&A forum where I found the so I give credit where credit is due. I want to show you really quickly, there is a bit of a hidden issue here, which we'll solve a little later on in coming Watch what happens when I try to engage the Teleport() and fade the Cube. Did you notice that? The Cube faded out but not the eyes. Alright. I want to put off fixing this issue for now as it requires some understanding of concepts that we haven't covered yet. In particular, arrays. We'll be covering that and other topics in the I'll see you there.


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