How do I prevent player and objects jittering at collision?

79 Views Asked by At

I have a problem with the 2D physics for my game. When the player jumps and collides with an object, the object is pushed into the ground a little bit. How do I prevent colliders from going through each other?

Play Screen

I've first tried changing the rigidbody settings to continuous and interpolating, but it didn't work. I then changed the Unity Physics 2D Settings such as contact offset, but also didn't work.

These are my settings and the player movement code:

Player Rigidbody and Collider

Object Rigidbody and Collider

Physics 2D

using JetBrains.Annotations;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.Rendering;

public class MovePlayer : MonoBehaviour
{
    public Rigidbody2D rb;
    Vector2 moveInput;
    public Animator anim;
    [SerializeField]
    float moveSpeed = 10f, acceleration = 1f, decceleration = -1f, velPower = 1f, jumpForce = 3f, coyoteTime = 0.1f, jumpInputBuffer = 0.1f, jumpCut = 1f;
    [SerializeField]
    float gravityScale = 6f, fallGravityMultiplier = 1.1f, jumpHangGravityThreshold = 0.1f, jumpHangGravityMultiplier = 0.8f, frictionAmount = 0.2f;
    [SerializeField]
    Vector2 groundChecksize = new Vector2(0.49f, 0.03f);

    public LayerMask ground;
    public GameObject groundCheckObject;
    
    PlayerAttack attackScript;

    float LastOnGroundTime = 0f, LastJumpPressed = 0f, LastMovePressed = 0f;
    float tmp = 1f;

    bool isJumping;
    bool isFacingRight = true;

    // Start is called before the first frame update
    void Start()
    {
        anim = this.GetComponent<Animator>();
        attackScript = this.GetComponent<PlayerAttack>();
    }


    // Update is called once per frame
    void Update()
    {
        LastOnGroundTime -= Time.deltaTime;
        LastJumpPressed -= Time.deltaTime;
        LastMovePressed -= Time.deltaTime;
        moveInput.x = Input.GetAxisRaw("Horizontal");
        moveInput.y = Input.GetAxisRaw("Vertical");
        if (moveInput.x != 0)
        {
            LastMovePressed = tmp;
        }
        if ((moveInput.x > 0f && isFacingRight == false) || (moveInput.x < 0f && isFacingRight == true))
        {
            FlipPlayer();
        }
        if(attackScript.isAttacking>=0)
        {
            moveInput.x = 0f;
        }
        groundCheck();

        if (isJumping && rb.velocity.y < 0) isJumping = false;

        if (Input.GetKeyDown(KeyCode.Space))
        {
            LastJumpPressed = jumpInputBuffer;
        }

        if (Input.GetKeyUp(KeyCode.Space) && isJumping && rb.velocity.y > 1f)
        {
            rb.AddForce(jumpCut * Vector2.down * (rb.velocity.y < 2f ? rb.velocity.y - 1 : 1), ForceMode2D.Impulse);
        }

        if (LastOnGroundTime > 0 && LastJumpPressed > 0)
        {
            Jump();
        }
        if (rb.velocity.y < 0)
        {
            rb.gravityScale = gravityScale * fallGravityMultiplier;
        }
        else if (Mathf.Abs(rb.velocity.y) < jumpHangGravityThreshold)
        {
            rb.gravityScale = gravityScale * jumpHangGravityMultiplier;
        }
        else
        {
            rb.gravityScale = gravityScale;
        }
        if (Input.GetAxisRaw("Horizontal") == 0)
        {
            anim.SetBool("walk", false);
        }
        else
        {
            anim.SetBool("walk", true);
        }
        if (LastOnGroundTime > 0 && Input.GetAxisRaw("Horizontal") == 0)
        {
            float friction = Mathf.Min(Mathf.Abs(rb.velocity.x), Mathf.Abs(frictionAmount));
            friction *= Mathf.Sign(rb.velocity.x);
            rb.AddForce(Vector2.right * -friction, ForceMode2D.Impulse);
        }
    }

    private void FixedUpdate()
    {
        float targetSpeed = moveInput.x * moveSpeed;
        float speedDif = targetSpeed - rb.velocity.x;
        float accelRate = (Mathf.Abs(targetSpeed) > 0.01f) ? acceleration : decceleration;
        float movement = Mathf.Pow(Mathf.Abs(speedDif) * accelRate, velPower) * Mathf.Sign(speedDif);
        rb.AddForce(movement * Vector2.right);
    }

    void Jump()
    {
        LastOnGroundTime = 0;
        LastJumpPressed = 0;
        isJumping = true;
        float force = jumpForce;
        if (rb.velocity.y < 0)
        {
            force -= rb.velocity.y;
        }
        rb.AddForce(force * Vector2.up, ForceMode2D.Impulse);
    }

    void groundCheck()
    {
        if (Physics2D.OverlapBox(groundCheckObject.transform.position, groundChecksize, 0, ground))
        {
            LastOnGroundTime = coyoteTime;
        }
    }

    void FlipPlayer() //이동 방향에 맞게 플레이어를 반전
    {
        isFacingRight = !isFacingRight;
        Vector3 PlayerScale = transform.localScale;
        PlayerScale.x = PlayerScale.x * -1;
        transform.localScale = PlayerScale;
    }

    public void InitPlayer()
    {
        LastOnGroundTime = 0f; LastJumpPressed = 0f; LastMovePressed = 0f;
        isJumping = false;
        if(!isFacingRight)
        {
            FlipPlayer();
        }
    }

    private void OnDrawGizmosSelected()
    {
        Gizmos.DrawWireCube(groundCheckObject.transform.position, groundChecksize);
    }
}
3

There are 3 best solutions below

0
AudioBubble On

After some experimenting, I can conclude that the reason is likely due to handling the movement inside of FixedUpdate():

private void FixedUpdate()
{
    float targetSpeed = moveInput.x * moveSpeed;
    float speedDif = targetSpeed - rb.velocity.x;
    float accelRate = (Mathf.Abs(targetSpeed) > 0.01f) ? acceleration : decceleration;
    float movement = Mathf.Pow(Mathf.Abs(speedDif) * accelRate, velPower) * Mathf.Sign(speedDif);
    rb.AddForce(movement * Vector2.right);
}

I tried moving my movement function into Fixed Update and it also glitched through objects and stuttered around, I would just place your movement code in a function:

private void Move() {
    float targetSpeed = moveInput.x * moveSpeed;
    float speedDif = targetSpeed - rb.velocity.x;
    float accelRate = (Mathf.Abs(targetSpeed) > 0.01f) ? acceleration : decceleration;
    float movement = Mathf.Pow(Mathf.Abs(speedDif) * accelRate, velPower) * Mathf.Sign(speedDif);
    rb.AddForce(movement * Vector2.right);
}

Then call it inside of the update function:

void Update() {
    Move()
    //...
}

Another thing I noticed is your using rb.AddForce rather than rb.velocity like me. I can see why you would use that but that may also be another reason why.

2
Ryan Gray On

I would start by changing the collision detection mode to continuous dynamic, turn off interpolation, and make sure the bounce threshold of the material is much larger than zero. Also make sure the bouciness is not overidden by either object's physic material.

If that doesn't solve it, try increasing the simulation speed. In our case, increasing the position iterations might the best way.

Otherwise ensuring that the Rigidbodies aren't sleeping for too long after collisions.

0
exabyte On

Oh I fixed it by myself. I don't know why but increasing the contact offset to 0.01 solved the problem. Maybe it was too small for the physics engine.