Creators – Week 9

This week we started a new game called Gem Search. Here are some of the key design points:

  1. The player can wander freely in a low-poly 3D environment with a first-person perspective
  2. There are random gems hidden around the environment
  3. Gems come in four levels
  4. Each level has several variants of gem
  5. The player holds a detector in their hands; a gauge and an indicator light show proximity to a hidden gem
  6. When very close to a gem, the user can dig to recover it and it will be placed in their inventory
  7. Each gem has a score value proportional to its level and scarcity
  8. The detector can initially only detect the lowest level gems
  9. Gems can be combined to make higher level gems
  10. Gem combination recipes are randomised each play-through
  11. When combining gems the original gems are destroyed.,
  12. If gem combination is successful, a single gem of the next level is produced
  13. Once a gem of a higher level is owned, it can be installed in the detector; the detector can then detect gems of that level
  14. The player is against the clock to get the highest possible score

Creating a New Project

We started first by creating a new project called “Gem Search”, using the 3D core template.

Asset Store

We took two free assets from the Unity Asset store as a basis for our game:

We logged into the Asset Store using the same login we use for Unity, searched for the above assets and on the web page for each one, chose “Add to my assets”.

After that, we returned to Unity and opened the Package Manager from the Window menu. In the package manager we changed the option from “Packages: In Project” to “My Assets”.

Our two packages from the Asset Store were then visible. We downloaded and imported them both. For the Starter Assets – First Person Character Controller package, there was an import warning about switching to the new input system, which we accepted.

We then spent a little time examining the assets we’d acquired; both asset packs have demo scenes in them.

Setting up our Main Scene

We opened the LowPoly Environment Pack\Demo\Demo 3 scene and, after creating a Scenes folder at the top-level of our project, saved a a copy of this scene there as “Main”.

We then did a few adjustments. First we deleted the existing Main Camera. Immediately the fog in the scene was apparent (settings on the camera was suppressing it). We looked at where the fog settings were specified (select Window | Rendering | Lighting and then look on the Environment tab of this panel) and saw how it was causing a problem for the Scene View if we were zoomed out far enough to see the entire scene. We disabled the fog in the Scene View (it will still be visible in the game) from this drop down:

We noted that the entire terrain was offset by -2.6m vertically and rotated by 100% around the vertical. It woulds suit us better if it was square to the world and at (0, 0, 0). The difficulty was the if we moved or rotated the terrain, nothing would move with it and all the trees, bushes, rocks, etc. would end up misplaced. The solution that we followed was as follows:

  1. Move the Terrain_2 GameObject out to the top-level of the hierarchy
  2. Move every other GameObject with a mesh (but not the empties they were previously children of) to be a child of the Terrain_2 GameObject
  3. Reset the transform on Terrain_2. As all other GameObjects are children, they move with it
  4. Move all the children from under Terrain_2 back under the empties that originally held them
  5. Move Terrain_2 itself back inside “Environment”

Now the environment is still organised as before, but everything is neat and square.

Raycasting and Defining the Ground

The ground isn’t level and we are going to be looking for the height of the ground at any point we want to position a gem using the something called Physics.Raycast(). A ray-cast shoots an invisible ray looking to see if it hits a collider of any sort. We can control many things about this operation including:

  1. The location that the ray starts from
  2. The direction the ray points in
  3. The maximum distance the ray can project
  4. Which Layers to look at

All GameObjects start out in the “Default” layer, but it’s often handy to put things in specific layers when we want to be specific about them. This is one of those times. With any GameObject selected, we can add a layer by clicking the Layer drop-down at the upper right of the Inspector and choosing “Add Layer”. We took the first non-assigned Layer and called it “Ground”.

We then filtered the hierarchy to isolate those GameObjects with a MeshCollider by typing “t:MeshCollider” in the box at the top of the hierarchy. This showed that terrain and the rocks were the only ones; this is exactly what we want. We used Shift to select all of them and then changed them to layer “Ground”. We then cleared the box at the top of the Hierarchy to remove the filter.

