prime31 / CharacterController2D

1.08k stars 248 forks source link

Collision issues when character collides with corner of platform #6

Closed RyanNielson closed 10 years ago

RyanNielson commented 10 years ago

I have the CharacterController2D script on a character. In my test level I have some 2d colliders that are set as the ground. If I jump diagonally into a corner of the collider with my character, my character clips through the platform like in the picture attached. I'm assuming this is because rays are sent out vertically and horizontally, and I'm hitting right on my character's corner.

Blue block on the right jumped and corner hit the platformer, causing him to clip through and get stuck within the platform. capture

RyanNielson commented 10 years ago

@prime31 Tagging you in case you aren't watching the repo. Have any good solutions to this one? Kinda makes it a no go for me in the game I'm working on.

prime31 commented 10 years ago

If you up your horizontal and vertical ray count it should remedy that. Just play around with the numbers to get the right amount. Once Unity gets us proper sweep tests (hopefully in 4.4) this will be handled without the extra rays.

Mike

On Fri, Dec 20, 2013 at 8:18 PM, Ryan Nielson notifications@github.comwrote:

@prime31 https://github.com/prime31 Tagging you in case you aren't watching the repo. Have any good solutions to this one? Kinda makes it a no go for me in the game I'm working on.

— Reply to this email directly or view it on GitHubhttps://github.com/prime31/CharacterController2D/issues/6#issuecomment-31056444 .

RyanNielson commented 10 years ago

Ya, I upped it to 20 horizontal and 20 vertical rays and I can still produce it consistently. Bummer.

prime31 commented 10 years ago

One other quicky solution you can do is add a diagonal ray check in the directly of movement before the horizontal and vertical ones. That should catch the edge cases.

Mike

On Fri, Dec 20, 2013 at 8:32 PM, Ryan Nielson notifications@github.comwrote:

Ya, I upped it to 20 horizontal and 20 vertical rays and I can still produce it consistently. Bummer.

— Reply to this email directly or view it on GitHubhttps://github.com/prime31/CharacterController2D/issues/6#issuecomment-31056609 .

RyanNielson commented 10 years ago

Ya, I'll have to give that a go and see how it works. Thanks.

prime31 commented 10 years ago

Do you happen to have a simple repro project you can send my way? If so, send it over to mike (at) prime31.com

RyanNielson commented 10 years ago

I can try to set something up this evening and send it your way. — Sent from Mailbox for iPhone

On Mon, Dec 23, 2013 at 2:02 PM, Mike notifications@github.com wrote:

Do you happen to have a simple repro project you can send my way? If so, send it over to mike (at) prime31.com

Reply to this email directly or view it on GitHub: https://github.com/prime31/CharacterController2D/issues/6#issuecomment-31133798

RyanNielson commented 10 years ago

@prime31 Sent, hopefully you can repro it and find a decent solution.

prime31 commented 10 years ago

So, a quick and dirty fix for the issue until we get some more Physics2D methods exposed by Unity is to just add the following method to the CharacterController2D class:

private void performDiagonalChecks( ref Vector3 deltaMovement )
{
    Vector2 ray;

    // figure out which ray origin to use based on movement direction
    if( deltaMovement.x > 0 && deltaMovement.y > 0 )
        ray = new Vector2( _raycastOrigins.topRight.x, _raycastOrigins.topRight.y );
    else if( deltaMovement.x > 0 && deltaMovement.y < 0 )
        ray = new Vector2( _raycastOrigins.bottomRight.x, _raycastOrigins.bottomRight.y );
    else if( deltaMovement.x < 0 && deltaMovement.y > 0 )
        ray = new Vector2( _raycastOrigins.topLeft.x, _raycastOrigins.topLeft.y );
    else
        ray = new Vector2( _raycastOrigins.bottomLeft.x, _raycastOrigins.bottomLeft.y );

    DrawRay( ray, new Vector3( ray.x, ray.y, 0 ) + deltaMovement, Color.white );
    _raycastHit = Physics2D.Raycast( ray, deltaMovement, deltaMovement.magnitude, platformMask & ~oneWayPlatformMask );
    if( _raycastHit )
    {
        deltaMovement.x = _raycastHit.point.x - ray.x;
        deltaMovement.y = _raycastHit.point.y - ray.y;
    }
}

