Notes for weeks 9-12 covering the creation of the lantern, candle, lantern stand and tree.
Copies of files can be found on our Github.
Notes for weeks 9-12 covering the creation of the lantern, candle, lantern stand and tree.
Copies of files can be found on our Github.
This week we looked at:
Border Resource
To define a new border resource, we created a new script in the scripts folder that inherits Resource. Here’s the code:
class_name Border extends Resource
@export var Top : int
@export var Bottom : int
@export var Left : int
@export var Right : int
It’s a very simple resource that just stores four values.
Note that we used “class_name Border” to ensure this new class had a name. This means, among other things, that we can create variables of this type in code.
In our mover.gd script, we added an additional exported variable of type Border to the class:
@export var direction : Vector2
@export var speed : float
@export var wander_time_min : float
@export var wander_time_max : float
@export var border : Border
Once we saved the file and looked at the inspector, we could see that this new exporter variable is there and by clicking on the drop down arrow, we could create a new Border resource and assign values to it. We entered the values 80, 20, 20 and 20 for the border.
In the mover.gd code we could then update our correct_dir_for_bounds() function to take this border into account. Note that they way we’ve structured the code, a border is optional:
func correct_dir_for_bounds() -> void:
var top = 0
var bottom = 0
var left = 0
var right = 0
if (border):
top = border.Top
bottom = border.Bottom
left = border.Left
right = border.Right
if (direction.x < 0 && parent.position.x < left):
direction.x = 1
if (direction.x > 0 && parent.position.x > vp_rect.size.x - right):
direction.x = -1
if (direction.y < 0 && parent.position.y < top):
direction.y = 1
if (direction.y > 0 && parent.position.y > vp_rect.size.y - bottom):
direction.y = -1
This now keeps the enemy from going too close to the edge of the screen, especially at the top.
Creating a Missile
We went to Piskel and created a new 16×16 sprite to represent a missile, exported it and imported it into our Textures folder in Godot.