Creating a Prefab for Testing

We made a Prefabs folder at the top-level of the project, right-clicked and choose Create | Prefab. We renamed this prefab Lamp Post and combined a plane, a cylinder and a lamp, using some materials that came from the LowPoly Environment Pack, into a simple lamp-like shape 2m tall:

Creating the Item Scatterer

We set out to create something that could scatter gems across our ground surface, comprised of the terrain and the rocks.

We made a scripts folder and created as new C# script there called ItemScatterer.cs.

Editing this we added a single property to start:

public Vector3 size;

This is to represent the size of the area over which we’ll be scattering our gems. It would be nice to be able to see this area. Unity allows you to draw Gizmos which show up in the Scene View (but never in the game). We add the following code:

    private void OnDrawGizmos()
    {
        Gizmos.color = Color.yellow;
        Gizmos.DrawWireCube(transform.position, size);
    }

This is going to draw a yellow wireframe box at the position of the GameObject this component is attached to at the requested size.

We add an empty to our game, and call it “Item Scatterer”. We then attach our latest script to it. Setting the size to something like (150, 150, 150) we can see a large yellow box outlining our area. I move the area slightly to centre it closer to where the most trees are on the environment.

We progressed the scatter code a little, but I’ll cover it fully in next weeks notes.

Code Download

The code for this week’s project is on our GitHub, as always. 

Creators – Week 8

This week we finished off our feeding animals game and played another challenge round. This concludes our second project and we’ll be starting on a brand new project after Christmas.

Collisions in Unity

There are two types of collider objects in Unity:

  1. A collider that acts like a solid barrier (standard collider)
  2. A collider that allows things to pass through it, but detects the collision (a trigger collider)

There is an “Is Trigger” check box on all collider components that allows us to change the type.

Standard colliders are used for physical interactions – like walls you can’t pass. Trigger colliders are used detect things being in the same space, but not physically interacting.

To detect collisions between two objects in Unity:

  1. Both of them have to have collider components attached
  2. At least one of them needs a RigidBody component attached

When Unity detects collisions, it sends messages to all components on the impacted GameObjects. We can choose to receive these messages by having the right functions in one (or more) of our components:

  1. For physical collisions we implement OnCollsionEnter()
  2. For trigger collisions we implement OnTriggerEnter()

These are similar but differ in information they receive.

Adding Collision to Our Prefabs

We selected each of the prefabs in our Prefabs folder and added a Box Collider to them. In the Game View we used the “Edit Bounding Volume” tool, as shown below, to adjust the Box Collider to make it a good fit.

To the Pizza Slice Prefab, we added a Rigid Body component and make sure to clear the option to “Use Gravity”.

We created a new script called DetectCollisions.cs and attached it to the animal prefabs. Editing this script we added the following function:

void OnTriggerEnter(Collider other)
{
  Destroy(gameObject);
}

Testing our code after this, we note that the animals disappear when they’re hit by a piece of pizza, but the pizza slices keep going. Once more change allows this code to remove the pizza slice as well:

void OnTriggerEnter(Collider other)
{
  Destroy(gameObject);
  Destroy(other.gameObject);
}

A Very Basic “Game Over”

We implement the most basic possible “Game Over’ message by updating the Update() function in DestroyOutOfBounds.cs as follows:

    void Update()
    {
        if (transform.position.z > topBound)
        {
            Destroy(gameObject);
        }
        else if (transform.position.z < lowerBound)
        {
            Destroy(gameObject);
            Debug.Log("Game Over!");
        }
    }

Now, when an animal reaches the bottom of the screen, the message “Game Over” gets printed to the Console window Unity.

Code Download

The code for this week’s project is on our GitHub, as always. 

Creators – Week 7

This week we continued our Prototype 2 project.

Shooting Pizza

The first change we made was have it so that the pizza slices only emanated from the player when the space bar was pressed. We removed the temporary SpawnOnInterval component from the Player as this just spawned pizza slices continuously. We edited PlayerController.cs and added a new public property:

