3

I have a Cube(Player) in my game scene. I have written a C# script to constrain the movement(using Mathf.Clamp()) of the Cube so it does not leave the screen.

Below is my FixedUpdate() method from the Script

private void FixedUpdate()
    {
        float moveHorizontal = Input.GetAxis("Horizontal");
        float moveVertical = Input.GetAxis("Vertical");

        Vector3 movement = new Vector3(moveHorizontal, 0.0f, moveVertical);
        rb.velocity = movement * speed;

        rb.position = new Vector3(
            Mathf.Clamp (rb.position.x, x_min, x_max),
            0.5f,
            Mathf.Clamp(rb.position.z, z_min, z_max)
            );

    }

Values of x_min, x_max, z_min, z_max are -3, 3, -2, 8 respectively inputted via the unity inspector.

PROBLEM

The script is working fine but my player(Cube) can move up to -3.1 units in negative X-AXIS (if I keep pressing the left arrow button) which is 0.1 units more in negative X-AXIS(This behavior is true for other axes too). It obviously clamps -3.1 to -3 when I stop pressing the button.

enter image description here

  • Why is this happening? Why doesn't it(Mathf.Clamp()) restrict the Cube to -3 units in first place?
  • Why am I getting that extra 0.1 units?
  • And Why is this value 0.1 units only? Why not more or less?
ray an
  • 1,132
  • 3
  • 17
  • 42
  • 2
    I could be wrong but I think it's because you're setting velocity and then setting position, but before the next frame, again I'm guessing, unity calculates the next position using the velocity of the object. Which ends up drawing it at such a position. I think you'd need to reset the velocity to zero if it's position is leaving your boundaries. – Alox May 02 '17 at 13:13
  • @Alox You should put that as an answer. I think that's likely the problem too. I don't even think this is possible to fix other than removing the Rigidbody component entirely or using the `Rigidbody.MovePosition` function. – Programmer May 02 '17 at 13:19
  • Then why is it always 0.1 units only? – ray an May 02 '17 at 13:20
  • 2
    Like Alox said......Because the physics is still running and the old force applied to it is still there it. Try setting the velocity to `Vector.Zero` when it reaches the limit. That should remove that force. I suggest you use the if statement for this. – Programmer May 02 '17 at 13:34

3 Answers3

5

Your issue could be caused because you are setting velocity and then position, but before the next frame unity adds the velocity to your objects position. Which is why it ends up 0.1 units off.

To fix this, try resetting the velocity of the object to zero if it's about to leave your boundaries.

Alox
  • 651
  • 12
  • 24
4
  • This is happening because the internal physics update moves the cube after you have put it in its clamped position. Mathf.Clamp() does restrict the cube to the expected position in the first place, as you can verify by putting a Debug.Log directly after you assign the position
  • You are getting the extra .1 units because you give the object a speed that gets applied after you clamp
  • It is 0.1 units because of the speed and the setting for Fixed Timestep (in Project Settings > Time). If you would set a higher speed it would be more. If you would lower the Fixed Timestep it would also be a bit more.
Tubeliar
  • 836
  • 7
  • 20
  • So, Is it right to say that 0.1 units are added after `FixedUpdate()` is called in internal physics update and this value depends on the speed(of the cube) and Timestep? – ray an May 02 '17 at 15:27
  • Am I correct? And yes Debug.Log never showed the value -3.1 – ray an May 02 '17 at 16:39
  • Yes, you are correct, the 0.1 units are added after FixedUpdate() is complete, and it depends on the speed of the cube and timestep. So a possible fix is setting the speed to 0 before you step over the boundary as Alox suggests. – Tubeliar May 03 '17 at 10:02
1

You already got the reason why this happens.

In order to fix the problem, you can use this code to prevent the cube to go outside your boundaries:

private void FixedUpdate() {
    Vector3 movement = new Vector3(Input.GetAxis("Horizontal"), 0f, Input.GetAxis("Vertical"));

    // Get new position in advance
    float xPos = rb.position.x + movement.x * speed * Time.fixedDeltaTime;
    float zPos = rb.position.z + movement.z * speed * Time.fixedDeltaTime;

    // Check if new position goes over boundaries and if true clamp it
    if (xPos > x_max || xPos < x_min) {
        if (xPos > x_max)
            rb.position = new Vector3(x_max, 0, rb.position.z);
        else
            rb.position = new Vector3(x_min, 0, rb.position.z);
        movement.x = 0;
    }
    if (zPos > z_max || zPos < z_min) {
        if (zPos > z_max)
            rb.position = new Vector3(rb.position.x, 0, z_max);
        else
            rb.position = new Vector3(rb.position.x, 0, z_min);
        movement.z = 0;
    }

    rb.velocity = movement * speed;
}
Galandil
  • 4,169
  • 1
  • 13
  • 24