Monday, April 6, 2020

Tutorial: How to Make a 2d Platformer in Unity - Custom Physics Shapes on Tiles and Slopes

Creating Custom Physics Shapes:

The source code used for this post is available here.
Open the sprite Editor and click on the ground_46 sprite.  It looks like this:
At the top left of the Window click on the dropdown that says Sprite Editor.  Select Custom Physics Shape.  Then, click in drag inside that sprite to begin creating a shape or you can click the generate button to have Unity generate a shape.  Next, click on the white squares and drag them to create the shape that best fits the sprite.  Also make sure to toggle on snap at the top of the window to make the process easier.  The most important thing is to make sure that neighboring sprites have shapes that line up well.  Once you have the shape you want hit apply to save the shape.  The Tile Palette will automatically be updated.  
 Create Custom Physics Shapes for the following Tiles (ground_1, ground_33, ground_47, ground_48,) and add them all to the Tile Palette to paint with. Below is a screen shot of the Tile Palette I created.

If the Tilemap Collider doesn't update correctly try disabling and reenabling it.

Another error I encountered while making this Tilemap is in the screenshot below.  If you encounter this problem you can solve it by making a sprite material Pixel Snap enabled and apply it to your TilemapRenderer.


Your Tilemap should look like the screenshot below.  Some shortcut keys will help you achieve this result.  
Press [ : Rotate Tile Counter Clockwise
Press ] : Rotate Tile Clockwise
Press Shift+[ : Flip Tile on X axis
Press Shift+] : Flip Tile on Y axis

Our character will have a few issues if we hit play right, now.  The character will launch into the air when reaching the top of a hill and if you move against a wall while in the air character will get stuck.  I'll illustrate this below. 

To fix the stuck to wall problem the character needs a physics material with no friction.  Right click in the Project window and click Create>Physics Material 2D.  Set the material's friction to 0.  This creates another problem which is illustrated below.  This character while lacking friction will simply slide down hills.

To fix this we need to add these we need to freeze the Rigidbody2D's X position when the character isn't moving.  We can do this by adding these lines that are highlighted in red:
     if (x < 0)  
     {  
       playerRig.constraints =  
         RigidbodyConstraints2D.FreezeRotation;  
       playerAnimator.SetFloat(movingFloatName, 1f);  
       playerSpriteRenderer.flipX = true;  
       playerAnimator.speed = 1f;  
     }  
     else if (x > 0)  
     {  
       playerRig.constraints =  
         RigidbodyConstraints2D.FreezeRotation;  
       playerAnimator.SetFloat(movingFloatName, 1f);  
       playerSpriteRenderer.flipX = false;  
       playerAnimator.speed = 1f;  
     }  
     else  
     {  
       playerRig.constraints =  
         RigidbodyConstraints2D.FreezeRotation |  
         RigidbodyConstraints2D.FreezePositionX;  
       playerAnimator.SetFloat(movingFloatName, 0f);  
       playerAnimator.speed = .5f;  
     }  

Next, we need to fix this problem the causes the character to fly up in the air when hitting the top of a wall:
This is caused by the bottom of the capsule collider pressing against another collider and sliding by the collider.  The character gains upward momentum while the sliding against the other collider.  To fix this we will just keep the character from gaining this momentum.  We need to create a new variable to determine when the character is jumping off the ground.
bool leftGround; 

Then we need to add code to use this variable:

In the Idle code block, add this line:
 if (IsGrounded())  
       {  
         playerRig.gravityScale = groundedGravityScale;  
         leftGround = false;  
         if (Input.GetKeyDown(KeyCode.Space))  
         {  
           Jump();  
         }  
       }  


In the Jump state code block add this code:
       if (IsGrounded())  
       {  
         if (playerRig.velocity.y <= 0)  
         {  
           playerAnimator.SetBool(jumpBoolName, false);  
           currentPlayerPhase = PlayerPhase.IDLE;  
           playerRig.gravityScale = groundedGravityScale;  
         }  
         else if (leftGround)  
         {  
           playerRig.gravityScale = groundedGravityScale;  
         }  
       }  
       else if (playerRig.velocity.y <= 0)  
       {  
         playerRig.gravityScale = fallingGravityScale;  
       }  
       else  
       {  
         leftGround = true;  
       }  
What this does is apply higher gravity when the character returns to the ground after jumping.  The result is this:




That finishes up navigating slopes.  The next post will cover parallax backgrounds.


The source code used for this post is available here.

No comments:

Post a Comment