Adventures in Unity – 1.6 PickUps

Official_unity_logo

 

#1 What would I ideally like to achieve by the time I finish working today – Add objects, floating above platforms which can be collected/picked-up by the player. Objects should be arranged into custom shapes.

#2 What’s the minimum I’d like to achieve by the close of play – Add objects, floating above platforms which can be collected/picked-up by the player.

 


 

It seemed a good time to add some pickups – Something like coins in a Mario game.

I had a vague idea of how I wanted the final code to work – Each platform would have X number of pickups above it & I liked the idea of being able to lay the pickups out in the shape of an image – Unfortunately, I had absolutely no idea of how to implement it.

The closest code reference I could think of was from the Unity tutorial Creating a basic platformer. In this 2D game a pickup is created and positioned in one of three  locations above a platform (left, right & middle of the platform). The pickup is a prefab (2D sprite) and the three positions are pre-defined transforms (set-up as child objects to the main platform element). When the platform is created, a simple C# script attached to the platform instantiates a pickup referencing one of the transforms (selected at random) for it’s position.

Basic2DPlatformer

That’s fine and groovy – Unfortunately in my game, platforms are generated with random heights – So I can’t use predefined positions for the pickups – & since the platform scale isn’t determined until after it’s instantiation – I can’t instantiate the pickups when the platform is created.

 


 

I played around with a few potential setups – Most of which I could get working after a fashion. However, I wanted to implement this as cleanly as possible, something that at best guess fit in with Unity’s structural practices & would make sense to anyone looking at the project.

This being my first project, I don’t really know how things should be structured – At the moment I’m working from best guesses.

With that in mind I ultimately decided to attach a pickup manager script to the platform prefab. This script would instantiate, manage and ultimate destroy a set of pickups positioned above the platform.

 


 

With a script added, I had to deal with the issue of how to instantiate the pickups. As I mentioned previously, a platforms height & position isn’t determined until after it has been created.

Knowing the width (localscale.x) of the platform allows me to identify how many pickups I can create (along the X axis) and stay within the bound of the platform.

Knowing the height (position.y + (scale.y/2)) of the platform allows me to position the pickups at a uniform position above each platform (along the Y axis).

My fudged work around to this, was to create the pickups in the first call of the Update() method rather than in a Start() or Awake() method. It’s not a particularly clean way to manage this issue, but it works without making too much mess.

All subsequent calls to the update();

  1. Automatically calculates the correct position for each pickup.
  2. Moves each pickup along the X axis at scroll speed (allowing them to move with the platforms) -also adding a little bounce (by stealing code from an older Xbox 360 project) to give the pickups a little personality.
  3. It also checks to see if a pickup has moved off-screen – If it has, the pickup is destroyed.
void Update()
{

    //---

    if (GameController.gameState == GameController.GameState.InGame)
    {
        if (pickups != null)
        {

            //---

            baseValueY = gameObject.transform.position.y + (gameObject.transform.localScale.y / 2) + scaleBound;

            float offset = ((sizeX * 1.0f) * scaleBound) / 2;
            baseValueX = gameObject.transform.position.x - offset;

            //---

            for (int ocounter = 0; ocounter < sizeX; ocounter++)
            {

                for (int icounter = 0; icounter < sizeY; icounter++)
                {

                    if (pickups[ocounter, icounter] != null)
                    {

                        //---

                        float bounce = Bounce(pickups[ocounter, icounter].transform.position);
                        pickups[ocounter, icounter].transform.position = new Vector3((baseValueX + (ocounter * scaleBound)), (baseValueY + (icounter * scaleBound) + bounce), 0);

                        if (pickups[ocounter, icounter].transform.position.x <= farLeft)
                            Destroy(pickups[ocounter, icounter]);

                        //---

                    }

                }
            }

            //---

        }
        else if (pickupInit == false)
        {
            PickUpInit();
        }
    }

    //---

}




 

Now the game automatically positions and manages (create, move, destroy) pickups – I need define a layout. When creating a layout for the pickups I tried two approaches;

The first was to create a rectangular ‘block’ of pickups above each platform.

The ‘PickUpInitBlock()‘ method creates 5 rows of pickups (sizeY = 5;) , each row containing X number of pickups – The number of pickups being determined by the width of the attached platform (sizeX = ((int)gameObject.transform.localScale.x) + 2;)

void PickUpInitBlock()
{

    sizeY = 5;
    sizeX = ((int)gameObject.transform.localScale.x) + 2;

    pickups = new GameObject[sizeX, sizeY];

    for (int ocounter = 0; ocounter < sizeX; ocounter++)
        for (int icounter = 0; icounter < sizeY; icounter++)
        {
            pickups[ocounter, icounter] = Instantiate(pickup, new Vector3(999, 999, 999), Quaternion.identity) as GameObject;
            pickups[ocounter, icounter].transform.Rotate(90, 0, 0);
        }

}

This worked well, but the number of pickups created seemed a little oppressive – Too many pickups and I found the game was less fun to play.

 


 

My next attempt/expansion was to try displaying out the pickups in a shape.

The way I implemented this is a bit of a cheat – It effectively uses the same process as in the ‘PickUpInitBlock()‘ method to create a block of pickups – However this time some pickups are turned ‘off’ from the start, allowing a ‘shape’ to be created.

To do this I needed a method of defining and storing shapes which could be quickly referenced at run-time – Something I can reference when instantiating the pickup objects.

With this in mind, I first needed to define/draw some test shapes to work with – To do this I drew a block of ‘.’ characters in an 8×8 square which operated as my blank canvas;

