Unity's Introduction to Scripting (videos: 2 hours 45 minutes) SCRIPTING
A script is a type of computer program (or just ‘program’, for short).A scripting language is a programming language that controls a software application. (Unity is an example of a software application.) A script is code that is written in a scripting language. Scripting refers to the process of writing a script. In Unity, scripts tell GameObjects how to behave. Scripts allow you to:
Unity runs in a BIG LOOP: it reads all the data in the current scene, renders a frame, repeats. It runs this loop many times every second while the game is playing. C# Scripting Language
Unity uses Object Oriented scripting languages (more on this later).C# (read as ‘C sharp’) is the recommended scripting language, and the one used here. Differences Between C# Programming Inside/Outside Unity Experienced C# programmers should be aware: there are differences in conventions and ‘best practices’ when using C# for Unity game development. Here's an example. Normally, it is considered bad practice to use public fields in C# programming. (Public fields are discussed shortly.) Instead, C# provides an alternative mechanism (properties) which offer greater flexibility, easier upgrades between versions, more functionality, and better ability to work as a team. However, Unity only exposes public fields (not properties) in the editor. Therefore—although public fields are generally discouraged—they are required for editing from within the Unity Editor. Unity Scripting Reference
This is a better link than the Unity Manual, for C# scripting questions:Unity Scripting Reference Create Your First Script
To keep files organized, create a folder in .We want to create our new script in this folder, so double-click on it to move into the folder. (Our first few scripts are for learning purposes only, and won't be used in the final game.) (1a) Create a New Script Alternatively, you can use: There are other ways, too! Scripts are vital to Unity, so there are lots of ways to do the same thing. (1b) Rename the Script The script appears with a default name, which is highlighted and ready to be changed. Change to a meaningful name now rather than later, since the name you enter is used to create the initial text inside the file. Capitalize the First Letter of Script Filenames The first letter of the filename should be capitalized: MoveWolf is fine, but moveWolf isn't. Change the name to and press Enter. After a moment, you'll see the contents of the just-created file in . (The ‘.cs’ filename extension stands for (for C Sharp). (1c) Double-click ; it opens in Visual Studio. Visual Studio offers lots of help in writing/editing scripts:
|
(1a) Create a new script: (There are other ways to create a new script.) (1b) The script appears with a default name, which is highlighted and ready to be changed. Change the name to and press Enter. (Your name should begin with a capital letter.) After a moment, you'll see the contents of the just-created file in . (The ‘.cs’ filename extension stands for (for C Sharp). Note that the filename you choose is used in the initial script text. |
(1c) Double-click ; it opens in Visual Studio. (Don't worry if your interface looks a bit different.) |
int
type (integers) are: $\,5\,,$ $\,7\,,$ $\,-13\,$
string
type are: "
hello"
, "
goodbye"
class
Cats
.public class Cats
{
}
class
is preceded by its access level.public
’ means that anyone is allowed to create instances of this class.public class Cats
’ instead of
‘public class cats
’ .FallingRectangle
type by writing:
public class FallingRectangle
{
}
FallingRectangle
type:
FallingRectangle
type’ are
called ‘FallingRectangle
objects’.We say: | THE | ‘FallingRectangle type’, |
since there is only one type definition. |
We say: | A | ‘FallingRectangle object’, |
since there may be many instances of the FallingRectangle type. |
FallingRectangle type
’ and ‘FallingRectangle class
’ are almost synonyms.class
’, we communicate the concept that ‘FallingRectangle
’
was a type defined using the ‘class
’ keyword.
Controller
class.MainPlayerController
and EnemyController
.MainPlayer
namespace; the other in an Enemy
namespace.MainPlayer.Controller
and Enemy.Controller
|
(2a) Hover over either of the top two lines; a helpful suggestion appears. Click the light bulb to see suggested actions. |
(2b) Here, C# lets you know that nothing in your code requires either of the top two lines. It recommends that you remove them. You would click beneath the light bulb if you choose to follow the recommendation. |
|
(3a) colored bars track changes for a single session (3c) restore default formatting with: (whole document) (highlighted part of document) |
(3b) If desired, you can turn off the colored bars for tracking changes by unchecking: |
(2a) The List type is fromthe System.Collections.Generic namespace.Here, C# is happy. |
(2b) If we remove the ‘ using System.Collections.Generic; ’ line, C# complains.Errors are indicated by red squiggly underlines. Hovering over errors gives information on what is wrong: notice that it asks if we are missing a ‘ using ’ directive.
|
(2c) If we put in the full namespace description, then C# is happy again. However, the code is now longer and more complicated. |
|
(2d) In Solution Explorer, you can view the referenced assemblies. Only a few are shown here. |
|
(3a) The UnityEngine namespace contains an enormous amount of stuff!Toggle buttons (at the bottom) allow you to view only one (or more) of the six categories. |
||||||||||||||||
(3b) Highlight an entry and then press Enter .Note the class icon highlighted at bottom; so, only class entries are showing.
|
(3c) Keep drilling down to see what UnityEngine has to offer.
|
Drilling Down ... May Need a Bit of Help
As you seek C# help in Visual Studio, you may sometimes be left shaking your head,
thinking:Here's an example. UnityEngine contains a type called .If you seek help as in (4a) at right, only two methods are offered. Really? Is this all that has to offer? There are reasons (involving static versus non-static methods) that you aren't seeing more. However, these reasons are beyond the current scope. For now, just know that you can likely get the information you really want, by declaring a variable of type , and then ‘drilling down’ on this variable. See (4b) at right. Notes:
|
(4a) Really? Is this all that has to offer? (4b) More offerings! (The ‘ // stuff ’ are comments to help you understand the code.)
|
The Unity C# Script ‘Skeleton’ (continued)
Reference and Change Information
Between lines 4 and 5, on an un-numbered line, is information in a light grey color.(Similar information appears between lines 7-8, and between lines 13-14.)
|
(1) The Unity C# script ‘skeleton’: discussion of each line is continued at left. |
(2a) First part: How many references are there to whatever appears on the next line? |
(2b) Second part: status of Git Commits |
(2c) Third part: more detail on Git Commits |
|
(3) Note how the braces on lines 6 and 18 line up. The contents of the MoveWolf class go inside these braces.Inheritance sets up a parent/child relationship, where the child inherits functionality from the parent. |
Comments
|
(5a) Single-line comments start with two forward slashes. They can occupy a single line by themselves, or they can go after some code on a single line. (5b) Multi-line comments go between /* and */ .The ‘block’ style shown here makes them really stand out. (5c) You don't have to use the ‘block’ style shown in (5b). |
The Unity C# Script ‘Skeleton’ (continued)
Only two parts of the skeleton remain to be investigated.This is where the fun stuff happens! Two methods (also called functions) appear inside the class:
The Void Keyword
On lines 8 and 14, before each method name, you see the void keyword.In this context, the void keyword means that these methods do not return a value.The methods simply execute the code inside their code block—that is, inside the braces { } .More information on methods will appear shortly, as we get Wolf moving across the screen. EXAMPLE:
Start() and Update() In ActionDebug.Log() is a simple command that prints a message to Unity’s console output.The Console tab is next to the Project tab in the
Unity editor. See (2) at right.Do the following:
Modifying a Script
When you play a game in the Unity Editor, Unity looks at every script in the Assets folder and notes
the date/time that each was modified. As long as you've saved your script after making changes, the script
will be re-imported and the changes applied.
|
(1) two methods inherited from MonoBehaviour (2) the Console tab(3a) add the two lines of code circled here (3b) play your game; you'll see this console output |
Statements; Strings
Here is some information related to the Debug.Log() lines you typed above.
Statement
The actions of a program are expressed using statements.
C# statements generally must end with a semicolon: ‘ ; ’ Exception: Statements ending with a block { } do not use a semicolon after the block. String Literal
A
string literal is a sequence of characters enclosed in double quotation marks:
" "
A string literal is often more simply referred to as a string. Since a double quote terminates the string, you must use to actually see a double quote in your output. In this context:
|
(4a) illustrating the “ @ ” syntax for strings (4b) The Debug.Log() statement in (4a) produces this Console output.Only two lines show initially in the Console ;when you click on the Console entry, the entire message appears at the bottom.
|
Get Wolf Moving Across the Screen
With the current state of our game, is indeed ‘moving’, since we've endowed it with an animation.However, it's just moving in place. It's not changing position on the screen. Next, we'll get moving left-to-right across the screen. In the process, more fundamental concepts are introduced. Eventually, we want to respond to user input: when a user clicks anywhere on the screen, should move to that location. But, we need to practice with simpler code first. Note: There are different—and better—ways to move. The basic code discussed on this web page is for learning purposes. Methods
The time has come for a more precise discussion of methods.What follows is information on methods from the official Microsoft C# documentation. The green text specializes to the C# script skeleton, which is repeated in (1) at right.
|
(1) the C# script skeleton |
Only One Line of Code is Needed to Move Wolf Left-to-Right
Every GameObject has a Transform Component. See (2) at right.Consequently, Unity has made it extremely easy to work with Transform Components in code. Only one line of code is required to make move left-to-right. However, we'll include a second line to better understand what is happening with this movement. Put Two Statements Inside
As shown in (3) at right, put two statements inside Update() Update() .Again, use code completion. Start typing; then press Enter after making your selection.
Play the Game
Save the script after making the changes, and then play your game.Watch move from its current position to the right of the screen! Look at the Console output (see (4) at right).On each call to Update() , the $x$-value of is being written to the Console .When the play button was pressed, had a starting $x$-value equal to $\,-3\,.$ The Console is showing: $\,-2.9\,,$ $\,-2.8\,,$ $\,-2.7\,,$ and so on.On each frame, $\,0.1\,$ is being added to the $x$-value. Of course:
(It's a good warm-up to the more complicated coding we'll do in the next part of this lesson.) |
(2) A typical Transform Component. For me, has a starting $x$-value of $\,-3\,.$ (3) code to move left-to-right (and to better understand what is happening) (4) Console output after playing the game with the new code added
|
Understanding the ‘Move Right’ Code
We now explore the concepts needed to understand the two lines of code circled in (1) at right.
|
(1) Time to understand these two lines of code! |
|
|
(2) hovering over Vector3 gives information on the Vector3 constructor
|
Improving the ‘Move Right’ Code
At right, some changes have been made to the code to:
Conform to Good Coding Practices
The vector (0.1f,0,0) is an example of a constant.A constant (as opposed to a variable) is a number that does not change—its value is constant (hence the name). Whenever you find yourself typing a literal number (like $\,0.1\,$ or $\,-9\,$) into your code, that's a constant. Put Constants at the Top of a Class It's good coding practice to put constants at the top of a class (before any methods), for these reasons:
Fields
In line 7 of (1) at right, a field has been used to ‘hold’ the constant vector (0.1f,0,0) .Here is some information on fields. The green entries specialize to line 7:
|
(1) code changes to: conform to good coding practices, illustrate new concepts |
Field Declaration/Initialization Syntax
The
green text again specializes to line 7.
|
(2) Public fields in C# scripts are available in Unity. They can be changed in or . (3a) Unity inserts spaces before capital letters for use. (3b) You can change field values from the Unity Editor. (3c) Reset to your C# script values using the dropdown menu.
|
Field Declarations Versus Statements
A field declaration ends with a semicolon, just as a statement (usually) ends with a semicolon.However, a field declaration is not a statement. A compiler is a program that converts a programming language into a lower-level form, so that it can be understood by a computer. Field declarations tell the compiler the fields in a class. The order that the declarations appear is irrelevant (although they're usually grouped together at the top of the class). Statements, however, are executed sequentially. Here's a way to think of it: Alphabetizing a random collection of words doesn't lose any content. Alphabetizing the words in the sentence ‘Carol and Ray enjoy working on Unity game development together’ destroys the usefulness of the sentence. Similarly:
|
Code Refactoring
Code refactoring refers to editing/cleaning up code, without changing code function.
Changing a Field/Variable Name
If you decide to change a field/variable name,
it could potentially be a big nuisance to make sure you locate all occurrences in your code.Renaming a field/variable is one type of code refactoring. Fortunately, Visual Studio makes it really easy, as follows:
|
(4) Visual Studio makes it really easy to rename fields/variables. |
Moving Wolf Back-and-Forth Across the Screen
Now, we want to implement more advanced movement:
Feel free to study the script on your own (before reading the material that follows) and see if you can figure out what it does. Type it all into your own C# script and play your game! Below, we'll talk about the new information that appears in the script:
|
(1) the code to move Wolf back-and-forth repeatedly;a text file containing this MoveWolf code |
Make Your Code General
You should strive to make your scripts as general as possible, for potential future reuse.I initially chose names like ‘ MoveWolf.cs ’ and ‘WolfMovementVector ’ to keep things friendly
for a first exposure to code.However, these are not good names. This script could be pulled onto almost any GameObject, which certainly may not be a Wolf! So, some renaming was done in (1) to make things more generic. (The class name wasn't changed, since then the file would need renaming to match. Don't worry—this script won't be used in our final game—our ‘real’ one will be better-named.) Lines 8, 29, and part of 31
Line 8:int is the integer type.DirectionMultiplier will only hold two possible values, $\,1\,$ and $\,-1\,,$ which are both integers.Line 29: Recall: DirectionMultiplier *= -1; DirectionMultiplier = DirectionMultiplier * -1; Here is how you multiply a vector by a scalar:
$$
\overbrace{k}^{\text{scalar}}
\overbrace{(x,y,z)}^{\text{vector}}\ \ \ :=
\overset{\text{every coordinate}}
{\overbrace{(kx, ky, kz)}^{\text{is multiplied by the scalar}}}
$$
Thus:
MovementVector * DirectionMultiplier MovementVector
by the current value of DirectionMultiplier .When a vector is multiplied by $\,-1\,,$ it changes the direction of the vector. Since DirectionMultiplier is a public field, it is exposed in the Unity Editor.It's fun to see DirectionMultiplier bouncing between
$\,-1\,$ and $\,1\,$ as the character changes direction during game play.
Using General Components in C# Scripts
As mentioned earlier, every GameObject has a Transform Component,so Unity makes it really easy to work with the Transform Component. However, working with other Components requires a bit more coding. Here's an analogy to help with understanding. When you store your belongings, you first need the right size box for an item. For example, a shoe box won't hold your Christmas wrapping paper. Once you have the right size box, then you can put your stuff away. It's the same way with Components other than the Transform Component. There are two lines of code required to start working with them. Think of the first line as ‘getting the right size box’. Think of the second line as ‘putting your stuff in the box’. |
Understanding Lines 9 and 19:
has a Component with a checkbox.How to Access Sprite Renderer Component Information When unchecked, faces right. When checked, faces left. See (2a) and (2b) at right. We need to access and change this Component information in order to flip as it runs back-and-forth across the screen. Low-Level Understanding of Lines 9 and 19
|
|
Higher-Level Understanding of Lines 9 and 19
This explanation is a bit closer to the truth.At right are three ‘index cards’. Think of them as representing chunks of memory where information is stored. (There's lots of other information on these cards, but we're only showing what's relevant to this discussion.)
Line 9 happens at compile time, when the game is built. Line 19 happens only after the game starts running. |
|
Moving Wolf Back-and-Forth Across the Screen (continued)
The code to move back-and-forth is repeated in (1)
at right, for your convenience.
Booleans
A boolean value is either true or false.In C# code:
Practice With the Mathematical Words ‘and’, ‘or’, ‘is equivalent to’ . Boolean Operators
Negation
!A ’ is read as ‘not A’ Line 28: If _mySpriteRenderer.flipX is false (unchecked box; facing right),then !_mySpriteRenderer.flipX is true.If _mySpriteRenderer.flipX is true (checked box; facing left),then !_mySpriteRenderer.flipX is false.In both cases, the new truth value gets stored back in _mySpriteRenderer.flipX ,which updates the Sprite Renderer Component and causes to change the direction it is facing. |
(1) the code to move Wolf back-and-forth repeatedlya text file containing this MoveWolf code |
operands | AND | short-circuit AND | OR | short-circuit OR | EXCLUSIVE OR | EQUIVALENCE | |
A |
B |
A & B |
A && B |
A | B |
A || B |
A ^ B |
== |
T |
T |
T |
T |
T |
T |
F |
T |
T |
F |
F |
F |
T |
T |
T |
F |
F |
T |
F |
F |
T |
T |
T |
F |
F |
F |
F |
F |
F |
F |
F |
T |
an AND sentence is true only when BOTH operands are true |
an OR sentence is true when ONE or BOTH operands are true |
an EXCLUSIVE OR is true only when EXACTLY ONE operand is true |
an EQUIVALENCE is true when the operands have the SAME truth values |
&
and &&
return the same truth values. (See the blue columns above.)|
and ||
return the same truth values. (See the green columns above.)&
and |
always evaluate both operands.
&&
and ||
only evaluate both operands when needed :
A AND B
:A
is false, then the AND
sentence is false (regardless of the truth value of B
).B
is evaluated only when A
is true.
A OR B
:A
is true, then the OR
sentence is true (regardless of the truth value of B
).B
is evaluated only when A
is false.
if() { }
if
’ statement is the simplest conditional statement.if(BooleanCondition) { // Let BODY refer to the statements between the braces. // If BooleanCondition is true, then BODY is executed. Then, control passes to the next statement in the flow. // If BooleanCondition is false, then BODY is NOT executed. Instead, control passes immediately to the next statement in the flow. } // This is the next statement in the flow.Lines 2630:
BooleanCondition
is:
transform.position.x > 10.0f || transform.position.x < -10.0f
true
if 's current position is beyond either the right or left edge of the screen.