Skip to content
Adam Graham edited this page Jul 5, 2023 · 23 revisions

Links

FAQs


Is the source project free to use?

Yes, you are free to use the project for any purpose. However, keep in mind that copyright and/or trademark laws can still apply to the original material since many of the games in my tutorials were not originally authored by me. I open source my own code for the community to learn from, but the project is intended for educational purposes only.


Why not use a rigidbody for movement?

Using a rigidbody for the player/bird is a perfectly fine solution. I chose not to use one for this game because I found it much easier to get the physics to feel the way I want with my own simple code. When you use a rigidbody, you are letting the object be controlled by the physics simulation which means you don't always have direct control over the result. Sometimes this makes it hard to get objects to feel a particular way, especially for arcade-like games that have unrealistic physics.


How do I fix objects displaying behind the background?

In any game, objects are rendered in a particular order. We usually want objects farthest from the camera, such as the background, to be rendered first, thus all other objects are rendered on top of it. There are 3 common ways to change the render order of objects, with the following priority:

  1. Sorting Layer
  2. Order in Layer
  3. Distance to Camera (Z transform)

The "Sorting Layer" and "Order in Layer" are properties available on the SpriteRenderer component. However, for this particular game, we used a 3D object for the background and ground because it was a much easier solution to create the parallax and scrolling animation. For this reason, we don't have access to those properties, so we must use the third option.

Change the z-axis position on the Transform component for each of your objects to set the render order, with the largest value being rendered first. I set my background to 1, pipes (prefab) to 0, the ground to -1, and the player/bird to -2. It's important the player is rendered last so it always displays on top of everything.

You can read more about 2D sorting here: https://docs.unity3d.com/Manual/2DSorting.html


How do I tilt the bird up and down?

I am actually disappointed I did not include this in the video because the solution is pretty simple, requiring only a few lines of code. We'll need to modify the Player.cs script.

First, let's add a new variable to customize how much the bird tilts:

public class Player : MonoBehaviour
{
    //...

    public float tilt = 5f;
}

Then, we just need to change the bird's rotation based on the current direction. We'll do this at the bottom of the Update function:

private void Update()
{
    //...

    Vector3 rotation = transform.eulerAngles;
    rotation.z = direction.y * tilt;
    transform.eulerAngles = rotation;
}

How can I add more variance to the pipes spawn position?

In the tutorial we spawn the pipes a random distance away from the center within a given range. The problem with random is...well it's random. You could theoretically get the same value over and over. Although this is random, it might not feel random.

There are a couple ways we can handle this differently, but a simple way is to check the position of the previous pipes and always spawn the new ones in the opposite direction. This can make it feel more variable, even though technically it's less random.

First, let's add a new variable to our Spawner.cs script to set a minimum variance value. This will ensure the pipes always spawn at least the given amount away from the center.

public float minVariance = 0.5f;

We also need a new variable to keep track of the position of the previously spawned pipes.

private Vector3 previousPosition;

Finally, we can adjust our Spawn function to move the pipes in the opposite direction of the previous.

private void Spawn()
{
    GameObject pipes = Instantiate(prefab, transform.position, Quaternion.identity);

    if (previousPosition.y > 0f) {
        pipes.transform.position += Vector3.up * Random.Range(minHeight, -minVariance);
    } else {
        pipes.transform.position += Vector3.up * Random.Range(minVariance, maxHeight);
    }

    previousPosition = pipes.transform.position;
}

The score is not increasing when passing through pipes

There's a few possible reasons for this problem.

First, make sure you have added a game object with a BoxCollider2D component to your Pipes prefab. The collider component needs to have the Is Trigger property checked on. Additionally, this game object needs to be tagged "Scoring", since that is the tag the code is looking for to determine which trigger the player entered.

private void OnTriggerEnter2D(Collider2D other)
{
    if (other.gameObject.CompareTag("Obstacle")) {
        FindObjectOfType<GameManager>().GameOver();
    } else if (other.gameObject.CompareTag("Scoring")) {
        FindObjectOfType<GameManager>().IncreaseScore();
    }
}

As seen in this code in the Player.cs script, we compare the tag of the collider that was entered. The tag we use here in the code needs to exactly match the tag set in the editor, same casing, no extra spaces, etc. I've seen several times people accidentally add an extra space at the end of the tag in the editor.


Error: Cannot implicitly convert type 'Pipes' to 'Pipes[]'

This error is caused by a simple typo I have seen several people make. In the GameManager.cs script, we have the following line of code in the Play function to find all of the objects in the scene with the Pipes script attached to it:

Pipes[] pipes = FindObjectsOfType<Pipes>();

The error is easily caused if you use the singular version of the function FindObjectOfType rather than the plural version FindObjectsOfType. By using the plural version, it returns an array of pipes Pipes[] whereas the singular version returns one instance of Pipes, hence the error trying to convert between the two. Make sure to use the plural version of the function.

I think part of the confusion stems from "pipes" already being a plural word, even if we have a single instance of it. The Pipes script represent a single group of pipes, but there are multiple groups within the scene, thus we want an array of them.


Error: IndexOutOfRangeException: Index was outside the bounds of the array

This error occurs when you try to access an element of an array with an invalid index. For this particular game, this error is usually caused by the sprite animation code in the Player.cs script:

private void AnimateSprite()
{
    spriteIndex++;

    if (spriteIndex >= sprites.Length) {
        spriteIndex = 0;
    }

    spriteRenderer.sprite = sprites[spriteIndex];
}

This code cycles through the array of sprites and updates the renderer to use the sprite at the current index. The only way the error is caused in this situation is if the sprites array has a length of zero. Make sure you have assigned the relevant sprites to the array within the Unity editor. If you do not need your player/bird to be animated, you should still add your single sprite to the array, or the animation code can be removed entirely.

Something else we can do to ensure the error is never caused is to add a couple safeguards before accessing the array of sprites. This is a good practice and something I should have done in the video.

private void AnimateSprite()
{
    spriteIndex++;

    if (spriteIndex >= sprites.Length) {
        spriteIndex = 0;
    }

    if (spriteIndex < sprites.Length && spriteIndex >= 0) {
        spriteRenderer.sprite = sprites[spriteIndex];
    }
}