Loops - Digital Prototyping - Introduction to Game Design, Prototyping, and Development (2015)

Introduction to Game Design, Prototyping, and Development (2015)

Part II: Digital Prototyping

Chapter 21. Loops

Computer programs are usually designed to do the same thing repeatedly. In a standard game loop, the game draws a frame to the screen, takes input from the player, considers that input, and then draws the next frame, repeating this behavior at least 30 times every second.

A loop in C# code causes the computer to repeat a certain behavior several times. This could be anything from looping over every enemy in the scene and considering the AI of each to looping over all the physics objects in a scene and checking for collisions. By the end of this chapter, you’ll understand all you need to know about loops, and in the next chapter, you’ll learn how to use them with Lists and arrays.

Types of Loops

C# has only four kinds of loops: while, do...while, for, and foreach. Of those, you’ll be using for and foreach much more often than the others because they are generally safer and more adaptable to the challenges you’ll encounter while making games:

Image while loop: The most basic type of loop. Checks a condition before each loop to determine whether to continue looping.

Image do...while loop: Similar to the while loop, but checks a condition after each loop to determine whether to continue looping.

Image for loop: A loop statement that includes an initial statement, a variable that increments with each iteration, and an end condition. The most commonly used loop structure.

Image foreach loop: A loop statement that automatically iterates over every element of an enumerable object or collection. This chapter contains some discussion of foreach, and it is covered more extensively in the next chapter as part of the discussion of C# Lists and Arrays.

Set Up a Project

In Appendix A, “Standard Project Setup Procedure,” detailed instructions show you how to set up Unity projects for the chapters in this book. At the start of each project, you will also see a sidebar like the one here. Please follow the directions in the sidebar to create the project for this chapter.


Set Up the Project for this Chapter

Following the standard project setup procedure, create a new project in Unity. For information on the standard project setup procedure, see Appendix A.

Image Project name: Loop Examples

Image Scene name: _Scene_Loops

Image C# Script names: Loops

Attach the script Loops to the Main Camera in the scene.


while Loops

The while loop is the most basic loop structure. However, this also means that it lacks the safety of using a more modern form of loop. In my coding, I almost never use while loops because of the danger that using one could create an infinite loop.

The Danger of Infinite Loops

An infinite loop occurs when a program enters a loop and is unable to escape it. Let’s write one to see what happens. Open the Loops C# script in MonoDevelop (by double-clicking it in the Project pane) and add the following bolded code.

1 using UnityEngine;
2 using System.Collections;
3
4 public class Loops : MonoBehaviour {
5
6 void Start () {
7 while (true) {
8 print( "Loop" );
9 }
10 }
11
12 }

Save this script by choosing File > Save from the MonoDevelop menu bar. After you’ve done this, switch back to Unity and click the triangular Play button at the top of the Unity window. See how nothing happens... see how nothing happens forever? In fact, you’re probably going to have to Force Quit Unity now (see the sidebar for instructions). What you have just encountered is an infinite loop, and as you can see, an infinite loop will completely freeze Unity. It is lucky that we all run multithreaded computer operating systems now, because in the old days of single-threaded systems, infinite loops wouldn’t just freeze a single application, they’d freeze the entire computer and require a restart.


How to Force Quit an Application

On OS X

Implement a force quit by doing the following:

1. Press Command-Option-Esc on the keyboard. This brings up the Force Quit window.

2. Find the application that is misbehaving. Its name will often be followed by “(not responding)” in the applications list.

3. Click that application, and then click Force Quit. You may need to wait a few seconds for the force quit to happen.

On Windows

Implement a force quit by doing the following:

1. Press Shift+Control+Esc on the keyboard. This brings up the Windows Task Manager.

2. Find the application that is misbehaving.

3. Click that application and then click End Task. You may need to wait a few seconds for the force quit to happen.

If you have to force quit Unity while it is running, you will lose any work that you’ve done since your last save. Because you must constantly save C# scripts, they shouldn’t be an issue, but you might have to redo unsaved changes made to your scene. For example, in _Scene_Loops, if you did not save the scene after adding the Loops C# script to the Main Camera, you will need to attach it to the Main Camera again.


So, what happened there that caused the infinite loop? To discover that, take a look at the while loop.

7 while (true) {
8 print( "Loop" );
9 }

Everything within the braces of a while loop will be executed repeatedly as long as the condition clause within the parentheses is true. On line 7, the condition is always true, so the line print( "Loop" ); will repeat infinitely.

