Please note that there was no Week 14 session as it had to be cancelled owing to bad weather.
Closing The Gap
When we finished our last session, our EnvController was creating random pieces of environment continuously and scrolling them under the ship. It looked good, but there were gaps between the environment pieces, getting larger the higher we set the environment speed.
Why was this gap happening? Well it’s easy to understand if you look at it this way: if something is moving and you shout stop at a random time, what are the chances that it’s moved some exact distance? Almost no chance of that at all. This is what the current code is doing and why, the faster it’s moving, the larger the gaps get.
The first thing we can do to make this easier is to check more often if the blocks have moved far enough to create a new one. By changing Update() to FixedUpdate() this helps because while Update() is called every frame, FixedUpdate() is called much more often and on a regular timer, independent of frame rate. It’s normally used for physics calculations.
The second thing we can do is to not create our new block at _spawnPoint but to work out the proper position to create it so it’s exactly 100m from the previous one. This is what that code looks like:
void FixedUpdate()
{
if (_lastGo == null)
return;
float distToLast = Vector3.Magnitude(_lastGo.transform.position -
_spawnPoint);
if (distToLast >= Spacing)
{
Vector3 spawnAt = _lastGo.transform.position + Vector3.right * Spacing;
_lastGo = CreatePrefab(spawnAt);
}
}
Bounds Checking
I thought we’d done this ages ago, but we never implemented bounds checking for our ShipController. It’s only supposed to be able to move within a fixed area. To achieve this we added a new property to ShipController of type Vector2d called Bounds. We are going to use the x part of this property to store how far we want to allow the ship to move forward and backwards and we’re going to use the y part to store how much we want to allow the ship to move side-to-side. In testing we found good values for this were (20, 40). Here’s the change to the ShipController.Update() function:
Vector3 newPos = transform.position +
new Vector3(moveInput.y * MoveSpeed * Time.deltaTime,
0,
-moveInput.x * MoveSpeed * Time.deltaTime);
if (newPos.x > Bounds.x)
newPos = new Vector3(Bounds.x, newPos.y, newPos.z);
if (newPos.x < -Bounds.x)
newPos = new Vector3(-Bounds.x, newPos.y, newPos.z);
if (newPos.z > Bounds.y)
newPos = new Vector3(newPos.x, newPos.y, Bounds.y);
if (newPos.z < -Bounds.y)
newPos = new Vector3(newPos.x, newPos.y, -Bounds.y);
transform.position = newPos;
Creating a Missile

We made a simple missile in Blender and exported it as an FBX file and imported it into Unity. We then created and empty GameObject, placed the missile model under it in the hierarchy and created a prefab from them.
Once in the prefab folder, we edited the prefab and added a ConstantMovement behaviour and a DestroyOnDistance behaviour. This makes the missile fly and eventually destroy itself.

Shooting
Controls
First we had to edit our ShipControlls.inputaction in our Inputs folder by double clicking on it. We then added a new action of type Button called Shoot with a single Binding to the Spacebar on the keyboard.


We used the Save Asset button on the dialog above to save the input actions.
In our code then in ShipController we added the following at the bottom of update:
// Shooting
if (_controls.Ship.Shoot.WasPerformedThisFrame())
{
Debug.Log("Shoot was pressed on frame " + Time.frameCount.ToString());
}
This allowed us to see that the shoot command was being picked up with a message getting written to the console every time the spacebar was pressed.
What and Where
We added two new properties to ShipController:
public GameObject MissilePrefab;
public Transform MissileSpawnPoint;
The first is to store our Missile prefab and the second is to store a location under the ship where the missile can be launched from. To create this we added an empty called MissileSpawnPoint under Player in the scene and, in a side view, moved it down below the ship and towards the front of the ship.

We could then assign this and the missile prefab to the appropriate properties of ShipContoller attached to the Player object.
Launching a Missile
In PlayerController we swapped the call in Update() to Debug.Log() for a call to a new private function Shoot() which we also created, to look like this:
private void Shoot()
{
// Create a missile at the missile spawn point
Instantiate(MissilePrefab,
MissileSpawnPoint.position,
MissileSpawnPoint.rotation);
}
Every time now that the spacebar is pressed, a missile is created below the spaceship and flies forward for approx. 8 seconds before destroying itself.
Rate Limiting
It’s unrealistic to be able to shoot missiles as quickly as we can hit the spacebar, so some idea of fire rate is useful. It’s easy to convert a rate into an interval, all you have to do is divide one by the rate to get the interval. For example, if the rate is 2 per second then the minimum interval between shots is 1/2 = 0.5 seconds. Similarly, if the rate is 10 per second then the minimum interval between shots would be 1/10 = 0.1 seconds.
We add a new property to ShipController called FireRate.
public float FireRate = 1.0f;
We also add a couple of private properties:
private float _minTimeBetweenShots;
private float _lastShotTime = -10.0f;
The first of these is the minimum interval between shots. We calculate if from the rate, as described above. Since we just need to do this once, we pop the code in Start():
void Start()
{
_minTimeBetweenShots = 1.0f / FireRate;
}
The second private property stores the last time we fired a missile. We set it to a arbitrary large negative value so that at the start of the game it’s the same as if it’s been ages since we fired a missile, even though we’ve never actually fire one yet.
We can now check the time since we last fired a missile against the minimum time between shots in the Shoot() function:
private void Shoot()
{
if (Time.time - _lastShotTime < _minTimeBetweenShots)
return;
// Create a missile at the missile spawn point
Instantiate(MissilePrefab,
MissileSpawnPoint.position,
MissileSpawnPoint.rotation);
_lastShotTime = Time.time;
}
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.