Alright so in that last video, I made a bit of an error. I don't know if you caught it ... actually it wasn't really an error, but I violated our rule of sorts that we established of putting code in it's own particular method that handles a certain process and has a single responsibility. In particularly referring to the EnemyProximity() method that we created that housed the calculating the distance between the cube and the sphere But unwittingly in this very same method we also stored the code for moving the sphere towards the cube. This is a movement method. Even though it's two lines of code, it probably should go in its own method. Let's quickly refactor that and put it in its own method here for the SphereController just like we did for the CubeController. Take that out of the EnemyProximity() method and then put it in private void Movement(). This is a movement method specific for the SphereController. Then call it here. Build it. We already got an error. What's our error? Cube ref position does not exist in the current context.
Remember we took CubeRefPosition from a field, a class level variable. We made it local to the EnemyProximity() method. See that's not going to suffice any longer because now we have two methods that need to reference this variable. We pretty much have to go back to ... The easiest thing to do is put it back into a field of its own because now it needs to be kept alive in between each method's call. Right? Let's do exactly that. Let's put it as a field back up here and remember our naming convention that we were going to follow. Upper-case, PascalCase convention for fields and lower-case, camelCase convention for local variables. This is no longer a local variable. Going to do these all manually here. There we go. We need to remove that so that it's only declared once. That solves that. But what we could have put as a local variable, using much the same logic, was the Distance field here. We can just make that into a local variable. It was only public because we wanted to see its value in the inspector for a brief period of time. That's not really relevant. We can just go Vector2 Distance right there. And that works just fine.
I guess this is a good chance to explain constructors. In the last video I kind of left the idea hanging and gave you sort of an example of how a constructor could be used, either in a struct or an object when setting it. When you first set it as a variable you have the chance or the option to invoke the constructorStructs and objects both have constructors. What is a constructor exactly? We could invoke a constructor here. We don't have to because it's a struct but nonetheless what is a constructor? Constructors are really simple to understand. Basically they are methods that, either for a struct or an object, are named the exact same name as the struct or object. The type, in other words, the class or struct type. When you create a new version of that struct or object you can run this constructor and that constructor, when it runs, is meant to put that object or struct into some sort of valid initial state. It's like an initialization or construction method that's particular to the type in question. Again, structs do not require a constructor. You don't see the new keyword followed by new Vector2 unless you want to, so it's an option. Again, because it's a value type as I mentioned in the previous video, but with objects you are required to run the constructor right at instantiation, right after the new keyword. We've always seen this, the wizard = new Wizard() with the empty parentheses. Or, of course, you could put something in those parentheses if there's input parameters available.
Let's look a little closer at constructors. Even though it's a struct we can still look at constructors for Vector2. There you go. There's a constructor right here, constructs a new vector with given X,Y components. That's that constructor right there. That's not very instructive so what we'll do actually is we'll make our own constructor. Let's go back to our test right here, RPGController, RPGClasses. Right here in the Weapon class. It doesn't actually have a constructor yet, so how do we run this method when we say weapon = new Weapon()? How do we even run that method? It doesn't exist in the Weapon class that we created. Actually it does exist. It just gets automatically created. An empty constructor in this case. That's why there's empty parentheses. There's nothing as an input parameter. There's no implementation details either. Here we create our own constructor, if we want, so simply say public Weapon(). It has to be the same name. Parentheses. We'll put something in there if we wish. Now we can set its fields, for example, to some sort of value so that might be a good idea to put it in some immediate valid state right at instantiation because you could get some sort of weird errors if you don't do it right when you instantiate it in memory. That's why constructors are particularly useful.
Actually, just for the sake of visual appeal I'm going to put the constructor right above the first actual method. As you see, a constructor doesn't have a return type because it doesn't return anything. It's meant to just modify the values, predominantly of the fields and properties of that class. Here we have two class fields and so we can say, we can pass in a string and let's say lower-case weaponType following our naming convention rules. How about int degradation, again local to this method as we did in the deflect method. What we'll do is use this constructor to set this.WeaponType, which refers to whatever object is created, whatever instance. There's going to be maybe several instances of this so it's going to refer to this WeaponType. The class level instance field is going to equal whatever we pass in here as WeaponType in the constructor. This upper-case D Degradation. I don't know if I mentioned too much about the this keyword. It's totally optional. In this case it's actually not required at all because I'm differentiating the variables upper-case for the field, lower-case for the local scoped input parameter. Nonetheless I try to be in the habit of using the this keyword in these kinds of exact usages because it just makes it that much more apparent when glancing at the code what's going on. I'll reference the lower-case d degradation and take that out because we're no longer needing to initialize it to a consistent state of 100. We can pass in whatever degradation value we want it to hold at instantiation.
As you see here, now it's got an error because it expects there to be a constructor with two input parameters. This wasn't too well thought out because we want to have each Combatant have its own kind of Weapon. I don't really want to get too much into that because we're not going to use this code in our actual project. I just want to focus on constructors right now so bear in mind this is going to be not really useful, but it will still illustrate how constructors are used. We can pass in here for Combatant a weaponType Let's say again, "sword", as we did before and degradation 100. Again, if you're creating a game this is definitely not going to be the right way to do it. You'd probably want to have a constructor for the Combatant and then have a combatant take in a Weapon and create that object elsewhere, put it into the Combatant's constructor and so on. Anyway, it doesn't matter because we're not using this code. Actually we'll just revert to the way it was previously because it's going to ruin our references. It's going to screw up how we set things in the RPGController. We effectively overrode what the Weapon was holding. It doesn't actually ruin the references, but let's give it a default. Just say "rusty sword" just for creativity's sake, and we'll leave that.
That's the constructor for the Weapon. Now, going back to the Vector2 distance here, we have the option of running the constructor. Right? As I said, with an object you need to run the constructor, even if it's empty, but with a value type, like an int, Vector2, any kind of struct, it immediately gets set in memory with either nothing ... It just sets out that memory space, or whatever you assign at that declaration of that variable and the variable type. Right now we could use the constructor, if we want, for distance, and re-work this a bit. What do we have available to us? Have to say new Vector2 to access the constructor, so we can put in X,Y as floats because we have either an empty constructor or specify the X and Y properties as input arguments to the constructor. Sure, why not? Let's just do this a little bit differently and use the constructor. I already wrote this out. Just copy and paste it. Basically, copying this right here, but as before with the Vector3, just put them in floats. In the constructor, reference that X and Y. Right? I don't like that space. Take it out. Oh yeah. Never did fix this, did we? This has to be upper-case C. Go ahead and build it. Awesome. There we go. A lot cleaner, I think, this code.
One other thing I want to quickly mention about this local variable, choosing to create a locally scoped distance variable. Again, later, just like we did with the CubeRefPosition field. We decided to make that local. We might decide to make this distance local variable into a field again, right? But there's another consideration. When you make it locally scoped, it gets created and destroyed each time that method is run. It gets re-written over and over again in memory. It's basically like the memory space is being allocated and then erased. Presumably. I'm not sure exactly how memory management works in C#. It's something called a garbage collector, so it automatically gets destroyed when there's no longer any use of that throughout the code. A garbage collector just automatically gets rid of that memory allocation for that variable if it's no longer used. Presumably with this EnemyProximity() method. In the Update() method, updates running again, what? If it's 60 frames per second it's running 60 times a second, so presumably this distance variable is being created and destroyed 60 times a second.
That might seem exceedingly injudicious to the performance and to keeping things nice and clean and tidy in memory. It seems like an extra process that's unnecessary. In this case we could have left it as a field, like a private field or whatnot, and it would have been maybe even a better choice. I don't really care. Honestly. I'm trying to keep things as clean and separate as possible. Keep my fields as few as possible, as necessary. To put it simply, this game that we're creating, is not going to be pushing anybody's machine's performance boundaries. Right? If it is, I'm sorry to say maybe it's time to upgrade. Alright? I'm not going to worry too much about it in this case. There's always going to be some cases where performance really does matter. You want to squeeze out every CPU cycle and every little bit of memory usage as you can. But it doesn't really matter. It's pretty negligible. That's about it for this lesson. We'll go into a lot more coding with our game project in the next lesson. See you there.
Lesson 29 - Constructors, Local Variables in the Update Method