Lesson 39 - Working with Arrays and Loops

Tutorial Series: Introduction to Unity with C# Series

Previous Article  |  Next Article


Transcript

In this lesson, I'm going to cover another important C# concept that we can immediately apply to our game project, and that is the topic of arrays, as well as the closely related concept of looping statements, which are also called iteration statements, or just loops. So, to better understand arrays, we can kind of recycle a previous concept of containment that we looked at earlier, but use it in a bit of a different way. So, remember when I dragged out those boxes and showed all of these containment structures? You know our squiggly brackets? They're kind of like these containers within containers. Well the concept is pretty much the same with arrays. The simplest kind of array is just a single box that in turn it contains a bunch of boxes. Just like why you'd have boxes within other boxes in the real world, you would do this whenever you want to logically group things together, and yet all still be reachable from within a single reference point, that broader single box that all those boxes are inside of. So now I'll show you how to create an ordinary array 'box' in script.

So first in our Tests folder create a script called ArraysAndLoops and then will attach it. ArraysAndLoops. And we'll attach it as usual to our Test GameObject. Open it up., So, in Start() this how you declare an array. So, we want an array of integers. After int, use those left bracket and right bracket. Sort of looks like a box actually. Beside the type, so that's an array of ints. That's what we're saying here, we're declaring that as the type here. And we'll call this, we'll label this as arrayExample, as the name or identifier., And then we'll use the new keyword, and then again reference the type with this box. And then inside this box, we declare how many sub-boxes how many boxes within this greater array box we have. So we're going to set it to five. One things you know right away is this is fixed, the declared amount of sub-boxes that you have. Once you set it here, it's not really easy to change with arrays. So now we have five boxes, each of these sub-boxes can each hold a single integer value. Now, each of these inner boxes have preset labels called indexes or indexers or indices, whatever you prefer. And the boxes are labeled starting from zero. So we can stuff into these boxes their individual integer values by referencing the indexes as follows.

So reference the the name and then the index. So the first box, starting at zero we'll put in the value 22, and the second box, see arrays are zero-based, so it's kind of awkward, it's one of most challenging things to remember with arrays, that they start from zero. So the first item is zero and the second item is one. Something you have to get used to, not a big deal. It actually can cause errors the in compilation if you make a mistake, as it's almost certain you will with having index out of range. We'll probably do without at some point. In 36 for this, 2, and for the fifth one. Give it 8. Now, when you want to reference these sub-boxes in whatever way, you sort of open up the box and get that number by referencing the array through its index. So, for example, do something like this., We'll output it to the Debug.Log() arrayExample. Now let's let's grab the third box, alright, that's what I'm saying. You got to get used to that, you're probably thinking, "That's the second box." No, it's the third one. We're going to expect to output that. So let's run it, just to make sure it's working. There you go, 36. OK. That's all really great and all, but you must be wondering, "Why are arrays so useful? Why not just make five individual integer variables, and name them something like example1, example2, example3, and so on, and then just treat that as your sort of pseudo grouped collection of values?

Well in this simple scenario that I showed you here, that would probably work just fine. But where arrays really show their power is when combined with iteration statements, or loops, right? So there are a few different types of loops in C#, but to my mind, the most foundational loop is the for loop, as it has built into it much of what you would need when iterating through a collection such as arrays. So you have a counter, a conditional evaluation that continues the loop until it becomes false. So sort of like a built in if statement, and an incrementing calculation for the counter. So to set up a for loop, I'm going to show you a cool thing in Visual Studio called code snippets. Code snippets are just a simplified shortcut of sorts for establishing a common code template. So for example, the code snippet for for loop is 'for, [TAB] [TAB] ' and I do that kind of fast. So to actually show that the code snippet shows up in IntelliSense, and there's a bunch of code snippets for lots of common code elements. So there is a code snippet for the for, there's other stuff here, you'll see the foreach later. So when you use the code snippet, it sort of sets up a preset, like a template for what makes up a typical for loop. So as I mentioned earlier, you have the counter which is initialized here to zero. Typically it is, no doesn't have to be. And then if the counter is less than, and here it gives you sort of a random variable that has no meaning right now, just reminds you have to put in whatever the uppermost limit number you want to reference is. Also, you have the way that the counter increments.

So with this for loop, I'll show you how to loop through an array to output all of its values. So we have five individual values in our array. So, I can write in, if our counter is less than five, right? But this isn't always going to work, because sometimes you don't know what the size of the array is. In this case we do, but when you don't, you can reference a really handy property of arrays built in that's called length. And just to mouse over here, it says, 'Gets the integer that represents the total number of elements in all the dimensions of the array.' So, that will return five, we know that that's what that will return. And then simply do this, Debug.Log() and then what we can do is reference that counter right there. So we know that the way this works is it's sorta like an if statement that just loops. So it first checks the condition. If the counter is less than five, so it starts off at zero, if that's the case then do this. We know that this is going to first print out 0, or the first index array, this one, right? Starts at 0, that's why that's important. And then loops back around until this evaluates false, which will once it reaches five.

