Creators – Week 10

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.

Angles in Unity

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.

Variables for Ship Tilt

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.

Determining What Tilt We Should Have

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.

Moving Smoothly Between Rotations

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:

  1. Find the angular different between the rotation we’re at and the rotation we’d like to be at
  2. Calculate how long, given the speed we’ve specified for changing angle, it would take to fully make that change in rotation
  3. Calculate, given the time since the last frame, what proportion of that change we can actually make (it will be just a small bit of the change).

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);

Tuning the Variables

Testing the ship control code we found that good values for the variables were as follows:

Move Speed50
Tilt Speed100
Tilt Limit30

We also noted that any values entered in the inspector override defaults in the code.

Simple Blender Spaceship

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.

Getting the Code

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.

Creators – Week 9

I decided, since we weren’t making a lot of progress with our Dungeon game, that we might need to switch to something simpler. We are going instead to make a game in the style of an isometric side-scrolling arcade shooter. This style of game first appeared back in 1982 with Sega’s Zaxxon (pictured below) but there have been many examples since.

We decided to call our game “Speedy Spaceship”.

To start with the game we opened a new Unity project and used the Package Manager to install the new Unity Input Manager package. This is the preferred way to define user inputs in Unity these days as it’s far more flexible when you want to add alternative control schemes (gamepad instead of keyboard, for example).

We first created a folder called Input and in there we made a new “Input Actions” asset and called it “ShipControls”. Opening this, we defined a simple control scheme, for keyboard and mouse, as follows:

At the moment it only contains one action “Move” but we’ll be adding at least one more action later to enable shooting. Movement is bound to the WASD keys on the keyboard.

Once we’d done that, with this Input Actions asset still selected in the Project view, we selected the “Generate C# Class” option in the Inspector and pressed the “Apply” button. This made a script file called “ShipControls.cs” next to our Input Actions asset in the inputs folder. This file contains a lot of code for handling the interactions, but we don’t need to worry about it’s contents; it’s easy for us to make use of it.

We then added a plane, which we added a material to, and a cube positioned just above the plane, to our current scene. With the cube selected, we positioned ourselves in the Scene View such that the cube’s X-axis (red) was pointing right and away from us and the cube’s Z-axis (blue) was pointing left and away from us.

We then selected “Main Camera” in the scene and used the GameObject|Align with View menu command to set the camera to the same angle as the Scene View. Toggling between Scene View and Game View now show the exact same angle, at least until we move in the scene view again.

We create a Scripts folder and made a new C# script inside called “ShipController”. We dragged and dropped this script over the cube in the scene view to assign it.

The first thing we needed to do was to attach the ShipControls to the ShipController (similar names, but two different things). We made a new private property in ShipController to hold a reference to a ShipControls instance, added the Awake() function where we created a new ShipControls object and then used the OnEnable() and OnDisable() functions to enable and disable the ShipControls when the ShipController was itself enabled or disabled. This is what that code looks like:

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class ShipController : MonoBehaviour
{
  private ShipControls _controls;

  private void Awake()
  {
    _controls = new ShipControls();
  }

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

  private void OnEnable()
  {
    _controls.Enable();
  }

  private void OnDisable()
  {
    _controls.Disable();
  }
}

For movement we then just need to find out what the value of our input is at every frame and move appropriately. First though, we need some control over the speed we’re going to move. We add a new public property for MoveSpeed:

public class ShipController : MonoBehaviour
{
  public float MoveSpeed = 10.0f;


Now we can add code to Update(), the function that Unity calls every time that a frame is drawn, to move the cube around in response to player input.

If we have a speed and a time, we just need to multiply them together to see how far we’ve gone. This is a simple mathematical way of expressing it:

speed * time = distance

If, for example, I’m travelling 100kph and I drive for two hours then the distance I’ve gone is 200km.

100km/h * 2h = 200km

The time between frames in Unity is stored in a special variable Time.deltaTime. This value for time is dependent on our framerate. As long as we use this value when we’re calculating distance moved in a single frame, the answer will be correct no matter if our computer is generating 20FPS or 200FPS.

Let’s see the final Update() function code:

