- Home /
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.
Have a look at this method of clamping to screen bounds... It may be a better way to do it than hard coding the coordinates https://www.youtube.com/watch?v=mq4BlZLRe$$anonymous$$k
Thanks for the link. Honestly it's a bit too much for my actual basic skills. I started learning a bit of Flash before deciding to switch to Unity, and in AS3 there was no easy solution like the one adopted in the Space Shooter tutorial, so i wanted to understand if the imbedded Unity functions could do this job correctly, though i understand if you have the skills to code everything your own way, then you can get way more than simply using prefab functions. I'll keep the link to this video for future projects ;)
When you run it in editor does the actual transform of the spaceship exceed the boundary (check the inspector)? The boundary doesn't look like it's adjusting for the actual size of player's model. The transform position is an infinitely small point, so a model centered on this point will have about half cross whatever the clamped values are.
Answer by fffMalzbier · Mar 11, 2015 at 02:12 PM
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)
);
}
Thanks.
I previously tried your code (line 3 - transform.position ins$$anonymous$$d of rb.position) in the FixedUpdate() function but nothing changed from the original tutorial code that i pasted, so i guessed it was another issue.
The same code inside an Update() function works as you suggested, and the bounds are never overlapped respecting the figures of -6 and 6.
Though i don't yet clearly understand the reason of this different behavior when the same code is added in Update() ins$$anonymous$$d of FixedUpdate() i guess i'll better understand with a bit more of experience.
This page can help you to understand in what order and how the update routines are called http://docs.unity3d.com/$$anonymous$$anual/ExecutionOrder.html
Answer by Darkskyb2 · Nov 03, 2016 at 07:38 PM
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);
}
}
}
Answer by $$anonymous$$ · Dec 06, 2015 at 03:23 PM
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..
Answer by dhulke · May 10, 2016 at 02:18 PM
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.
Fixed update is called every .02 seconds. If you change the time then your limit will increase to the speed*the update time.. Does this make sense to you? The update occurs and then after we update the movement of the ship we check the boundary. So it could potentially be at the boundary and then after an update it could be past the boundary momentarily.
Actually it doesn't make sense. When FixedUpdate is called, I first change the position of the ship (velocity), which could get past the limit, but then, after that, I clamp the position. So it doesn't matter how long a timestep takes, I'm always clamping the position after moving the ship. $$anonymous$$athf.Clamp is the last function called before Update is called and renders the ship. So that makes absolutely no sense. What does make sense is to say that regardless of the order you set the position, the velocity is always processed at the end of FixedUpdate. That would make sense.
Answer by seakyihong · Sep 11, 2017 at 02:44 AM
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