Create a new Unity Project
In the hierarchy create an empty game object to hold the components of our clock.
Rename the game object and ensure that the scale is uniform and position and rotation are zeroed out.
Add a Cylinder object to our Clock object. Ensure that it's transform values are the same as those for the clock.
Turn the cylinder into a clock face by flattening the Y scale and make the face bigger by increasing the X and Y scale.
We want the clock to be upright as if hanging on a wall.
In Unity the X axis points right, the Y axis points up, and the Z axis points forward.
Set the cylinder's X rotation to 90 and adjust the scene view so the clock's front is visible, so the blue Z arrow of the move tool points away from you, into the screen.
Change the name of the cylinder game object to Face, as it is one component of the clock, it is a child of the clock.
We need to create the hour positions on the clock.
A clock is a circle which has 360 degrees.
There are 12 hour positions.
Therefore 360 divided by 12 gives us 30 degrees.
30 degrees between each hour position.
Add a cube object to the scene, name it Hour Indicator 12,
and also make it a child of Clock.
Set its X scale to 0.5, Y scale to 1, and Z scale to 0.1 so it becomes a narrow flat long block. Then set its X position to 0, Y position to 4, and Z position to −0.25. That places it on top of the face to indicate hour 12. Also remove its BoxCollider component.
Create a new material for the 12th hour indicator.
Add the material to the hour indicator.
Albedo is a Latin word which means whiteness. It's the color of something when illuminated by white light.
We need an indicator for every hour but we will start with the cardinal points.
Begin by orienting the scene view camera so we look straight down the Z axis. You can do this by clicking on the axis cones of the view camera gizmo at the top right of the scene view. You can also change the axis of the scene grid to Z via the grid toolbar button.
Duplicate the Hour Indicator 12 object.
Rename it to Hour Indicator 6.
Now Negate it's Y position.
Do the same for indicators 3 and 9
Their X positions should be 4 and -4 with 0 Ys.
Also their Z rotation should be 90 degrees.
Then create another duplicate of Hour Indicator 12, this time for hour 1.
Set its X position to 2, its Y position to 3.464, and its Z rotation to −30.
Then duplicate that one for hour 2, swap its X and Y positions, and double its Z rotation to −60.
How are the positions calculated?
Duplicate these two indicators and negate their Y positions and their rotations to create the indicators for hours 4 and 5.
Then use the same trick on hours 1, 2, 4, and 5 to create the remaining indicators, this time negating their X positions and again their rotations.
To create the hour hand duplicate hour indicator 12 and rename it to Hours Arm.
Create a Clock Arm material, I chose solid black, use it on the arm.
Decrease the arms X scale to 0.3
Increase the arms Y scale to 2.5
Change the Y position to 0.75 so that it points up to 12 but is offset slightly downwards.
The arm needs to rotate around the CENTER of the clock
PROBLEM: If we change its Z rotation the arm rotates around its own center!
SOLUTION: Create a PIVOT object and rotate that instead.
Create an empty game object
Name the object Hours Arm Pivot
Ensure its position and roation is 0 and its scale is 1
Make Hours Arm a child of the Pivot
Duplicate Hours Arm Pivot twice to create a Minutes Arm Pivot and a Seconds Arm Pivot. Rename them accordingly, including the duplicated arm child objects.
Minutes Arm should be narrower and longer than Hours Arm
set its X scale to 0.2 and Y scale to 4, then increase its Y position to 1.
Change its Z position to −0.35 so it sits on top of the hours arm.
Note that this is for the arm, not its pivot!
Adjust Seconds Arm as well.
This time use 0.1 and 5 for the XY scale
and 1.25 and −0.45 for the YZ position.
The seconds arm should stand out.
Make a different material for it using a bright colour.
Add a new script asset to the project via Assets / Create / C# Script and name it Clock.
Open the Clock Script in your code editor by double clicking it.
Delete all code in the Clock Script.
An empty file defines nothing. It must contain the definition of our clock component.
We define the general class or type known as Clock.
Once that's established, we could create multiple such components in Unity, even though we'll limit ourselves to a single clock in this tutorial.
In C#, we define the Clock type by first stating that we're defining a class, followed by its name.
Because we don't want to restrict which code has access to our Clock type, it is good form to prefix it with the public access modifier.
At this point we don't have valid C# syntax yet. If you were to save the file and go back to the Unity editor then compilation errors will get logged in its console window.
We indicated that we're defining a type, so we must actually implement it. That's done by a block of code that follows the declaration. The boundaries of a code block are indicated with curly brackets. We're leaving it empty for now, so just write {}.
Our code is now valid. Save the file and switch back to Unity.
The Unity editor will detect that the script asset has changed and triggers a recompilation.
After that is done, select our script.
The inspector will inform us that the asset does not contain a MonoBehaviour script.
What this means is that we cannot use this script to create components in Unity.
At this point, our Clock defines a basic C# object type.
Our custom component type must extend Unity's MonoBehaviour type, inheriting its data and functionality.
What does mono-behavior mean?
The idea is that we can program our own components to add custom behavior to game objects. That's what the behavior part refers to.
The mono part refers to the way in which support for custom code was added to Unity.
It used the Mono project, which is a multi-platform implementation of the .NET framework.
Hence, MonoBehaviour. It's an old name that we're stuck with due to backwards-compatibility.
We must make Clock into a subtype of MonoBehaviour.
Change the type declaration so that it extends that type, which is done with a colon after our type name, followed by what it extends.
This makes Clock inherit everything of the MonoBehaviour class type.
However, this will result in an error after compilation.
The compiler cannot find the MonoBehaviour type.
This happens because the type is contained in a namespace, which is UnityEngine.
To access it, we have to use its fully-qualified name, UnityEngine.MonoBehaviour.
It is inconvenient to always have to include the UnityEngine prefix when accessing Unity types. Fortunately we can declare that the namespace should be searched automatically to complete type names in the C# file.
This is done by adding using UnityEngine; at the top of the file.
The semicolon is required to mark the end of the statement.
Now add the custom component to the Clock game object in Unity.
This can be done either by dragging the script asset onto the object,
or via the Add Component button at the bottom of the object's inspector.
To rotate the arms, Clock objects need to know about them.
Start with the hours arm.
Like all game objects, it can be rotated by adjusting its Transform component.
So we have to add knowledge of the arm pivot's Transform component to Clock.
This can be done by adding a data field inside its code block.
Defined as a name followed by a semicolon.
The name, hours pivot would be appropriate for the field. However, names have to be single words.
The convention is to make the first word of a field name lowercase and capitalize all other words, then stick them together.
We also have to declare the type of the field, which in this case is UnityEngine.Transform.
It has to be written in front of the field's name.
Our class now defines a field that can hold a reference to another object, whose type has to be Transform.
We have to make sure that it holds a reference to the Transform component of the hours arm pivot.
Fields are private by default, which means that they can only be accessed by the code belonging to Clock.
But the class doesn't know about our Unity scene, so there's no direct way to associate the field with the correct object.
We can change that by declaring the field as serializable.
This means that it should be included in the scene's data when Unity saves the scene, which it does by putting all data in a sequence—serializing it—and writing it to a file.
Marking a field as serializable is done by attaching an attribute to it, in this case SerializeField.
It's written in front of the field declaration between square brackets, typically on the line above it but can also be placed on the same line.
Once the field is serializable Unity will detect this
and display it in the inspector window of the Clock.
To make the proper connection, drag the Hours Arm Pivot from the hierarchy onto the Hours Pivot field. Alternatively, use the circular button at the right of the field and search for the pivot in the list.
In both cases the Unity editor grabs the Transform component of Hours Arm Pivot and puts a reference to it in our field.
Add two more serializable Transform fields to Clock with appropriate names.
Because these fields are all the same data type, we can make this code much shorter:
Now connect the other two arms in the editor.
We will rotate the arms as the components awaken. We will do this in the Awake method.
Methods are somewhat like mathematical functions, for example:
That function takes a number—represented by the variable parameter x —doubles it, then adds three.
It operates on a single number, and its result is a single number as well.
In the case of a method, it's more like
where p represents input parameters and c represents whatever code it executes.
Like a mathematical function a method can produce a result, but this isn't required.
We have to declare the type of the result—as if it were a field—or write void to indicate that there is no result.
In our case, we just want to execute some code without providing a resulting value, so we use void.
We also don't need any input data.
However, we still have to define the method's parameters, as a comma-separated list between round brackets.
It's just an empty list in our case.
We now have a valid method, although it doesn't do anything yet.
Just like Unity detected our fields, it also detects this Awake method.
When a component has an Awake method, Unity will invoke that method on the component when it awakens.
This happens after it's been created or loaded while in play mode.
We're currently in edit mode, so this doesn't happen yet.
To rotate the arms we have to create a new rotation.
We can change the rotation of a Transform by assigning a new one to its localRotation property.
Although the rotation of a Transform component is defined with Euler angles in degrees per axis in the inspector, in code we have to do it with a quaternion.
We can create a quaternion based on Euler angles by invoking the Quaternion.Euler method.
Do this by writing it in Awake, followed by a semicolon to end the statement.
The method has parameters used to describe the desired rotation.
In this case we'll provide a comma-separated list of containing three arguments, all between round brackets, after the method name.
We supply three numbers for the X, Y, and Z rotations.
Use zero for the first two and −30 for the Z rotation.
If you enter play mode now
you will see that the clock is set to 1pm.
Getting the current time.
We can use the DateTime struct to access the system time of the device we're running on.
DateTime isn't a Unity type, it is found in the System namespace.
It is part of the core functionality of the .NET framework, which is what Unity uses to support scripting.
DateTime has a Now property that produces a DateTime value containing the current system date and time.
To check whether it's correct we'll log it to the console at the start of Awake.
We can do that by passing it to the Debug.Log method.
Now we get a timestamp logged each time we enter play mode.
You can see it both in the console window and in the status bar at the bottom of the editor window.
We're getting close to a working clock.
Let's again start with the hours.
DateTime has an Hour property that gets us the hours portion of a DateTime value.
Invoking it on the current timestamp will give us the hour of the day.
So to have the hours arm show the current hour we have to multiply the −30° rotation by the current hour.
Multiplication is done with the asterisk * character.
We also no longer need to log the current time so can get rid of that statement.
Play the game and see what effect this has had.
We should tidy up our code.
To make it clear that we're converting from hours to degrees, we can define an hoursToDegrees field containing the conversion factor.
The angles of Quaternion.Euler are defined as floating-point values, so we'll use the float type.
Because we already know the number, we can immediately assign it as part of the field declaration.
Then multiply with the field instead of the literal -30 in Awake.
If we declare a whole number without a suffix then it's assumed to be an integer.
Integers are a different value type - whole numbers.
Although the compiler converts them automatically, let's make explicit that all our numbers are of type float, by adding the f suffix to them.
The amount of degrees per hour is always the same.
We can enforce this by adding the const prefix to the declaration of hoursToDegrees.
This turns it into a constant instead of a field.
Let's give the same treatment to the other two arms.
Both a minute and a second are represented by a rotation of negative six degrees.
We're using DateTime.Now three times, to retrieve the hour, minute, and second.
Each time we go through the property again, which requires some work, which could theoretically result in different time values.
To make sure that this doesn't happen, we should retrieve the time only once.
We can do this by declaring a variable inside the method and assign the time to it, then use this value afterwards. Let's name it time.
In case of a variable it's possible to omit the type declaration, replacing it with the var keyword.
This can shorten the code but is only possible when the variable's type can be inferred from what is assigned to it when it is declared.
We get the current time when entering play mode, but after that the clock remains motionless.
This is because the Awake method only runs once - when the component is created.
To keep the clock synchronized with the current time, change the name of our Awake method to Update.
This is another special event method that gets invoked by Unity every frame instead of just once, as long as we stay in play mode.
Note that our Clock component has gained a toggle (checkbox) in front of its name in the inspector.
This allows us to disable it, which prevents Unity from invoking its Update method.
Continuous Analogue Motion
DateTime doesn't contain fractional data.
Fortunately, it does have a TimeOfDay property.
This gives us a TimeSpan value that contains the data in the format that we need, via its TotalHours, TotalMinutes, and TotalSeconds properties.
Begin by getting the TimeOfDay struct value from DateTime.Now and store that in the variable instead.
This will result in compiler errors, complaining that we cannot convert from double to float.
This happens because the TimeSpan properties produce values with the double-precision floating point type, known as double.
These values provide higher precision than float values.
Unity's code only works with single-precision floating point values!
To fix this we must explicitly convert from double to float.
This process is known as casting and is done by writing the new type within round brackets in front of the value to be converted.
Well done on completing the tutorial!