Creators – Week 13

This week we continued writing the code to control our environment.

Creating a Random Piece of Environment

We started by adding a new function CreatePrefab() to EnvController.cs. It uses Random.Range() to pick a random prefab from EnvPrefabs and then uses Instantiate() to create a copy of that prefab in the scene. Here’s the first version we wrote:

  private GameObject CreatePrefab(Vector3 position)
  {
    // Pick a random prefab
    int numPrefabs = EnvPrefabs.Length;
    GameObject prefab = EnvPrefabs[Random.Range(0, numPrefabs)];

    // Create an instance of it
    GameObject go = Instantiate(prefab, position,
                                Quaternion.identity, transform);

    // Add DestroyAfterDistance to it
    DestroyAfterDistance dad = go.AddComponent<DestroyAfterDistance>();
    dad.Distance = 700.0f;

    // Add ConstantMovement to it
    ConstantMovement cm = go.AddComponent<ConstantMovement>();
    cm.Direction = Vector3.left;
    cm.Speed = 100.0f;

    // Return it
    return go;
  }

Note that CreatePrefab() takes a position to create the new instance at as an argument. It also returns the GameObject we created, which will prove useful later.

To make the code actually run, we added this to the Start() function:

CreatePrefab(Vector3.zero)

Vector3.zero represents (0, 0, 0).

We quickly found that this created our random piece of environment, but it wasn’t rotated the way we wanted. We updated CreatePrefab() as follows to correct that:

    // Create an instance of it
    Quaternion rotation = Quaternion.Euler(0, -90, 0);
    GameObject go = Instantiate(prefab, position,
                                rotation, transform);

We create a new variable rotation which represents a rotation of -90 degrees around Y (the vertical) and then use it in the call to Instantiate() instead of Quaternion.identity.

Initial Environment Setup

When the game starts, we want to have the ground under and ahead of the spaceship filled in. To do this, we first create some new class properties:

  public GameObject[] EnvPrefabs;
  public float Spacing = 100.0f;
  public float Speed = 50.0f;
  public int Count = 6;

  private Vector3 _spawnPoint;
  private GameObject _lastGo;

There are three new public properties. Spacing represents the size of each environment piece. Speed is the speed we’d like everything to move at. Count is how many environment pieces to have visible at a time.

There are also two private properties. _spawnPoint represents the place we’d like new pieces of environment to appear. _lastGo stores the last piece of environment we created.

In Start() we initialise _spawnPoint() like this:

    // Calculate the spawn point based on spacing and count
    _spawnPoint = Vector3.right * Count * Spacing;

We’ll use all these properties eventually. For now, we just write a new function Preload() and call it from Start() instead of CreatePrefab().

  private void Preload()
  {
    for (int i = 0; i < Count; i++)
    {
      Vector3 pos = Vector3.right * i * Spacing;
      _lastGo = CreatePrefab(pos);
    }
  }

This function just loops for Count times, working out a new location to place the environment piece and calling CreatePrefab(). It stores each prefab as we create it in _lastGo. When we run, we see the environment under the ship, but once it passes it’s gone and no more is created.

Having EnvController Control the Speed of the Environment Pieces

Instead of having each environment piece we create have it’s speed set and constant, we decided to link it to the EnvController. To to that we create a new class called MoveWithEnvironment.cs. It looks like this:

using UnityEngine;

public class MoveWithEnvironment : MonoBehaviour
{
  public EnvController EnvController;

  // Update is called once per frame
  void Update()
  {
    if (EnvController == null)
      return;

    transform.position = transform.position +
      (Vector3.left * EnvController.Speed * Time.deltaTime);
  }
}

It’s very similar to ConstantMovement but it always moves towards Vector3.left and it takes speed from the EnvController. This allows us to vary the speed in the EnvController and have all environmental pieces immediately react to that. We need to add this, instead of ConstantMovement, to our newly created piece of environment in EnvController.CreatePrefab():

    :   :    :
    // Add DestroyAfterDistance to it
    DestroyAfterDistance dad = go.AddComponent<DestroyAfterDistance>();
    dad.Distance = 700.0f;

    // Add MoveWithEnvironment to it
    MoveWithEnvironment mwe = go.AddComponent<MoveWithEnvironment>();
    mwe.EnvController = this;

    // Return it
    return go;
}

All the code relating to ConstantMovement is replaced with code relating to MoveWithEnvironment instead. We assign the special variable this to set the EnvController property in MoveWithEnvironment.

Creating More Environment as We Need It

To create environment as we need it, we added code to Update() as follows:

  void Update()
  {
    if (_lastGo == null)
      return;

    float distToLast = Vector3.Magnitude(_lastGo.transform.position -
                                         _spawnPoint);
    if (distToLast >= Spacing)
    {
      _lastGo = CreatePrefab(_spawnPoint);
    }
  }

The code first checks to make sure that _lastGo has been set. If it has, it worked out how far the last piece of environment has moved from the _spawnPoint. If it’s equal to, or greater than, the spacing then we make a new piece of environment.

Now, as pieces of the environment move towards Vector3.left new ones are created as needed in front of our spaceship.

This code almost works great, but there’s one thing – there’s clearly gaps between the pieces of environment some times. This is because it’s not likely that on a specific frame the environment piece will have moved exactly Spacing meters. It will usually have moved a little more, hence the gaps.

Next session, we’ll correct for this.

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.

Leave a comment