public GameObject projectilePrefab;

This has a type of GameObject which means it can store a reference to an object in the scene, or to a prefab, which is what we want. In the Inspector, we assigned our pizza slice prefab to this property.

In the Update() function in PlayerController.cs we added the following at the end of the function (just above the final curly brace that marks the end of the function):

        if (Input.GetKeyDown(KeyCode.Space))
        {
            // Launch a projectile from the player
            Instantiate(projectilePrefab, transform.position, transform.rotation);
        }

This checks to see if the space bar is pressed and, if it is, then it creates an “instance” (copy) of the pizza slice and places it at the player’s location with the player’s rotation.

Since the pizza slice already knows how to fly forwards, once it’s created it will automatically fly up the screen.

Creating Animal Prefabs

We have three animals in our scene. We checked to make sure that they had been rotated 180 degrees, so that the were pointing down the screen. We dragged each one from the Heirarchy to our Prefabs folder in turn, and then removed them from the scene.

In the prefabs folder, we selected them one at a time and added the MoveForwards.cs script in the Inspector, adjusting the speed from the default of 40 to a more sedate 5.

To test this, we ran the game with the Scene and Game views side-by-side. We could drag and drop animal prefabs into the Scene view and watch them run down the screen. Note that because we added them in run-mode, once the game was stopped, they were not left permanently in the scene.

Keeping Things Tidy

At the moment, any pizza slices and animals added to the scene are there for ever, long after they’ve moved off screen. In any game keeping stuff around that we don’t need any more is wasteful and might mean the game became ever slower as we continued to play it.

We create a new script in our Scripts folder called DestroyOutOfBounds.cs. We give it one private property:

private float topBound = 30;

This represents how far up something can travel in the game before we should delete it. Given our current game camera, up on the screen is equivalent to the global Z axis.

In Update() we add the following code:

        if (transform.position.z > topBound)
        {
            Destroy(gameObject);
        }

What does this say? It says: if we have moved such that our position in Z is greater than 30 (topBound) we should call Destroy(gameObject). The property “gameObject” hold a reference to the item in the scene that this component is attached to. Destroy() removes it from the scene.

We attach this new script to our pizza slice prefab and test it. Now pizza slices don’t stay in the scene forever, when they move to the top of the screen, they are removed.

What about the animals? We can use this same script for them as well. We add another private property:

   private float lowerBound = -10;

This represents how far down the screen things can move before they’re removed.

We change Update() so that it now looks like this:

        if (transform.position.z > topBound)
        {
            Destroy(gameObject);
        }
        else if (transform.position.z < lowerBound)
        {
            Destroy(gameObject);
        }

We have the earlier check for things going out the top, but now we have else if and another check for things going out the bottom. Note that with else if the second thing won’t be checked if the first thing turns our to be true, only when it isn’t.

Adding this now to our animal prefabs as well, we can see that when we drag them into the scene during runtime, they only last until the bottom of the screen.

Spawning Animals

We added a new empty object to the scene and called it “Spawn Manager”. We then created a new script file called SpawnManager.cs and attached it to this object.

In SpawnManager.cs we added a new property:

public GameObject[] animalPrefabs;

This property has the type GameObject, so it can store references to items in the scene, or prefabs. Note the square brackets after the type. This means this isn’t a normal property that stores a single value, this is an array and it can hold multiple values all at the same time. Looking at it in the Inspector, we find we can set its size to three and add all our animal prefabs to it:

We add a second property called animalIndex that will allow us to select which individual prefab we’re talking about at any one time:

public int animalIndex;

We can then add this code to Update():

if (Input.GetKeyDown(KeyCode.S))
{
  Instantiate( animalPrefabs[animalIndex],
                     new Vector3(0, 0, 20),
                     animalPrefabs[animalIndex].transform.rotation );
}

Every time we press the S key, an animal is spawned at the position of (0, 0, 20) (i.e. centre-top of the screen). By changing the value of animalIndex in the inspector between 0, 1 and 2 we can see different animals get spawned.

