Unity Engine - Roll a Ball Tutorial

This article contains my notes from the Unity's introductory "Roll a Ball" tutorial, covering basic concepts like "Scenes", "GameObjects" and "Components". The original tutorial is a short video series following the creation a simple game click-by-click and line-by-line, with some very useful justifications for each step. I really enjoyed this tutorial, and I'd recommend it to anyone looking to pick up Unity. You can play my version of the final game here, and find my source code on GitHub here.

Note that the Roll a Ball tutorial is a few months old as of this writing, and it does not represent the latest version of Unity. In particular, the tutorial's UI section is outdated so I wrote my own in the "Displaying Text" section of these notes.


This tutorial will create a simple game where players roll a sphere around on board and collect pick-ups. The pickups are counted on the game's UI, and the game ends when all have been picked up.


The directories "Library" and "Temp" are generated by Unity automatically and can be ignored for version-control. Some additional files are generated by Unity's built-in coding environment MonoDevelop. Here is a sample .gitignore which I will be using for course control of my own projects:

    Unity generated

    Visual Studio / MonoDevelop generated


Unity has "projects," "scenes" analogous to "games," "levels." Roll-a-ball is one project with one scene.

"GameObjects" are the things which a player sees/hears in a scene. 2D and 3D objects, UI, lighting, audio, etc. 


SCENE SETUP (Board, Player and Lighting)

The first GameObject we create is a plane, which acts as our game board. on which the ball will roll. For convenience, we set the position of the plane the origin 0/0/0 (X/Y/Z) which puts it in the absolute center of the scene. Planes can be scaled by length and width in terms of "unity units", but have no height. They are also one-sided, and we can reverse the facing side by setting height to a negative number.

Now we create another game object, a sphere representing our player. The sphere is a "primitive object," meaning it is built-in to Unity and thus we don't need to define it. In unity, primiitive objects are 1x1x1, and the sphere defaults to be halfway through our ground plane. We move it up the Y-axis by half a unit to make it sit on the ground.

Next we create a main light at 30/60/0 with shadows enabled so that we get a light shining from above and one side and causing the ball to case a shadow. We then add a fill light with a bit of colour, a lower intensity and rotation -30/-60/0 (ie 330/300/0) so that the ball has a bit of light case from below, separating it from its shadow. The combination of these two lights make the ball easier to see against the ground.

