Home Game Tech Books Unity Apprentice

4
Creating & Using Scripts With C# Written by Eric Van de Kerckhove

In the last chapter, you learned about GameObjects, components and prefabs. You learned that components are the brains of GameObjects and can be created via scripting. In this chapter, you’ll learn how to create your own scripts using C# and how they can affect components in the scene.

At the end of the previous chapter, you made the humble beginnings of a small game. In this chapter, you’re going to expand on that game and get things moving to make the experience more engaging.

A key tool you’ll wield for this is Scripting, an essential skill for any developer — Unity or otherwise. Even the smallest games need some logic to drive them, after all.

Scripting with C#

Unity uses C#, an extremely powerful and versatile object-oriented programming language developed by Microsoft around the year 2000. This was an excellent choice by the Unity developers, as C# is a widely used, mature language for client, server and even mobile software development. This means there’s a ton of information out there on how to use the language to do just about anything — including making games!

Where to learn C#

There’s way too much to cover in a single chapter about scripting in Unity with C#, so this chapter will focus on the basics to get you started. Don’t fret though — it won’t leave you hanging! There’s a lot of information on C# out there, but where should you start your journey?

Well, it just so happens that there is a full video course on programming with C# on raywenderlich.com: Beginning Programming with C#

If you’re more interested in a written C# tutorial with an emphasis on Unity, look no further than the Introduction to Unity Scripting tutorial.

Where to find documentation

There are several ways to get more information about scripting for Unity. For starters, Unity comes with a scripting reference that covers the scripting API and the Unity manual that covers all things Unity in good detail.

Both of these can be found in the Help menu in the top bar of the Unity Editor. They’re also just a click away at Unity’s Scripting API Reference or the Unity User Manual, should inspiration strike elsewhere.

There’s also Unity Answers, a platform that makes it easy to search for questions and answers. You can ask your own questions there, and the community is quite active, so you’ll probably get an answer quickly.

And, there’s the Unity Forum, which is similar to Unity Answers, but the topics are split up per category. A lot of the Unity developers are active there, so you can get answers straight from them.

Finally, there’s the official Unity Discord, a place to discuss game development with others and ask questions. The Discord server is extremely popular, so there’s a good chance there will be someone online to help you.

Choosing an IDE

In theory, you could write all of your C# scripts using applications like Notepad or Textedit — but only if you’re feeling particularly adventurous. A better solution is to use an IDE — an Integrated Development Environment. That’s a fancy way of saying a code editor. :]

There are a lot of editors that are compatible with C# in some way, but there are two applications that rule when it comes to C# because of their features and price tag (or their lack thereof, rather).

If you’re a Windows user, all you need is Visual Studio Community, the free tier of Visual Studio. When you install Unity, the installer even provides an option to install VS Community by default and will set it up for Unity game development. How considerate! VS Community is an absolute beast of an IDE that you can expand on with extensions and fully customize to your liking.

If you’re a macOS or Linux user (or you simply want to try a lighter IDE), try Visual Studio Code, the little brother of Visual Studio. Make no mistake though — VS Code is a fully-fledged IDE with a ton of features, just like its older sibling. The setup required for VS Code to work nicely with Unity is a bit longer, but Microsoft has written a good guide to get you started quickly.

Or, if you want to try something else entirely, here are some free alternatives (that might involve a bit more work setting up):

After installing and configuring the IDE of your choice, you’re ready to enter the wonderful world of C# scripting.

Scripting fundamentals

Why write scripts, anyway? Unity comes with a lot of built-in components, after all. While it’s true that you could make a game solely with the built-in components — to an extent — you’d be extremely limited. The built-in components are like building blocks for you to work with or extend to get the logic you need working.

For example, you might attach a Box Collider and a Rigidbody to a wooden crate so it can fall to the floor with simulated gravity. You might want the crate to break and spill its contents when it drops from a great height, but there’s no way to do this with the built-in components. This is where scripting comes in! You can write a script that hooks onto the event that triggers whenever a collision happens, replaces the crate with a broken one when the collision is a hard one, and your problem is solved.

Before diving right into creating your own scripts, though, it’s a good idea to take a look at what makes a script tick.

Inspecting a script

Let’s begin by opening the starter project. Navigate to the Chapter 4 Starter folder, and open it in the Unity Editor. Take a quick look, and you’ll see that the structure is quite similar to the previous chapter.

This starter project comes with a few scripts that you can find in the RW / Scripts folder. For example, double-click on the Ammunition script in RW / Scripts / Ammunition to open it in your IDE of choice. This script moves (or “translates”) a GameObject it’s attached to the right by manipulating its Transform component.

When the GameObject moves past 20 units on the x-axis, the GameObject gets destroyed.

Here are the different parts, broken down:

using UnityEngine;

The using directive allows you to use the types defined in the UnityEngine namespace. This includes classes like MonoBehaviour, Transform and GameObject.

public class Ammunition : MonoBehaviour