Random Animals in Random Places

We enhanced our SpawnManager.cs by randomising both the which animal is spawned and where it’s spawned. At the top of the class, we delete the animalIndex property and add these private properties instead:

    private float spawnRangeX = 20;
    private float spawnPosZ = 20;

The first we’ll use to determine the position of the animal horizontally, a random number between the left and right sides of the screen. The second will be the animals vertical position, and this will always be the same.

Inside Update() we change it to look like this:

int animalIndex = Random.Range(0, animalPrefabs.Length);
Vector3 spawnPos = new Vector3(Random.Range(-spawnRangeX, spawnRangeX),
                                       0, spawnPosZ);

if (Input.GetKeyDown(KeyCode.S))
{
  Instantiate( animalPrefabs[animalIndex],
                     spawnPos,
                     animalPrefabs[animalIndex].transform.rotation );
}

Now we’re setting animalIndex automatically every time to a random integer between 0 and up-to, but not including, animalPrefabs.Length. By using animalPrefabs.Length we can ensure this code works, regardless of how many animal prefabs we add to SpawnManager.

The position we’re also creating as a variable called spawnPos. The X value is random float value between -spawnRangeX and spawnRangeX. Y is aways zero and Z is always spawnPosZ. Note that down in Instantiate() we’re now using spawnPos and not the former new Vector3(0, 0, 20).

Automatic Spawning

The final change is automatic spawning. We move all the spawning code to a new function called “SpawnRandomAnimal”, where the check for the key press has been removed:

void SpawnRandomAnimal()
{
    int animalIndex = Random.Range(0, animalPrefabs.Length);
    Vector3 spawnPos = new Vector3(Random.Range(-spawnRangeX, spawnRangeX),
                                   0, spawnPosZ);

    Instantiate(animalPrefabs[animalIndex],
                spawnPos,
                animalPrefabs[animalIndex].transform.rotation);
}

Now all we need is to call this automatically. We create a new pair of private properties:

    private float spawnDelay = 2;
    private float spawnInterval = 1.5f;

and add this to the Start() function:

       InvokeRepeating("SpawnRandomAnimal", spawnDelay, spawnInterval);

This function will wait spawnDelay seconds, then call the named function for the first time. It will then call it repeatedly after that every spawnInterval seconds.

Now when we run, animals getting created constantly at random! No more need to press S.

And Finally…

The code for this week’s project is on our GitHub, as always. To download our stuff from GitHub, you can just click on the green button and choose “Download ZIP”

Note that this ZIP file will contain all our projects for the year!

Creators – Week 6

This week we started a new project, a top-down game where we’ll be feeding food to hungry animals who are rushing at us.

We had another asset bundle to download to get the assets we will be using for this project. It’s available on our Teams site.

Upon importing the asset bundle, we start with a basic scene which has three planes, two black ones flanking one with a grass texture. The asset bundle contains a total of four suitable textures for the ground and we had a look at those, and saw how to use them.

The asset pack also contains prefabs, these are pre-made combinations of Unity objects, ready to use. We had a look at some these prefabs by clicking once on them and viewing the preview at the bottom of the inspector. We also saw how double-clicking on them opens the prefab in the game view for editing and how using the back arrow at the upper-left returns us to the scene.

We brought in three animals, a human and a slice of pizza (scaled up by 3 or 4 to make it visible), laid out roughly as shown below:

Making the Player Move

We made a Scripts folder and added a new C# script called PlayerController.cs. At the top, we added two properties:

    public float horizontalInput;
    public float speed = 10.0f;

And Update() we changed to the following:

    void Update()
    {
        horizontalInput = Input.GetAxis("Horizontal");
        transform.Translate(Vector3.right * horizontalInput * Time.deltaTime * speed);
    }

Now, when testing, we see the player can go side-to-side, but it can also go completely off screen! How do we limit this? We do that by adding an if statement; this allows us to check if something is true, and then do something only when it is true.