So let's just run this so you can see how it outputs. It's going to output to the console. Comment this out first. There you go, just outputs in order each element or each item in the array. Let's put a little comment here. I'm going to mess around with this to show you how you can go through different variations on these loop conditions. And so we'll put a comment here, says "Increments through the array," and we'll comment that out for now. Actually, copy and paste it, it will be quicker. Make the changes to it. So, we'll want to take this, place it here. So it starts off at the uppermost limit in the array, which will be 4. And, now this is going to turn 5, right, because we have we have five items, right? But if we start by referencing the fifth element, this will return five, so it will reference the fifth element that doesn't exist. So what we''ll want to do is just take that and and minus one from that. So that it returns 4, right?. And if i is less than.. Actually, greater than or equal to 0. So as long as it's not below 0, because again there's no array element or item below 0, that's just not possible. We'll actually decrement that, counter, so now we'll loop through the array in reverse, essentially.

So I'll actually put the comment there for that. So there, goes in reverse. So we get some more versatility with array, looping through arrays. Comment this out. Now we can do is decrement and output only even index items in the array. So, let's do this, copy this. Comment. Here, we'll put a little conditional, wrap around that Debug.Log() in a conditional. So if i, use the module operator, if they returned zero. So, in other words, two, four, six, eight, and so on. Then only output those. So, the second index, fourth index, and so on. Try to anticipate what it's going to spit out. 8, 36, 22, in reverse order, right? And 0 also. Alright, a little variation on that. If you want to output only odd values in the array itself. So why don't we do that as well. And, this time we'll actually check the value in this conditional. So, the value that stored in this actual box, so is it an odd number, that's how we do that. So we look at this as the 2 goes into 3, and it leaves a remainder of 1, right? Or 2 goes into 5 twice, and leaves a remainder of 1. 2 goes into 7 three times, leaves a remainder of 1. So that's how this works. That's how we get the odd values. We only have one odd value I guess. These are all even, except for 33.

Right, so that's a basic for loop and just a taste of what you can do with for loop and typical kind of for loop usages. Now in a game scenario, this might have a multitude of uses. And the only reason you maybe don't see this kind of loop much in Unity is because, if you recall, we already have a game loop predominantly through the Update() method, which Unity calls on every frame, creating the loop state, right? So what that means is, if you have a loop occurring within the game loop, the entire loop executes on a single frame. So I can show you this by putting in the Update() method instead of Start() and you'll see what I mean. What's going it going to output? On every frame, it's just going to go through that loop and output on every frame 33, and that's not what we want in many gameplay scenarios. So there is a solution for this in Unity, with something called coroutines. Coroutines can be seen as something kind of like another Update() method that runs beside the normal one, except in a coroutine, you can sort of pause the loop execution, in say a for loop, on each frame and so then it will continue from the previous iteration point on the next frame. But we'll get into that a little bit more down the line. Now, we can actually make use of loops and arrays as is, just all executing on a single frame.

So there are many actual uses of this in many different cases. So, one example that will be looking at right now is, we had a problem with the SpriteRender in the teleport fading algorithm. Now, it just faded out the current SpriteRenderer for the Cheesehead GameObject, but ignored the children GameObjects, right? What we want to do is fade out the parent along with the children. So, to solve that, we can simply use the Unity method called GetComponentsInChildren() instead of just GetComponent() and that method will return an array of SpriteRenderers. So all of them, our parent GameObject as well as children SpriteRenders. And then what we can do is we can loop through each of those SpriteRenderers, and apply the same Lerp process on each frame for each renderer. So we can do that like this.. First, actually remember to deselect this so it doesn't get in our way, and go to CubeAnimate. Right here, and that's where we had this previous call to GetComponent, so it only grabbed the component for the main SpriteRender, but not the children. So we can fix that by doing this. Well what we should do is actually put this in a method called TeleportFade() And I'll just put this in, don't have to make it private, but I will. Been doing that with our private methods that we've been creating.

So keep up with consistency. So I'll put all the previous code in its own method called CubeFade() just for posterity not for not because we're going to end up using it. Just so you remember that it's there, contrast it with this other solution that we're going to create here to our problem of fading. Now, just so I don't forget, right away I'll call TeleportFade here in the Update() And here in TeleportFade(), what I'll do is this., I'll use GetComponentInChildren() and it's a generic method and we'll want the SpriteRenderer returned for each GameObject. The parent in and the children's SpriteRenders. And we'll return take the return value and put into a temporary SpriteRender. See here I'll just show you, this method returns. Sorry I used the wrong method, and actually this is illustrative of, remember you can have big problems in code with just a single character missing. GetComponents, plural, in children, and that returns a array of SpriteRender, as you see there in the little box. So we'll return that to a SpriteRenderer array, a local variable that holds an array of SpriteRenders. CuberRenderers, that's what we'll call it. Then we use a little for loop here. And so we'll start at 0, and then we'll reference cubeRenderers.Length, property. And we'll want to increment one time through each element in the array, and for each element, we'll want to sort of repeat the same process as we had before.

