Problem with a gameObject's box collider when resizing ??

Hi !

i dont understand what is happening there :

when my player hits a wall, he could climb over it.
Since my game is 2.5D, i have 2 2Dcolliders on my 2Dplayer, and boxcollider2D on the wall (wich is in 3D)
it works correctly.

but when i reduce the size of the wall’s gameObject, it’s quite dysfonctionnal… the collision is eratic, the player is “pushed” away whitout reason.

Here is a capture : on the right the wall working ok, on the left, the same gameObject reduced.
You could see the player pushed back…the collision is lost…

could you tell me what’s happening ?
is there a conflict between boxcollider size and resizing a gameObject ?

What coulb a solution to this problem (i like to build my level with copies of the same gameObject at different size, etc.)

thanks !

The top part of the capsule is hitting the bottom-right corner of the wall.

I guess you can reduce the capsule width. For example:
8417817--1113414--upload_2022-9-5_21-11-9.png

or maybe increase it until the capsule is tall enough.
8417817--1113417--upload_2022-9-5_21-12-23.png

Or both.
8417817--1113420--upload_2022-9-5_21-13-2.png
In any case, try to eliminate that annoying OCD-friendly empty space.

1 Like

Hi lightbug14, thanks for your solutions, with screencaps, i appreciate. :wink:
I tried all of them and the third worked : reduced a little the capsule collider and overlap the two of them.

What’s an OCD i’m curious (and newbie…) ?

OCD :wink:

Though not sure what the reference here is. Possibly that such a collider “dent” (empty space) could cause the character to vibrate repeatedly or getting stuck in a constant jerky motion. Like when a corner of a box collider gets right in that “crotch space” of the two character colliders, they will want to stay together while being forced away from each other at the same time.

But the third solution will also have issues where player is sliding down a wall and gets his crotch stuck on a ledge while feet are dangling down. I’m not sure how he is supposed to hold on in that case unless he’s got a very strong, well trained muscle in that area. :sunglasses:

I’d try #2 but with minimally smaller capsule. Or better: two capsules.

Anyway, be sure to also enable drawing 3D colliders, just in case the 3d box collider is simply slightly too large.

Thanks CodeSmile !

I’m French, so i wasn’t sure to have the OCD reference ! :smile:

I see what you mean…
i would do some tests. But the 2d solution worked not so well in my case.

My game is “2.5D” :
the world is 3D mixing 3d objects (rocks, ground, platforms, obstacles) and 2D (in the foreground and backround mostly). My Player is 2D so i add BoxCollider2D on my 3D obstacles (or edge collider 2D)
but i suppose you advice would apply the same : drawing 2D colliders when 2D box collider are a bit too large ?

No, as you can read what was said about “3D colliders”, it’s simply that you’re saying “3D” which is ambiguous and could mean 3D physics and mixing 2D/3D physics obviously won’t work.

Do whatever you like with your visuals (2D/3D whatever), it’s irrelevant. :slight_smile:

When mentioning 2D or 3D. I would suggest being specific about what you’re talking about i.e. visuals, physics, audio etc.

ok not sure i understand what you’re telling me, sorry.
so :

  • my player is 2D, with 2D colliders one for the legs and one for the up of the body (as you can see on the gif)
  • the ground is a 3D object (ground.obj) with a 2D collider (boxcollider2D)
  • the platforms on wich the player could walk, climb, have collisions, etc. are mostly 3d objects (.fbx or .obj) with 2D colliders on it (BoxCollider2D / EdgeCollider) but some could be also 2D sprite (.png / .psd) with 2D colliders on it too.
  • the foreground and background are a mix of 2D/3D objects but are just visuals, with no colliders…

I hope it’s more clear.

so when you say " mixing 2D/3D physics obviously won’t work"
are you saying that for instance have a 3D ground with a 2D collider is a bad idea and won’t work correctly ?
or what i’m trying to do, the 2Dplayer climbing over a 3Dplatform wich has a 2Dcollider, won’t work correctly ?

