Adventures in Unity – 1.4b Tighten & Tidy

Official_unity_logo

 

There were a few sections of code I added during the ‘tighten & Tidy’ process that I thought I’ve cover in a little more detail; Collision detection, jumping & platform generation.

 


 

Collision Detection

One of the problems with the code prior to the update, was that a player was able to jump whenever they wanted – whether they were rolling along a platform or already in mid-air. This allowed players to potentially ‘fly’ though the game – even jumping up, safely outside the screen\gamer area.

It seemed more sensible if the player could only jump when they were ‘on’ the platform – A ground level from which they could jump – No jumping in mid-air and no jumping if they fall between platforms.

I cheated a little with the solution – Rather than creating a universal collision detection test, I relied on the idea that platforms implemented used non-rotated/rotating box colliders.

I created a C# script called ‘platform.cs‘ which is attached to the platform prefab.

I check ‘OnCollisionEnter‘ & ‘OnCollisionStay‘ – ‘OnCollisionStay‘ is used for situations where the player-platform collision occurs/registers first against the side of the platform rather than the top.

The collision check itself first confirms that the ‘player‘ component is being tested for collision;

if (collision.gameObject.CompareTag("Player"))

if that is the case, it then checks to see if the collision has occurring against the top of the cube;

if (collision.contacts[0].normal == -(transform.up))

If all these conditions are met,  the player component is referenced directly and the players static bool ‘grounded‘ is set to true (allowing jumps to be performed).

Also at this point we check the platforms colour, if it’s set to white (it’s original colour) – We change it to something a little brighter. If the colour is still set, we keep things as they are (to avoid flashing cubes)

& that’s it – Unity’s built in physics engine does the hard work of keeping the objects apart.

void PerformCollisionCheck(Collision collision){

     //Ensure we are checking aginst the player.
     if (collision.gameObject.CompareTag("Player")){

          //---

          //If the top of the cube is being hit
          if (collision.contacts[0].normal == -(transform.up)) {

               //Grab the player
               Player player = collision.gameObject.GetComponent(); 

               //Set 'grounded' to true (so the player can jump again)
               player.grounded = true; 

               Renderer rend = GetComponent();

               //If the cubes colour hasn't already been set
               if (rend.material.GetColor("_Color") == Color.white) {

                    //Change it from white
                    Color whichColour = WhichColour(); 
                    rend.material.SetColor("_Color", whichColour);
                    rend.material.SetColor("_SpecColor", whichColour);
                    rend.material.SetColor("_EmissionColor", whichColour);
                    rend.material.SetColor("_ReflectColor", whichColour);

                    //Update the players score.
                    gameController.AddScore(GetColor()); 

               }
          }

     //---

     }

 }

 


 

Jumping

The original jump code was taken from the Unity Basic Platform Game tutorial. It’s pretty straight forward, and it does a good job.

Update‘ method checks for user input;

if (!GameController.inGame)
     rb.transform.position = new Vector3(0, 11, 0);
else if (grounded && Input.GetButtonDown("Jump")){
     jump = true;
     grounded = false;
}

If so, the ‘FixedUpdate‘ method applies force to the ‘rigidbody‘ – performing the actual ‘jump’;

if (jump)
{
    velocityY = JumpLaunchVelocity * (1.0f - Mathf.Pow(jumpTime / MaxJumpTime, JumpControlPower));
            
    rb.AddForce(new Vector3(0f, jumpForce, 0f));
    jump = false;
}

This worked well – Certainly well enough that I may reimplement it by the time this game is finished.

The problem is, platforms have random heights & widths – & using a static jump size sometimes made the game feel a little unfair – Some ‘game over’ screens felt undeserved.

Instead I thought I’d try allowing the player to control their jump a little more. The longer the player holds down the jump button – The higher they jump.

This, I hoped, would allow a greater deal of control for the player & making the game a little more skill based & putting the reason for the ‘game over’ back in their court.

I wrote a game for the Xbox 360 a year or two back called Magic Thighs & Slightly Phil – A Bomb Jack clone which implemented this kind jump system.

So, rather than reinvent the wheel, I used a modified (simplified) version of the code for this project.

 

The updated jump code follows the same structure as the original jump code; the update function checks for user input;

if (GameController.gameState != GameController.GameState.InGame)
    rb.transform.position = new Vector3(0, 11, 0); //Set game init position

else
{

    //---

    isJumping = false;

    if (Input.GetButtonDown("Jump") || Input.GetButton("Jump"))
        isJumping = true;

    //---

}

While the ‘FixedUpdate‘ method applies force to the ‘rigidbody

Though the jump code is now a little more complex (though still pretty simple).

With this jump code, we use a timer. When the player initially presses jump, the timer is started.

if ((!wasJumping && grounded) || jumpTime > 0.0f)
{
     jumpTime += Time.deltaTime;
 }

While the timer is running we apply force to the player objects ‘rigidbody

velocityY = JumpLaunchVelocity * (1.0f - Mathf.Pow(jumpTime / MaxJumpTime, JumpControlPower));                 