Then add the 2 lines in the screenshot below to the move method.

screen shot 2013-12-24 at 9 25 56 am

RyanNielson commented 10 years ago

Awesome, thanks! Seems to get rid of the clipping issue. Only problem that exists because of this code, is when my character jumps while moving left or right into a wall he doesn't jump the normal height.

You can repro this by holding the left key when you're touching a wall, and jumping.

prime31 commented 10 years ago

Aha. Good point. An extra check will be needed in there to account for when moving left/right while already having a collision on the left/right. In those cases the diagonal check should be skipped.

Mike

On Tue, Dec 24, 2013 at 9:45 AM, Ryan Nielson notifications@github.com wrote:

Awesome, thanks! Seems to get rid of the clipping issue. Only problem that exists because of this code, is when my character jumps while moving left or right into a wall he doesn't jump the normal height.

You can repro this by holding the left key when you're touching a wall, and jumping.

Reply to this email directly or view it on GitHub: https://github.com/prime31/CharacterController2D/issues/6#issuecomment-31180226

savethejets commented 10 years ago

Just for anyone else, I was having trouble getting prime31's last fix comment to work. I think I have it working so here is the code that I modified if it helps anyone...

    public void move( Vector3 deltaMovement )
    {
        // save off our current grounded state
        var wasGroundedBeforeMoving = collisionState.below;
        var wasCollidingLeftOrRight = collisionState.left || collisionState.right;

        // clear our state
        collisionState.reset();

        var desiredPosition = transform.position + deltaMovement;
        primeRaycastOrigins( desiredPosition, deltaMovement );

        // first we check movement in the horizontal dir
        if( deltaMovement.x != 0 )
            moveHorizontally( ref deltaMovement );

        // next, check movement in the vertical dir
        if( deltaMovement.y != 0 )
            moveVertically( ref deltaMovement );

        if( deltaMovement.x != 0 && deltaMovement.y != 0 && !wasCollidingLeftOrRight)
            performDiagonalChecks( ref deltaMovement );

        // move then update our state
        if( usePhysicsForMovement )
        {
            rigidbody2D.velocity = deltaMovement / Time.fixedDeltaTime;
            velocity = rigidbody2D.velocity;
        } 
        else 
        {
            transform.Translate( deltaMovement );
            velocity = deltaMovement / Time.deltaTime;
        }

        // set our becameGrounded state based on the previous and current collision state
        if (!wasGroundedBeforeMoving && collisionState.below) {
            collisionState.becameGroundedThisFrame = true;          
        }
    }
prime31 commented 10 years ago

Just in case you havent seen it yet, I believe the latest commit should avoid any and all issues with corner penetration: https://github.com/prime31/CharacterController2D/commit/b6fc72f2bbc8f774d854c44d1283608d731fca51

RyanNielson commented 10 years ago

@prime31 Oh neat, thanks for the update. I'll have to take a look at it again.

savethejets commented 9 years ago

I dug into this more since I was still getting this issue...

If you're using Tk2dTilemaps, the way Tk2d creates the physics colliders is by creating a bunch of edge colliders.

Unfortunately if the CharacterController falls in between the edge colliders there is no "volume" to speak of and the ray casts will treat the inside of the edge as the ground.

I would say this is actually a flaw with tk2d and not with the CharacterController.

Another way would be for tk2d to use polygon or box colliders instead. But looking on the tk2d forums it seems like the author is against that for performance reasons. Any thoughts on a way to prevent this with our ray casts?

prime31 commented 9 years ago

Perhaps doing a diagonal raycast from the forward corner's position before movement to its position after movement would do the trick.

Mike

On Jan 29, 2015, at 10:53 PM, Kyle Reczek notifications@github.com wrote:

I dug into this more since I was still getting this issue...

If you're using Tk2dTilemaps the way Tk2d creates the physics colliders is by creating a bunch of edge colliders.

Unfortunately if the CharacterController falls in between the edge colliders there is no "volume" to speak of and the ray casts will treat the inside of the edge as the ground.

I would say this is actually a flaw with tk2d and not with the CharacterController.

Any thoughts on a way to prevent this with our ray casts? Another way would be for tk2d to use polygon or box colliders instead...

— Reply to this email directly or view it on GitHub.