We then added a new Area2D to the scene and called it “Missile”. Under this we added a CollisionShape2D with a RectangleShape of size 16×16 and a Sprite2D containing the missile image.
We added a new script to MIssile:
class_name Missile extends Area2D
@export var direction : Vector2
@export var speed : float
# Called every frame. 'delta' is the elapsed time since the previous frame.
func _process(delta: float) -> void:
position += direction * speed * delta
rotation = Vector2.UP.angle_to(direction)
This code moves the missile in the given direction at the given speed, pointing it towards the direction of travel. Because we’ve specified a class name, we can access the variables here easily in other code.
We also wanted a way to ensure that missiles we create don’t go on for ever. A cheap and easy way to do this is with a timer. We added timer under the Missile node and set its time out to 3s and enabled both One Shot (fire the timer once and don’t repeat) and Autostart (start counting down as soon as the node appears in the tree).
We then needed to connect this timer to the missile code, we selected the Timer and opened the Node panel in the UI. Right-clicking on Timer – timeout and connected it to the missile.gd script with a function _on_timer_timeout(). All we need in _on_time_timeout() is to call queue_free(). Now the missile self destructs after three seconds.
Finally we dragged Missile from the tree into the scenes folder in the file system view, making it a separate scene. With that done. we could remove it from the scene.
Shooting
We went to the Project | Project Settings | Input Map and added a new Action called “Shoot”. We bound this to the spacebar and the left mouse button.
In ship.gd we added the following export variable to store the missile scene:
@export var missile_scene : PackedScene
We then added a _process() function to check for the shoot action being triggered and to spawn a missile a little ahead of the player and moving in the same direction the player is moving in:
const MISSILE_OFFSET : int = 32
func _process(delta: float) -> void:
if (Input.is_action_just_pressed("Shoot")):
var new_missile = missile_scene.instantiate() as Missile
new_missile.direction = velocity.normalized()
if (new_missile.direction == Vector2.ZERO):
new_missile.direction = Vector2.UP
new_missile.position = position + new_missile.direction * MISSILE_OFFSET
get_parent().add_child(new_missile)
This code:
Rate Limiting Shots
Finally, we added a little code to implement a minimum time between shots. We added two new variables, one an export and the other internal:
@export var min_time_between_missiles : float = 0.1
var missile_countdown : float = 0
We updated _process() as follows:
func _process(delta: float) -> void:
missile_countdown -= delta
if (Input.is_action_just_pressed("Shoot") && missile_countdown < 0):
var new_missile = missile_scene.instantiate() as Missile
new_missile.direction = velocity.normalized()
if (new_missile.direction == Vector2.ZERO):
new_missile.direction = Vector2.UP
new_missile.position = position + \
new_missile.direction * MISSILE_OFFSET
get_parent().add_child(new_missile)
missile_countdown = min_time_between_missiles
Every time we run _process() (i.e. every frame) we count down missile_countdown. Since it starts at zero, it will keep getting negative until we first shoot,. We only shoot when it is less than zero. If we shoot, we set it to min_time_between_missiles this means there can’t be another shot until this time has elapsed.
Getting the Code
All our code for this year is available on our GitHub.
This week we took our cube and made it vaguely more spaceship-shaped by moving it up to (0, 2, 0) and resizing it to (5, 1, 4). We also took our ground plane and scaled it to (10, 10, 10). Now it looks vaguely more like a flying craft than before!
The ship controls still work as before. WASD move the ship horizontally over the ground and there’s no vertical movement. To make it look more like a flying craft, we’d like it to bank (aka. tilt over) as it moves horizontally. That’s going to take a bit of figuring out.
Let’s take a little aside to talk about angles in Unity.
In the Unity Inspector we see three numbers for a transform’s rotation. These numbers represent rotations around the X, Y and Z axis respectively. This way of storing a rotation is known as “Euler angles”. It seems simple and straightforward and, if you’re rotating around just one of the X,Y or Z axes, it is, but for complex rotations it actually has a lot of problems. The order in which you apply the rotations matters. If you rotate first in X then Y and the Z, you get one orientation, but choose a different order and you’ll get something different. It’s ambiguous.
Look at these two boxes. Each are rotated by 90degrees around the X and Z axis but in a different order. They end up in completely different orientations:
Rotations internally in Unity are stored using a special variable called a Quaternion. Euler angles can easily be converted to Quaternions and back again. Quaternions store the axis that the object rotates around and the amount of the rotation. It’s completely unambiguous with no order to worry about.
When we code, we use Quaternions for rotations because that’s what Unity uses.
In ShipControls.cs we add two variables TiltSpeed and TiltLimit to control banking. TiltLimit represents the amount of degrees we’re going to bank as we move horizontally and TiltSpeed is how quickly we can change angle. We don’t want the craft immediately going from tilting one direction to tilting the other – we want a smooth change of rotations. That will look a lot better.
public float TiltSpeed = 10.0f;
public float TiltLimit = 10.0f;
Because these are public properties, we see them appear in the inspector for Ship Controller.
Tilt is dependent on the user input. If the user is pressing right, we should be titled to the right. If the user is pressing left, we should be tilted to the left. If the user isn’t pressing left or right, we should be level.
In the Update() function in ShipControls.cs we add this code near the top:
float tiltTarget = 0;
if (moveInput.x < 0)
tiltTarget = TiltLimit;
else if (moveInput.x > 0)
tiltTarget = -TiltLimit;
Quaternion targetRotation = Quaternion.Euler(tiltTarget, 0, 0);
So depending on user input, tiltTarget will either be 0, TiltLimit or -TiltLimit (note the minus). We then turn this into a Quaternion representing this amount of a rotation around the X axis.
How do we move smoothly between two rotations, namely the rotation we’re at and the rotation we want to be at?
There’s a general concept called “linear interpolation” for finding a value some proportion of the way between two other values. Let’s imagine a trivial example; if our numbers were 0 and 10 then the number 50% of the way (aka. half) between these would be 5. In Unity linear interpolation is known as “Lerp” and it can be used not just for simple numbers but for Vector3’s (representing positions and directions) and Quaternions (representing rotations) too.
For Quaternions, Unity has a special “Lerp” that works really well for rotations called Quaternion.Slerp(). “Slerp” stands for Spherical liner interpolation. Not a function name you’ll quickly forget. So to “Slerp” between the angle we’re currently at and the angle we’d like to be at, we need to know what proportion of that change we can make this frame. Here’s how we calculate that proportion:
Here’s what the code looks like:
float rotationDiff = Quaternion.Angle(targetRotation,
transform.rotation);
float timeToFullyRotate = rotationDiff / TiltSpeed;
float rotationProportion = Time.deltaTime / timeToFullyRotate;
Finally we actually set the transform’s rotation using Slerp(), the rotation we’re at, the rotation we’d like to be at and the proportion of the change that we’ve calculated:
transform.rotation = Quaternion.Slerp(transform.rotation,
targetRotation,
rotationProportion);
Testing the ship control code we found that good values for the variables were as follows:
| Move Speed | 50 |
| Tilt Speed | 100 |
| Tilt Limit | 30 |
We also noted that any values entered in the inspector override defaults in the code.
We started on a simple spaceship model in Blender, using the proportions of our cube in Unity as a guide [5m x 1m x 4m].
This was mine, but everyone had their own take. If anyone wants to download mine, it can be found here on our Teams site. If you’re not a member of our Teams site yet, get in touch to be added.
When we return after our break, we’ll look to texture this model and export it from Blender and import it into Unity.
All the code we’re made this year is on our GitHub. It can be found here. If you don’t have the latest version of our code before any session, use the green Code button on that page to get the “Download ZIP” option which will give you a full copy of all code we’ll be writing this year.