........
........
........
........
........
........
........
........

I then drew/filled in the shape using ‘*’ characters;

..****..
.******.
**.**.**
**.**.**
********
**.**.**
.*....*.
..****..

Finally, merged each line into a string which I could use for storage/retrieval;

..****...******.**.**.****.**.************.**.**.*....*...****..

After creating a few example shapes this way – I added them a string array;

string[] shapes = new string[7]
{
"****************************************************************", //block
"..****...******.**.**.****.**.************.**.**.*....*...****..", //circle
"...**......**.....****..********..****....****...**..**.**....**", //star
"...**.....****...******.********...**......**......**......**...", //arrowup
"....*.......**......***.****************....***.....**......*...", //arrowleft
"...*......**.....***....****************.***......**.......*....", //arrowright
"...**......**......**......**...********.******...****.....**..." //arrowdown
};

Now, when instantiating the pickups, the game creates an 8×8 array of ‘pickup’ objects – randomly selects one of the shapes from the ‘shapes’ array, then loops through the string char-by-char. If the character returned is a ‘*’ it creates a pickup, otherwise it sets the value to null.

Since the Update() method manages the position of each pickup , this method just needs to place them in the correct position in the ‘PickUp’ array;

void PickUpInitShape()
{

    sizeY = GetSize((int)gameObject.transform.localScale.x);
    sizeX = sizeY;

    string shape = GetShape(sizeY);

    //---

    if (shape == "")
        PickUpInitBlock();

    else
    {

        //---

        pickups = new GameObject[sizeX, sizeY];
        int fullSize = (shape.Length - 1);

        for (int ocounter = 0; ocounter < sizeX; ocounter++)
            for (int icounter = 0; icounter < sizeY; icounter++)
            {
                if (shape[fullSize - ((ocounter * sizeY) + icounter)] == '*')
                {
                    pickups[icounter, ocounter] = Instantiate(pickup, new Vector3(999, 999, 999), Quaternion.identity) as GameObject;
                    pickups[icounter, ocounter].transform.Rotate(90, 0, 0);
                }
                else
                    pickups[icounter, ocounter] = null;
            }

        //---

    }

    //---

}

The problem was how to ensure the shapes stayed within the bounds of a randomly sized platform – For a thin platform an 8×8 block of pickups may be too wide.

I only needing a basic implementation that would work for now, but which could potentially expand on at a later date (possibly add more shapes & sizes, set different colours, possibly rotate them around a central point – that kind of thing).

I’m not completely happy with the current implementation, hopefully future amendments will help tighten things up[ quite a lot.

The current fix was to create another sets of shapes – One set  defined within an 8×8 block the other within a 5×5 block.

string[] shapes5 = new string[7]
{
"*************************", //block
"..*...***.*****.***...*..", //circle
"*.*.*.***.*****.***.*.*.*", //star
"..*...***.*****..*....*..", //arrowup
"..*....**.*****..**...*..",  //arrowleft
"..*...**..*****.**....*..",  //arrowright
"..*....*..*****.***...*.."  //arrowdown
};

In implementation – The code first checks the platforms scale.x as an int and determines it’s available width.

public int GetSize(int inWidth)
{

    if (inWidth >= 8)
        return 8;

    else if (inWidth >= 5)
        return 5;

    else
        return inWidth;

}

If the platform can accommodate the 8×8 pickups it randomly selects an 8×8 pattern to use – If 8×8 is too large, it checks to see if a 5×5 pattern would suit – If that fails, it reverts back to the original code and creates a block of pickups instead.

void PickUpInitShape()
{

    sizeY = GetSize((int)gameObject.transform.localScale.x);
    sizeX = sizeY;

    string shape = GetShape(sizeY);

    //---

    if (shape == "")
        PickUpInitBlock();

    else
    {

        //---

        pickups = new GameObject[sizeX, sizeY];
        int fullSize = (shape.Length - 1);

        for (int ocounter = 0; ocounter < sizeX; ocounter++)
            for (int icounter = 0; icounter < sizeY; icounter++)
            {
                if (shape[fullSize - ((ocounter * sizeY) + icounter)] == '*')
                {
                    pickups[icounter, ocounter] = Instantiate(pickup, new Vector3(999, 999, 999), Quaternion.identity) as GameObject;
                    pickups[icounter, ocounter].transform.Rotate(90, 0, 0);
                }
                else
                    pickups[icounter, ocounter] = null;
            }

        //---

    }

    //---

}

 


 

Now we have pickup objects which can be created, positions, moved and destroyed. However they can’t actually be ‘picked up’.

The pickups themselves are prefabs made up of little more than cylinder objects scaled to a suitable coin-type size.

I set the attached capsule collider to function as a trigger . Attached a simple C# script whose main function is to check and see if the player has collided with the pickup – If a collision has occurred, the pickup is destroyed, a sound effect plays and the score is updated.

void OnTriggerEnter(Collider other)
{
    if (other.gameObject.CompareTag("Player"))
    {
        gameController.AddScorePickupHit();
        Destroy(gameObject);
    }
}

 


 
That’s about it.

I did update the jump code again – Modifying the original jump code to allow for double-jumps. I think the game plays better with this code -But I’m not sure how apparent the double jumps are to other players?

 


Play the game (WebGL)

Grab a copy of the project here


 

 


 

Next post: 1.7a Highscores (Server Side)

Last post: 1.5 Initial Audio

Contents page.

 


 

4 Replies to “Adventures in Unity – 1.6 PickUps”

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