Ammunition is the class name and should match with the file name (Ammunition.cs in this case) for it to work as a component. The : MonoBehaviour part means that this script derives from Unity’s MonoBehaviour, which is the base class from which all Unity components are derived. It provides hooks into useful events such as Awake, Start, Update, OnCollisionEnter and OnDestroy. Every script you want to become usable in the Unity editor as a component should derive from MonoBehaviour. The Order of Execution page from Unity is an excellent resource to understand the lifecycle of these key event functions.

You may be confused at this point in differentiating between a script, a MonoBehaviour and a component. To clarify:

  • A script is a C# code file that lives in your assets, just like prefabs, sound effects and 3D models.
  • MonoBehaviour is the class you derive your script from to hook into built-in Unity events like Awake and Update.
  • All scripts derived from MonoBehaviour are compiled and turned into components that can be attached to GameObjects in the editor to give extra functionality to said GameObjects.

Next up is this line:

public float movementSpeed = 5f;

This is a public float variable that saves the speed at which the GameObject should move. A float is a real number — meaning it can represent any negative or positive numerical value to the order of a hundred undecillion. That’s 38 zeroes after a number!

Below that, there’s the Update method:

private void Update()

Anything inside the Update method will be executed every single frame. This is one of the events built into MonoBehaviour.

Next is this line of code:

transform.Translate(Vector3.right * movementSpeed * Time.deltaTime,
  Space.World);

This piece of code moves the GameObject this component is attached to smoothly to the right. There are a few things to note here:

  • transform is shorthand for GetComponent<Transform>(), which gets a reference to the Transform component attached to the same GameObject the Ammunition component is attached to.
  • The Translate method moves a GameObject by providing it with a Vector3 that has an X, Y and Z value.
  • Vector3.right is a pre-defined value that’s shorthand for the Vector3 value of (X:1, Y:0, Z:0). This is multiplied by movementspeed and Time.deltaTime.
  • Time.deltaTime is the time in seconds between the previous frame and the current one. Remember that Update is called every single frame, so multiplying any values that change gradually by Time.deltaTime smooths the changes out as the game’s frame rate is not guaranteed to be a steady 60 frames per second, for example. You might have tried playing some ancient games that play way too fast on modern machines, but played perfectly back in the day. That’s because the developers tied the game speed directly to how fast a new frame can be generated instead of using delta time.
  • Space.World is an argument provided to transform.Translate so the movement happens in World space instead of Local space.

The last part to consider is this if-statement:

if (transform.position.x > 20f)
{
    Destroy(gameObject);
}

The if-statement simply checks if the GameObject has moved 20 units to the right of the center of the scene, and if so, it will destroy the GameObject the Ammunition component is attached to. As you can see, these few lines of code make for an interesting behavior.

Return to the Unity editor and click the Play button at the top center. Then click the Create ammunition! button to see a weapon spawn. You can repeat this to make as many weapons as you like, but with some delay. You’ll address this soon.

Now, in Play mode, take a good look at the Game view, and you’ll notice the weapon moves along the x-axis of the scene until it’s out of the view of the game camera and then simply disappears. That’s what making games is about — smoke and mirrors to create the illusion that there’s something more to it!

Fixing the UI

Before creating a script of your own, open the AmmunitionSpawner script in RW / Scripts / Ammunition. Don’t worry — you don’t have to understand all of this script. In broad terms, this script has a timer running in the background, and it can react to a button press by checking if the timer has been running for long enough. If this cool-down time has elapsed, a random piece of ammunition (picked from a list of prefabs) is instantiated into the scene and the timer is reset. That’s it!

At the moment, the user interface is kind of passive — as in, it doesn’t do anything except react to button presses. You’re here to fix that while learning about comments along the way. Take a look around the script to get your bearings first. Did you notice anything strange?