This week we continued the second week of our Gem Search project. We completed the ItemScatterer component by:
We then finally tested it by adding an ActivateNearest() function to light up the nearest lamp post and give us a visual validation that things are working.
Our function to distribute the items comes in has five main pieces to it:
The top-left-back corner, which we’re calling start, is calculated using the position of the ItemScatterer and the size that’s been specified:
Vector3 start = new Vector3(transform.position.x - size.x / 2.0f,
transform.position.y + size.y / 2.0f,
transform.position.z - size.z / 2.0f);
The transform.position of ItemScatterer is going to be in the centre of its box. To get to the left and the back we take off half the box’s size in the X and Z directions respectively. To get to the top of the box, we add half the box’s size in the Y direction.
The pair of loops look like this:
for (float xOffset = 0.0f; xOffset < size.x; xOffset += spacing)
{
for (float zOffset = 0.0f; zOffset < size.z; zOffset += spacing)
{
....
}
}
The first loop, or outer loop, takes a variable called xOffset, initially zero, and keeps increasing it by spacing (default of 3) until it’s bigger than size.x. The represents us moving from the left to the right of the ItemScatterer.
The second loop, or inner loop, takes a variable called zOffset, initially zero, and keeps increasing it by spacing (default of 3) until it’s bigger than size.z. The represents us moving from the back to the front of the ItemScatterer.
The other important thing to note is that because the second loop is inside the first, for every step we make in X, we cover all positions in Z. Here’s what this looks like:

The calculation if the current position is relatively straightforward, the offsets in the X and Z directions are our loop variables and already calculate and we’re not changing the value of Y at all:
Vector3 thisPos = start + new Vector3(xOffset, 0.0f, zOffset);
We already have our probability property for us to specify the chances of a location having an item. We compare this to Random.value, which is a random value between 0 and 1. If Random.Value is less than probability, then we’ll place something:
// Check probability
if (Random.value < probability)
{
// Place an item
}
We have already placed our rocks and terrain into a layer named “Ground”. We only want to place our items on this layer. We can make sure that we only check this layer by using a LayerMask in our call to Physics.Raycast(). We add a new pubic string property to our class called groundLayerName and give it the default “Ground”:
public string groundLayerName = "Ground";
In our Distribute() function then, at the top before the loops, we can create the LayerMask:
LayerMask lm = LayerMask.GetMask(groundLayerName);
We also need to create a RayCastHit variable. This is somewhere that Physics.RayCast() can fill in details about what it hit. The complete code looks like this:
// Check for ground
RaycastHit hit = new RaycastHit();
if (Physics.Raycast(thisPos, Vector3.down, out hit, size.y, lm))
{
Instantiate(item, hit.point, Quaternion.identity, transform);
}
Physics.Raycast() returns a bool value (either true or false) and the if statement determines whether or not to Instantiate an item depending on whether or not ground was hit. The version of Physics.Raycast() we’re using takes the following inputs:
The Instantiate is very standard, but we’ve also supplied our own transform as the parent transform. This means when the items are created, they are beneath ItemScatterer in the Heirarchy.