Because i though i could use whatever i want (2D sprite or 3d) but i have to stay on the same logic ( so 2D in my case for all the colliders and physics). Maybe is that you’re telling me instead ?

Sorry, my english is not very accurate so… sometimes i’m not sure i understand exactly.

Thanks :slight_smile:

Yes! Exactly! :slight_smile:

In short: 2D and 3D physics are different things. They don’t interact with each other. You can only use one or the other on a GameObject.

Rendering, use whatever you like 2D or 3D.

OK thanks :slight_smile:

I think my problem is fixed :
i replace the upper BoxCollider2D of my player by a CapsuleCollider2D which overlap more with the legs BoxCollider2D, but i have better results with a slightly smaller legs BoxCollider2D than exactly the same width.

But now, i tried another thing, quite simple : each time my character the water, he would die.
i create a 3D cube (the black square on the gif below), with a BoxCollider2D on it, tagged it “DeathZone” and in the player script, whith “void OnCollisionEnter2D”, when he hits that gameObject, he would die with a death animation playing.

it works except when the player is falling from above on the surface… then the collision seems to be missed.
if i jump, then the collision is effective and the death animation plays…

i don’t understand why the collision is so capricious here ? it quite configuration, isn’t ?

and with the player colliders on display :

Oh i tryied to change the function with “OnCollisionStay2D” instead, and it fixed it !

I don’t see why OnCollisionStay2D would solve your problem. You won’t ever get that without a OnCollisionEnter2D before. OnCollisionStay2D is essential OnCollisionEnter2D. “Enter” means just started touching. "Stay: means still touching. “End” means stopped touching.

Ok then.
But it works every time since i changed, and when i go back to "OnCollisionEnter2D " i have the same problem.
i don’t know why… :eyes:

i will investigate…!

Since you haven’t showed us the code it is impossible to know what’s going on.
Based on that GIF (OnCollisionEnter version), it seems like the death action requires two OnCollisionEnter calls for whatever reason. The same thing is probably happening with OnCollisionStay now. Since this method is being called more than once, it is really hard to notice this “delay”.

Oh ok !
i’ll post the full codes next week then. (I have to interrupt for a week inexpectedly, sorry for that)

It might be worth putting your collision handling code into a function that accepts a Collision2D and call this from OnCollisionEnter2D and OnCollisionStay2D which are essentially the same thing, just called at different times.

If you do post code though, please use code-tags .

Hi !
Sorry for the delay…

So there is my “Frankencode” : i’m a newbie, and i’m learning everything with this game project. as you could expect, “my” code is a mix of several tutorials, codes found here and there, and some ahem personnal tries… it’s a mess, i’m trying to keep it clean and to simplify. Sorry for that… and some names are in french (i’m french) so tell me if you need an translation/explanation. sorry for that !

Here’s in the inspector, the components for my player :

as you can see there is a several scripts :

  • a character controller,
  • a script for moving the player
  • a script for collisions

and two 2D capsule colliders : one for the top of the body wich is disconnected when the player crouches, and the other it for the low part of the body.

the player (“joueur” in french) as several gameobjects parented :

  • Ceiling check
  • Ground check
  • Nouvelle Position

the first and second are obvious enough i think, the third “nouvellePosition” (=NewPosition) is placed near the feet and is used to “teleport” my player when climbing a ledge. At the very last frame of my climb animation, the gameobjecti is moved from feet to the hands position and then, replace by the idle animation.
i hope i’m clear.

And finally, the 3 scripts i’m using :

  • Character Controller
using UnityEngine;
using UnityEngine.Events;

public class CharacterController2D : MonoBehaviour
{
    [SerializeField] private float m_JumpForce = 3400f;                         // Amount of force added when the player jumps >> à priori juste pour la hauteur donc ?
    [Range(0, 1)] [SerializeField] private float m_CrouchSpeed = .36f;            // Amount of maxSpeed applied to crouching movement. 1 = 100%
    [Range(0, .3f)] [SerializeField] private float m_MovementSmoothing = .05f;    // How much to smooth out the movement
    [SerializeField] private bool m_AirControl = false;                            // Whether or not a player can steer while jumping;
    [SerializeField] private LayerMask m_WhatIsGround;                            // A mask determining what is ground to the character
    [SerializeField] private Transform m_GroundCheck;                            // A position marking where to check if the player is grounded.
    [SerializeField] private Transform m_CeilingCheck;                            // A position marking where to check for ceilings
    [SerializeField] private Collider2D m_CrouchDisableCollider;                // A collider that will be disabled when crouching