rb.AddForce(new Vector3(0f, velocityY, 0f));

Once the timer reaches a predefined ‘MaxJumpTime‘ or the player releases the jump button, we stop applying velocity to the ‘rigidbody‘ and don’t allow the player to try jumping again until he hits the ground/platform top

if (isJumping)
{

    if ((!wasJumping && grounded) || jumpTime > 0.0f)
    {
        jumpTime += Time.deltaTime;
    }

    // If we are in the ascent of the jump
    if (0.0f < jumpTime && jumpTime  jumpForce)
        {
            velocityY = jumpForce;
        }

        rb.AddForce(new Vector3(0f, velocityY, 0f));

    }

    else // Reached the apex of the jump
    {
        jumpTime = 0.0f;
    }

}

else // Continues not jumping or cancels a jump in progress
{
    jumpTime = 0.0f;
}

wasJumping = isJumping;

 

It’s not perfect, but, for now at least, it does the job.

 


 

Platform Generation

I’m intending to play around with platform a little before this game is complete, so for the moment I’ve kept things pretty simple.

The idea is I need each platform to be a different height – Not so different that the player cant jump onto it – But not so similar that the game doesn’t present a challenge.

I populate the level with platforms (from left to right) – Using a ‘farLeft‘/’farRight‘ variables to determine desired start and end ‘X‘ positions.

Every game loop I scroll the platforms left a little – Removing the platforms whose ‘X‘ position is < 'farLeft‘ – Adding platforms until the ‘X‘ position of right most platform  reaches (or exceeds) ‘farRight‘.

private void PopulatePlatforms()
{

    float posX = farLeft;
    if (platforms.Count <= 0)
        initalPlatform();

    //---

    //Find the X psition of the last platform in the list (furthest right)
    posX = (platforms[platforms.Count - 1].transform.position.x + (platforms[platforms.Count - 1].transform.localScale.x / 2)) + 1;

    //If we don't have enough platofrms to fill the screen - we need to add some
    while (posX < farRight)
    {

        float width = Random.Range(widthMin, widthMax); //randomise platform width
        float height = NextHeight(); //Find platforms height

        posX += width / 2; //Find the relevent X pos for the new platform.

        //Combine and define the final position
        Vector3 position = new Vector3(posX, basePosition + (height / 2), 0); 

        //Combine and define the final scale
        Vector3 scale = new Vector3(width, height, 10); 

        //---

        //Add the platform
        platforms.Add(Instantiate(platform, position, Quaternion.identity) as GameObject);
        platforms[platforms.Count - 1].transform.position = position;
        platforms[platforms.Count - 1].transform.localScale = scale;

        //Update the X pos so the while loop to chack (+1 to add a space between platforms)
        posX += (width / 2) + 1; 

    };

}

When adding a new platform I randomise the platforms width (within a range) – Use the ‘NextHeight‘ method to determine it’s height.

NextHeight‘ initaly sets the height of the new platform to match that of the last platform.

It them applies a random value to the height (within a range). This range is between -‘heightRangeMax‘ and +’heightRangeMax

height = lastHeight + Random.Range(-heightRangeMax, heightRangeMax);

heightRangeMax‘ represents the largest difference in height between platforms that a player can still be expected to jump up to.

(Note: The minimum range could really be any height, since falling isn’t a problem, for for now this seems more succinct).

After generating the platforms height, which we ensure the platform stays within the acceptable maximum and minimum height for the play area/screen.

        if (height > heightMax)
            height = heightMax;
        else if (height < heightMin)
            height = heightMin;

We make sure to the difference in height between the last platform and the new platform is suitably large.

while (Mathf.Abs(height - lastHeight) <= heightRangeMin)

& If everything looks good, we update the ‘lastHeight‘ variable with this platforms height and return to ‘PopulatePlatforms‘.

float NextHeight()
{

    float height = 0;

    do
    {

        //Randomise a height for the new platform
        height = lastHeight + Random.Range(-heightRangeMax, heightRangeMax);

        //Ensure the height isn't too large or too small
        if (height > heightMax)
            height = heightMax;
        else if (height < heightMin)
            height = heightMin;

    } while (Mathf.Abs(height - lastHeight) <= heightRangeMin); //Check the height difference is at least equal to the minimum acceptable size difference.

    lastHeight = height; //Save height to last height so the next platform has something to measure against.

    return height;
}

 


 

There;s lots more to tweaks with this code still to go – The player jump feels a little sluggish & the size difference between platforms is still a little small.

But I can tweak these as things go on.

Next I’m going to try to add a little audio to the game.

(I’ve absolutely been relying far too much on the ‘energetic‘ tune from Microsoft’s ‘MovieMoments‘ app to keep things sounding interesting).

 


Play the game (WebGL)

Grab a copy of the project here


 

 


 

Next post: 1.5 Initial Audio

Last post: 1.4a Tighten & Tidy

Contents page.

 


 

2 Replies to “Adventures in Unity – 1.4b Tighten & Tidy”

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