0
\$\begingroup\$

I'm making a 2D platformer with Unity and I have a problem with platform edges.

This problem is usually visible in the situation where I want the player to walk up the stairs by jumping, but the problem is, it can climb the stairs by just moving, not jumping. This means that the edges make the player jump, so it can jump higher and higher when collides with edges. I have this problem with almost all objects that player should jump on.

This looks like a bug to me. I've been searching for the answer and all the existing answers are related to a situation where the player is stuck on the edge and can't move anymore, which I realized that sometimes can happen but a little bit different than that scenarios. In this situation, the player freezes in the air after jumping and sticks to the platform, while I'm not holding the move button.

I tried adding a physics material and using zero friction but it didn't work. My player has a CapsuleCollider2D and the stairs object has a PolygonCollider2D. The player script inherits from a PhysicsObject script which handles the game physics. (Maybe this script is the root of the problem).

Player script (this script is so long, so I deleted the parts that were unrelated to this problem):

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
 
public class NewPlayer : PhysicsObject
{
    [Header("Attributes")]
    [SerializeField] private float jumpPower = 10;
    [SerializeField] private float maxSpeed = 1;
    [SerializeField] private float fallForgiveness = 1; //This is the amount of seconds the player has after falling from a ledge to be able to jump
    [SerializeField] private float fallForgivenessCounter; //This is the simple counter that will begin the moment the player falls from a ledge
    bool isFacingLeft;

    void Update()
    {
        targetVelocity = new Vector2(Input.GetAxis("Horizontal") * maxSpeed, 0);
    
        //If the player is no longer grounded, begin counting the fallForgivenessCounter
        if (!grounded)
        {
            fallForgivenessCounter += Time.deltaTime;
        }
        else
        {
            fallForgivenessCounter = 0;
        }
    
        //If the player presses "Jump" and we're grounded, set the velocity to a jump power value
        if (Input.GetButtonDown("Jump") && fallForgivenessCounter < fallForgiveness)
        {
            velocity.y = jumpPower;
            grounded = false;
            fallForgivenessCounter = fallForgiveness;
        }
    
        //Flip the player's localScale.x if the move speed is greater than .01 or less than -.01
        if (isFacingLeft)
        {
                transform.localScale = new Vector2(Mathf.Abs(transform.localScale.x) * -1, transform.localScale.y);
        }
        else
        {
            transform.localScale = new Vector2(Mathf.Abs(transform.localScale.x), transform.localScale.y);
        }
    }  
}

Physics Object script:

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

public class PhysicsObject : MonoBehaviour
{

    [Header("Physics Object Attributes")]
    public float minGroundNormalY = .65f;
    public float gravityModifier = 1f;

    protected Vector2 targetVelocity;
    protected bool grounded;
    protected Vector2 groundNormal;
    protected Rigidbody2D rb2d;
    protected Vector2 velocity;
    protected ContactFilter2D contactFilter;
    protected RaycastHit2D[] hitBuffer = new RaycastHit2D[16];
    protected List<RaycastHit2D> hitBufferList = new List<RaycastHit2D>(16);


    protected const float minMoveDistance = 0.001f;
    protected const float shellRadius = 0.01f;

    void OnEnable()
    {
        rb2d = GetComponent<Rigidbody2D>();
    }

    void Start()
    {
        contactFilter.useTriggers = false;
        contactFilter.SetLayerMask(Physics2D.GetLayerCollisionMask(gameObject.layer));
        contactFilter.useLayerMask = true;
    }

    void Update()
    {
        targetVelocity = Vector2.zero;
        ComputeVelocity();
    }

    protected virtual void ComputeVelocity()
    {

    }

    void FixedUpdate()
    {
        velocity += gravityModifier * Physics2D.gravity * Time.deltaTime;
        velocity.x = targetVelocity.x;

        grounded = false;

        Vector2 deltaPosition = velocity * Time.deltaTime;

        Vector2 moveAlongGround = new Vector2(groundNormal.y, -groundNormal.x);

        Vector2 move = moveAlongGround * deltaPosition.x;

        Movement(move, false);

        move = Vector2.up * deltaPosition.y;

        Movement(move, true);
    }

    void Movement(Vector2 move, bool yMovement)
    {
        float distance = move.magnitude;

        if (distance > minMoveDistance)
        {
            int count = rb2d.Cast(move, contactFilter, hitBuffer, distance + shellRadius);
            hitBufferList.Clear();

            for (int i = 0; i < count; i++)
            {
                PlatformEffector2D platform = hitBuffer[i].collider.GetComponent<PlatformEffector2D>();
                if (!platform || (hitBuffer[i].normal == Vector2.up && velocity.y < 0 && yMovement))
                {
                    hitBufferList.Add(hitBuffer[i]);
                }
            }

            for (int i = 0; i < hitBufferList.Count; i++)
            {
                Vector2 currentNormal = hitBufferList[i].normal;
                if (currentNormal.y > minGroundNormalY)
                {
                    grounded = true;
                    if (yMovement)
                    {
                        groundNormal = currentNormal;
                        currentNormal.x = 0;
                    }
                }

                float projection = Vector2.Dot(velocity, currentNormal);
                if (projection < 0)
                {
                    velocity = velocity - projection * currentNormal;
                }

                float modifiedDistance = hitBufferList[i].distance - shellRadius;
                distance = modifiedDistance < distance ? modifiedDistance : distance;
            }

        }

        rb2d.position = rb2d.position + move.normalized * distance;
    }
}

enter image description here

As you can see, the player just jumps once or twice and then automatically goes up only by moving forward. At that point, I was just holding the right arrow key on the keyboard.

enter image description here

\$\endgroup\$
9
  • \$\begingroup\$ I don't know where you got that PhysicsObject script from and what exactly it is supposed to do (inheritance instead of composition is a valid but rather unusual approach to Unity programming), but it seems like the intended way to extend it to implement your own game mechanics is not to overwrite Update() but rather to implement the protected virtual void ComputeVelocity() method. \$\endgroup\$
    – Philipp
    Feb 8, 2021 at 16:52
  • \$\begingroup\$ @Philipp I got the PhysicsObject script from here. \$\endgroup\$
    – Ali
    Feb 8, 2021 at 17:12
  • \$\begingroup\$ @DMGregory I added a gif and hope that the problem is visible. As you can see, the edges make the player jump while it's just moving. Currently I have a weird performance problem in the play mode and the frames drop drastically. That's why the gif looks like this. I'll ask a question about that later with more details. \$\endgroup\$
    – Ali
    Feb 8, 2021 at 18:17
  • \$\begingroup\$ To be sure since the gif makes it hard to see. At the start you are jumping and you are landing with the players curve on the edge of the stairs, right? If I just place a single cube on the ground, with a high enough speed the player moves diagonal upwards. It is not using the stairs, just using his round belly as a ramp to shoot to the sky. And skipping a few stairs while he is flying. On maxSpeed 4 he can climb on top of the edge, at five he starts taking of. At 25 he goes to the moon. What is your speed? \$\endgroup\$
    – Zibelas
    Feb 8, 2021 at 18:58
  • \$\begingroup\$ Actually the player didn't jump at the start. I was just holding the right arrow key all the time. It looks like the stairs are using a kind of force to move the player up. My speed is 10.25. \$\endgroup\$
    – Ali
    Feb 8, 2021 at 20:21

1 Answer 1

0
\$\begingroup\$

I wasn't able to find the root cause of this problem, but I found a way to prevent it and that is using a Box Collider 2D instead of a Capsule Collider 2D for the player. This solved the problem for me.

\$\endgroup\$

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .