Git Product home page Git Product logo

Comments (2)

prime31 avatar prime31 commented on June 17, 2024

I can see how the edge colliders could cause the issue you are seeing. The fix looks like it should do the trick. I'll push the changes in a little bit. Feel free to give it a try when the fix goes live and let me know if it takes care of the issue for you.

from charactercontroller2d.

tm011064 avatar tm011064 commented on June 17, 2024

cheers. I tested the fix a bit and had to adjust it so the character doesn't lose velocity when bumping into another edgecollider edge. Rather than setting the character to the first raycast hit point, I now send another ray along the next edge with the remaining target length. Also, in order to do the raycast test properly, I had to pass in the initial ray cast origin from the horizontal move method:

// call from moveHorizontally(ref Vector3 deltaMovement)
if (handleHorizontalSlope(ref deltaMovement, Vector2.Angle(_raycastHit.normal, Vector2.up), _raycastHit.point))

// handleHorizontalSlope method
private bool handleHorizontalSlope(ref Vector3 deltaMovement, float angle, Vector2 horizontalRaycastHit)
{
// disregard 90 degree angles (walls)
if (Mathf.RoundToInt(angle) == 90)
{
return false;
}

// if we can walk on slopes and our angle is small enough we need to move up
if (angle < slopeLimit)
{
// we only need to adjust the deltaMovement if we are not jumping
// TODO: this uses a magic number which isn't ideal!
if (deltaMovement.y < jumpingThreshold)
{
// apply the slopeModifier to slow our movement up the slope
var slopeModifier = slopeSpeedMultiplier.Evaluate(angle);

  Vector2 rayOrigin = deltaMovement.x > 0
    ? new Vector2(horizontalRaycastHit.x - .1f, horizontalRaycastHit.y)
    : new Vector2(horizontalRaycastHit.x + .1f, horizontalRaycastHit.y); // Note (Roman): added/subtracted .1f to make sure we stay on the same side
  Vector2 currentdelta = Vector2.zero;
  Vector2 targetDelta = new Vector2();

  // we dont set collisions on the sides for this since a slope is not technically a side collision

  // smooth y movement when we climb. we make the y movement equivalent to the actual y location that corresponds
  // to our new x location using our good friend Pythagoras
  float targetMoveX = deltaMovement.x * slopeModifier;
  float targetMoveMultiplier = targetMoveX >= 0 ? -1f : 1f;

  targetDelta.x = targetMoveX;
  targetDelta.y = Mathf.Abs(Mathf.Tan(angle * Mathf.Deg2Rad) * targetDelta.x);

  RaycastHit2D raycastHit;

  do
  {
    bool isGoingRight = targetDelta.x > 0;

    // check whether we go through a wall, if so adjust...
    if (collisionState.wasGroundedLastFrame)
      raycastHit = Physics2D.Raycast(rayOrigin, targetDelta.normalized, targetDelta.magnitude, platformMask);
    else
      raycastHit = Physics2D.Raycast(rayOrigin, targetDelta.normalized, targetDelta.magnitude, platformMask & ~oneWayPlatformMask);

    if (raycastHit)
    {//we crossed an edge when using Pythagoras calculation, so we set the actual delta movement to the ray hit location
      Vector2 raycastHitVector = (raycastHit.point - rayOrigin);

      currentdelta += raycastHitVector;
      targetMoveX = targetMoveX + Mathf.Abs(currentdelta.x) * targetMoveMultiplier;

      // we have adjusted the delta, now do the same thing again...
      angle = Vector2.Angle(raycastHit.normal, Vector2.up);
      if (angle < slopeLimit)
      {
        rayOrigin = rayOrigin + currentdelta;

        targetDelta.x = targetMoveX;
        targetDelta.y = Mathf.Abs(Mathf.Tan(angle * Mathf.Deg2Rad) * targetDelta.x);
      }
      else
      {
        Logger.Trace(TRACE_TAG, "handleHorizontalSlope -> slope limit exceeded after hit.");
        break; // exit here
      }
    }
    else
    {
      currentdelta += targetDelta;

      Logger.Trace(TRACE_TAG, "handleHorizontalSlope -> no hit; final delta movement: {0}, final new position: {1}"
        , currentdelta
        , this.transform.position + new Vector3(currentdelta.x, currentdelta.y));
    }
  }
  while (raycastHit);

  deltaMovement.y = currentdelta.y;
  deltaMovement.x = currentdelta.x;

  _isGoingUpSlope = true;

  collisionState.below = true;
  collisionState.lastTimeGrounded = Time.time;
}

}
else // too steep. get out of here
{
deltaMovement.x = 0;
}

return true;
}

Also, I ran into another problem which is probably an edge case and therefore my fix probably isn't needed. When going up a slope, the vertical hit test is performed by moving the character to the delta adjusted x position and then sending rays up. However, in some circumstances this would result it moving the character across the slope and then hitting the collider from the other side:

vert ray

My workaround is that if _isGoingUpSlope, call another vert move method which sends the test rays along the delta vector:

private void moveVerticallyOnSlope(ref Vector3 deltaMovement)
{
var rayDistance = deltaMovement.magnitude + _skinWidth;
Vector3 rayDirection = deltaMovement.normalized;

var initialRayOrigin = _raycastOrigins.topLeft;

// if we are moving up, we should ignore the layers in oneWayPlatformMask
var mask = platformMask;
if (!collisionState.wasGroundedLastFrame)
mask &= ~oneWayPlatformMask;

for (var i = 0; i < totalVerticalRays; i++)
{
var ray = new Vector2(initialRayOrigin.x + i * _horizontalDistanceBetweenRays, initialRayOrigin.y);

DrawRay(ray, rayDirection * rayDistance, Color.red);

_raycastHit = Physics2D.Raycast(ray, rayDirection, rayDistance, mask);
if (_raycastHit)
{
  // set our new deltaMovement and recalculate the rayDistance taking it into account
  deltaMovement = _raycastHit.point - ray;

  rayDistance = deltaMovement.magnitude;
  // remember to remove the skinWidth from our deltaMovement

  if (deltaMovement.x > 0)
  {
    deltaMovement -= new Vector3(rayDirection.x * _skinWidth, rayDirection.y * _skinWidth, 0f);
  }
  else
  {
    deltaMovement += new Vector3(rayDirection.x * _skinWidth, rayDirection.y * _skinWidth, 0f);
  }
  collisionState.above = true;

  _raycastHitsThisFrame.Add(_raycastHit);

  // we add a small fudge factor for the float operations here. if our rayDistance is smaller
  // than the width + fudge bail out because we have a direct impact
  if (rayDistance < _skinWidth + kSkinWidthFloatFudgeFactor)
    break;
}

}
}

thanks again for all your work on this!

from charactercontroller2d.

Related Issues (20)

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.