  void Update()
  {
    Vector2 moveInput = _controls.Ship.Move.ReadValue<Vector2>();

    transform.position = transform.position +
                         new Vector3(moveInput.y * MoveSpeed * Time.deltaTime,
                                     0,
                                     -moveInput.x * MoveSpeed * Time.deltaTime);
  }

The first thing is that we ask for the value of the moveInput. This is defined as Vector2 value so it has an x and a y part (representing the horizontal and vertical axes respectively). The y component will be +1 when we’re pushing W (Up) and -1 when we’re pushing S (Down). Similarly the x component will be +1 when we’re pushing A (Left) and negative when we’re pushing D (Right).

We take transform.position, which controls the position of the cube and we set it to a new value which is it’s current position plus an new Vector3 which represents the change in position this frame. The x and z portions, which represent horizontal movement, are both calculated from the input. The y portion, representing vertical movement, is always zero. The other two are of the form:

distance = input * speed * time

Distance being speed multiplied by time we’ve seen, but what’s input doing in there? Well input is like a switch. When you’re not providing input, it’s zero. Anything multiplied by zero is zero, so distance must be zero when we’re not actively providing input. When you’ve providing input it’s either 1 or -1 which means we’ll move forward or backwards depending on what the input is.

Next week we’re going to make our ship a little more ship-like in proportion and make it bank when it goes sideways. We’ll also be creating a simple ship model in Blender.

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.

Creators – Week 5

This week we saw where we were going with the dungeon:

The screenshot shows a completed dungeon, made from the modular parts that we are currently building.

We finished off the two pillar designs, shaping one straight-sided and the other rounded and we worked on a wall section. For the wall section, we used a mirror modifier to make it two sided while only having to work on a single side.

Our file from today can be found here.

Creators – Week 4

This week we first created a crate:

This used many of the same techniques as the barrel last week.

We then started with a plan for our dungeon pieces. As long we we stick to these generic dimensions for floor, pillar and wall pieces, we can mix and match and everything will slot together easily:

Our file for today can be downloaded from here.

Creators – Week 3

This week we started Blender in earnest and created our first model, a wooden barrel with metal rings. The file is available here. You need to be a member of the Creators’ Teams group to download it, so let me know next week if you aren’t yet a member and I can get you added.

For those catching-up, Blender can be downloaded through the Windows App Store or from https://www.blender.org/download/.

You can review Blender basics using these videos:

Creator – Week 1

Hi folks, we kicked off our Creators group for 2023/2024 today. The slides from today’s session can be found here:

CoderDojo Athenry – Creators – Session 1.pptx

For next week, please try to use one of these files:

to document your game idea. Remember that we don’t need a timeline, just the game concept. These files are just templates, so feel free to add anything you thinks is important and to ignore or delete anything you don’t think is important.

You will need to be connected to Microsoft Teams to download these files. Once you’re successfully connected to Teams, remember to say “Hello” in the 2023 Say Hello channel.

Also try to have Blender installed before next weeks session. This will avoid problems with passwords/IT locks/etc. Blender can be downloaded here.

See you next week!

Modellers – Weeks 5 & 6

We completed our dioramas with materials and lights and rendered the final images. Some people chose their own colours and some used a palette generator (see: https://coolors.co/bf4e30-c6ccb2-093824-e5eafa-78fecf) to choose a complimentary set of colours to provide a harmonious look for our scene.

We used the Cycles rendered and turned on the denoising options, which automatically remove any graininess from the resulting render, to give us a high-quality output in a reasonable timeframe.

We then built a simple model of an articulated desk lamp. Built as a single mesh and composed of simple primitive objects, all starting as cylinders or cubes, we developed it initially in a completely linear vertical configuration.

We then created vertex groups for each distinct portion of the lamp and assigned those parts of the lamp to those vertex groups. From the bottom up, these groups were:

  • base
  • pivot
  • upperarm
  • elbow
  • lowerarm
  • wrist
  • shade

We then added a spot light to represent the lamp’s bulb.

Next we generated a new armature. In edit mode, we scaled the initial bone until it was just the size of the base vertically and called it base, the name matching that of the vertex group in the lamp model. We then extruded the tip of this bone vertically to the centre of the first hinge pin in the model. This bone was named pivot again matching the name of the corresponding vertex group. We repeated this until there was a bone, named for the corresponding vertex group, across each section of the model.

We then returned to edit mode and, selecting first the armature and then shift-selecting the model, we used Parent | Armature Deform from the object menu to associated them together. The mesh became a child of the armature. Selecting the armature and changing to Pose mode, we were able to see how rotating the bones allowed us to move the model in a simple and non-destructive way.

We then wanted to move the light with the armature as well, as it currently remained in space where we’d located it originally. To do this we selected the spotlight object and added an object constraint tying it to the the shade bone in the armature, as shown below. This moved the light, so we needed to reposition and rotate it again to get it in the correct location.

The final step was adding an inverse kinematic control bone into the rig. Moving an inverse kinematic (IK) bone automatically moves all connected bones to try to follow the movement of the control bone. In the armature, in edit mode, we added a single bone and located it near the rim of the lamp shade. We called it “control” and, selecting it and then the shade bone, used Armature | Parent | Make | Keep Offset to join them.

We then switched to pose mode. Once in pose mode, we could add an IK constraint to the control bone as shown below. All defaults here are fine in this case.

Then we saw that moving this bone in Pose mode caused the rest of the lamp to follow, however some parts of the lamp were moving in ways we didn’t want. The final step was to place some IK limits on certain bones in the Bone Properties Panel. For the base we locked x, y and z because we didn’t want it to rotate at all:

For pivot and shade, we locked then in x and z, this will allow them to turn around their own axis but not bend over. For all the others we locked them in y and z allowing them to bend over corresponding to the way they were pinned in the model.

Files for these final models can be found on our Sharepoint site.

Modellers – Week 4

This week we continued with adding more props to our diorama. Of these the gaming chair was the most complex.

To make the five-footed design of chair base we started with a cylinder with twice as many sides and then, after placing an edge loop near the bottom, extruded every second face to form the feet. We then extruded these feet ends a second time and extruded a few more times to form a roughly spherical shape to represent coasters.

The chair seat was just a cube, scaled to the appropriate proportions and then the front and back faces extruded and scaled slightly to round out the shape. We duplicated the seat and moved and rotated it to form the chair back as well.

The arms were created by first duplicating the faces on the left and right of the chair seat, scaling them vertically and moving them up into position. The reason for doing this with copied faces was because it ensured they were perfectly aligned to the rest of the chair. These faces where then extruded with the “Extrude along face normals” command [Keystroke: Alt-E] to give them width. Finally we extruded the back faces of these two arms, down to the bottom of the chair back, by selecting them, going to a side view and using the Ctrl-Right Click command.

We also added a few more simple props. These were all based on cubes, scaled to size and shaped using inset, extrude and edge loops.

This is the final room, from a geometry perspective. We still need to give materials to the objects, light the scene and render a final image.

Files for this weeks work-in-progress model can be found on our Sharepoint site.

Modelers – Build and Rig a Basic Humanoid Character – Week 15

This week we continued with our super-simple rigged humanoid character. We texture painted the character, animated them, added a rigged camera and finally rendered everything to video.

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 humanoid_wip2.blend, containing everything we did this week.

Modelers – Build and Rig a Basic Humanoid Character – Week 14

This week we build a super-simple humanoid character mesh using metaballs, a new modelling technique. We then rigged the character for animation with a simple armature.

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 humanoid_wip.blend, containing everything we did this week.

As an aside, my wife and I were playing with this character. Here’s what we did:

  1. Exported the mesh as an FBX (Select the mesh, File|Export|FBX and choose “export selected”)
  2. Loaded that FBX file into Mixamo.com
  3. In Mixamo, identified the key parts of the body and allowed it to process
  4. Attached a breakdancer mo-cap to the model
  5. Exported it again from Mixamo
  6. Imported it back into Blender (File|Import|FBX)
  7. Added some props (a hat and glasses) and made sure to add them to the head vertex group
  8. Texture painted the character
  9. Added some basic props, a ground, some cardboard and graffiti walls
  10. Animated the camera a little
  11. Imagined the music in our heads… 🙂

Hopefully you might be encouraged to try something like this yourself!