    const float k_GroundedRadius = .2f; // Radius of the overlap circle to determine if grounded
    public static bool m_Grounded;            // Whether or not the player is grounded.
    const float k_CeilingRadius = .2f; // Radius of the overlap circle to determine if the player can stand up
    private Rigidbody2D m_Rigidbody2D;
    private bool m_FacingRight = true;  // For determining which way the player is currently facing.
    private Vector3 m_Velocity = Vector3.zero;
      
    public static bool isWalking;
    public static bool isJumping;
    public static bool isDead = false;
    public static bool isGrabbing; // virer = false si necessaire ?

    public static bool isLedging = false; //test

  





    [Header("Events")]
    [Space]

    public UnityEvent OnLandEvent;

    [System.Serializable]
    public class BoolEvent : UnityEvent<bool> { }

    public BoolEvent OnCrouchEvent;
    private bool m_wasCrouching = false;

    private void Awake()
    {
        m_Rigidbody2D = GetComponent<Rigidbody2D>();

        if (OnLandEvent == null)
            OnLandEvent = new UnityEvent();

        if (OnCrouchEvent == null)
            OnCrouchEvent = new BoolEvent();


    }


  


    private void FixedUpdate()
    {
        bool wasGrounded = m_Grounded;
        m_Grounded = false;

        // The player is grounded if a circlecast to the groundcheck position hits anything designated as ground
        // This can be done using layers instead but Sample Assets will not overwrite your project settings.
        Collider2D[] colliders = Physics2D.OverlapCircleAll(m_GroundCheck.position, k_GroundedRadius, m_WhatIsGround);
        for (int i = 0; i < colliders.Length; i++)
        {
            if (colliders[i].gameObject != gameObject)
            {
                m_Grounded = true;                      
                if (!wasGrounded)
                    OnLandEvent.Invoke();
            }
        }
    }


    public void Move(float move, bool crouch, bool jump)
    {
        // If crouching, check to see if the character can stand up
        if (!crouch)
        {
            // If the character has a ceiling preventing them from standing up, keep them crouching
            if (Physics2D.OverlapCircle(m_CeilingCheck.position, k_CeilingRadius, m_WhatIsGround))
            {
                crouch = true;
            }
        }

        //only control the player if grounded or airControl is turned on
        if (m_Grounded || m_AirControl)
        {

            // If crouching
            if (crouch)
            {
                if (!m_wasCrouching)
                {
                    m_wasCrouching = true;
                    OnCrouchEvent.Invoke(true);
                }

                // Reduce the speed by the crouchSpeed multiplier
                move *= m_CrouchSpeed;

                // Disable one of the colliders when crouching
                if (m_CrouchDisableCollider != null)
                    m_CrouchDisableCollider.enabled = false;
            } else
            {
                // Enable the collider when not crouching
                if (m_CrouchDisableCollider != null)
                    m_CrouchDisableCollider.enabled = true;

                if (m_wasCrouching)
                {
                    m_wasCrouching = false;
                    OnCrouchEvent.Invoke(false);
                }
            }

            // Move the character by finding the target velocity
            Vector3 targetVelocity = new Vector2(move * 10f, m_Rigidbody2D.velocity.y);
            // And then smoothing it out and applying it to the character
            m_Rigidbody2D.velocity = Vector3.SmoothDamp(m_Rigidbody2D.velocity, targetVelocity, ref m_Velocity, m_MovementSmoothing);

            // If the input is moving the player right and the player is facing left...
            if (move > 0 && !m_FacingRight)
            {
                // ... flip the player.
                Flip();
            }
            // Otherwise if the input is moving the player left and the player is facing right...
            else if (move < 0 && m_FacingRight)
            {
                // ... flip the player.
                Flip();
            }
        }
        // If the player should jump...
        if (m_Grounded && jump && !isDead)
        {
            // Add a vertical force to the player.
            m_Grounded = false;
            m_Rigidbody2D.AddForce(new Vector2(0f, m_JumpForce));
          
        }

      


    }


