Creating abilities - Ground pound
Tags: beginners, gamedev, programming, tutorial
Published: 2023-08-11 22:21:12

Cool! Our character can move left, right and jump! (and fall off the edge of the map 😅).

But we can add something to our character to make our jump more, spicy, what about we add a Ground pound option?

It's pretty simple and can open a lot of doors for movement interaction, such as breakable ground and force push enemies once we hit the ground.

Also, it's not needed to create a new binding action in our `PlayerInput` script, we're going to use what we've already created and check for 3 things, if the player is holding down on the controller (or S or down arrow key) pressed jump and it's not grounded.

## Ground Pound code

### New fields

Let's start by creating something similar to `jumpForce`, but instead of making our character go up, we'll make our character go down.

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/5m5igrkbef7v00g78o93.png)

And since we've added a `Boolean` field to tell us if the player pressed the jump button while grounded, we're going to use the same principle and create a field to tell us if the player met the conditions to do a `ground pound`.

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/ebdytip9w7k2sshigqza.png)

And a couple more fields to help us, such as `verticalMovement` ( which follows the same idea of horizontal movement, but for, well, vertical) and `baseGravityScale`, which is the default gravity value our `RigidBody2D` starts with (in our scenario, 1).

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/88fsu72nx7odliz5rx5l.png)

And finally, set our `baseGravityScale` in the `Awake` method.

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/jhdj3xblv8ei66kh1lw0.png)

### Updating our new fields

Let's starting by updating our `verticalMovement` by reading the `Y` value from our input.

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/e4kvf2mmi9w0iz7coo9n.png)

With our `verticalMovement` updating, we can do our check.

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/26lmr0alkpcv4bedzy75.png)

### Updating `FixedUpdate` method



Now, it's time to use these new fields in our `FixedUpdate` method, which controls the character.

First, let's update our `velocityX` variable, because we don't want to our player to move while ground pound is active.
To do that, we check if `isForceDown` is true, if it's true, we set our X movement to 0, otherwise, we keep the same movement logic as before.

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/uejzu1f5b1jjzocped0a.png)

And if it's force down, we set our `gravityScale` in the `RigidBody` to our `downforce`.

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/smudyppubr7b6m29mkpe.png)

### Some other code changes

Since we're creating more and more fields, I've decided to handle them in a single `if` statement instead of handling them individually.

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/gtr62sc649vy4v4po8x0.png)

In here, I'm setting `isForceDown` and `hasJumpButtonTriggered` back to false, and `body.gravityScale` back to its default.

Now that we have a single `if` statement to handle these fields, we can remove `hasJumpButtonTriggered = false` from our `hasJumpButtonTriggered` check, making the code simply:

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/6u92cmab4j7tn87ccb4j.png)

## Important changes

In some occasions, we're falling to fast to Unity to detect collision, this will break and make the player fall through the ground, to fix that, change the `Collision Detection` from `Discrete` to `Continuous` in the RigidBody2D options.

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/znj9arm2rg1xtyxgv4rx.png)

## Thanks

And that's pretty much it! There's a lot of room for improvements but, its taking shape, slowly but surely! Hope this series is helpful, fun and easy to read! Until next chapter!

## Full code

```csharp
public class PlayerMovement : MonoBehaviour
{
    private PlayerInput playerInput;
    private Rigidbody2D body;
    private float horizontalMovement;
    private float verticalMovement;
    private float baseGravityScale;
    private bool hasJumpButtonTriggered;
    private bool isForceDown;


    [SerializeField]
    private float speed = 5f;

    [SerializeField]
    private float jumpForce = 5f;

    [SerializeField]
    private float downForce = 5f;

    [SerializeField]
    private bool isGrounded;
    [SerializeField]
    private Transform groundCheck;
    [SerializeField]
    private LayerMask groundLayer;

    private void Awake()
    {
        playerInput = new PlayerInput();
        body = GetComponent<Rigidbody2D>();
        baseGravityScale = body.gravityScale;
    }

    // Start is called before the first frame update
    void Start()
    {
        
    }

    // Update is called once per frame
    void Update()
    {
        horizontalMovement = Mathf.RoundToInt(playerInput.Player.Move.ReadValue<Vector2>().x);
        verticalMovement = Mathf.RoundToInt(playerInput.Player.Move.ReadValue<Vector2>().y);

        isGrounded = Physics2D.OverlapCircle(groundCheck.position, 0.02f, groundLayer);

        if(playerInput.Player.Jump.triggered && isGrounded)
            hasJumpButtonTriggered = true;

        if (playerInput.Player.Jump.triggered && verticalMovement == -1 && !isGrounded)
            isForceDown = true;
    }

    private void FixedUpdate()
    {
        var velocityX = !isForceDown ? speed * horizontalMovement : 0;

        body.velocity = new Vector2(velocityX, body.velocity.y);

        if (hasJumpButtonTriggered)
        {
            body.velocity = new Vector2(body.velocity.x, jumpForce);
        }

        if (isForceDown)
            body.gravityScale = downForce;

        if (isGrounded)
        {
            isForceDown = false;
            hasJumpButtonTriggered = false;
            body.gravityScale = baseGravityScale;
        }
    }

    private void OnEnable()
    {
        playerInput.Player.Enable();
    }

    private void OnDisable()
    {
        playerInput.Player.Disable();
    }
}
```