We can create an empty GameObject to hold both lights. This creates a Parent/Child hierarcy, and we can modify some properties of the parent to modify all of the children simultaneously. In our game, we will move the lights to position 0/50/0 which gets them out of the way of our editor view, but does not change the in-game appearance of the lights (though I'm sure that is an option).



We want the Player GameObject to behave like a solid object with physics. First, we have to attach a "Rigidbody Component" to Player.

Next, we want to allow player input to control the sphere. We will do this by attaching a script. We want this script to act as a component of Player, and we can shortcut setting up this script by first selecting Player and then pressing "Add Component -> New Script" in the Inspector view. This creates the script file at the top level of our Assets folder, so we'll have to move it to the place we want it to be.

Unity comes attached to a coding environment "MonoDevelop"

Within the script, we want to check for player input on every frame, and then apply movement on every frame in response. Unity calls "Update()" every frame. "fixedUpdate()" is called just before performing physics calculations, but physics calculations don't necessarily occur every frame. We want to deal with the player's rigidbody movement which is physics, so we'll put that in fixedUpdate().

Now we need to start reading input from the player, which will involve the "Input" class in our script. "Ctrl ' " searches the unity API documentation for the highlighted text... in this case we will search for the Input class.

The documentation tells us that movement should be read through Input.GetAxis which can be mapped to a keyboard, mouse, etc in the "Input Manager" section of "Project Details" in Unity. This function returns movement as a float on the axes "Horizontal" and "Vertical".

Now we use the "AddForce" method for our RigidBody object. This takes a Vector3 (ie a set of three floats) representing the force to add on the x, y and z axes. We want to move on the x and z axes, so we use x = Input.GetAxis("horizontal"), y = 0, z = InputGetAxis("vertical").

Because forces are being read/applied every frame, the result of some amount of input will depend on the user's framerate. That's bad. We can eliminate this by using "Time.deltaTime" as a coefficient when we add forces. This method returns the the time since the last frame update, meaning it will be bigger for low framerates and offset the lower number of force applications.

Global parameters created in a script which is attached to a GameObject will be modifiable from the inspector view of that GameObject. This is really handy for tuning physics variables, such as  "speed" coefficient for the Player sphere.



We want to make the camera follow the player sphere and rotate with it, in a typical third-person perspective. We could simply make the camera a child of the sphere in the object hierarchy. However because the player is a sphere which is rolling according to physics, the camera rolls with the sphere becoming extremely disorienting.

Instead, we will keep the camera independent of the sphere and use a script to make the camera follow the sphere's position. We need a public GameObject variable to set to the player sphere, and a private Vector3 offset to determine how far the camera sits from the sphere. First we will store the camera's starting transform into offset (in the "Start()" function), and then we will update that transform according to the player's position after every frame.

Inside the script, we can access the position of the camera's transform using "transform.position," and if we store the player GameObject in a variable "player", then we can access its position in "player.transform.position"

The above code should be placed in "LateUpdate()" which ensures our camera position is updated AFTER the physics engine has determined the sphere's new position. There are probably a lot of good reasons for this, but I can only speculate.


We'll put walls around the edge of the game board to keep the sphere from falling off. We create an empty Walls GameObject, then create a child cube representing one wall, then scale the cube to match the size of the board. Duplicate and rotate for the other three walls.

Now for pickups. We create a cube named "PickUp" at origin, and notice that the player sphere is blocking our view. We can set the player sphere to "inactive" for this scene, which removes it from view. 

Now to make the pickups "enticing" for players, we want to do a few things. Make it small, make it hover above the board, put it on an angle, and make it rotate. The first three are easy, but rotation will require a script. This is fairly simple, we can just use "transform.Rotate(new Vector3 (x, y, z) * Time.deltaTime)" in the regular Update() function to rotate the object by x, y and z every frame while accounting for differences in framerate.

There should be multiple pickups, so we want to make our single PickUp object into a "prefab" which can be reused in multiple scenes and modified from a single location. We create a new "prefabs" folder in assets and drag the PickUp object into that folder. 

Lastly, create a an empty "PickUps" parent, place our single PickUp under it, and start duplicating.


The sphere, walls and pickups all have "collider" components which tell us about their collisions with eachother. We will use "void Collider.OnTriggerEnter(Collider other){}" to perform some action when the player object touches some other object.

First, we need to identify PickUp objects with a "tag." We can do this for all PickUp objects simultaneously by selecting the prefab in the assets folder, then clicking through the Inspector window -> Tag dropdown and adding a tag with whatever name we want.

Now over in the PlayerController script, we can test if "other" is a pickup, and if it is then we can deactivate the PickUp. The first part is done with "other.gameObject.tag", and the second is done with "other.gameObject.SetActive(false)." 

So at this point we test... and it doesn't work! When the sphere hits a PickUp, the PickUp dosn't deactivate. Why not? Right now, Unity's physics engine is not allowing the sphere object to "enter" the pickups, because they are both solid objects. Thus our method "onTriggerEnter" is never called. We need to change the collision type of our PickUp objects to "trigger" by checking the Is Trigger checkbox in the pickups' Box Collider component. Alternately, we could use the method "OnCollisionEnter(Collision col)" but then we would have to go a roundabout way to finding out which object our player collided with.

Now, remember how we added a RigidBody component to the player sphere? In addition to giving the sphere physics for movement, it tells Unity to treat the sphere as a "dynamic" object, meaning it can move and thus its collision box needs to be recalculated constantly. On the other hand, our PickUp objects are currently static, meaning their collision boxes don't get recalculated. This is bad, because when our PickUps rotate, their collision box doesn't rotate with them. So we need to make the PickUps dynamic, by adding the RigidBody component. 

To let the RigidBody PickUps float, we COULD simply uncheck the "Use Gravity" checkbox, but then the PickUps would fall through the floor because their "trigger" status means they do not collide with other objects. Instead, we should check "Is Kinematic," which prevents the object from being affected by physics forces (ie, gravity) while still allowing the object to be moved and animated via their Transform (ie, rotation).


Now we want to count PickUps collected, display the number, and end the game.

Counting is quote easy, we add a private "count" variable to PlayerController, intialize it to 0 during the Start() function, and then increment it during Update() during the PickUp collision resolution.

Now to display the count. The tutorial says to use a "GUI Text" GameObject but this has been deprecated, so I'm going to have to wing it from here:

Unity currently implements a UI system with a "Canvas" object as a container for several smaller UI objects (text, images, etc). We want to display a piece of text, so we'll need to create a Canvas GameObject and then a child Text GameObject (which I named CounterText). I then reset the Canvas transform to 0 and set the CounterText's "anchor" values to the top-left corner (which is responsive to changes in screen size). From there I game the text a small transform to ensure there is always a gap between the text and the edge of the screen.

Hooking up the text to player's counter meant modifying the PlayerController script. I added a public Unity.UI.Text variable named PickUpUI which I then set to CounterText in the Unity GameObject inspector. To control the text being displayed, I added "PickUpUI.Text = counter" to the Update() function. I could've also placed this in FixedUpdate() to only run when a new pickup occurs, but then I would need to duplicate my UI-updating code if I ever want other factors to modify counter (ie, make it a "score" which decreases over time). The benefit of reducing the number of UI updates is probably trivial by comparison. An added benefit is that now the UI will update itself on the first frame to read "0".

Last, we want to display some text when all PickUps have been picked up, telling the player they won. I created another Text GameObject under Canvas, added another Unity.UI.Text variable to the PlayerController script, and added an if-statement to Update() checking if count is greater than the hardcoded number of pickups in my game (7). If yes, set WinText.text = "YOU WIN!". The count-to-win could probably be made dynamic by adding a reference to the PickUps parent and checking something like "PickUps.numberOfChildren" but the tutorial doesn't do that and I don't want to get too far out of sync.


So now we have a finished-ish game, and we want to deploy it to users.. specifically to the unity Web Player. First go into File -> Build Settings and set the target Platform to "Web Player". Then we drag the scene we want from the Assets view into the build window.

Note that we could build without specifying any scenes, in which case Unity would only build the scene we currently have open for editing.

Now hit Build, choose a folder to put our build in, and finish.

Since we chose to build to a "Web Player," unity produces two files: A .unity3d file containing the game's data, and an HTML file through which we can actually play the game. Note that this HTML file can be modified, and unity can be given custom templates to generate a custom HTML file automatically.