ground detection on unity 2D keeps wobbling on and off.

When player starts running in either way he sometimes detects he is touching the ground while other times he doesn’t. so he constantly keeps switching between running and jumping animations.
I have two bools set, One for jumping and the other for running. I have also set the transitions correctly, triple checked everything.

these are the scripts I’m using:

_This one is linked to the player:

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

public class PlayerManager : MonoBehaviour
{
    Animator anim;
    public ButtonMovement leftButton, rightButton;
    int buttonL, buttonR, lRSum;
    public bool onGround = false;


    void Start ()
    {
        anim = this.GetComponent<Animator>();
    }
   
    void FixedUpdate ()
    {
        buttonL = leftButton.isOn;
        buttonR = rightButton.isOn;
        lRSum = buttonL + buttonR;

        if (lRSum > 0 && onGround)
        {
            anim.SetBool("Move", true);
        } else if (lRSum == 0 && onGround)
        {
            anim.SetBool("Move", false);
        }
    }

    void OnCollisionEnter2D (Collision2D obj)
    {
        if (obj.gameObject.tag == "ground")
        {
            anim.SetBool("Jump", false);
            onGround = true;
        }
    }

    void OnCollisionExit2D(Collision2D obj)
    {
        if (obj.gameObject.tag == "ground")
        {
            anim.SetBool("Jump", true);
            onGround = false;
        }
    }
}

I have all floor objects tagged “ground” just like shown on script.

_This one is linked to the buttons on screen since I’m making a mobile game:

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

public class ButtonMovement : TouchManager
{
    public enum type {LeftButton, RightButton, JumpButton};
    public type buttonType;

    public int isOn = 0;
    public float jumpHeight = 0.0f, moveSpeed = 0.0f;  

    public GameObject playerObject = null;
    Rigidbody2D playerRigidBody;

    public GUITexture buttonTexture = null;

    void Start ()
    {
        playerRigidBody = playerObject.GetComponent<Rigidbody2D>();
    }
   
    void Update ()
    {
        TouchInput(buttonTexture);
    }

    void OnFirstTouchBegan ()
    {
        switch (buttonType)
        {
            case type.JumpButton:
                if (playerObject.GetComponent<PlayerManager>().onGround)
                {
                    playerRigidBody.AddForce(Vector2.up * jumpHeight, ForceMode2D.Impulse);
                }
                break;
        }
    }

    void OnSecondTouchBegan ()
    {
        switch (buttonType)
        {
            case type.JumpButton:
                if (playerObject.GetComponent<PlayerManager>().onGround)
                {
                    playerRigidBody.AddForce(Vector2.up * jumpHeight, ForceMode2D.Impulse);
                }
                break;
        }
    }

    void OnFirstTouch ()
    {
        switch (buttonType)
        {

            case type.LeftButton:
                playerObject.transform.eulerAngles = new Vector2(0, 180);
                playerObject.transform.Translate(Vector2.right * moveSpeed * Time.deltaTime);
                isOn = 1;
                break;
            case type.RightButton:
                playerObject.transform.eulerAngles = new Vector2(0, 0);
                playerObject.transform.Translate(Vector2.right * moveSpeed * Time.deltaTime);
                isOn = 1;
                break;
        }
    }

    void OnSecondTouch ()
    {
        switch (buttonType)
        {

            case type.LeftButton:
                playerObject.transform.eulerAngles = new Vector2(0, 180);
                playerObject.transform.Translate(Vector2.right * moveSpeed * Time.deltaTime);
                isOn = 1;
                break;
            case type.RightButton:
                playerObject.transform.eulerAngles = new Vector2(0, 0);
                playerObject.transform.Translate(Vector2.right * moveSpeed * Time.deltaTime);
                isOn = 1;
                break;
        }
    }

    void OnFirstTouchEnded ()
    {
        isOn = 0;
    }

    void OnSecondTouchEnded ()
    {
        isOn = 0;
    }
}

I also have a Touch manager script set up :

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

public class TouchManager : MonoBehaviour
{

    public static bool guiTouch = false;

    public void TouchInput(GUITexture texture)
    {
        if (texture.HitTest(Input.GetTouch(0).position))
        {
            switch (Input.GetTouch(0).phase)
            {
                case TouchPhase.Began:
                    SendMessage("OnFirstTouchBegan",SendMessageOptions.DontRequireReceiver);
                    SendMessage("OnFirstTouch",SendMessageOptions.DontRequireReceiver);
                    guiTouch = true;
                    break;
                case TouchPhase.Stationary:
                    SendMessage("OnFirstTouchStayed",SendMessageOptions.DontRequireReceiver);
                    SendMessage("OnFirstTouch",SendMessageOptions.DontRequireReceiver);
                    guiTouch = true;
                    break;
                case TouchPhase.Moved:
                    SendMessage("OnFirstTouchMoved",SendMessageOptions.DontRequireReceiver);
                    SendMessage("OnFirstTouch",SendMessageOptions.DontRequireReceiver);
                    guiTouch = true;
                    break;
                case TouchPhase.Ended:
                    SendMessage("OnFirstTouchEnded",SendMessageOptions.DontRequireReceiver);
                    guiTouch = false;
                    break;
            }
        }
        if (texture.HitTest(Input.GetTouch(1).position))
        {
            switch (Input.GetTouch(1).phase)
            {
                case TouchPhase.Began:
                    SendMessage("OnSecondTouchBegan",SendMessageOptions.DontRequireReceiver);
                    SendMessage("OnSecondTouch",SendMessageOptions.DontRequireReceiver);
                    break;
                case TouchPhase.Stationary:
                    SendMessage("OnSecondTouchStayed",SendMessageOptions.DontRequireReceiver);
                    SendMessage("OnSecondTouch",SendMessageOptions.DontRequireReceiver);
                    break;
                case TouchPhase.Moved:
                    SendMessage("OnSecondTouchMoved",SendMessageOptions.DontRequireReceiver);
                    SendMessage("OnSecondTouch",SendMessageOptions.DontRequireReceiver);
                    break;
                case TouchPhase.Ended:
                    SendMessage("OnSecondTouchEnded",SendMessageOptions.DontRequireReceiver);
                    break;
            }
        }
    }

}

I checked everything and I still don’t know what did I do wrong, If anyone can help out I’d be truly grateful. Thanks in advance.

Either the character is “bouncing” on the ground, or its moving between colliders would be my guess.
If you think either of those are possible, consider that your “OnCollisonExit” always calls, even if it leaves 1 collision (bounce/different collider) and tells it you want to jump right away.
I had a very similar problem recently, building my own 3rd person controller.
In my case, I wrote an int variable into my script, which increments on enter and decrements on exit.
I check if I’m at 1, i set grounded and at 0 I set (fall/ jump, depending if i initiated a jump or not).
Hope that helps :slight_smile:

1 Like

Apparently the “moving between colliders” was the issue, since I had different tiles each with it’s own 2D collider he kept switching to the jumping state when he passed between them despite the fact I set one collider for all of them, After I fixed it things went smoothly. Thank you so much for helping me out. It was driving me crazy for the past 2 days.

Hey, no problem. I’m glad that you got it resolved :slight_smile:

1 Like