Little problem with Mathf.Clamp as used in Space Shooter Tutorial

Hi,
I’m new to Unity and programming in general, when possible i follow tutorials and try to get answers to my noob questions googling and hoping someone had the same issue before, but this time I couldn’t find any answer solving a similar problem.

In the Space Shooter Unity tutorial, the movement of the spaceship is bound to the camera view by using the Mathf.Clamp function.
This is the code:

[System.Serializable]
public class Boundary
{
	public float xMin, xMax, zMin, zMax;
}

public class PlayerController : MonoBehaviour 
{
	public float speed;
	public Rigidbody rb;
	public Boundary boundary;

	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, boundary.xMin, boundary.xMax),
			0.0F,
			Mathf.Clamp (rb.position.z, boundary.zMin, boundary.zMax)
		);
    }
}

The Rigidbody is attached to the spaceship 3D model, and the xMin, xMax, zMin, zMax and speed values are user-defined in the inspector.

Now the code works almost perfectly, with the exception that, when reaching the border limits (e.g… -6 and 6 in the x axis), the spaceship slightly overlaps this limit (-6.2, 6.2), returning to the limit values when you stop pressing the movement buttons.
The excess movement (over the bounds) is affected by changing the speed amount, so i guess it must be something related to Physics, RigidBody, velocity or similar…

I guess if there’s a way to limit the movement exactly to the values in the Mathf.Clamp function or if a total different approach has to be used.

Thanks in advance.

Mathf.Clamp does his job fine.
The problem is that in the inspector you see the position of the transform but you are not clamping the position of the transform but the position of the rigidity body.

In the FixedUpdate function is called more often and not in the same cycle like the normal Update function that can cause some interesting behavior if you set the / read the position.

If you are really need to clamp the transform position you can add this to your script (but in in the tutorial you should be fine without it):

	void Update()
	{
		transform.position = new Vector3 
			(
				Mathf.Clamp (rigidbody.position.x, boundary.xMin, boundary.xMax), 
				0.0f, 
				Mathf.Clamp (rigidbody.position.z, boundary.zMin, boundary.zMax)
				);
	}

This is my code and it’s working fine. Hope it helps

using UnityEngine;
using System.Collections;

[System.Serializable]
public class Boundary 
{
	public float xMin, xMax, zMin, zMax;
}

public class PlayerController : MonoBehaviour 
{

	private Rigidbody rb;
	public float speed;
	public float tilt;
	public Boundary boundary;
	private AudioSource audio;

	public GameObject shot;
	public Transform ShotSpawn;
	public float fireRate;

	private float nextFire;

	void Start() {
		
		rb = GetComponent<Rigidbody> ();
		audio = GetComponent<AudioSource> ();
	}

	void Update() 
	{
		if (Input.GetButton ("Fire1") && Time.time > nextFire) {
			nextFire = Time.time + fireRate;
			Instantiate (shot, ShotSpawn.position, ShotSpawn.rotation);
			audio.Play ();
		}
		
	}

	void FixedUpdate() 
	{
		float moveHorizontal = Input.GetAxis ("Mouse X");
		float moveVertical = Input.GetAxis ("Mouse Y");

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

		rb.position = new Vector3 (

			Mathf.Clamp (rb.position.x, boundary.xMin, boundary.xMax), 
			0.0f,
			Mathf.Clamp (rb.position.z, boundary.zMin, boundary.zMax)
		); 

		rb.rotation = Quaternion.Euler (0.0f, 0.0f, rb.velocity.x * -tilt);

		DecelerateWhenHittingXBorders();
		DecelerateWhenHittingZBorders();

	}

	void DecelerateWhenHittingXBorders()
	{
		if (rb.position.x >= boundary.xMax || rb.position.x <= boundary.xMin) {
			//reduce velocity indirectly by force
			//rb.AddForce(-speed * rb.velocity.x, 0, 0);

			//reduce velocity directly
			rb.velocity -= new Vector3 (rb.velocity.x / 3f, 0, 0);
		}
	}

	void DecelerateWhenHittingZBorders()
	{
		if (rb.position.z >= boundary.zMax || rb.position.z <= boundary.zMin) {
			//reduce velocity indirectly by force
			//rb.AddForce(-speed * rb.velocity.x, 0, 0);

			//reduce velocity directly
			rb.velocity -= new Vector3 (0, 0, rb.velocity.z / 3f);
		}
	}

}

I have similar problems, but in this particular case, the whole script works fine till we use

   Mathf.Clamp (rigidbody.position.x, boundary.xMin, boundary.xMax), 
                0.0f, 
                Mathf.Clamp (rigidbody.position.z, boundary.zMin, boundary.zMax)
            );

and

 rigidbody.rotation = Quaternion.Euler (0.0f, 0.0f, rigidbody.velocity.x * -tilt);

Simply put the limiting of position and giving the tilt option code in current and latest Unity 5.1-2 using Visual Studios or Mondevelop code editors say these strings are in error and do not give solutions that make them work.

Ad while the link reference to the “Flow chart” on how things “should work” is all fine and dandy, on how things should “work flow” together, its not an explanation why this string of code is “No Worky” in current Unity5 and I have looked through the Scripting tutorials and do not find any examples.

Its great it works in 4.6 but even the expanded version does not work properly in the latest versions of Unity5, and incidently with the enemies, and weapon code, “No worky” right in 4.6 either…