    private void Flip()
    {
        // Switch the way the player is labelled as facing.
        m_FacingRight = !m_FacingRight;

        // Multiply the player's x local scale by -1.
        Vector3 theScale = transform.localScale;
        theScale.x *= -1;
        transform.localScale = theScale;
    }
}
  • Player Movement
 using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class PlayerMovement : MonoBehaviour
{

    public CharacterController2D controller;
    public Animator animator;
    public float horizontalMove = 0f;
    public static float runSpeed = 150f;
    bool jump = false;
    bool crouch = false;




    void Update()
    {      
        // Inputs
        // fleches gauche et droite = avancer
        horizontalMove = Input.GetAxisRaw("Horizontal") * runSpeed;
        animator.SetFloat("Mouvement", Mathf.Abs(horizontalMove));
            

        // Bouton Saut
        if (Input.GetButtonDown("Jump"))
        {
            jump = true;
            CharacterController2D.isJumping = true;
            animator.SetBool("Saute", true);
        }
    

        // Bouton se baisser
        if (Input.GetButtonDown("Crouch"))
        {          
            crouch = true;          
        }
        else if (Input.GetButtonUp("Crouch"))
        {
            crouch = false;         
        }


        // Bouton monter plateforme
        if (CharacterController2D.isLedging = true && Input.GetButtonDown("Up"))
        {
            animator.SetBool("Monte", true);
        }
     



      




        ///////// Pour utiliser la Manette PS4 ///////
        // Bouton Saut PS4
        if (Input.GetButtonDown("PS4_X"))
        {
            jump = true;
            CharacterController2D.isJumping = true;
            animator.SetBool("Saute", true);
        }

        // TEST PS4
        if (Input.GetButtonDown("PS4_C"))
        {
            crouch = true;
        }
        else if (Input.GetButtonUp("PS4_C"))
        {
            crouch = false;
        }

    }


    public void OnLanding ()
    {
        animator.SetBool("Saute", false);
    }


    public void OnCrouching (bool IsCrouching)
    {
        animator.SetBool("SeBaisse", IsCrouching);
    }
  

    void FixedUpdate()
    {
        // Déplacer le joueur
        // avancer
          controller.Move(horizontalMove * Time.fixedDeltaTime, crouch, jump);
        CharacterController2D.isGrabbing = false;
        jump = false;      



    }
}
  • and Collisions