First we added a new property to the top of the class:

    public float xRange = 10.0f;

This represents the range (between -10 and +10) that we want to confine the player’s position to.

In Update(), after the transform.Translate(), we add the following code:

       if (transform.position.x < -xRange)
        {
            transform.position = new Vector3(-xRange,  transform.position.y,  transform.position.z);
        }

What does this say? If the x part of the players position gets smaller than -xRange (-10) (meaning it goes too far to the left), then we change the player’s position so that the x part is exactly -xRange (-10).

When we test now, we’ll find that we can move as far right as we want, but when we move left, once the player’s x position gets to -10, we can’t move any further.

It’s easy now to add in the same check for the right-hand-side. We can copy-paste the last block of code and make a few small changes to the copy:

        if (transform.position.x > xRange)
        {
            transform.position = new Vector3(xRange, transform.position.y, transform.position.z);
        }

So, the less-than (<) changes to greater-than (>) and -xRange (-10) changes to xRange (+10) in two places.

Now the player is constricted in two directions.

Making a Moving Pizza Prefab

To make the pizza move we make a new C# script called MoveForward.cs and attach it to our pizza slice. In this script we have one property:

    public float speed = 40.0f;

And a single line in Update():

        transform.Translate(Vector3.forward * Time.deltaTime * speed); 

We make a new folder called Prefabs and then just drag our pizza slice from the Heirarchy into it. We now have a prefab of the moving pizza slice. We can delete the instance of the pizza slice in the scene; we’ll be spawning them automatically later.

And Finally…

We looked into spawning the prefab on a regular interval from the player’s position. That’s not part of the final game, but it was just to illustrate making an instance of a prefab from code.

The code for this week’s project is on our GitHub, as always. There is also small additional project there called “ScriptableObjects for a Recipe System” which is demonstrating a way to define ingredients and recipes and was in response one if our ninja’s queries. To download our stuff from GitHub, you can just click on the green button and choose “Download ZIP”

Note that this ZIP file will contain all our projects for the year!

Creators – Week 5

This week we turned our original scene, a truck driving down a straight road. into a two-player racing game around a curved track.

We deleted the road and crates from the old scene, imported a track asset from our Sharepoint site and moved our truck to be on it.

Fixing The Camera

The truck still drives, just as before, as it has the PlayerController component attached, but the camera, while it follows the truck, doesn’t behave as we’d like. It’s always looking in the same direction. We’d like it to point in the same direction as the truck itself.

The easiest way to achieve this is to remove the FollowPlayer script from the camera, and make it a child of the truck directly. We do this by dragging it onto the truck in the hierarchy:

We then select the camera, reset it’s transform component and tweak the Y (height), Z (distance back-and-forward along the truck) and X-rotation (tilt of the camera up and down) while keeping an eye on the camera preview (bottom right-hand corner of the game view) until we’re happy with the alignment.

Fixing the Controls

The controls on PlayerController are set to always use the input axes called “Horizontal” and “Vertical”. If we put two trucks in our scene now, they’d move together, even if one player was using the WASD keys and the other was using the arrow keys.

To fix this, we first update PlayerController.cs. We add two new public variables at the top:

    public string HorizontalAxis = "Horizontal";
    public string VerticalAxis = "Vertical";

And then down in the code where the “Horizontal” and “Vertical” were used explicitly, we replace them with the names of these two variables:

        // Get the player input
        horizontalInput = Input.GetAxis(HorizontalAxis);
        forwardInput = Input.GetAxis(VerticalAxis);

Everything behaves the same as before, but now, looking at the inspector, we can see that we can easily tell PlayerController to use other axes instead of these ones.

The Input Manager can be found under the Edit | Project Settings menu. In there are all the axes currently defined. Note that it’s not usual to see a few duplicates in this list; Unity will always pick up the first one matching the name it’s looking for and ignore the others. The number of axes defined is at the top. We increase the number by four (4) to allow us to make four new axes. In my case this meant changing the number from 18 to 22. Note that the four new axes created start out as copies of the last one in the list.

