0

hope someone here smarter than me can figure out my mistake. I have a simple platformer game using raycasts to check if you’re standing on the ground. I tried colliders, but started using raycasts to be able to jump just before the player collides and thereby losing his x-velocity to friction, which would be a problem to how the game is supposed to be played.

That is exactly what happens. If you fall fast enough, then the x-velocity turns to 0 instantly instead of the player jumping just before collision and retaining his x-velocity.

It seems my raycasts do not check in time, or something else fails that I just cant figure out. Here’s what I have tried so far by reading other posts complaining about raycasts not registering, all to no different result:

  • Turning on Continous collision detection in my players rigidbody instead of Discrete
  • Putting the raycasts in FixedUpdate (made it worse)
  • Putting my Movement script right after Default Time in the section Script Execution Order in Project Files

 public void Jump()
    {
        LayerMask ground = LayerMask.GetMask("Ground");

        Vector2 bottomCenter = new Vector2(transform.position.x, transform.position.y - transform.GetComponent<Renderer>().bounds.size.y / 2);
        Vector2 bottomLeftCornerPos = new Vector2(transform.position.x - transform.GetComponent<Renderer>().bounds.size.x / 2, transform.position.y - transform.GetComponent<Renderer>().bounds.size.y / 2);
        Vector2 bottomRightCornerPos = new Vector2(transform.position.x + transform.GetComponent<Renderer>().bounds.size.x / 2, transform.position.y - transform.GetComponent<Renderer>().bounds.size.y / 2);

        // Cast a ray straight down.
        RaycastHit2D down = Physics2D.Raycast(bottomCenter, Vector2.down, 1f, ground);
        RaycastHit2D downleft = Physics2D.Raycast(bottomLeftCornerPos, Vector2.down, 1f, ground);
        RaycastHit2D downright = Physics2D.Raycast(bottomRightCornerPos, Vector2.down, 1f, ground);

        // Determine the closest hit point
        RaycastHit2D hit = down.collider != null ? down : (downleft.collider != null ? downleft : downright);
        if (down.collider != null && (hit.collider == null || down.distance < hit.distance)) hit = down;
        if (downleft.collider != null && (hit.collider == null || downleft.distance < hit.distance)) hit = downleft;
        if (downright.collider != null && (hit.collider == null || downright.distance < hit.distance)) hit = downright;

        // If it hits something...
        if (hit.collider != null)
        {

            if (hit.distance >= 0.01 && hit.distance <= 0.1 && rb2d.velocityY <= 0)
            {

                rb2d.velocityY = jumpPower;
                comboExperiration = ComboExpireTime;
                comboCounter++;


                //Since we are not colliding with floor, send a message to Player Stats that we jumped on a floor
                if (hit.collider.gameObject.CompareTag("Floor"))
                {
                    GetComponent<PlayerStats>().CollisionByJump(hit.collider.gameObject);
                }

            }
        }
    }

Jump() gets called in inputManager script:

void Update()
    {

        if (playerInput.MovementMap.Jump.inProgress)
        {
            player.GetComponent<PlayerMovement>().Jump();
        }

Hope anyone here recognises this problem or can see my wrongings

3
  • 1
    Every frame, I "shoot a ray" along my line of travel, and measure the distance to anything in the way. If that distance is greater than the amount I will travel in that frame (based on velocity), I "pass", since I won't hit it until the next frame. If it's shorter, I will penetrate it or go through it. In that case, I "take" whatever distance I want and then "collide" or penetrate or pass thru. (I have all 3 scenarios). The faster the frame rate (and the smaller the time interval), the more accurate it becomes. Commented Jul 7 at 14:59
  • @GerrySchmitz Thanks for your input. Any chance I can see how you do this?
    – Madswint
    Commented Jul 13 at 22:08
  • It's hard to show, because it's a pipeline. The animations run, and a "travel monitor" records center points on each frame update. In that way it measures "interval travel". At the same time, a collision detector is measuring distances, and raises a collision event when the distances are "in range". Then the decision is made to "bounce", smash, penetrate, merge, etc. Commented Jul 14 at 5:30

1 Answer 1

0

Edit: I am changing my answer because my original assumption was not quite right. The problem is not the raycast origin piercing through the object. Though I was close in some sense.

How it works is this:

FixedUpdate
Update - no hit
Update - no hit
Update - no hit
FixedUpdate
Update - hit, distance 0.7
Update - hit, distance 0.7
Update - hit, distance 0.7
FixedUpdate
Update - hit, distance 0
Update - hit, distance 0
Update - hit, distance 0
FixedUpdate
Collision
  • FixedUpdate - updates physics and moves the object, this is why it can't really pierce into the ground.
  • Update - where raycasts are updated

So, it doesn't pierce through the object. Instead, when velocity is high enough, it stops exactly at 0 distance. Which is filtered out by this check:

hit.distance >= 0.01 

Can this be fixed by hit.distance >= 0 check? Only partially, the code will process a hit, but the collision will still be reported in next FixedUpdate.

So the options are:

  • use prediction to avoid collision, basically what @Gerry Schmitz describes in the comment,
  • use 0 distance, but ignore the collision when there's a hit right before it. Seems easier as it uses the same code, but could be tricky to synchronize, as hits are calculated in Update and collisions in FixedUpdate.
6
  • I think the simplest fix might be to just not limit the down raycast distance but instead rather simply check the distance afterwards => would always track if above ground also in infinite distance -> but can then still check whether the actual distance is considered as grounded
    – derHugo
    Commented Jul 9 at 11:55
  • Hi Vladimir, thanks for coming back and looking even more into it. I've been trying to fix on and off for days now, since the post more or less. I came to terms with the fact that using predicition is a little over my head at this point, so I thought "what if I just disable friction when jumping, and enabling it on a short timer after player hits the ground?". The logic seems to work and the player retains x-velocity when colliding with the ground, although it's cluncky as the player will slide for too long and Unity has an ancient bug with changing 2d friction during runtime
    – Madswint
    Commented Jul 13 at 19:29
  • pt2 (bug in question: stackoverflow.com/questions/29767817/…). I've done a workaround by instantiating a new PhysicsMaterial2D, settings its friction and then applying that as the new physics material for the collider. I feel this is a very heavy/taxing solution to what can be a more elegant one.. I'm interested in trying what Gerry and you have suggested, but not sure how to approach it. Can you by any chance draft up a simple example or logic behind the implementiation? Or anywhere I can read into something similar.
    – Madswint
    Commented Jul 13 at 19:31
  • @Madswint you can raycast down using var estimatedFallDistance = -_rigidbody.velocity.y * Time.fixedDeltaTime; instead of 1. Your raycasts will then show whether you will have a collision in next FixedUpdate. When jumping, you should not only update your velocity, but also X & Y of your transform. For Y, this is var estimatedUpDistance estimatedFallDistance - hitDistance * 2;. It has to be added to transform.position.y. X is a different story...
    – Vladimir
    Commented Jul 14 at 22:11
  • You also have to fix a lot of edge cases. E.g. when your hit.distance is 0 (happens frequently at low speeds), the collision is likely to happen. You need to never let that happen... Another issue is when you recalculate your X, for angled motion. In this case you have to raycast from the point where the ray hits the ground in the direction of the movement to check for other obstacles etc. In fact, you are kinda writing your own physics system here...
    – Vladimir
    Commented Jul 14 at 22:16

Not the answer you're looking for? Browse other questions tagged or ask your own question.