There are a bunch of double slashes ( // ) spread around the file. These are comments, and anything after them is excluded from the compiler. This means you can add useful remarks (“comments”) or remove (“comment out”) certain pieces of code so they won’t be used. In the case of the AmmunitionSpawner script, everything that references the UIManager component has been commented out. No wonder the user interface was so lifeless!

Simply remove all of the double slashes and save the script (you can use Control-S or Command-S for this). Next, return to the Unity editor and you might see a small window pop up that says “Compiling C# Scripts.” This means Unity detected changes to one or more scripts and is (re)compiling so they can be used as components.

When this small window disappears, select Managers / Ammunition Spawner in the Hierarchy and take a look at the Inspector. A new property named Ui Manager has appeared.

The Ui Manager property needs a reference to a UI Manager component, so go ahead and drag User Interface / UI Manager to the property.

Next, play the game again in the editor and you’ll notice the UI actually works now. The yellow bar now fills to indicate when the machine is ready to create another piece of ammunition and the text on the left shows the amount created.

This improves the feel of the game quite a bit!

Your first script

Now that you know the basic structure of scripts, it’s time to make your own. This first script will rotate the giant gear of the machine to make it more lively. Create a new folder named Transforms inside RW / Scripts by right-clicking the folder in the Project view and selecting Create ▸ Folder.

Right-click this new Transforms folder and select Create ▸ C# Script.

Name this new script RotateAround and double-click the asset to open it in your script editor.

You’ll be greeted by Unity’s default C# script template. At the top are using directives that include UnityEngine, and the class itself is derived from MonoBehaviour so the script can be compiled into a component. There are already two Unity events added: Start and Update. You only need the Update method for this script, so go ahead and delete the Start method.

// Start is called before the first frame update
void Start()
{
    
}

Next, add the following variables right above the Update method:

public Vector3 rotationSpeed;
public Space rotationSpace;

rotationSpeed is a Vector3 used to set the direction and speed of the rotation. rotationSpace sets the coordinate space — either Self (local) or World. Since both of these variables are public, they can be changed in the Unity Editor once this script is compiled into a component.

Finally, add the following inside of the Update method:

transform.Rotate(rotationSpeed * Time.deltaTime, rotationSpace);

Just like Transform.Translate moves a GameObject, Transform.Rotate rotates it around the provided axes in a certain coordination space. Notice there are no actual values provided except for the variables and Time.deltaTime to smooth the rotation out over time. Now save this script and return to the Unity Editor.

Select AmmunitionMachine / Wheel in the Hierarchy. Add your newly created Rotate Around component by clicking the Add Component button at the bottom of the Inspector, searching for “Rotate” and selecting Rotate Around.

As-is, this won’t do anything because the Rotation Speed is set to 0 on all axes. Go ahead and set the Rotation Speed to (X:60, Y:0, Z:0) to give it some speed. Next, set the Rotation Space to Self so the gear will spin around its own x-axis instead of the world’s x-axis.

Start the game again, and you’ll notice the gear on the machine is continuously spinning. Pretty nice!

Congrats on making your very own script!

Using scripts with other components

Scripts can reference other components, just like how the Ammunition Spawner triggers the UI Manager to change certain parts of the user interface. Any script can read the values of other components and change them or call methods on other components.

You might have noticed it’s a bit strange how the weapons are sliding along the belt even though it’s not moving! One way to solve this would be to actually move the belt itself, but this is just a small plane — a flat piece of geometry that lays on the machine. Moving that would result in something like this:

The belt would move for a bit and then reveal the wood below. This isn’t any good of course. The belt needs to infinitely appear to move. To achieve this, you’ll need to manipulate the material’s texture of the belt to make it visually slide without affecting the GameObject’s position. You can manually scroll this texture to see what happens by selecting M_Belt in RW / Materials and dragging its Y Offset property to the left.

While dragging the offset, look at the Game view and you’ll see the belt moving toward the screen. This is called scrolling the texture offset. It’s a cool trick that’s used all over for faking movement or rotation. To implement this effect yourself via scripting, create another folder inside of RW / Scripts and name it Rendering. Create a C# script in this folder via the right-click menu, name it ScrollTexture and double-click it to open it in your script editor.

The script you’re about to create is generic, so it can be used for all of your scrolling texture needs — not just for the belt! To get started, add the following three variables right above the Start method:

public Vector2 scrollSpeed; // 1

private Vector2 offset; // 2
private Renderer rend; // 3

Here’s what these will be used for:

  1. scrollSpeed is a Vector2. That means it has an X and a Y value, but not a Z value like a Vector3. This variable is used to set axes and speeds at which the texture needs to scroll. Note that this is a public variable, so it can be changed in the Unity Editor.
  2. The offset is used to keep track of how far the texture has been scrolled. For example, this value starts out at (X:0, Y:0) and after running the script for 10 seconds with a scrollSpeed of (X:0, Y:10), the offset will be (X:0, Y:100).
  3. This is a reference to a Renderer component, which in itself holds a reference to the material.

Now, add this line inside of the Start method:

rend = GetComponent<Renderer>();

This line gets the Mesh Renderer component on the same GameObject the Scroll Texture component is attached to and saves it in rend.

Next, add the final piece of code inside of the Update method:

offset += Time.deltaTime * scrollSpeed; // 1
rend.material.SetTextureOffset("_MainTex", offset); // 2

Going through the comments, this code:

  1. Cumulatively adds the delta time multiplied by the scrollSpeed to offset every frame.
  2. Sets the texture offset on _MainTex, which is the regular color texture (the Albedo when you look at the material).

That concludes this script! Save the script and return to the Unity Editor to take it for a spin.

Select AmmunitionMachine / Belt in the Hierarchy and add a Scroll Texture component to it.

Set the Scroll Speed to (X:0, Y:-0.4) and start the game again. You’ll see the belt steadily moving towards the screen. Pretty cool!

Now you know how to create your own scripts, and you know that these get turned into components in Unity. In the next chapter, you’ll learn about Unity’s camera and lights to make a scene look good.

Key points

  • Unity uses C# as its scripting language.
  • A script provides functionality to a GameObject as it’s compiled into a component.
  • Visual Studio and Visual Studio Code are recommended IDEs.
  • You can open scripts by double-clicking their assets in the Project view.
  • New C# scripts can be created by right-clicking in any folder in the Project view and selecting Create ▸ C# Script.
  • Scripts expose variables that you can modify from the Unity Editor.
  • Unity automatically calls event methods in the script to perform operations on the associated component.

Have a technical question? Want to report a bug? You can ask questions and report bugs to the book authors in our official book forum here.

© 2022 Razeware LLC