Creating an Item Catch mini-game in Unity

I’ve been wanting to practice my game making skills recently, and with the Christmas break, I finally had a chance to scratch that itch!

I wanted to craft something small, like a mini-game, as it would be easy to complete in a short amount of time. I was inspired by my love of the Pokemon Stadium mini-games. One that I love in particular is Chansey’s Egg Emergency, where you control the pink blob to ensure it doesn’t drop any precious eggs.

It was only fair that I show my appreciation for the adorable little guy by recreating the mini-game that cemented her as a front-runner for my favourite Pokemon.

Now, I didn’t go for a completely faithful recreation: I attempted to emulate the item dropping and item catching parts of the game, and reskinned it for release.

Chansey is able to collect eggs by tilting left, tilting right, or staying in the middle of her lane. Eggs and Voltorbs (bombs) drop from three places on the top of the screen, and the player must maneuver Chansey so she collects eggs and avoids Voltorbs. So, let’s see what we can do!

Creating droppable items with polymorphism and inheritance

To start I created an empty 2D project in Unity.

The droppable items fall into two categories: eggs and bombs. They both need a sprite, a sound when they are picked up, and need to do something when they collide with the player.

For this reason, I decided to have an abstract class called Item that derives from the ScriptableObject class, and then have Egg and Bomb classes that derive from the Item class.

using UnityEngine;

public abstract class Item : ScriptableObject
{
    public Sprite image;
    public AudioClip collectSound;
    public abstract void Interact(GameObject obj);
}
[CreateAssetMenu(fileName = "Egg", menuName = "Egg")]
public class Egg : Item
{
    public int points;

    public override void Interact(GameObject obj)
    {
        PlayerLogic playerLogic = obj.GetComponent<PlayerLogic>();
        if (playerLogic == null)
            throw new MissingComponentException("Missing PlayerLogic component on " + obj.name);

        playerLogic.AddPoints(points);
    }
}
[CreateAssetMenu(fileName = "Bomb", menuName = "Bomb")]
public class Bomb : Item
{
    public override void Interact(GameObject obj)
    {
        PlayerLogic playerLogic = obj.GetComponent<PlayerLogic>();
        if (playerLogic == null)
            throw new MissingComponentException("Missing PlayerLogic component on " + obj.name);

        playerLogic.TakeLife();
    }
}

(The Interact functions contain some logic from the player, I’ll explain that down below).

Now that we have scriptable objects, we can create the objects, place them in our project folder, and create another script to hold our scriptable objects as variables.

The Scriptable Objects placed neatly in their folder.
public class DroppableItem : MonoBehaviour
{
    public Item item;
    public SpriteRenderer sprite;
    public float dropSpeed = 5f;

    private AudioSource audioSource;

    void Update()
    {
        transform.position -= transform.up * Time.deltaTime * dropSpeed;
    }

    void OnTriggerEnter2D(Collider2D collision)
    {
        if (collision.tag == "Player")
        {
            audioSource.PlayOneShot(item.collectSound);
            item.Interact(collision.gameObject);
            Destroy(gameObject);
        }
    }
}

Pay attention to the public Item item variable, and the item.Interact(collision.gameObject) lines. The declaration of type Item takes in both Eggs and Bombs, as they derive from the Item class. And again, calling the item.Interact function will either give the player points, or takeaway lives, depending on the scriptable object passed in.

This is great because the ability to add more droppable objects can reuse the above script. All that is required is for the new scriptable object to derive from the Item class, and to override the Interact function, and it can be dropped into the script without any other changes.

Creating the Item Dropper

I created an empty game object and placed three empty game objects as children within that object. Those children were spaced out and represent the drop locations for the items to come.

A script was attached to the parent object, and this was called ItemDropper. It holds parameters for the items to drop, the drop locations, as well as all the parameters that change the drop settings.

Now, this file is a little big, but you can find the entire thing on the github repository here. Through the iterations I had quite an ugly looking piece of script. To some experienced coders, it’s probably still ugly now.

Anyways, it’s fairly simple in its workings: on start of the scene, this script with loop forever with an IEnumerator DropItems(), which will drop items. Every loop it will decide:

  • The items dropped
  • The amount of items dropped
  • The time between item drops

And those rates also change with each drop, based on the amount of loops already executed. This way the difficulty increases the longer that game lasts.

You probably see something really gross in that new refactored image. I’ll explain it at the end of this post (don’t judge).

There is also an Item Destroyer that destroys items when collided with. This is placed below the screen of the player, and ensures missed items aren’t left in the scene.

Now, let’s move onto the player.

Player

Player Movement

In the original Chansey’s Egg Emergency, chansey only tilted left and right. In our interpretation it will be very similar. However, instead of titling left and right, our character moves to the left and right parts of the screen.

