Home > front end >  Unity - the player jumps too high when stuck to a wall
Unity - the player jumps too high when stuck to a wall

Time:07-19

I'm writing some code to create a Minecraft Quake like game but I have an issue with the jump mecanic. When I'm stuck to a wall the player jump to high (see the video).

I use a Rigidbody for the physics and I modify that velocity to move the player. There is a Physic Material on the player's Collider with no consideration for friction or bouncing.

If you have ideas to fix the bug or an alternative to work around the problem, I'm interested.

How it looks like

Here is my code

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public enum PlayerMovementState {
    Sneak,
    Walk,
    Run
}

public class PlayerControls : MonoBehaviour
{
    Rigidbody rb;

    Vector3 velocity, desiredVelocity;
    PlayerMovementState moveState;

    float groundDistance;
    [SerializeField]
    bool forwardAir, backAir, rightAir, leftAir;
    [SerializeField]
    LayerMask groundLayer;
    [SerializeField]
    bool onGround;

    bool desiredJump;
    float jumpHeight = 1.0f;

    private void Awake() {
        rb = GetComponent<Rigidbody>();
        moveState = PlayerMovementState.Walk;
        groundDistance = GetComponentInChildren<Collider>().bounds.extents.y;
    }

    private void Update() {
        Vector2 playerInputs = Vector2.ClampMagnitude(
            new Vector2(
                Input.GetAxis("Horizontal"),
                Input.GetAxis("Vertical")
            ), 1.0f
        );

        if (Input.GetKey(KeyCode.LeftShift)) moveState = PlayerMovementState.Sneak; 
        else if (Input.GetKey(KeyCode.LeftControl)) moveState = PlayerMovementState.Run;
        else moveState = PlayerMovementState.Walk;

        float speed = moveState == PlayerMovementState.Run ? 10f : (
            moveState == PlayerMovementState.Sneak ? 2f : 5f
        );

        RaycastGround();
        onGround = !forwardAir && !backAir && !rightAir && !leftAir;

        if (Input.GetButtonDown("Jump")) desiredJump = true;

        if (moveState == PlayerMovementState.Sneak)
        {
            if (forwardAir && playerInputs.y > 0) playerInputs.y = 0f;
            if (backAir && playerInputs.y < 0) playerInputs.y = 0f;
            if (rightAir && playerInputs.x > 0) playerInputs.x = 0f;
            if (leftAir && playerInputs.x < 0) playerInputs.x = 0f;
        }

        desiredVelocity = 
            (transform.forward * playerInputs.y   transform.right * playerInputs.x) * speed;
    }

    private void FixedUpdate() {
        velocity = rb.velocity;

        float acceleration = 10;
        velocity.x = Mathf.MoveTowards(velocity.x, desiredVelocity.x, acceleration);
        velocity.z = Mathf.MoveTowards(velocity.z, desiredVelocity.z, acceleration);

        if (desiredJump && onGround)
        {
            desiredJump = false;
            float jumpSpeed = Mathf.Sqrt(-2f * Physics.gravity.y * jumpHeight);

            velocity.y  = jumpSpeed;
        }

        rb.velocity = velocity;

        desiredJump = false;
    }

    void RaycastGround()
    {
        forwardAir = !(Physics.Raycast(
            transform.position   Vector3.forward * 0.1f,
            -Vector3.up,
            groundDistance   0.1f,
            groundLayer
        ));

        backAir = !(Physics.Raycast(
            transform.position - Vector3.forward * 0.1f,
            -Vector3.up,
            groundDistance   0.1f,
            groundLayer
        ));

        rightAir = !(Physics.Raycast(
            transform.position   Vector3.right * 0.1f,
            -Vector3.up,
            groundDistance   0.1f,
            groundLayer
        ));

        leftAir = !(Physics.Raycast(
            transform.position - Vector3.right * 0.1f,
            -Vector3.up,
            groundDistance   0.1f,
            groundLayer
        ));
    } 
}

CodePudding user response:

Very likely the problem is that the script thinks it's still grounded while it is jumping upwards along the wall.

Depending on what feeling you want, either fix the raycasts such that they only trigger when you are standing directly on top of an object, or you check if the y part of your velocity is <= 0 for your onGround variable.

CodePudding user response:

I did not find a solution to my problem but I found a workaround anyway. By detecting the walls around the player, I prevent him from moving in the direction of the wall which prevents him from being stuck on it and having this bug when he jumps.

(It means that my problem is not resolved and that I am still looking for a solution)

Video

...
float wallDistance;
...
[SerializeField]
bool forwardWall, backWall, rightWall, leftWall;

...
SpherecastWall();
...
if (forwardWall && playerInputs.y > 0) playerInputs.y = 0f;
if (backWall && playerInputs.y < 0) playerInputs.y = 0f;
if (rightWall && playerInputs.x > 0) playerInputs.x = 0f;
if (leftWall && playerInputs.x < 0) playerInputs.x = 0f;

void SpherecastWall() {
    forwardWall = (Physics.SphereCast(
        new Ray(transform.position, Vector3.forward),
        wallDistance,
        .2f,
        groundLayer
    ));

    backWall = (Physics.SphereCast(
        new Ray(transform.position, -Vector3.forward),
        wallDistance,
        .2f,
        groundLayer
    ));

    rightWall = (Physics.SphereCast(
        new Ray(transform.position, Vector3.right),
        wallDistance,
        .2f,
        groundLayer
    ));

    leftWall = (Physics.SphereCast(
        new Ray(transform.position, -Vector3.right),
        wallDistance,
        .2f,
        groundLayer
    ));
}

CodePudding user response:

I think that's because the spheres center gets over the corner of the wall. So when you apply a force the sphere will be pushed over it. Maybe you could replace the sphere collider of your player with a capsule or a square collider.

  • Related