Search Unity

  1. Megacity Metro Demo now available. Download now.
    Dismiss Notice
  2. Unity support for visionOS is now available. Learn more in our blog post.
    Dismiss Notice

Object wont fall when i apply horizontal velocity and is colliding with wall.

Discussion in 'Scripting' started by xandermacleod, Jul 14, 2012.

  1. xandermacleod

    xandermacleod

    Joined:
    Apr 27, 2012
    Posts:
    17
    Hi there.

    Im currently trying to create a simple 2d platforming game, and im having a little trouble with my character when he collides with another object mid-fall/mid-jump. He is jumping, falling and walking sideways like i want him to. However, whenever he jumps into a wall or falls into a wall, if I hold down the 'walk left' or 'walk right' button (i.e. keep trying to get him to walk sideways into that wall), he freezes in mid air. Could anyone tell me how I might fix this?

    Here is some of my script (Javascript):

    Code (csharp):
    1.  
    2. public var horizontal_speed : float = 5.0f;
    3. public var jump_Power : float = 15.0f;
    4.  
    5. private var directionFacing : int = 1;
    6. private var isJumping : boolean = false;
    7.  
    8. public function Update () : void {
    9.  
    10.     //Horizontal Movement
    11.     if (Input.GetButton("Horizontal")) {
    12.         // for directionFacing variable -----
    13.         //            1 = character is facing right
    14.         //            -1 = character is facing left
    15.         directionFacing = Input.GetAxis("Horizontal") < 0 ? -1 : 1;
    16.         rigidbody.velocity = new Vector3((directionFacing * horizontal_speed), rigidbody.velocity.y, 0);
    17.     }
    18.    
    19.     //If character isn’t jumping
    20.     if (!isJumping) {  
    21.         //Jump
    22.         if (Input.GetButton("Jump")) {
    23.             isJumping = true;
    24.             rigidbody.velocity = new Vector3(rigidbody.velocity.x, jump_Power, 0);
    25.         }
    26.     }
    27. }
    28.  
    Please note: I later on in this script have a section where "isJumping" is turned back to being false, but it involves a lot of raycast detection stuff that i dont think is relevant. If however you would like me to post that as well, Im more than happy to do so.

    Currently the character has a box collider (not trigger) and a rigidbody. It is using gravity and 'is kinematic' is off. Constraints are set to freeze all rotation and z translation. Mass = 1. Walls and floor are also using box colliders (not trigger) - no rigidbodies on these.

    I'm assuming when I hold down left or right the amount of velocity i am applying to the character is overpowering the effect of gravity, but i dont know how to stop this effect (plus it doesnt make sense to me that this should happen).

    Can anyone please help? I dont mind if someone can suggest a C# solution as well, as it looks like it should be a fairly simple issue.
     
    KpyaccaH likes this.
  2. john-essy

    john-essy

    Joined:
    Apr 17, 2011
    Posts:
    464
    why do you have the : void? i have never seen that before it knows it is of type void
     
  3. xandermacleod

    xandermacleod

    Joined:
    Apr 27, 2012
    Posts:
    17
    I do a lot of scripting around people from more C orientated backgrounds with slightly anal coding standards. Like you say, it's not at all of value; but it makes things 'friendlier' for them when they come to read it .... apparently. But yeh, it doesnt add anything.
     
  4. Myx

    Myx

    Joined:
    Nov 29, 2010
    Posts:
    196
    Hello there!

    In my experience the Unity physics seems to be reactive as opposed to proactive. Objects are allowed to end up inside eachother, and when they do forces are applied to make sure they'll get out of eachother. Therefor when you directly modify the rigidbody's velocity you counter-act the physics attempt of making your object leave the thing it's collided with.

    This appears to cause problems with walking into walls, as long as you're inside the wall physics is bound to behave a bit odd. Hence as long as your keep applying the velocity towards the wall, you will stay inside the wall and have unwanted effects on your physics.

    The way I've solved this in previous projects is by implementing a proactive physics check. That is to say, see if your wanted movement will result in a collision and if so prohibit that movement.

    Code (csharp):
    1.  
    2. private Rigidbody mBody;
    3.  
    4. void Awake()
    5. {
    6.     mBody = rigidbody;
    7. }
    8.  
    9. void FixedUpdate()
    10. {
    11.     // Get the velocity
    12.     Vector3 horizontalMove = mBody.velocity;
    13.     // Don't use the vertical velocity
    14.     horizontalMove.y = 0;
    15.     // Calculate the approximate distance that will be traversed
    16.     float distance =  horizontalMove.magnitue * Time.fixedDeltaTime;
    17.     // Normalize horizontalMove since it should be used to indicate direction
    18.     horizontalMove.Normalize();
    19.     RaycastHit hit;
    20.  
    21.     // Check if the body's current velocity will result in a collision
    22.     if(mBody.SweepTest(horizontalMove, out hit, distance))
    23.     {
    24.         // If so, stop the movement
    25.         mBody.velocity = new Vector3(0, mBody.velocity.y, 0);
    26.     }
    27. }
    28.  
    That's a rough implementation of the concept, hopefully enough to give you a general understanding of what I mean.
     
    victor_RSQ and MyDarkEvilTwin like this.
  5. xandermacleod

    xandermacleod

    Joined:
    Apr 27, 2012
    Posts:
    17
    thanks for this solution, I may end up having to use it. I actually discovered another little extra thing on this topic. Turns out that Unity's default physics material on all objects has friction settings of 0.4, if you create a new physics material and apply this to your walls then adjust the friction to be 0, then the problem will disappear. HOWEVER. Your floors need to still have some kind of friction material on them, otherwise your character will skid. The problem will consequently still exist if you fall off a floor platform then try to walk mid-air into its side. Another long winded workaround here might eb to divide up your floor into 3 separate meshes: the main floor with default physics material, and a separate mesh for either end of the platform with your new frictionless physics material applied.
     
    KpyaccaH and juanimumbach like this.
  6. aorata

    aorata

    Joined:
    Feb 10, 2012
    Posts:
    28
    @xandermacleod

    The material solution you mentioned worked fine for me. I have no issues with the drag because I am stopping the moving object with rigidbody.velocity. I have no need for drag. I'm so happy I found this thread!
     
  7. frankrs

    frankrs

    Joined:
    Aug 29, 2009
    Posts:
    300
    I see I'm not the only one who got "Stuck" on this issue, lol. I just changed the Interpolate variable on my characters rigidbody2d component to Extrapolate and that worked like a charm for me.
     
  8. cranky

    cranky

    Joined:
    Jun 11, 2014
    Posts:
    180
    Two things: set the player collider physics material to have 0 dynamic and static friction coefficient. Also set it to minimum instead of average.

    Next: do not set velocity manually. Use Rigidbody.AddForce(). Say your target velocity is a variable named targetSpeed. You'd do: rigidbody.AddForce(targetSpeed - rigidbody.velocity, ForceMode.Velocity). By subtracting target speed by the current velocity, this does the same thing as changing the velocity directly without the flaws of doing so. This also fixes your sliding problem, since if targetSpeed = 0,0,0 (the player isn't touching the controls), the net result of the function sets rigidbody.velocity to 0.
     
  9. axmoran

    axmoran

    Joined:
    Jan 24, 2016
    Posts:
    1
    For another 2D example, I created a few variables which would start the raycast at the boundary of the player on left and right, with a sufficiently small raycast max distance. Set velocity to 0 if you hit the wall. Additionally, I add a small amount to the boundary in order to give a bit of tolerance to the collision detection. I'm writing th

    I'm sure i'll eventually get something cleaner, but this works for now until i need something with a bit more elegance. Also note that my comments are from my code, so some of that may not be clear out of context, so apologies in advance.


    Code (CSharp):
    1.      
    2.         //Create a box to start raycast outside of.  Keeps hitting player instead if we do not do this.
    3.         Vector2 boxVectorStartRight = new Vector2(body2D.position.x + boxSize.size.x/2+ 0.05f, body2D.position.y);
    4.         Vector2 boxVectorStartLeft = new Vector2(body2D.position.x + -boxSize.size.x/2 - 0.05f, body2D.position.y);
    5.  
    6.  
    7.         if (inputState.moveRight) {
    8.  
    9.             //Set velocity to zero if we hit a boundary.  fixes speed thing.  Might want to slow descent in the future.  Allow to move normally otherwise.
    10.             if (Physics2D.Raycast (boxVectorStartRight,  -Vector2.right, 0.001f))
    11.                 playerVelocity = 0;
    12.             else
    13.                 playerVelocity = 20f;
    14.             //playerVelocity = Acceleration (playerVelocity, accelerationBase);
    15.      
    16.      
    17.         }
    18.  
     
  10. James2Games

    James2Games

    Joined:
    May 2, 2016
    Posts:
    23
    Although this may be an old topic and I found the above posts to be very helpful; but they didn't quite work for what I wanted in 2D.

    This method should be used at the end of FixedUpdate. Then it will stop the rigid body from clinging clinging to walls as well as stopping the user from being unable to jump when on the floor.


    Code (CSharp):
    1.     private void FinalCollisionCheck()
    2.     {
    3.         // Get the velocity
    4.         Vector2 moveDirection = new Vector2(rigidBody.velocity.x * Time.fixedDeltaTime, 0.2f);
    5.      
    6.         // Get bounds of Collider
    7.         var bottomRight = new Vector2(playerCollider.bounds.max.x, player.collider.bounds.max.y);
    8.         var topLeft = new Vector2(playerCollider.bounds.min.x, player.collider.bounds.min.y);
    9.  
    10.         // Move collider in direction that we are moving
    11.         bottomRight += moveDirection;
    12.         topLeft += moveDirection;
    13.              
    14.         // Check if the body's current velocity will result in a collision
    15.         if (Physics2D.OverlapArea(topLeft, bottomRight, EnvironmentLayer))
    16.         {
    17.             // If so, stop the movement
    18.             rigidBody.velocity = new Vector3(0, rigidBody.velocity.y, 0);
    19.         }
    20.     }
    Note:
    EnvironmentLayer is the layer of which we want to walk on. Floors, Walls... etc
    The layer I used was 1 << 9 as the Environment is considered layer 9
     
    Jroel likes this.
  11. Alex23087

    Alex23087

    Joined:
    Jul 4, 2016
    Posts:
    1
    Ok, in this way we can avoid this behaviour. But what if we need to also move the object that would have collided with the player?
     
  12. BradyIrv

    BradyIrv

    Joined:
    Jan 16, 2018
    Posts:
    12

    If you do this and update the player velocity.x based on input, then the next frame will render the velocity.x above zero because the distance calculated will be 0. So you are going to ping pong between dropping and pushing against the wall.

    If it's a 2D game, then you're better off not calculating the position of the character on the next frame and just running a SweepTest() based on the horizontal input and the (+/-)Vector.right. Then use an arbitrary distance value. For 3D I'm not sure how you would approach this issue.

    Code (CSharp):
    1. // We are going to hit a wall next frame if we keep moving in this direction
    2.         if (rb.SweepTest((Mathf.Sign(HorizontalInput) * Vector3.right), out hit, 0.3f))
    3.         {
    4.             // Nuke horizontal velocity
    5.             rb.velocity = new Vector3(0, rb.velocity.y, 0);
    6.         }
     
  13. Kathlar

    Kathlar

    Joined:
    Nov 7, 2017
    Posts:
    6
    Great stuff, thanks!
     
  14. dailong

    dailong

    Joined:
    Jul 18, 2019
    Posts:
    6
    In 2d game, i have a solution that is detect collision combine with on ground checking, if true set velocity horizontal to 0 or movement horizontal to 0.


    private bool m_isCollided;

    private void OnCollisionEnter2D(Collision2D other) {
    m_isCollided = true;
    }
    void OnCollisionExit2D(Collision2D other) {
    m_isCollided = false;
    }

    void Update() {
    float horizontalMove = Input.GetAxis("Horizontal");

    // Setting movement if was collided in the air or not
    if (!controller.isGrounded && m_isCollided) {
    horizontalMove = 0;
    }
    // my Move function is normal change rigidbody2d velocity;
    controller.Move(horizontalMove);
    }
     
    II-Games likes this.
  15. palex-nx

    palex-nx

    Joined:
    Jul 23, 2018
    Posts:
    1,748
    That's how friction works. Have you tried frictionless physics material?
     
    XazeRekt likes this.
  16. Adonis28

    Adonis28

    Joined:
    Mar 13, 2018
    Posts:
    1
    for me the solution was to simply create a Material2D with friction equals to zero.