But, you may wonder, if this line was repeating infinitely, why did you never see “Loop” printed in the Console pane? Though the print() function was called many times (probably millions of times before you decided to force quit Unity), you were never able to see the output in the Console pane because Unity was trapped in the infinite while loop and was unable to redraw the Unity window (which would have needed to happen to see the changes to the Console pane).

A More Useful while Loop

Open the Loops C# script in MonoDevelop and modify it to read as follows:

1 using UnityEngine;
2 using System.Collections;
3
4 public class Loops : MonoBehaviour {
5
6 void Start () {
7 int i=0;
8 while ( i<3 ) {
9 print( "Loop: "+i );
10 i++; // See the sidebar on Increment and Decrement Operators
11 }
12 }
13
14 }


Increment and Decrement Operators

On line 10 of the code listing for the “more useful” while loop is the first instance in this book of the increment operator (++). The increment operator increases the value of the variable that it follows by 1. So, if i=5, then the i++; statement would set the value of i to 6.

There is also a decrement operator (--). The decrement operator decreases the value of the variable by 1.



Tip

In most of the examples in this chapter, the iteration variable used will be named i. The variable names i, j, and k are often used by programmers as iteration variables (i.e., the variable that increments in a loop), and as a result, they are rarely used in any other code situations. Because these variables are created and destroyed so often in various loop structures, you should generally avoid using the variable names i, j, or k for anything else.


Save your code, switch back to Unity and click Play. This time, Unity does not get stuck in an infinite loop because the while condition clause ( i<3 ) eventually becomes false. The output from this program to the console (minus all the extra stuff Unity throws in) is as follows:

Loop: 0
Loop: 1
Loop: 2

That is because it calls print( i ) every time the while loop iterates. It’s important to note that the condition clause is checked before each iteration of the loop.

do...while Loops

A do...while loop works in the same manner as a while loop, except that the condition clause is checked after each iteration. This guarantees that the loop will run at least once. Modify the code to read as follows:

1 using UnityEngine;
2 using System.Collections;
3
4 public class Loops : MonoBehaviour {
5
6 void Start () {
7 int i=10;
8 do {
9 print( "Loop: "+i );
10 i++;
11 } while ( i<3 );
12 }
13
14 }

Make sure that you change line 7 of the Start() function to int i=10;. Even though the while condition is not ever true (10 is never less than 3), the loop still goes through a single iteration before testing the condition clause on line 11. Had i been initialized to 0 here as it was in thewhile loop example, the console output would have looked the same, so we set i=10 in line 7 to demonstrate that a do...while loop will always run at least once regardless of the value of i. It is important to place a trailing semicolon (;) after the condition clause in a do...whileloop (as is shown on line 11).

Save this script and try it out in Unity to see the result.

for Loops

In both the while and do...while examples, we needed to declare and define a variable i, increment the variable i, and then check the condition clause on the variable i; and each of these actions was performed by a separate statement. The for loop handles all of these actions in a single line. Write the following code in the Loops C# script, and then save and run it in Unity.

1 using UnityEngine;
2 using System.Collections;
3
4 public class Loops : MonoBehaviour {
5
6 void Start() {
7 for ( int i=0; i<3; i++ ) {
8 print( "Loop: "+i );
9 }
10 }
11
12 }

The for loop in this example sends the same output to the Console pane as was sent by the preceding “more useful” while loop, yet it does so in fewer lines of code. The structure of a for loop requires an initialization clause, a condition clause, and an iteration clause to be valid. In the preceding example, the three clauses are as follows:

Image

The initialization clause ( int i=0; ) is executed before the for loop begins. It declares and defines a variable that is scoped locally to the for loop. This means that, the variable int i will cease to exist once the for loop is complete. For more information on variable scoping, see the “Variable Scope” section of Appendix B, “Useful Concepts.”

The condition clause ( i<3 ) is checked before the first iteration of the for loop (just as the condition clause is checked before the first iteration of a while loop). If the condition clause is true, the code between the braces of the for loop is executed.

Once an iteration of the code between the braces of the for loop has completed, the iteration clause ( i++ ) is executed (that is, after print( i ); has executed once, i++ is executed). Then the condition clause is checked again, and if the condition clause is still true, the code in the braces is executed again, and the iteration clause is executed again. This continues until the condition clause evaluates to false, and then the for loop ends.

Because for loops mandate that each of these three clauses be included and that they all be on the same line, it is easier to avoid writing infinite loops when working with for loops.


Warning