using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class Collisions : MonoBehaviour
{
    public Collider2D ColliderHaut;
    public Collider2D ColliderBas;
    public Animator animator;
    private float TempsEnLair = 0f;
    public float HauteurMortelle = 3f;


    public GameObject Joueur;
    public Transform NouvellePosition;


    private void Update()
    {
        ChuteMortelle();
    }

    void ChuteMortelle()
    {
        CharacterController2D controller = GetComponent<CharacterController2D>();
        if (CharacterController2D.m_Grounded)
        {
            TempsEnLair = 0f;
        }
        if (!CharacterController2D.m_Grounded)
        {
            TempsEnLair += Time.deltaTime;

        }
        if (TempsEnLair >= HauteurMortelle)
        {
            CharacterController2D.isDead = true;
            Debug.Log("Chute Mortelle !");
        }
    }


    void OnCollisionEnter2D(Collision2D collision)
    {
        /// Pour se suspendre aux plateformes      
        if (collision.gameObject.tag == "MursGrimpables")
        {
            CharacterController2D.isGrabbing = true;
            animator.SetBool("EnCollisionAvecUnMurGrimpable", true);        

            //Quand le joueur touche un mur, sa vitesse tombe à 0. Mais pour repartir, il faut que cette valeur ne soit pas gelée. d'ou l'ajout d'une durée avec la methode 'invoke' juste après
            PlayerMovement.runSpeed = 0f;

            // attend un moment avant de revenir à la valeur définie par le private void plus bas
            Invoke("Reset_runSpeed", 0.05f);                  
        }
               


        if (collision.gameObject.tag == "Murs")
        {
            //Debug.Log("Contact");
            animator.SetBool("EnCollisionAvecUnMur", true);

            //Quand le joueur touche un mur, sa vitesse tombe à 0. Mais pour repartir, il faut que cette valeur ne soit pas gelée. D'où l'ajout d'une durée avec la methode 'invoke' juste après ->
            PlayerMovement.runSpeed = 0f;

            // Attend un moment avant de revenir à la valeur définie par le private void plus bas Reset_runSpeed
            Invoke("Reset_runSpeed", 0.05f);
        }  
    }

    void OnCollisionStay2D(Collision2D collision)
    {
        if (collision.gameObject.tag == "EauMortelle")
        {                   
            PlayerMovement.runSpeed = 0f;
            CharacterController2D.isDead = true;
            animator.SetBool("MortNoyade", true);  
        }

        if (collision.gameObject.tag == "Sols" && CharacterController2D.isDead)
        {
            //PlayerMovement.runSpeed = 0f;
            //CharacterController2D.isDead = true;
            animator.SetBool("ChuteMortelle", true);
        }
    }

 

    private void Reset_runSpeed()
    {
        // la valeur à laquelle revenir après le délai 'invoke'
        PlayerMovement.runSpeed = 150f;
    }

    private void MortNoyade()
    {
        animator.speed = 0f;
        PlayerMovement.runSpeed = 0f;
    }

    private void Monte()
    {
        CharacterController2D.isLedging = true;
    }

    private void EstMonte()
    {
        CharacterController2D.isLedging = false;
        CharacterController2D.isJumping = false;
        animator.SetBool("Monte", false);
        CharacterController2D.m_Grounded = true;
        Joueur.transform.position = NouvellePosition.transform.position;


    }


    void OnCollisionExit2D(Collision2D collision)
    {
        if (collision.gameObject.tag == "MursGrimpables")
        {
            CharacterController2D.isGrabbing = false;
            animator.SetBool("EnCollisionAvecUnMurGrimpable", false);
        }

        if (collision.gameObject.tag == "Murs")
        {
            animator.SetBool("EnCollisionAvecUnMur", false);
        }
    }
}

Well sorry for the mess.
My intention is to symplify all that, with a better and clearer scripts.
thanks for you patience :slight_smile:

