Problem with constraining object to move inside Plane

I’m trying to constrain the object to move inside the plane.
Example 7406036--905369--ezgif-4-1284a8703f07.gif

  1. I check to see if object point is within bounds
  2. If it’s within bounds or equals to bounds, then allow movement
  3. If it’s out of bounds, point = bound.x or bound.y accordingly

Problem is that when it does go out of bounds, in this case point.x < -0.5f, it would instead return -0.500001f, instead of exactly -0.5f. This causes the logic to break.

I tried moving all logic to Update(), then I get the issue of teleporting. And still having the problem as above.
Teleport 7406036--905387--ezgif-6-3dda1648fbbe.gif

I can simply break out of the movement loop to stop all interactions until the player clicks again. Which works fine. But I’m wondering if I can achieve what I want without forcing the player to reclick the object to move it back.

Any ideas on how I’d achieve this mechanic?

Don’t allow your logic to break because of floating point imprecision. That’s the LAST thing you want in your codebase!

https://discussions.unity.com/t/851400/4

If it’s a rectangle, just check the coordinate and stop it at the indicated bounds.

1 Like

I have the movement in a while loop. So as long as left click is held, it’ll attempt to move to position. I can skip the iteration when the point goes out of bound.

Problem is that since it sets the point back at the bound, it’ll just bounce back and forth from the frame that goes out of bound then back to the bound, until the player stops holding the left click.

While Loop

while (PlayerRaycast._onButtonDown)
        {
            //Update the ray position and direction
            r.origin = GameManager.Instance._Character.cameraTransform.position;
            r.direction = GameManager.Instance._Character.GetEyeForwardVector();

            if (objectPlane.Raycast(r, out enter))
            {
                //Converting the object world position relative to constraint plane to match rotations
                rbPosLocalToConstraint = _constraintPlane.transform.InverseTransformPoint(rb.position);
             
                //Store a temp RB pos to later be applied back to RB position
                tempRBPos = rbPosLocalToConstraint;
             
                if (!IsOutOfConstraintBounds(tempRBPos))
                {
                    //Start the lerp movement if we're not at the target destination
                    if (rb.position != r.GetPoint(enter))
                    {
                        t += Time.fixedDeltaTime * 2f;
                        t = Mathf.Clamp01(t);

                        //Lerp to the target position to get a smoother transition.
                        rb.MovePosition(Vector3.Lerp(rb.position, r.GetPoint(enter), t));
                    }
                    //Reset t if we're at the target.
                    if (t.Equals(1f))
                    {
                        t = 0f;
                    }
                }
            }


            yield return Timing.WaitForOneFrame;
        }

When Out of Bounds

bool IsOutOfConstraintBounds(Vector3 rbLocalPos)
    {
        //If the object is out of bound then set the position back to the bound
        //Check for the X axis constraints
        if (rbPosLocalToConstraint.x < _contraintCorners[0].x)
        {
            //Set the x position to match the minX
            rbLocalPos.x = _contraintCorners[0].x;
            rb.position = _constraintPlane.transform.TransformPoint(rbLocalPos);

          
            return true;
        }
        else if (rbPosLocalToConstraint.x > _contraintCorners[3].x)
        {
            //Set the x position to match the maxX
            rbLocalPos.x = _contraintCorners[3].x;
            rb.position = _constraintPlane.transform.TransformPoint(rbLocalPos);

          
            return true;
        }

        //Check for the Y axis constraints
        if (rbPosLocalToConstraint.y < _contraintCorners[0].y)
        {
            //Set the y position to match the minY
            rbLocalPos.y = _contraintCorners[0].y;
            rb.position = _constraintPlane.transform.TransformPoint(rbLocalPos);

          
            return true;
        }
        else if (rbPosLocalToConstraint.y > _contraintCorners[3].y)
        {
            //Set the y position to match the maxY
            rbLocalPos.y = _contraintCorners[3].y;
            rb.position = _constraintPlane.transform.TransformPoint(rbLocalPos);

          
            return true;
        }

        return false;
    }

Edited: Clarify the problem.

One way to solve this logically would be to do the out of bounds check BEFORE you move the object and only move it if the new position would be valid. This requires you to simulate/calculate the movement beforehand. That way it can never move outside (problem solved). Though it would also never move to the very edge. To do that too you need to calculate the bounding positions and move it there.

Here is a more step by step guide:

  1. Get your current position
  2. Calculate what your next position would be if moved
  3. Check if the calculated position is valid
  4. If valid then apply the calculated position
  5. If invalid then calculate position on bounds and apply that.
2 Likes

Thanks! I got it to work. Essentially it’s what @_geo1 suggested, and that lead me in the right direction.

  1. Get desired target position, and store a copy
  2. Check if target position is within bounds
  3. If yes, object goes to target.
  4. if no, set the copied target position to bounds, and object goes to copied target.

With this method, we’re adjusting the object based on a external variable, therefore eliminating all flickering.
Results 7406810--905498--ezgif-2-56294f68856f.gif

1 Like