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);
}
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
This week we took a look at a technique for generating random dungeons. Although never mentioned on the day, this technique is often called “marching squares”. It looks at the four corners of a space at a time, some of which are open and some of which are closed, and picks a shape that blocks off the closed corners.
There are sixteen possible combinations of corners on and off. All of these can be covered with these five shapes (or a rotation of them) to represent the closed off areas:
Generating the Dungeon
We generated a 2D array (a list of lists) to store our dungeon layout. At each point we used a 2D Perlin noise value (using the noise() function) to calculate a value. The point was deemed to be either open or closed based on whether this value was higher or lower than a threshold value we specified. Varying this threshold value can make the dungeon more open, or more closed in.
Drawing the Dungeon
To draw these shapes we first defined each of them as a list of x, y points defining the shape.
We then used beginShape(), vertex(), and endShape() functions to draw them at the correct size, location and orientation by scale(), transform() and rotate().
Once we were able to draw the shapes, we just needed to loop over our grid, inspecting each set of four adjacent corners in turn and drawing the appropriate shape.
Here’s a screenshot of one random dungeon. Dots (green for open, red for closed) are drawn to show the grid and the lines between the individual shapes are also shown for clarity:
and here is is without these overlays:
Download
The files for this week can be found on our GitHub repository.
This week we did our last iteration on Shootah and added a Bomb to be dropped by the enemy to try to hit the player.
Bomb Class
We copied the existing Bullet class and called it Bomb instead. The main changes we had to make were:
Changing the speed to a negative number so that it moved down instead of moving up (as the Bullet does)
Changed the check for the top of the screen to one for the bottom of the screen instead (to account for the fact it moves down)
Changed the check in its hit() function so that the Bomb doesn’t interact with the Enemy when it’s dropped, but will be marked as inactive it if hits anything else.
Changed the colour of the bomb to red
ManagesBombs
It happened that the code we had already written to manage bullets was already perfect for managing bombs as well.
We used Visual Studio Code’s built-in capability to automatically rename symbols to:
Rename the bullets array to projectiles
Rename the manageBullets() function to manageProjectiles()
This was enough to have bombs move, draw and be removed when it becomes inactive.
Dropping Bombs
We added a new function to Ememy called shoot(). In that function we generated a random number from one to two hundred. We then dropped a bomb every time that number was less than five (we tuned this small number to get a good rate of bomb drops). This meant that the enemy dropped a bomb at random intervals, to make it impossible for the player to anticipate.
Download
The files for this week can be found on our GitHub repository.
This week we took a break from hardware and we looked at Pygame Zero. Pygame Zero is for creating games quickly and with fewer lines of code than would be used with Pygame. It is intended for use in education, so we can learn basic programming without needing to understand the Pygame API or how to write an event loop.
Pygame Zero is designed for the Raspberry Pi but it can be installed on other systems, we used Windows without much hassle. It comes installed on the Raspberry Pi already if you have a recent version of Raspian. To install on windows open a command prompt and type:
pipinstallpgzero
You may need to install Pip if you didn’t install it when you installed Python. To do this go to Control Panel/Settings and then Add/Remove Programs, scroll down and click on Python then click on modify, tick the Pip check box and save your changes.
To run your code from Idle or any other IDE you need to add two lines to your code, at the beginning before any other code you need:
importpgzrun
and at the end of your code put:
pgzrun.go()
We will be using Pygame Zero for graphical input/output in our projects but if you want to have a go at writing a game, CodeConjuring’s Froggit youtube tutorials are a good place to start.
The snakes head moves and the rest of the snake’s body is made up of places the head’s already been, up to a certain point. For this we use a JavaScript array (we’ve also called it a list at times).
We don’t want the snake’s length to grow indefinitely, so we have a maximum length. Once the list of stored locations gets larger than this, we use the JavaScript splice() command to remove the first (oldest) element from the list.
Direction and turning
We assign numbers to represent directions on the screen. Zero is right, one is up, two is left and three is down. Note then that if the snake is heading right (in the screen sense) and turns left it goes to up (in the screen sense); direction goes from zero to one. Similarly, if going up (in the screen sense) and it turns left then it goes to left (in the screen sense).
Generally then we note that turning to the left makes the direction number get bigger while turning to the right makes it get smaller. This rule hold until we get to a number bigger than three or smaller than zero; these make no sense. If direction is at zero and the snake goes right, we set direction to three. Similarly, if direction is at three and we turn left, we set direction to zero.
Getting this week’s code
As always, all the code from our Creator group can be found on our GitHub repository.
The last two weeks we were looking at something quite different: a text-based adventure system built in Unity. This was inspired by the Henry Stickmin games and also by the old 80s-style choose-your-own-adventure books.
Basic Program Design (Story Card)
The user is presented with a “story card”. A card consists of some text describing their current situation and (normally) a number of options to choose from.
Depending on which option they pick, the story branches from there, card-by-card, until it reaches an end (a card with no options).
Setting up a card is straightforward:
You enter the card description (the text the user sees on screen), the text that the user will see on each option button (the program supports up to four at the moment) and the card (or branch, but we’ll get to that later) to jump to when that option is selected.
There’s also the mention of “States to Set True/False” and we’ll explain that next.
Story States & Story Branches
We could have programmed the system entirely using only cards, but there’s one situation where this becomes tedious. Imagine that you have a choice; hitting a button for example. The consequences of this choice won’t become apparent until later in the story. If we only had story cards, then we’d have to branch the story immediately at his point, replicating the same steps on both branches until the point at which the consequences of your action played out.
Fortunately, there is a better way.
First, to remember the value we introduce the idea of a “story state’. It’s just a container for a true or false value. Cards can set the value of specific states when they are activated as seen above.
So, that covers remembering values, how do we then make use of them? This requires a “story branch”. A branch references a state and two places for the story to go (either of these can be a card or another branch). The value of the state determines which is picked.
Game Manager
This class looks after the story. It is responsible for updating the UI to the details of the current card, for handling the use clicking on specific buttons and generally directing the flow of the story. It also needs a StoryCard to start off the story with.
Project
The project, including a very simple story using a number of cards and also a couple of states, can be found here. There are two scenes in the project, Adventure which contain a simple story and Basic which is a good starting place for your own story.
This week we finished coding our Reaction Game. We then had Reaction Game tournament congratulations to Darragh our winner. I have a few slides from today which are available here reaction and you can also check out our code on Dropbox here.
For the next few weeks we will be building an Attendance/Clock In machine using our Laptops. You will need a version of Python 3, if you don’t have it installed you can download it from here.
This week we worked on random pin placement for our game. We updated the existing PinManager component to randomly (as opposed to regularly) place a number of pins into the scene.
Arrays
An array is a way for a variable to hold more than one value. It’s a a very useful concept in programming; complex programs would be practically impossible without them. In C# arrays are declared as follows:
The first line creates a one-dimensional array called nums which has four entries: nums[0], nums[1], nums[2] and nums[3].
The second line creates a two-dimensional array called grid. Note the size in this case depends on the values of the variables rows and cols. The first entry is grid[0,0] and the last is grid[rows – 1, cols – 1]. The total number of locations in this array is rows * cols.
This week we worked on getting input from the user and making the game react to it.
InputManager
Unity has a build in system for handling user input. The InputManager can be found under the Edit|Project Settings|Input menu:
The Unity input system is based on the concept of “axes”. The game request if there is input on a specific axis, giving the axis name. What that means in terms of button presses, mouse or controller input depends on the settings in the InputManager. Axes have a value that ranges from -1.0f (full negative, if negative values are allowed) through 0.0f (no input) to 1.0f (full positive).
Pre-defined Input Axes
While the game programmer can define as many input axes themselves as they like, Unity come with many common ones built-in. The first two shown above, called “Horizontal” and “Vertical” are commonly used for movement. The third “Fire1” is usually used for shooting.
We redefined “Fire1” from it’s default key (Left Ctrl) to bind it to the space key instead.
Updating the Code to Detect “Fire1”
We originally had a force applied to our ball in the “PushSphere” script as soon as the game started. We modified the code so that in the Start() method all we did was to retrieve and store the Rigidbody component. In the Update() method (called every time the game draws) we ask if there’s been any input on “Fire1” and only if there has, do we apply a force to the ball:
using UnityEngine;using System.Collections;publicclassPushSphere:MonoBehaviour{publicfloatPushStrength=1.0f;privateRigidbodyrb;//Usethisfor initializationvoidStart(){rb=GetComponent<Rigidbody>();}//Updateiscalledonceper framevoidUpdate(){floatfire=Input.GetAxis("Fire1");if(fire>0.0f){rb.AddForce(Vector3.back*PushStrength);}}}
Before proceeding, we ran our game and tested to ensure it was working correctly.
Moving Our Aimer
The aimer was rotated using input from the horizontal axis. We added a script called AimerController to our Aimer game object. A public property called RotateSpeed gives us a way to tune the speed our our aimer. We don’t need anything in the Start() method, and it’s been removed here for clarity. The Update() method looks for input on the “Horizontal” axis (which maps to the left and right keys) and applies a rotation to the Aimer’s transform using the following logic:
amount to rotate = speed of rotation * user input * time
User input here will either be -1.0f, 0.0f or 0.0f. This equates to rotate in a negative direction (anti-clockwise), don’t rotate or rotate in a positive direction (clockwise).
using UnityEngine;using System.Collections;publicclassAimerController:MonoBehaviour{publicfloatRotateSpeed=1.0f;//UpdateiscalledonceperframevoidUpdate(){floathoriz=Input.GetAxis("Horizontal");transform.RotateAround(transform.position,Vector3.up,RotateSpeed*horiz*Time.deltaTime);}}
Note that we’re rotating about the vertical axis and the rotation is centred around the aimer’s position.
Linking the Aimer and the Sphere’s Direction of Fire
To link the aimer’s with the sphere’s direction of fire, we needed to provide the sphere with a new public property which could store a reference to the Aimer’s game object:
publicGameObjectAimer;
We then simply had to update the AddForce call, in the Update() method, to take the aimer’s direction into account:
Note that we had to also multiply by -1.0f as the ball was initially going opposite to the direction we wanted.
Prefabs
Prefabs are objects, or collections of objects that we want to re-use multiple times. To create a prefab, all we need to do is to drag an object from the Hierarchy to a project folder. We made a prefab of our pin by doing this. Note that prefabs show up blue in the hierarchy to mark them out. When the prefab is changed, all copies of it in the hierarchy are updated as well.
Automatic Creation of Game Objects at Run-Time
Once we created our pin prefab, we worked on adding pins automatically into the game a run-time.
We added a new empty game object and named it PinManager, we then added a component to it of the same name. This script was given a public property, of type GameObject, to store a reference to the prefab. In the Start() method, we use two loops to place nine pins automatically into the scene, using the Instantiate() method:
using UnityEngine;using System.Collections;publicclassPinManager:MonoBehaviour{publicGameObjectPinPrefab;//Usethisfor initializationvoidStart(){for(intx=-1;x<=1;x++){for(intz=-2;z<=0;z++){Vector3pos=newVector3(x*2,0.5f,z*2);Instantiate(PinPrefab,pos,Quaternion.identity);}}}}
Note that there are several versions of the Instantiate() method. The version we’re using allows us to specify the position and rotation of the newly created object. A Quaternion is variable type that can store a rotation. Quaternion.identity simply means “no rotation”.
Testing
Project File
The latest project files can be downloaded from here.
This week we started our first game in earnest. Let’s talk about the design and the steps we took to begin implementing it.
Game Design
The game involves a simple play area with pins. The players tries to knock down the pins by controlling the direction and force at which the ball is projected. The number of pins knocked constitutes the score. The game shares element of classic games such as pinball, bowling and skittles, to name a few.
Setting up the play area
To establish the play area, we used a plane (for the ground) and four cubes stretched to size to provide the walls:
Because we don’t want gaps in our walls, we’ve been careful to be precise with the positions of everything.
Applying a Force to our Sphere
We added a sphere to our scene and made sure that it had a RigidBody body component. The RigidBody component makes it so that the physic engine takes care of the movement of the sphere.
To test adding a force to the sphere we added a temporary script to the sphere and had it add a force to the RigidBody as soon as the game starts.
Immediately it was clear that the force was too weak, so we added a property to the script which allowed us to scale the force. In testing 1000 proved to be a good value. Here is the script:
using UnityEngine;using System.Collections;publicclassPushSphere:MonoBehaviour{publicfloatPushStrength=1.0f;//Usethisfor initializationvoidStart(){Rigidbodyrb;rb=GetComponent<Rigidbody>();rb.AddForce(Vector3.back*PushStrength);}//Updateiscalledonceper framevoidUpdate(){}}
We also experimented with creating a physic material and assigning it to the sphere. Different levels of friction and bounciness give different behaviours.
Testing With a Single Pin
To test the sphere colliding with a pin we added a single cylinder to the scene. Like the sphere, we made sure it had a RigidBody component attached to it. When the game started the ball shot forward and knocked the pin, as we’d hoped.
Aimer
To aim our sphere and control the strength of our shot, we created an aimer. The aimer is composed of an empty object containing two cylinders, one pointing forwards and one pointing crossways.
We didn’t want the aimer cylinders interacting with anything so we made sure to remove the colliders from the cylinders.
We also wanted the aimer to be partially transparent so we created a new material, setting the Rendering Mode to “Transparent”, choosing a colour from the Albedo colour picker and setting the Alpha value to a value less than 255.
Project
Updated project files for this week’s project can be found here. This requires Unity Version 5.4.1f1 or later.