For a human to scan a classroom and determine which desk is closest is something we do very quickly and intuitively, but a computer program generally has to take a more methodical approach. We look at each desk in turn and calculate how far away it is from us. If this is the first desk, or it’s closer than the previous desk we thought was the closest, we remember this desk and distance is is from is. We keep doing this until there are no more desks to compare against.
Here’s what the code to do this looks like:
public Transform player;Q
public GameObject nearestItem;
private void FindNearest()
{
GameObject foundNearest = null;
float nearestDist = 0.0f;
for (int childIndex = 0; childIndex < transform.childCount; childIndex++)
{
Transform thisChildTransform = transform.GetChild(childIndex);
float thisDistToPlayer = (player.position - thisChildTransform.position).magnitude;
if (foundNearest == null || thisDistToPlayer < nearestDist)
{
foundNearest = thisChildTransform.gameObject;
nearestDist = thisDistToPlayer;
}
}
nearestItem = foundNearest;
}
We have two new public properties, one to hold the Transform of the player so we can know their position, and the other which will get assigned the nearestItem to the player every time it’s calculated. We call this FindNearest() function from Update() so that it gets run every frame.
Since we Instantiated our items as children of ItemScatterer, we can use transform.childCount and transform.GetChild(n) to know how many children we have and to get each one in turn, respectively.
Subtracting one Vector3 from another Vector3 returns a third Vector3 containing the displacement between them. Getting the magnitude of this returns the actual distance.
Finally our check:
if (foundNearest == null || thisDistToPlayer < nearestDist)
Says if we’ve never assigned a value to foundNearest, which will true the first time, OR (that is what the double-bars || means) the distance we just calculated is smaller than the previously smallest distance we knew about, then make this item the nearest item and remember its distance.
The code for this week’s project is on our GitHub, as always.
This week we continued with our model of a car using box modelling techniques and added some additional detail to the car.
Here are the video instructions:
Here’s a link to the folder where we store all our files . You’ll find a new file in there called car_wip2.blend, containing everything we did this week.
Hi folks, hard to believe this was our last regular session of 2019. Next week is our Christmas pizza party and show-and-tell.
This week we took a photo of a cereal box and UV mapped it to a simple cube which we scaled to the appropriate proportions. One we made our model we built a very basic studio setup and did our first render with a camera and a light.
Here are the video instructions:
Here is the final render from my model:

The file for this week can be download from here.
Once we return in January I’ll be using Blender 2.81, instead of 2.80 which we have been using, so everyone should try to upgrade if they can by going here.
Hi everyone,
Thank you all for coming again on Saturday, Ruaidhrí ran the session this week and I am sure you were all glad not to have to listen to me for a change.
He did musical instruments and should you how to create variables for each one so that you could have a note repeat.
No notes this week but below is the code that was used.
This coming Saturday will be run by Eoin and will be Christmas themed based! This will be our last session before our Christmas party so dust off those Christmas Hats! and Jumpers
Martha, Ruaidhrí, Iseult, Julie and Eoin
Hi everyone
Thank you to everyone who came on Saturday.
We did scrolling backgrounds and this is very useful in any type of game and can add an extra dimension to your game whether its a driving game or a flying game.
We were scrolling horizontally so we were using the xpos but remember you can just as easily scrolling vertically by just using the ypos
Here are the notes in PDF form
CDA-S8 Week_10-scrollingbackgrounds.pdf
Enjoy the long weekend and we will see you all the week after.
Martha
Julie, Eoin and Ruaidhrí
This week, despite a small group, we finished off the Pin Bowling game. After Christmas we’ll move onto a different game – most likely something 1st person in design.
UI Elements – Text and Buttons
We added two UI Text elements to our game and aligned them to the top left and top right hand corners respectively. One is used for showing the current score and one is used for showing the number of shots remaining. Note that when we add UI elements, Unity automatically adds a Canvas and an EventSystem for us.
UI elements don’t have a standard Transform component, instead they have a RectTransform component which is used to describe their position within their Canvas. To align to a particular location we click on the square below the words “Rect Transform” and select how we’d like to align within the Canvas by clicking one of the options while holding the Shift and Alt keys to set both the pivot and position.

This week, the challenge in Beginners’ Scratch was to write your own version of the classic Breakout game.
This game uses lots of the big ideas that we have encountered in previous weeks, and that are good to know about when preparing to get yellow belts. These big ideas include:
Here are my notes from the day (PDF format): CDA-S2-Challenge09-BrickGame.pdf
Here is my version of the game: http://scratch.mit.edu/projects/cdathenry/2964870
If you would like a copy of my slides in PowerPoint format, get in touch via Twitter or the comments.