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

How to deal with characters at the corners of platforms?

Discussion in '2D' started by AllegroDigital, Jan 17, 2014.

  1. AllegroDigital

    AllegroDigital

    Joined:
    Jan 8, 2014
    Posts:
    4
    Hi there! I just started learning Unity two weeks ago, so I'm still pretty new to it all.

    After doing the Character Controller tutorial from the live training section, a problem that I'm having right now is dealing with a character at the edge of a platform.

    You can see the problem in action here: http://www.youtube.com/watch?&v=MxPzZkYirLw#t=66

    The basic problems are:
    1. If the ground detection is larger than the collider, then when the character is against the wall and jumps, the ground detection will spot the wall and say “hey, I'm still on the ground” and then it will keep the idle animation active until it jumps above the wall.
    2. If the ground detection is smaller than the collider, then when the character is standing on the corner of the ledge it will transition into a jump animation, but will still stay collided and on top of the ledge.
    3. If the ground detection is the same size as the collider, then we seem to get the exact same problem as number 2.
    4. The Unity tutorial seems to favour having a rounded collider so that when a character is at the edge it will slide off. I however don’t want an effect like this as it looks aesthetically unpleasing with pixel art... maybe if I use capsul colliders along the bottom so that there is a very slight roundedness instead of a larger one like what would happen with a circle?
    5. Is it recommended to change the collision box when I transition into other animations? If I had a narrower bounding box when it transitioned into the jump, then the character would just fall I imagine. Upon landing, would the new collider then solve the interpenetration and force the character away from the wall?
    6. How might I detect that I'm nearing an edge so that I might have an off-ballance style animation like what Sonic the Hedgehog uses (at least in Sonic 2)?
     
  2. zombiegorilla

    zombiegorilla

    Moderator

    Joined:
    May 8, 2012
    Posts:
    9,042
    Using the concept in mention in point 6, you could have have separate or additional colliders at the edge/corners that will let your character controller know it is on at the edge or near a wall and then just use that in determining how it will animate/react.

    Or flipping that around, you could have additional colliders on your character that are used like whiskers, that work as edge detectors. Say you have a very small collider on your character that is just in front and a few pixels below your main collider. You could use that to effectively determine "drop-offs" ahead of you.

    There was another thread about the Box 2d collision model where one of the devs pointed out that changing the scale of a collider during collision is problematic. Presumably swapping colliders would be as well.
     
  3. Invertex

    Invertex

    Joined:
    Nov 7, 2013
    Posts:
    1,539
    Use more complex detection. The way I have it setup is 3 Linecast's from the bottom for ground, and 3 from the side for walls.

    1. Are you sure you did the math correctly to find out the perfect value that is the edge of your collider? Just being close enough visually isn't always right. Get your collider size to a nice 2 decimal point or less number in width, divide it by 2 and offset your start and end points by that much, plus any distance the collider has been shifted. It's working fine for me.

    2. Use a 3 point system. If you're going to be landing on some really skinny objects, then for your middle point, have the linecast start from near the edge and be angled sharply inward towards you other edge, but still downward.

    4. That should be fine...

    5. It's not generally recommend, as you will have popping happen from interpenetration of colliders. But for the effect you want, yes that would work, although you could just do the same with Linecasts from the front that repel your character with AddForce() if they hit the wall. Much cleaner and bugfree method.

    6. The way they did it in Sonic is basically the way I'm suggesting. They had a really skinny collider though, so they only needed 2 linecasts/raycasts I think, but in this case 3 would be better. Basically, in your script you check to see if only the furthest back ray is active and hitting Ground, and if true, then you play the balance animation (but only if your rigidbody2D.velocity.x is low enough so you don't trigger it when merely running off an edge or landing on an edge from a long jump)
     
  4. AllegroDigital

    AllegroDigital

    Joined:
    Jan 8, 2014
    Posts:
    4
    I am able to get the problem of colliding whilst not grounded (therefore in a jump pose while on a corner) with and without "Pixel Snap" turned on, so that at least can be written out of the equation. My character is at 0,0,0, and a scale of 1,1,1 to start with, so it should only be a matter of matching the transforms of the ground check with that of the colliding box.

    grounded = Physics2D.OverlapCircle(groundCheck.position, groundRadius, whatIsGround); where groundRadius is 0.24f. The groundCheck is a child of the character. It's transforms are set to 0.04, -0.32, 0

    The Box Collider 2D has a Size of 0.48, 0.64. Since X is all we should care about, that should mean that the groundRadius is correctly set at 0.24. The box's Center is set to 0.04,0. Therefore, as best as I can tell, it's all set up accurately.

    That said, it must have something to do with floating point accuracy, because if I set my groundRadius to 0.2405f I cannot seem to reproduce the problem AND the jump animation intiates when standing next to the wall. 0.241f Will prevent the animation from triggering properly, and 0.24005 will still allow the jump pose when standing on a corner.


    As for the raycasting stuff, that seems to make sense for standing on edges to get the off balance animation. I'll have to look into that a bit more.
     
  5. Invertex

    Invertex

    Joined:
    Nov 7, 2013
    Posts:
    1,539
    Yeah, don't bother with using OverlapCircle to check the ground. Just use a LineCast. It works like this:

    Code (csharp):
    1. //your variables at top of script
    2.     const int maxReturnedIntersections = 1;
    3.     private RaycastHit2D[] hits = new RaycastHit2D[maxReturnedIntersections];
    4.         private int groundedR;
    5.         private int groundedM;
    6.         private int groundedL;
    7.         private bool isGrounded;
    8.  
    9. //Then in Update(), or you can create a custom function for better organization, called like, CheckCollision() and call CheckCollision(); in your update. But anyway:
    10. void Update()
    11. {
    12. //Check Collisions
    13. //We use the NonAlloc version of Linecast, since we're calling it every frame and this version won't allocate memory each time, it doesn't even use 0.01ms of processing time for one of these.
    14.         groundedR = Physics2D.LinecastNonAlloc(gndChkR_s.position, gndChkR_e.position,hits,(1 << LayerMask.NameToLayer("Ground")) | (1 << LayerMask.NameToLayer("PassthroughPlatform")));
    15.         groundedM = Physics2D.LinecastNonAlloc(gndChkM_s.position, gndChkM_e.position,hits,(1 << LayerMask.NameToLayer("Ground")) | (1 << LayerMask.NameToLayer("PassthroughPlatform")));
    16.         groundedL = Physics2D.LinecastNonAlloc(gndChkL_s.position, gndChkL_e.position,hits,(1 << LayerMask.NameToLayer("Ground")) | (1 << LayerMask.NameToLayer("PassthroughPlatform")));
    17.  
    18.         if ((groundedL > 0) || (groundedM > 0) || (groundedR > 0))
    19.         {
    20.             isGrounded = true;
    21.             anim.SetBool ("Grounded", true);
    22.         }
    23.         else
    24.         {
    25.             isGrounded = false;
    26.             anim.SetBool ("Grounded", false);
    27.         }
    28. }
    This makes it easy to create various Linecasts for detecting different types of surfaces as well, since you can just add more variables, like groundslopeL/M/R and change the LayerMask name to whatever you tagged Slopes as. The first two parts of LineCast, that have .position on them, are empty child objects of my Player which I have position where I want the start and endpoints of my Linecast to be. This makes testing a lot easier and clears up the code a bit too. To reference them, I just do a transform.Find() on Awake(). NOT in Update, that would be horrible for performance.

    But even still, you can still run into the wall detection issue. The best way to avoid that is to not use box colliders, but instead build your box out of Edge colliders. This way you can set the side edges to a Wall layer that your Linecast's won't detect, and the top ones to Ground. It will also allow you to avoid the ghost vertices collision issue with tiled box colliders, so you can move smoothly across the top with ease, using a square collider on your character. As explained in the link referenced here:
    http://forum.unity3d.com/threads/22...geCollider2D?p=1488478&viewfull=1#post1488478
     
  6. AllegroDigital

    AllegroDigital

    Joined:
    Jan 8, 2014
    Posts:
    4
    Ah thanks, I'm just noticing this response now. I'll check out the code in action as soon as I get a chance!