Open each of these last four axes in turn and name them “Horizontal P1”, “Horizontal P2”, “Vertical P1” and “Vertical P2”. We then need to set the negative button and positive button values on each of them like this:

Axis NameNegative ButtonPositive Button
Horizontal P1ad
Horizontal P2leftright
Vertical P1sw
Vertical P2downup

Close the Project Settings and select the truck. In the Inspector for PlayerController, change Horizontal Axis to “Horizontal P1” and change Vertical Axis to “Vertical P1”. Test the game, note that the truck now only moves in response to the WASD keys.

Fixing the Camera

The camera on the truck fills the screen when it draws, we want it to only draw to the left-hand side of the screen. To to this, select the camera and in the inspector change the Viewport Rect settings like this:

Look at the Game view, the camera’s only drawing to the left hand side of the screen, this is because we’ve set W (meaning width) to a half.

Adding the Second Player

Select the truck in the hierarchy, right-click and chose “Duplicate”. This makes a copy of the truck, including all its components and children. It doesn’t look like that though because they’re right on top of each other. Separate them by moving the copy a bit. I suggest using the overhead view for this.

Rename the trucks “Player 1” and “Player 2”.

We just need to tweak “Player 2” a little bit. Select it and change the PlayerController to use the axes “Horizontal P1” and “Horizontal P2”. Then select it’s camera and change the Viewport Rect making X = 0.5. This means that not only is it half width, but it also starts drawing at half way across the screen,

Play the game. Player 1 should draw on the left side of the screen and be controlled by the WASD keys. Player 2 draws on the right and is controlled by the arrow keys.

Code for This Week

Updated code is on our GitHub repo: https://github.com/coderdojoathenry/Creators-2022. It also includes some camera switching code that I briefly demonstrated, but was too complex to finish in time.

Creators – Week 4

This week we took a pre-made scene with some problems and set out to fix them.

The first two issues were that the plane was flying backwards and at great speed. The initial challenge was to identify what was making the plane move. Looking at the plane, called Player in the scene, in the inspector, we could see it has a script component called Player Controller X added to it.

Double-clicking on the name of the script file (PlayerControllerX.cs) in the inspector allows us to open it. The code for moving the plane is in the Update() method:

        // get the user's vertical input
        verticalInput = Input.GetAxis("Vertical");

        // move the plane forward at a constant rate
        transform.Translate(Vector3.back * speed);

        // tilt the plane up/down based on up/down arrow keys
        transform.Rotate(Vector3.right * rotationSpeed * Time.deltaTime);

We need to concentrate on the line that starts transform.Translate(…). The comment above it says “move the plane forward”, but the line itself is specifying Vector3.back as the direction of movement. Changing this to Vector3.forward makes it move in the right direction. The plane is still much too fast though. This line is asking the plane to move the distance specified by the speed parameter (by default 15m) every frame. What we need to do is to multiply here by Time.deltaTime, the amount of time since the last frame, to convert this movement into 15m per second, not per frame. Here’s the corrected line:

        // move the plane forward at a constant rate
        transform.Translate(Vector3.forward * Time.deltaTime * speed);

The next problem is that the plane turns on its own. In fact, although we’re gathering the value of the “Vertical” input axis in the code, pressing the arrow keys does nothing. We can identify the line of code that’s making the plane turn:

        // tilt the plane up/down based on up/down arrow keys
        transform.Rotate(Vector3.right * rotationSpeed * Time.deltaTime);

This is asking the plane to rotate around Vector3.right (horizontally through the plane) by the number of degrees specified by the property rotationSpeed (with a default value of 100) every second. The multiplication of Time.deltaTIme is what makes it “every second” and not “every frame”, as before. So the plane is turning 100degrees every second, making it do a full loop approximately every three and a half second.

Nowhere here are the user inputs taken into account. How can we use them? Remember that the “Vertical” input axis in Unity works like this:

Input Option 1Input Option 2Axis Value
WUp-arrow+1
<No key pressed><No key pressed>0
SDown-arrow-1

We’re gathering this axis value into a property called verticalInput already, we just need to use it. When multiplied in, it works like a switch. If its off (having the value zero) then no rotation happens. If it’s one or minus one, rotation happens in either a positive or negative direction:

        // tilt the plane up/down based on up/down arrow keys
        transform.Rotate(Vector3.right * rotationSpeed * Time.deltaTime * verticalInput);

Now the plane flys correctly and responds to user input, but the it flies directly into the camera and then can’t be seen any more. First we need to grab the camera and move it to the side of the plane, rotating it around to point at the plane. This is better, but it still doesn’t move. The plane quickly flys beyond the area the camera can see. If we look at the camera in the inspector, we can see that it already has a script called Follow Player X on it:

It has a property called Plane, but nothing’s assigned there. We can set this by dragging and dropping the plane from the hierarchy, or by clicking the small circle icon on the right above and picking the “Player” object from the pop-up list. Running and testing this shows that the camera now follows the plane, but it’s right on top of the plane. It needs to be some distance away. Examining the FollowPlayerX.cs script we see at the top there’s a private property called offset:

    private Vector3 offset;

that is used in the Update() method already:

    transform.position = plane.transform.position + offset;

We just have to give it a value. The easiest way is to change private to public and then specify a value for the offset in the inspector.

The final challenge is to turn the plane’s propellor. Looking in the inspector, we can see this is its own object, a child of the “Player” object:

To make it spin, we can make a simple new script called SpinPropellor.cs and attach it to the propeller. This code works well:

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

public class SpinPropellor : MonoBehaviour
{
    public float spinSpeed;

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

    // Update is called once per frame
    void Update()
    {
        transform.Rotate(Vector3.forward * Time.deltaTime * spinSpeed);
    }
}

This is very similar to what we’ve done before, but the axis of rotation is the Vector3.forward here to make the propellor spin in the expected way. I found a value of 1000 or more was good for spinSpeed.

Note that although we’re moving the propellor ourselves, or at least rotating it, that doesn’t interfere with the fact that it’s moving with the plane, because it’s a child object.

Code for this Week

Updated code is on our GitHub repo: https://github.com/coderdojoathenry/Creators-2022 The problems with projects not loading correctly when downloaded has been corrected.

Creators – Week 3

This week we continued our project from last week, looked at three main topics:

  1. Frame rate independence
  2. Creating our own properties on our components
  3. The Input Manager and getting and using user input

Frame Rate Independence

We covered how to make our actions independent of frame-rate. The code:

transform.Translate(Vector3.forward, 20.0f);

moves the object 20m in every frame. As we know, frame-rate isn’t generally consistent between machines. On my powerful laptop, I was getting up to 1400 frames per second at fastest. Most ninjas were getting a few hundred frames per second on theirs.

On the other hand, the code here:

transform.Translate(Vector3.forward, Time.deltaTime * 20.0f);

moves the object at a consistent 20m per second on everyone’s machine. The magic is that Time.deltaTime variable. It is the time, in seconds, since the last frame was drawn. The faster the frame-rate, the smaller this number gets and the result is a consistent 20m per second movement.

Creating Properties on our Components

We can add properties, variables where we can hold and values that we can then use, to our classes by typing a single line into our class definition:

class MyBehaviour : MonoBehaviour
{
  // The new property
  public float myProperty = 1.0f;  

  void Start()
  {
  }
  void Update()
  {
  }
}

Looking at the bits of the line in turn:

  • public – The access modifier. Can be private, internal (which we won’t use) or public. If it’s public we can see it in the inspector, and from other classes. If private we can only see and use it within the class itself.
  • float – The type of value we are storing. A float is a number with a decimal point. An int (short for integer) is a number without one. We might use an int for counting things, but we use floats for real world measures like speeds and positions, etc.
  • myProperty – the name of the variable
  • = 1.0f – Assigning a default value to this property. This portion is optional. the ‘f’ after the number is just a hint to the computer that this is a float value.
  • ; – The standard semicolon to end the line of code.

