A beginner tutorial creating a real clock in Godot Engine. This can be instanced and reused in games and projects.

You can download the Project Files from my video tutorial right here.


In this tutorial I will be demonstrating how you can create a clock that shows you your local time.
I will assume you have little to no programming experience, and walk you through each step of the way from the beginning.

Project Resources

You can download a .zip file containing all of the resources you will need to follow along this tutorial right here. Make sure to create a new Godot Engine project and insert the folder containing images inside it before continuing.

Creating our Scene

We start by creating our main scene. Hit the + sign and select Control from the list of nodes. Rename it to "main" by slowly clicking the node twice, and then save the scene (Shortcut: ctrl+s) by going to the top menu "Scene -> Save Scene".

Creating our Clock

Create a Node2D node as the child of the Control node. Rename it to "clock". Select the clock node and create a Sprite node as child. Rename it "body", since it will be the clock body.

If you have followed along, your scene tree should look like this.

In order to give the "body" a texture, click on <null> inside the Texture property and select "Load".
This is found inside the Inspector view.

Navigate to the folder you have placed the clock image and select clock.png.
Once that is done, you may notice that the clock is HUGE. 1000x1000 pixels. Let us change the display size to match it.

Go into the top menu "Scene -> Project Settings" and in the "Display" section, type in 1000 for "width" and 1000 for the height.
Make sure they are both selected, and press Close.

Now we are going to center the clock. On the "clock" node, inside the Inspector view, find the Transform for Pos. It should be by default (0, 0).


Change it to (500, 500).

Play the scene to make sure everything looks right.
It is the button on the most left. These buttons are found near the top of Godot Engine.

On your first run, it will prompt you to select a main scene; select our scene and continue.
Hit the play button again, and you should see a perfectly square window.
If all is good, continue.

Let us now create the three arms that the clock should have. A pointer for seconds, minutes and hours.
In order to do this, we are going to create three Position2D nodes as children of "clock" node.
Rename them accordingly; "pivot_hours", "pivot_minutes", and "pivot_seconds".

When that is done, create one Sprite under each of the pivot nodes. Name them "arm_hours", "arm_minutes" and "arm_seconds".
Let us start by loading the image for arm_hours. Do as you did with the body, but select the pointer_hour.png.

You have probably noticed that the arm is not correctly positioned.


This is because the image is Centered. We will have to move the image up. We can get the correct distance by dividing the height of the image by 2, and then setting it in a negative direction. In order to get the image height, click on the Texture inside the Inspector, select Edit and under the "Size" property, copy the Y property. That is the total height of the image.
Now, go one step back so that you are once again seeing the properties of your arm_hours node. Under Transform Pos (0, 0), type in - and paste in the height you copied. Then type in /2. Press enter and it should automatically insert the result of -180/2, which is -90.

Now, do the same for the other arms, pointer_minutes.png and pointer_seconds.png.

Once that is done, all of your arms should be correctly positioned, similar to a real clock.

We are now ready to begin writing our script, that will update and rotate our clock according to real local time.

Implementing Clock.gd Script

Right click the "clock" node, and select "Add Script". Inside the script, remove all the comments until you have only the following blank script.

extends Node2D

func _ready():
	pass

The first we are going to do, is to get references to our pivot nodes. We are only going to interact with the pivots in order to rotate them.
We do that by using export (NodePath), that allows us to neatly use the Editor properties inside the "clock" node to select the nodes.

extends Node2D

# Hours Pointer
export (NodePath) var pivot_hours_path
onready var pivot_hours = get_node(pivot_hours_path)

# Minutes Pointer
export (NodePath) var pivot_minutes_path
onready var pivot_minutes = get_node(pivot_minutes_path)

# Seconds Pointer
export (NodePath) var pivot_seconds_path
onready var pivot_seconds = get_node(pivot_seconds_path)


func _ready():
	set_process(true)

Make sure to select "clock" node, and load all the pivot nodes.
onready means that it will only run while running _ready() at startup.

Cool. Let us start by creating our process, that will continuously run and update our clock according to real time.
Inside _ready, insert set_process(true).

func _ready():
	set_process(true)

This will enable the use of func _process(delta). Inside func _process, we are going to put in the "math" that converts seconds into an angle in radian.

# Processing
func _process(delta):
	# Get radians
	radian_seconds = -OS.get_time()["second"] * (PI/30)
	radian_minutes = -OS.get_time()["minute"] * (PI/30) + radian_seconds/60
	radian_hours = -OS.get_time()["hour"] * (PI/6) + radian_minutes/12
	
	# Assignment
	pivot_seconds.set_rot(radian_seconds)
	pivot_minutes.set_rot(radian_minutes)
	pivot_hours.set_rot(radian_hours)


Here is a more detailed explanation of how the math work:

The reason we are adding the radian_seconds/60 on the minute radian, and radian_minutes/12 on the hours radian, is to include the small rotational values that happen when the second pointer is moving.
i.e. slightly move the hour and minute pointer each time the seconds pointer is moving. If we didn't do that, the minute and hour pointer would only move when we have reached 60 seconds and 60 minutes.

Lastly, we will need to add the radians outside with the rest of the code.

The full code should look like this (not including comments):

extends Node2D

# Hours Pointer
export (NodePath) var pivot_hours_path
onready var pivot_hours = get_node(pivot_hours_path)

# Minutes Pointer
export (NodePath) var pivot_minutes_path
onready var pivot_minutes = get_node(pivot_minutes_path)

# Seconds Pointer
export (NodePath) var pivot_seconds_path
onready var pivot_seconds = get_node(pivot_seconds_path)

# Pointer angles
var radian_seconds = null
var radian_minutes = null
var radian_hours = null


# Start
func _ready():
	set_process(true)


# Processing
func _process(delta):	
	# Get radians
	radian_seconds = -OS.get_time()["second"] * (PI/30)
	radian_minutes = -OS.get_time()["minute"] * (PI/30) + radian_seconds/60
	radian_hours = -OS.get_time()["hour"] * (PI/6) + radian_minutes/12
	
	# Assignment
	pivot_seconds.set_rot(radian_seconds)
	pivot_minutes.set_rot(radian_minutes)
	pivot_hours.set_rot(radian_hours)

All you have to do now, is to hit play! We are finished!

Feel free to post any questions you may have.