So the color, we'll set that to the particular SpriteRenderer that we're dealing with at any given moment through this loop. Grab its color property, and well you just copy and paste the previous condition here. And then now, we'll want to just assign it that color back to the actual SpriteRender that we're grabbing. The actual Component, and we do that by referencing its index in the array, just like that, alright? So let's run this now, and we expect all of the GameObjects that are part of Cube to fade now. When we engage a teleport, so let's see, there we go. Let's even see if. Let's see actually if the teeth renderer, if the Teeth SpriteRender is included. Expect it to, have to grab the PowerUp, so bear with me here, so I have a little more time. There we go. If you noticed that, I didn't have a detect a collision, that's because we have to have the eye sphere enemy overlap our Cube completely, just so the game's not too challenging. Right, well this works fine but actually there is an even easier type of loop, that is in a lot of ways a lot easier to read, that would be great for this task called foreach, the foreach loop, which operates in pretty much the exact same way as the for loop, except it has and then an implicit fixed incrementing counter. So what it does is it just iterates through the entire array, one after the other, incrementing one at a time, until it runs out of items.

So we can just change this to a foreach loop. Actually let's call this TeleportFadeFor() and.. Make a new method. So we're not going to use these anymore, I'm just going to let them hang around so you can reference them, and look at them. I'm going to call this foreach, and call that here. So this is how we're going to do this, we just grab the stuff that doesn't really change. Temporary color conditional aspect, and now we'll start with the same thing, with grabbing the components, the SpriteRender components and storing them to a temporary array. Components, this time remember the plural version, components inchildren. And then here, I'll use a foreach loop. I used the code snippet. So var item in collection, that's the sort of templated form here for foreach. I'll briefly go into this in a minute. So what we're going to do here is, because it's a fixed incrementing sort of implicit counter, what we use to reference the particular array item, whether it's the first one second one third one, or whatever, is we use a temporary variable that's local to the foreach method itself to store that particular item in the array. And we do that here. So we have a collection, or an array of cubeRenderers, so reference that there. So foreach item in the cubeRenderers, its of type SpriteRenderer, right? Each item is a SpriteRender in the array of SpriteRender. And we can call it item, but I'm just going to call it local to this foreach loop, renderer.

Again grab this stuff here, and we'll say color = renderer.color, and then assign to the actual SpriteRenderer component. Because each temporary variable here will act as a reference for that particular SpriteRender in the array. So that will be the actual, it will be referring to the actual SpriteRenderer component for each GameObject. And now let's run that, it should run the same way but it will look a little bit nicer in code. Runs the same way. So yeah, the most important thing to understand about this foreach loop is this temporary variable that's an individual SpriteRenderer within the broader box of SpriteRenders the array, right? So at the very first time this loops through, this renderer will be the first item in the array, right? The zero index, in other words. The second time it will be the first index and so on, so that's how that works. That's how you should be looking at it. Alright, so to summarize the key points, arrays are immutable, that is to say once the type they store is declared, that's it, that's all that array will hold. So either just strings or just ints, or just Wizards, or SpriteRenders, or whatever. And also their size is immutable, the amount of individual boxes within that main array box. And arrays are fine for grouping similar items, but their strength isn't putting them through an organized process to each item. So whenever you need to produce a or perform a process on a bunch of items, an array is usually a good place to start, and then use an iteration statement or loop to to do that task.

So a for loop uses a counter that you can reference in the loop and you can you use that counter to make for interesting variations. Or you can use the easiest kind of loop, in the case of arrays. The fixed incrementing foreach, which works with things that are more broadly called, actually collections, not just arrays. So that's arrays, lists, IEnumerable, we'll see some of these in action later on. So those are collections. But really we'll be able to do just about everything we need with just basic arrays. They're the most basic and foundational collection type object in in C#. So at least for a while, that's all we'll be dealing with. You'll often also see, in C#, a generic type called list, and then those angle brackets because it's generic, and in that in those angle brackets you can determine the type that it stores, right? So either ints or Wizards or SpriteRenderers, or whatever. And this has some benefits over arrays. In particular, lists can be easily resized, you know have elements added or removed as well. But if you don't need those kinds of functions, just stick with ordinary arrays, right? Because otherwise lists are just a little bit of extra cognitive load to deal with, at least at this point. So great job if you've been following along and writing all this on your end, and 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