(if needed, few translations :slight_smile:

  • murs = walls
  • MursGrimpables = climbing walls
  • EnCollisionAvecUnMurGrimpable = Collision with a climbing walls

I tried to simplify my code in one script, translated in englis, the best i could :

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

public class Joueur : MonoBehaviour
{
    // References
    public GameObject player;
    public Animator animator;
    public Rigidbody2D rigidbody2D;
    public Transform groundCheck;              
    public Transform ceilingCheck;             
    public LayerMask whatIsGround;             
    public Collider2D crouchDisableCollider;    // When player would crouch, the collider of the top of the body would be disabled.
    public Transform NewPosition;               // Child to the player, at his feet at idle position / at his hand in last frame of climbing ledge animation

    // variables
    public float speed = 40.0f;                
    public float crouchSpeed = 10.0f;             
    public float verticalJumpForce = 3400.0f;      
    private float horizontalInput = 0.0f;          
    const float groundedRadius = .2f;           // Radius of the overlap circle to determine if grounded
    const float ceilingRadius = .2f;            // Radius of the overlap circle to determine if the player can stand up
    private float fallingTime = 0f;             // used to kill the player if he's falling too long
    public float deadlyHeight = 3f;             // >the player dies

    // bools
    private bool facingRight = true;             
    public bool isGrounded = true;             
    public bool isWalking = false;
    public bool isJumping = false;
    public bool isCrouching = false;
    public bool canHang = false;                // when the player collides a climbable wall, allows to climb
    public bool isHanging = false;              // when the player is hanging to a ledge
    public bool isDead = false;

    //
    [Header("Events")]
    [Space]
    public UnityEvent OnLandEvent;
    [System.Serializable]
    public class BoolEvent : UnityEvent<bool> { }
    public BoolEvent OnCrouchEvent;
    private bool wasCrouching = false;


    //-------------------------------------------------------------------------------------------------//


    private void Awake()
    {
        rigidbody2D = GetComponent<Rigidbody2D>();

        if (OnLandEvent == null)
            OnLandEvent = new UnityEvent();

        if (OnCrouchEvent == null)
            OnCrouchEvent = new BoolEvent();
    }


    //-------------------------------------------------------------------------------------------------//


    void Update()  
    {
        if (isGrounded && !isDead)
        {
            // COMMANDES (Inputs)

            // BOUTON AVANCER
            horizontalInput = Input.GetAxisRaw("Horizontal") * speed;
            if (horizontalInput > 0 || horizontalInput < 0)
            {
                isWalking = true;
            }
            else if (horizontalInput == 0)
            {
                isWalking = false;
            }

            // BOUTON SE BAISSER
            if (Input.GetButtonDown("Crouch"))
            {
                isCrouching = true;
                speed = crouchSpeed;
            }
            else if (Input.GetButtonUp("Crouch"))
            {
                isCrouching = false;
                ResetSpeed();
            }


            // BOUTON SAUT
            if (Input.GetButtonDown("Jump"))
            {
                Jump();
            }


            // S'ACCROCHER A UN REBORD
            if (canHang && Input.GetButtonDown("Jump"))
            {
                Hang();
            }

        }


        if (!isDead)
        {
            // BOUTON ESCALADER UN REBORD
            if (isHanging && Input.GetButtonDown("Up"))
            {
                Climb();
            }


            // MORT : CHUTE MORTELLE
            //DeadlyFall();
        }


    }


    //-------------------------------------------------------------------------------------------------//


    void FixedUpdate()  // Moving a Rigidbody only in fixedUpdate
    {
        // GROUNDED
        bool wasGrounded = isGrounded;
        isGrounded = false;

        // On sait que le joueur touche le sol, "isGrounded", quand le gameObject groundCheck qui lui est parenté au niveau de ses pieds avec un collider, touche tout ce qui est identifié par calque comme étant le sol       
        Collider2D[] colliders = Physics2D.OverlapCircleAll(groundCheck.position, groundedRadius, whatIsGround);
        for (int i = 0; i < colliders.Length; i++)
        {
            if (colliders[i].gameObject != gameObject)
            {
                isGrounded = true;
                if (!wasGrounded)
                    OnLandEvent.Invoke();
            }
        }


        if (!isDead && isGrounded)
        {
            // AVANCER
            transform.Translate(horizontalInput * Time.deltaTime, 0, 0);
            animator.SetFloat("Mouvement", Mathf.Abs(horizontalInput));


            // DIRECTION
            if (horizontalInput > 0 && !facingRight)
            {
                Flip();
            }
            else if (horizontalInput < 0 && facingRight)
            {
                Flip();
            }


            // SE BAISSER
            if (isCrouching)
            {
                if (!wasCrouching)
                {
                    wasCrouching = true;
                    OnCrouchEvent.Invoke(true);
                }

                // Disabled the collider when crouching
                if (crouchDisableCollider != null)
                    crouchDisableCollider.enabled = false;
            }
            else
            {
                // enable the collider when done crouching
                if (crouchDisableCollider != null)
                    crouchDisableCollider.enabled = true;

                if (wasCrouching)
                {
                    wasCrouching = false;
                    OnCrouchEvent.Invoke(false);
                }
            }

            if (!isCrouching)
            {
                // if there is a ceiling, the player can't get up and stay crouched
                if (Physics2D.OverlapCircle(ceilingCheck.position, ceilingRadius, whatIsGround))
                {
                    isCrouching = true;
                }
            }
        }
    }


    //-------------------------------------------------------------------------------------------------//


    void Flip() // Fonction pour se retourner
    {
        // Switch the way the player is labelled as facing.
        facingRight = !facingRight;

        // flip the player by *-1
        Vector3 theScale = transform.localScale;
        theScale.x *= -1;
        transform.localScale = theScale;
    }


    //-------------------------------------------------------------------------------------------------//


    public void OnLanding() // Landing after a jump
    {
        isJumping = false;
        animator.SetBool("Saute", false);
    }


    //-------------------------------------------------------------------------------------------------//


    public void OnCrouching(bool isCrouching) // stay crouching
    {
        animator.SetBool("SeBaisse", isCrouching);
    }


    //-------------------------------------------------------------------------------------------------//


    private void ResetSpeed()
    {
        speed = 40.0f;
    }


    //-------------------------------------------------------------------------------------------------//

    private void Jump()
    {
        isJumping = true;
        rigidbody2D.AddForce(new Vector2(0f, verticalJumpForce)); // insuffle une force verticale au rigidbody du joueur
        animator.SetBool("Saute", true);
    }


    //-------------------------------------------------------------------------------------------------//

    private void Hang()
    {
        isJumping = false;
        isHanging = true;
        canHang = false;
        speed = 0f;
        verticalJumpForce = 0f;
        animator.SetBool("PeutSaccrocher", true);
        Hanged();
    }


    //-------------------------------------------------------------------------------------------------//

    private void Hanged()
    {
        speed = 0f;
        verticalJumpForce = 0f;
        animator.SetBool("EstAccroche", true);
    }


    //-------------------------------------------------------------------------------------------------//

    private void Climb()
    {
        speed = 0f;
        verticalJumpForce = 0f;
        canHang = false;
        isHanging = false;
        animator.SetBool("Monte", true);
    }


    //-------------------------------------------------------------------------------------------------//

    private void HasClimbed()
    {
        Debug.Log("Has Climbed");
        animator.SetBool("Monte", false);
        animator.SetBool("EstAccroche", false);
        animator.SetBool("PeutSaccrocher", false);
        player.transform.position = NewPosition.transform.position;
        canHang = false;
       
    }



    //-------------------------------------------------------------------------------------------------//

    // COLLISIONS


    void OnCollisionEnter2D(Collision2D collision)
    {
        /// Pour se suspendre aux plateformes       
        if (collision.gameObject.tag == "MursGrimpables")
        {
            canHang = true;
            animator.SetBool("EnCollisionAvecUnMurGrimpable", true);

            //when hitting a wall, player's speed is 0, he stop walk against the wall. but if the wall is climbable, he can climb
            speed = 0f;

            // after a while his speed get back, he could walk further
            Invoke("ResetSpeed", 0.05f);
        }

        if (collision.gameObject.tag == "Murs")
        {
            animator.SetBool("EnCollisionAvecUnMur", true);

            // the same with a simple wall
            speed = 0f;

            //
            Invoke("ResetSpeed", 0.05f);
        }
    }

    void OnCollisionExit2D(Collision2D collision)
    {
        if (collision.gameObject.tag == "MursGrimpables")
        {
            canHang = false;
            animator.SetBool("EnCollisionAvecUnMurGrimpable", false);
        }

        if (collision.gameObject.tag == "Murs")
        {
            animator.SetBool("EnCollisionAvecUnMur", false);
        }
    }

    void OnCollisionStay2D(Collision2D collision)
    {
        if (collision.gameObject.tag == "EauMortelle") // player drowns if he touches deadly Water
        {
            speed = 0f;
            isDead = true;
            animator.SetBool("MortNoyade", true);
        }

        if (collision.gameObject.tag == "Sols" && isDead) // player dies if he falls from too high
        {
            animator.SetBool("ChuteMortelle", true);
        }

    }

    //-------------------------------------------------------------------------------------------------//

    // DEATHS

    void DeadlyFall()
    {
        if (isGrounded)
        {
            fallingTime = 0f;
        }

        if (!isGrounded)
        {
            fallingTime += Time.deltaTime;
        }

        if (fallingTime >= deadlyHeight)
        {
            isDead = true;
            Debug.Log("Chute Mortelle !");
        }
    }


    //-------------------------------------------------------------------------------------------------//

    private void Drowning()
    {
        animator.speed = 0f;
        speed = 0f;
        isDead = true;
    }


    //-------------------------------------------------------------------------------------------------//

}

I try something different :
i notice that if i remove my second collider, if there is just one collider on my player, the collisions works perfectly.

so i changed the way worked my “crouching” system : instead of disable/enable a second collider placed over the upperbody when crouching, (to be able to go under some obstacles), it would resize the collider.
i found the solution quite simple, but i don’t know why 2 colliders on the same objects don’t work since i read taht now unity can handle with 20ish colliders on the same object… ?

anyway, what you think of this solution ? (resizing the collider and keep only one)

another thing i’m not sure about :
am i right to do multiply the collision condition under the same void OnCollisionEnter2D / OnCollisionExit2D ?

exemple :

void OnCollisionEnter2D(Collision2D collision)
    {
        /// Pour se suspendre aux plateformes       
       
        if (collision.gameObject.tag == "MursGrimpables")
        {           
            canHang = true;           
            animator.SetBool("EnCollisionAvecUnMurGrimpable", true);

            //Quand le joueur touche un mur, sa vitesse tombe à 0. Mais pour repartir, il faut que cette valeur ne soit pas gelée. d'où l'ajout d'une durée avec la methode 'invoke' juste après
            speed = 0f;

            // attend un moment avant de revenir à la valeur définie par le private void plus bas
            Invoke("ResetSpeed", 0.05f);
        }

        if (collision.gameObject.tag == "Murs")
        {           
            animator.SetBool("EnCollisionAvecUnMur", true);

            //Quand le joueur touche un mur, sa vitesse tombe à 0. Mais pour repartir, il faut que cette valeur ne soit pas gelée. D'où l'ajout d'une durée avec la methode 'invoke' juste après ->
            speed = 0f;

            // Attend un moment avant de revenir à la valeur définie par le private void plus bas Reset_runSpeed
            Invoke("ResetSpeed", 0.05f);
        }

        if (collision.gameObject.tag == "EauMortelle")
        {
            speed = 0f;
            isDead = true;
            animator.SetBool("MortNoyade", true);
        }

    }

or should i instead write it like this :

void OnCollisionEnter2D(Collision2D collision)
{
    /// Pour se suspendre aux plateformes       

    if (collision.gameObject.tag == "MursGrimpables")
    {
        canHang = true;
        animator.SetBool("EnCollisionAvecUnMurGrimpable", true);

        //Quand le joueur touche un mur, sa vitesse tombe à 0. Mais pour repartir, il faut que cette valeur ne soit pas gelée. d'où l'ajout d'une durée avec la methode 'invoke' juste après
        speed = 0f;

        // attend un moment avant de revenir à la valeur définie par le private void plus bas
        Invoke("ResetSpeed", 0.05f);
    }
}

void OnCollisionEnter2D(Collision2D collision)
{
    if (collision.gameObject.tag == "Murs")
    {
        animator.SetBool("EnCollisionAvecUnMur", true);

        //Quand le joueur touche un mur, sa vitesse tombe à 0. Mais pour repartir, il faut que cette valeur ne soit pas gelée. D'où l'ajout d'une durée avec la methode 'invoke' juste après ->
        speed = 0f;

        // Attend un moment avant de revenir à la valeur définie par le private void plus bas Reset_runSpeed
        Invoke("ResetSpeed", 0.05f);
    }
}

void OnCollisionEnter2D(Collision2D collision)
{
    if (collision.gameObject.tag == "EauMortelle")
    {
        speed = 0f;
        isDead = true;
        animator.SetBool("MortNoyade", true);
    }

}

Thanks !