I actually found out that the 0.2 part of the 6.2 is related to the fixed timestep. If you change the fixed timestep to 0.05 your ships limit will be 6.5. I dont exactly understand why and would apreciate if someone explained that to me.

You just need to input the numbers in xMin xMax zMin zMax in unity.

Move your player object in Scene to get the number you want

Hi,
Unity 5.6.1f1 - the code from tutorial definitely doesn’t work as expected.

No fixes: - ship moves past the borders

    void FixedUpdate () {
        float horizontalMovement = Input.GetAxis("Horizontal");
        float verticalMovement = Input.GetAxis("Vertical");

    Vector3 movement = new Vector3(horizontalMovement, 0.0f, verticalMovement);
    rigidbody.AddForce(movement * speed);
    //only this works but not exactly since we immediately have full speed - not very realistic
    //rigidbody.velocity = movement * speed;

    //limit movement
    //this makes the ship slowly move past boundaries
    rigidbody.position = new Vector3(
        Mathf.Clamp(rigidbody.position.x, boundary.xMin, boundary.xMax),
        0.0f,
        Mathf.Clamp(rigidbody.position.z, boundary.zMin, boundary.zMax)
        );



    //rigidbody.rotation = Quaternion.Euler(0.0f, 0.0f, rigidbody.velocity.x * -tilt);
}

Using velocity makes the ship immediately move very fast but it is only fix

rigidbody.velocity = movement * speed;

transform fix from couple posts above makes me wait until i can move from 1 direction to the other because apparently pushing right/left adds to the force despite its not moving the ship. Pushing other direction button waits until both leftForce==rightForce? until we can move other direction ( result: we have to wait couple of sec until ship moves other direction)

void Update()
    {
    
    //this code makes going the other way impossible for a 1st few seconds until forces working in other direction gets back to null ? After that we can move other direction.
    transform.position = new Vector3
        (
            Mathf.Clamp(rigidbody.position.x, boundary.xMin, boundary.xMax),
            0.0f,
            Mathf.Clamp(rigidbody.position.z, boundary.zMin, boundary.zMax)
            );
}

Why is it not working? Does it has something to do with how fast/slow pc is vs how often the FixedUpdate() is called?

I’m working on space shooter as well, and not only is it having the excess movement, but it only lets me go 0.4 units in any the x and z directions. How do I fix this?

well for me rigidbody.position doesnt work nor does rb.position so where am i going wrong?

Mathf.Clamp does his job fine. The problem is that in the inspector you see the position of the transform but you are not clamping the position of the transform but the position of the rigidity body.

I was also facing this issue and after reading the comments the Void Update() {transform.position […]} suggestion helped to keep the image of the ship within the boundary but that did not help the rigid body from slowly drifting out into space. Meaning the longer you went to the right when on the edge the longer it took to come back from the edge.

What I did to address this was stop applying force when the object reached the boundary. This may not be ideal for your game play but it works for my vision of this tutorial. You can see a snippet of the code below.

//Define the border of your game screen, just like in the video but as a separate variable. 
Vector3 border = new Vector3(
                Mathf.Clamp(rb.position.x, boundery.xmin, boundery.xmax),
                0.0f,
                Mathf.Clamp(rb.position.z, boundery.zmin, boundery.zmax)
            );

//Define your movement 
        Vector3 movement = new Vector3(moveHoizontal, 0.0f, moveVertical);

//check if the rigid body is at the border and if force is still being applied in that direction. X-Axis
        if((border.x == boundery.xmin && movement.x < 0) 
            || (border.x == boundery.xmax && movement.x > 0))
        {
//If the force is applied in the direction of the border set it to zero
            movement.x = 0.0f;
        }
//Z-Axis
        if ( (border.z == boundery.zmin && movement.z < 0) 
            || (border.z == boundery.zmax && movement.z > 0))
        {
//If the force is applied in the direction of the border set it to zero
            movement.z = 0.0f;

        }
//Apply updated force vector
        rb.AddForce(movement * speed);

,> Mathf.Clamp does his job fine. The problem is that in the inspector you see the position of the transform but you are not clamping the position of the transform but the position of the rigidity body.

I was also facing this issue and after reading the comments the Void Update() {transform.position […]} suggestion helped to keep the image of the ship within the boundary but that did not help the rigid body from slowly drifting out into space. Meaning the longer you went to the right when on the edge the longer it took to come back from the edge.

What I did to address this was stop applying force when the object reached the boundary. This may not be ideal for your game play but it works for my vision of this tutorial. You can see a snippet of the code below.

//Define the border of your game screen, just like in the video but as a separate variable. 
Vector3 border = new Vector3(
                Mathf.Clamp(rb.position.x, boundery.xmin, boundery.xmax),
                0.0f,
                Mathf.Clamp(rb.position.z, boundery.zmin, boundery.zmax)
            );

//Define your movement 
        Vector3 movement = new Vector3(moveHoizontal, 0.0f, moveVertical);

//check if the rigid body is at the border and if force is still being applied in that direction. X-Axis
        if((border.x == boundery.xmin && movement.x < 0) 
            || (border.x == boundery.xmax && movement.x > 0))
        {
//If the force is applied in the direction of the border set it to zero
            movement.x = 0.0f;
        }
//Z-Axis
        if ( (border.z == boundery.zmin && movement.z < 0) 
            || (border.z == boundery.zmax && movement.z > 0))
        {
//If the force is applied in the direction of the border set it to zero
            movement.z = 0.0f;

        }
//Apply updated force vector
        rb.AddForce(movement * speed);