Don’t Forget the Semicolons Between Each Clause of the for Statement It is critical that the initialization, condition, and iteration clauses be separated by semicolons. This is because each is an independent clause that must be terminated by a semicolon like any independent clause in C#. Just as most lines in C# must be terminated by a semicolon, so must the independent clauses in a for loop.


The Iteration Clause Doesn’t Have to Increment

Though the iteration clause is commonly an increment statement like i++, it doesn’t have to be. Any operation can be used in the iteration clause.

Decrement

One of the most common alternate iteration clauses is counting down rather than counting up. This is accomplished by using a decrement operator in a for loop.

6 void Start() {
7 for ( int i=5; i>2; i-- ) {
8 print( "Loop: "+i );
9 }
10 }

And the output to the Console pane would be as follows:

Loop: 5
Loop: 4
Loop: 3

foreach Loops

A foreach loop is kind of like an automatic for loop that can be used on anything that is enumerable. In C#, most collections of data are enumerable, including the Lists and arrays covered in the next chapter as well as strings (which are a collection of chars). Try this example in Unity.

1 using UnityEngine;
2 using System.Collections;
3
4 public class Loops : MonoBehaviour {
5
6 void Start() {
7 string str = "Hello";
8 foreach( char chr in str ) {
9 print( chr );
10 }
11 }
12
13 }

The Console output will print a single char from the string str on each iteration, resulting in:

H
e
l
l
o

The foreach loop guarantees that it will iterate over all the elements of the enumerable object. In this case, it iterates over each character in the string "Hello". foreach loops are covered further in the next chapter as part of the discussion of Lists and arrays.

Jump Statements within Loops

A jump statement is any statement that causes code execution to jump to another location in the code. One example that has already been covered is the break statement at the end of each case in a switch statement.

break

break statements can also be used to prematurely break out of any kind of loop structure. To see an example, change your Start() function to read as follows:

6 void Start() {
7 for ( int i=0; i<10; i++ ) {
8 print( i );
9 if ( i==3 ) {
10 break;
11 }
12 }
13 }

Note that in the this code listing, lines 1-5 and the final line containing only a closing brace } (which was formerly line 13) have been omitted because they are identical to the lines in previous code listings. They should still be there in your script in MonoDevelop, and you should just replace the foreach loop from lines 7-10 of the preceding foreach code listing with lines 7-12 of this code listing.

Run this in Unity, and you will get this output:

0
1
2
3

The break statement exits the for loop prematurely. A break can also be used in while, do...while, and foreach statements.

Image

7 int i = 3;
8 do { 3
9 print( i ); 2
10 i--;
11 if ( i==1 ) break; // 2
12 } while ( i > 0 );

7 foreach (char c in "Hello") {
8 if (c == 'l') { H
9 break; e
10 }
11 print( c );
12 }

The numbers in the following list refer to lines in the preceding code that are marked with // 1 and // 2 to the right of the line (these have been bolded in the preceding code for emphasis).

1. This line shows the single-line version of an if statement. If there is only one line, the braces are not necessary.

2. This code only outputs 3 and 2 because on the second iteration of the loop, the i-- decrement reduces i to 1, and then the condition clause for the if statement on line 11 is true and breaks out of the loop.

Take the time to look at each preceding code example and make sure you understand why each is generating the output shown. If any look confusing, type the code into Unity and then run through it with the debugger. (The debugger is explained in detail in Chapter 24, “Debugging.”)

continue

continue is used to force the program to skip the remainder of the current iteration and continue to the next.

Image

In the preceding code, any time that i%90 != 0 (that is, i/90 has a remainder other than 0), the continue will cause the for loop to move on to the next iteration, skipping the print( i ); line. The continue statement can also be used in while, do...while, and foreachloops.


Modulus Operator

Line 8 of the code listing for the continue jump statement is the first instance in this book of the modulus operator (%). Modulus (or mod) returns the remainder of dividing one number by another. For example, 12%10 would return a value of 2 because the remainder of 12/10 is 2.

Mod can also be used with floats, so 12.4f%1f would return 0.4f, the remainder when 12.4 is divided by 1.


Summary

Understanding loops is one of the key elements of becoming a good programmer. However, it’s fine if not all of this makes perfect sense right now. Once you start using loops in the development of some actual game prototypes, they will become clearer to you. Just make sure that you are actually typing each code example into Unity and running it to help with your understanding of the material.

Also remember that in my coding, I most commonly use for and foreach and rarely or never use while and do...while because of the danger of infinite loops.

In the next chapter, you will learn about arrays and Lists, two forms of enumerable, ordered collections of similar items, and you will see how loops are used to iterate over these collections.