Instead of using velocity to move the player, all we are going to do is move the player underneath the drop locations for items. We do this by matching the x position of the objects. The only stipulation (for later when we do collision) is to ensure the locations are far enough apart to ensure that the player doesn’t collide with the items dropped in adjacent lanes.

If the player has no lives, they won’t be able to move. Otherwise, if they press left, they go under the left drop location. If they press right, they go under the right drop location. If they press none (or both at the same time), they stay in the middle.

    void Update()
    {
        if (playerLogic.lives <= 0)
            return;

        if (Input.GetKey(KeyCode.LeftArrow) && Input.GetKey(KeyCode.RightArrow))
        {
            transform.position = new Vector2(dropLocationMiddlePos.x, transform.position.y);
        } else if (Input.GetKey(KeyCode.LeftArrow)) {
            transform.position = new Vector2(dropLocationLeftPos.x, transform.position.y);
        } else if (Input.GetKey(KeyCode.RightArrow))
        {
            transform.position = new Vector2(dropLocationRightPos.x, transform.position.y);
        } else
        {
            transform.position = new Vector2(dropLocationMiddlePos.x, transform.position.y);
        }
    }

Ah, the illusion of movement. Gotta love it.

Player collision

Thanks to our work with the Scriptable Objects and DroppableItem class, implementing the player collision is actually quite simple. The most important thing is to ensure all our objects (items and the player) have the required collisions.

Since our DroppableItem class calls the Interact function, and the Egg and Bomb classes both override it with calls to functions from the PlayerLogic, all we have to do is ensure those functions exist within a PlayerLogic class, and attach it to the player.

These are the functions called by the droppable items. The PlayerLogic class has some other bells and whistles too, but you can find them on GitHub.

    public void AddPoints(int pointsToAdd)
    {
        animator.SetTrigger("CollectedEgg");
        points += pointsToAdd;
        UpdatePointsText();
    }

    public void TakeLife()
    {
        animator.SetTrigger("CollectedBomb");
        lives--;
        UpdateLivesText();
        if (lives <= 0)
        {
            Death();
        }
    }

I think that’s pretty much all the necessary. And if I haven’t mentioned it before, you can find the entire project on Github.

Polish

So, here’s what we have after all that:

It looks okay, but it doesn’t really pop. So, lets get some art made! baelfin of https://www.baelfin.com/ created some art assets to really make the game pop. After that, adding some audio, and some particle effects, and here is the final result:

That’s much better! If you’re on a PC and want to test it out, you can do so here:

Egg Catch by impojr

Postmortem

This probably definitely is not a proper postmortem, but it’s always good to reflect on what went well and what didn’t!

What went well

Polymorhpism and abstraction impementation: I’m happy with how the droppable items were coded. This was my first time actually using these practices outside of theory, and is really makes for clean, robust code. I’m not sure if this is the first time I have used them because this is the first time I’ve made something that could benefit from them, but going forward polymorphism will be great to implement wherever required.

Using Scriptable Objects: This is also the first time I’ve used Scriptable Objects properly within Unity. I have followed tutorials on them before, but this is the first time where I have actually seen a use for them in one of my projects, and implemented them into scripts.

Source Control: This one is a little silly, since it’s just me on the project. But I made sure to upload to GitHub regularly while working on this little mini-game. The goal was to try and upload every new feature singularly, but I think a few were bundled together. I did have to roll back some stuff once or twice, and that was very easy thanks to the source control. Regardless, there’s now a snapshot of each iteration of development for me to go back to if needed.

What didn’t go well

ref parameter in the ItemDropper class: You may have picked up on this in the refactoring image above, but I passed a ref parameter into the RandomlyDropBombFromPercentageAndUpdateDropChance function, to refactor it. Using a ref parameter is not ideal. I think the best resolution would be to restructure the function entirely to remove the need for a ref function. However, this is a very small project: there’s no chance another developer is going to call this function, pass in a variable and have it unexpectedly change on the return. So I think it’s not too bad in this case.

Making the game mobile friendly: When you look at the finished product, it looks like it should work on mobile. The dimensions are that of a smartphone, yet if you navigate to the itch.io link for the game on a mobile, you will not be able to play the game. If I were to continue development on this project, my first upgrade would be to add controls to allow for touch, and allow for mobile use.

Taking more pictures and videos during development: It sure would be a lot easier to show the development progress of the game if I had taken more images and videos during development. Oh well, next time.


I hope you enjoyed this little write-up! If you have any feedback on the game, or any tips on development, or this write-up, please let me know!

Feel free to comment below, or contact me on LinkedIn.

Until next time,
Adrian

1 comment

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Google photo

You are commenting using your Google account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s