Search Unity

Platformer with physics character too slidey, how to reduce this? adding friction = fail.

Discussion in '2D' started by codejoy, Mar 17, 2015.

  1. codejoy

    codejoy

    Joined:
    Aug 17, 2012
    Posts:
    204
    I am building a platformer and so far things are okay, but after some play testing players think when the player lands from a jump they slide too much. I thought I could add a 2d physics material to increase the friction of the ground. Turns out though the way I move the character breaks very easily if I do that too much....is there another approach I am missing?

    I move the character by FixedUpdate using this:

    //standing is a variable true if on ground false if in air. speed and airSpeedMultiplier are:



    Code (CSharp):
    1.     public float speed = 30f;
    2.     public float jetSpeed = 45f;
    3.     public float airSpeedMultiplier = .3f;
    4.     public Vector2 maxVelocity = new Vector2(3, 5);
    5.  
    6.  
    7. if (controller.moving.x!=0)
    8.  {
    9.  
    10.           if(absVelX<maxVelocity.x)
    11.          {
    12.              forceX= standing ? speed * controller.moving.x : (speed * controller.moving.x*airSpeedMultiplier);  
    13.          }
    14.   . .. more code etc...
    15.    code to see if the controllers in the y direction and if so add a forceY up for the jetpacking...
    16.  
    17.  
    18. }
    19.  
    So if the character is on solid ground speed * the direction they are moving is what forceX is when this is done at the end:

    rigidbody2D.AddForce (new Vector2 (forceX, forceY));

    If in the air from a jump they can control it (really a lot its a jetpack) its just when they land they slide too much for players to feel comfy making a jump. Again I thought I could add a higher friction on the ground and /or the player but then the player moved slow if at all just by pressing right, and if I increased the speed it made it wonky... so I had to remove the friction to a saner level, but I am not sure if there is another approach I just don't know about (new to 2d unity).

    I read that for a platformer one shouldn't use the physics engine, its working okay but this slight issue I am hoping I could find a cool trick to resolve, I would love any ideas on some approaches to fix the player from sliding to much when coming in from a landing (think of it as a high arch jump)
     
  2. sleekdigital

    sleekdigital

    Joined:
    May 31, 2012
    Posts:
    133
    I wouldn't say that you "shouldn't" use physics. Its just that some aspects are more difficult when you use physics and you sometimes end up working against the physics a bit. The problem with friction is that it's always there. In your case, it sounds like you only want the friction when the player isn't trying to move. So one approach is to fake the friction yourself... when you want the player to stop sliding, add some force in the opposite direction of the current horizontal movement. In your case it might make sense to do that when there is no horizontal input and the player still has some velocity in the x direction. I suppose you could also try changing the friction value on the fly.. turn it up when there is no input, turn it down when there is. I've never tried that approach before tho.
     
    theANMATOR2b likes this.
  3. codejoy

    codejoy

    Joined:
    Aug 17, 2012
    Posts:
    204
    Thanks for your feedback! This gives some good ideas. Is there a quick function or just add a force that is -velocity in x direction if there is no horizontal input? That might be a hack. I also thought the minute they hit the ground to just stop them. But then i realized, my code can tell when they are "standing" and it is a total hack:

    Code (CSharp):
    1.     if (absVelY <=     0f) {
    2.      
    3.             standing = true;
    4.             isFlying=false;
    5.             maxFallSpeed=0;
    6.         } else {
    7.          
    8.             standing = false;
    9.             canRefuel=false;
    10.         }
    This works, mostly, though I have issues where I did up the physics on everything and now the walls (not the ground) like makes my character "stick" when falling and pushing against it making the pc thinking hes standing (guessing cause his absvel in y drops so low).

    I would have to do some fancy ray casting to make it where the standing is set only when he is really touching ground beneath his feet...and not say to the right of him up top or anything else correct? (touching the ground or some other stand-able game object)

    Am I right? That makes me worried implementing that might not go so smoothly how i have everything else set up.

    Another thing I might just use different box colliders for the walls (using 2d tool kit asset so that would mean another tilemap for walls, and a diff one for floors) and use different friction 2d physics materials). Hmmm love to hear any suggestions/recipes or ideas... I have no one else to bounce this stuff off of.
     
  4. sleekdigital

    sleekdigital

    Joined:
    May 31, 2012
    Posts:
    133
    Yes, it is common to use raycasts or linecasts to check if the player is grounded. The example 2d platformer project has an example of this as well as basic physics based movement.

    https://www.assetstore.unity3d.com/en/#!/content/11228

    Have a look at the PlayerController script in there. Keep in mind that they are using a specific layer to indicate what gameobjects are considered to be "ground".
     
  5. codejoy

    codejoy

    Joined:
    Aug 17, 2012
    Posts:
    204
    You are the man. I did notice the specific layers specifying ground, can I edit this line:

    Code (CSharp):
    1. standing = Physics2D.Linecast(transform.position, groundCheck.position, 1 << LayerMask.NameToLayer("Solid"));  
    To check more than one layer (I think I have to or that LayerMask somehow? is it possible?
     
  6. sleekdigital

    sleekdigital

    Joined:
    May 31, 2012
    Posts:
    133
    Yeah. What you can do is add a public property to your character controller script and set its type to LayerMask. Then in the inspector, you will get a nice dropdown where you can select which layers you wan to include. Then use that property in your linecast or raycast.
     
  7. codejoy

    codejoy

    Joined:
    Aug 17, 2012
    Posts:
    204
    Would I want this public property to be an array of LayerMasks since I do want more than one...
    Like I want my player to hit ground and some other things (ice block for instance) which is its own layer (for other purposes when it comes to the enemy).

    Like right now I just added:

    public LayerMask collideMask;


    not sure how I use that in this here:

    standing = Physics2D.Linecast(transform.position, groundCheck.position, 1 << LayerMask.NameToLayer("Solid"));

    when I need to do that test for more than one layer.. like if they are linecast colliding from Layer Solid OR also layer IceBlock etc
     
  8. sleekdigital

    sleekdigital

    Joined:
    May 31, 2012
    Posts:
    133
    You would use it as the last parameter of the Linecast call. A layermask can represent multiple layers with one value... that is why it is a "mask" and not an integer. You should notice in the inspector that it allows you to check off more than one layer.
     
  9. codejoy

    codejoy

    Joined:
    Aug 17, 2012
    Posts:
    204
    Ahhh so something like this:

    Code (CSharp):
    1. standing = Physics2D.Linecast(transform.position, groundCheck.position, 1 << collideMask);  
    Where collideMask is my LayerMask type? I am a dork too, I saw in the inspector the drop down and assumed it was a typical combo box, didn't know the check marks till you told me. I implemented the above line, it is acting like an AND not an OR. Or did I use the Linecast wrong?
     
  10. sleekdigital

    sleekdigital

    Joined:
    May 31, 2012
    Posts:
    133
  11. codejoy

    codejoy

    Joined:
    Aug 17, 2012
    Posts:
    204
    I got it, thanks! I was doing something stupid now I have all the layers and itw orks great...this is awesome I can really tinker then with the physics now I feel I have more control.
     
  12. MelvMay

    MelvMay

    Unity Technologies

    Joined:
    May 24, 2013
    Posts:
    11,497
  13. sleekdigital

    sleekdigital

    Joined:
    May 31, 2012
    Posts:
    133
    @MelvMay That is good to know. Any chance you can tell us how the performance would compare to a raycast or a linecast called in Update? I imagine the difference might be negligible in the grand scheme of things, but every little bit counts, right :)
     
  14. MelvMay

    MelvMay

    Unity Technologies

    Joined:
    May 24, 2013
    Posts:
    11,497
    Box2D maintains a list of contacts against each rigid-body anyway so the 'IsTouching' and 'IsTouchingLayers' are extremely fast; much faster than collision detection itself or performing line/ray/shape casts. In other words, when you ask if something is touching, the collision detection was already done anyway, you're just asking for the current results.

    Obviously this only helps you when you are in contact with something and currently only tells you if you are touching or not but know that in the near future, I'm going to extend this by providing the ability to check if a Rigidbody2D is touching (it will effectively check all colliders attached to it) as well as a new set of calls that allow you to return the existing contacts and get the contact information (Collision2D - same info that is passed to the OnCollision callbacks). This would mean you wouldn't need to keep this state using the callbacks but could query them whenever you liked as the info is always there in the engine ready.
     
  15. sleekdigital

    sleekdigital

    Joined:
    May 31, 2012
    Posts:
    133
    Good stuff! Thanks for the info.
     
  16. codejoy

    codejoy

    Joined:
    Aug 17, 2012
    Posts:
    204
    @MelvMay thanks for the info.

    @sleekdigital The more I thought about your one suggestion for fixing the game the more I am leaning to do it. Again the game is okay now but doesn't feel tight enough. With that raycast implemented I can now easily see when I am standing down on a ledge. I like your idea of adding force in the opposite direction when the player lets off the keys. I was curious if there was any good way to do this. Most i could figure is add some force in direction of travel *-1 on the horizontal. But I realized I want a tiny bit of motion forward when letting off just very little though. Not sure how I would calculate that force and figure out the direction travelling to add that force and make "the friction force" decay properly. Are there any recipes or common ways of doing this where I just don't know their names?
     
  17. sleekdigital

    sleekdigital

    Joined:
    May 31, 2012
    Posts:
    133
    You shouldn't have to do any fancy calculation if you don't mind a little trial and error. You could do something along these lines in your fixed update when you want the "friction" enabled...

    Code (CSharp):
    1. rb2D.AddForce( (rb2D.velocity* -1) * friction * Time.deltaTime, ForceMode.VelocityChange);
    ...make friction a property that you change in your inspector then you can try different values until you get what you like.

    Oh and use this to make the forcemode available...

    https://gist.github.com/bbuck/9717162
     
  18. codejoy

    codejoy

    Joined:
    Aug 17, 2012
    Posts:
    204
    oh cool never used an extension before, just add the cs file to my project somewhere and include it with a Using?
    then it automatically knows to use that AddForce instead of the original? Trial and error works for me :)
     
  19. sleekdigital

    sleekdigital

    Joined:
    May 31, 2012
    Posts:
    133
    Yeah, just add the cs file. No need for a using statement. It knows to use the extension method because the method signatures are different from the ones that already exist. These take the 3D force modes as the last param instead of the 2d forcemodes.