The Input Manager and Using User Input

Unity’s Input Manager contains the definition of input “Axes”. These can contain many ways of doing the same thing.

The default definition of the “Horizontal” axis means it can be triggered by the keys A and D, or the Left and Right arrow keys or by the joysticks or d-pad on a game controller.

This means input is “abstracted”; we can write our script to respond to input, without worrying how that input is generated.

To get the value of input on an access we need to use code like this:

forwardInput = Input.GetAxis("Vertical");

Here we’re getting the value of the axis called “Vertical” and storing it in a variables called forwardInput.

The value of the vertical axis goes between -1 and 1. Minus one means fully down, zero means no input and one means fully up. Because of this range, we can use this value like a switch, multiply it with other numbers. When there’s no input, it’s zero which will zero out the expression it’s part of.

Here’s the fully updated code for our PlayerController.cs:

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

public class PlayerController : MonoBehaviour
{
    public float speed = 20.0f;
    public float turnSpeed = 50.0f;
    private float horizontalInput;
    private float forwardInput;

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

    // Update is called once per frame
    void Update()
    {
        // Get the player input
        horizontalInput = Input.GetAxis("Horizontal");
        forwardInput = Input.GetAxis("Vertical");

        //  Move our vehicle forward
        transform.Translate(Vector3.forward * Time.deltaTime *
                            speed * forwardInput);

        // Rotate our vehicle 
        transform.Rotate(Vector3.up, turnSpeed * Time.deltaTime *
                         horizontalInput);
    }
}

Code for this Week

Updated code is on our GitHub repo: https://github.com/coderdojoathenry/Creators-2022

Creators – Week 2

Hi folks, this week we started our first project. We have a truck that can drive down a road, avoiding obstacles, or maybe not!

We created a new project in Unity, using the default 3D Core template, and called it Prototype 1.

We then downloaded and imported the assetpack from here: [Creators Teams Channel]

The asset pack already included an existing scene, which had a simple environment already. We them dragged in a vehicle and obstacle from the imported assets. Imported assets aren’t just models; they can contain Unity obstacles, such as colliders, already.

To make the truck move, we made a new C# script called PlayerController. The new C# files Unity creates always look the same (apart from the name of the Class, which matches the new file name):

We added the following code to the Update() method to change the transform of the vehicle:

    // Update is called once per frame
    void Update()
    {
        //  Move our vehicle forward
        transform.Translate(0, 0, 1);
    }

The Unity scripting documentation can be found:

https://docs.unity3d.com/ScriptReference/index.html

and the specific page for the Transform component is:

https://docs.unity3d.com/ScriptReference/Transform.html.

This method on the Transform component that we’re calling, Translate() has several forms. The one we’re using here expects us to provide X, Y, Z values. What we’re saying we want to happen is “Change the transform by moving it 1m in Z every frame.

When we run, the car moves very fast off the end of the road. That’s because we’re running at many frames a second. It’s too fast. Next week, we’ll look at making this frame rate independent and controlling the speed.

Finally, I’ve created a GitHub repo for our projects this year. Up-to-date versions of our projects will always be available here after our sessions: https://github.com/coderdojoathenry/Creators-2022

Bodgers – Getting started with Python

Hi Everyone

It was great to get back again with a full year to look forward to and it was also great to see so many people interested in getting involved with CoderDojo Athenry.

In the Bodgers group we got off to a great start, we installed the Mu Python editor, and we wrote our first few programs.

Bodgers

The Mu editor can be downloaded from here: https://codewith.mu/.

You can find our code from last Saturday on the last couple of slides from this week’s notes here: Bodgers Day 1

We also decided we’re going to do a few small projects between now and Halloween and then start on a bigger project after that.

Next week we are going to make a Space Invaders style game, you can find some images for it here: https://www.dropbox.com/home/Bodgers/Space%20invader/Images.

Looking forward to seeing you all next week.

Declan