I am trying to set the max velocity to a tank with the script below. It doesn’t matter if I place the velocity clamping code in Update or FixedUpdate, I’ve tried various code snippets from forums in each, nothing clamps the speed. Here is my code that controls the tank below. The tank is designed to move forward and backward by using forces and rotate left and right by using transform.Rotate. Someone please help me implement this the proper way.
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class PlayerMovement : MonoBehaviour
{
public float speed = 85.0f;
public float turnSpeed = 30.0f;
public float maxSpeed = 0.01f;
public float currentVelocityMagnitude;
private float horizontalInput;
private float verticalInput;
private Rigidbody tankRigidbody;
// Start is called before the first frame update
void Start()
{
// Lower the player tank's center of mass so that the vehicle won't flip during movement.
GetComponent<Rigidbody>().centerOfMass += new Vector3(0, -1f, 0);
// Get the tank's rigid body component to avoid unecessarily calling the "GetComponent<Rigidbody>()" function later.
tankRigidbody = GetComponent<Rigidbody>();
}
// Update is called once per frame
void Update()
{
// Gather input from axes in Input Manager
horizontalInput = Input.GetAxis("Horizontal");
verticalInput = Input.GetAxis("Vertical");
// Turn the tank left or right based on horizontal input
transform.Rotate(Vector3.up * Time.deltaTime * turnSpeed * horizontalInput);
}
void FixedUpdate()
{
// Record current velocity for TESTING.
currentVelocityMagnitude = tankRigidbody.velocity.magnitude;
// Move the tank forward and backward based on vertical input
tankRigidbody.AddRelativeForce(0, 0, speed * verticalInput, ForceMode.VelocityChange);
// Ensure player tank's max velocity doesn't exceed the defined max velocity.
if (tankRigidbody.velocity.magnitude > maxSpeed)
{
tankRigidbody.velocity = tankRigidbody.velocity.normalized * maxSpeed;
}
}
}
You can let the physics engine dictate the max speed by using drag and friction. The laws of physics dictates the maximum speed of things. There is no hidden intelligent force watching over a car to make sure its speed doesn’t go over a value in God’s database.
But if you really want to dictate the max speed then you can do something like this:
void FixedUpdate()
{
Vector3 moveDir=tankRigidbody.velocity.normalized;
Vector3 pushDir=new Vector3(Input.GetAxisRaw("Horizontal"),0,Input.GetAxisRaw("Vertical")).normalized;
// Push the tank if its moving below max speed or if pushed in the opposite direction of travel (we still want to be able to slow down if moving too quickly (maybe going downhill))
if (tankRigidbody.velocity.magnitude<maxSpeed || (Vector3.Dot(moveDir,pushDir)<0))
tankRigidbody.AddRelativeForce(0, 0, speed * verticalInput, ForceMode.VelocityChange);
}
Vector3 thrust = Quaternion.Inverse(body.rotation) * -body.localVelocity;
// "m" is how powerful force impulse is
// where 4.0 making velocity change sharp and lower values make movement "floaty"
float delta = (desiredVelocity + thrust.z) * m;
// set drag to zero or increase this value to overcome it
thrust.z = Mathf.Clamp(delta, deceleration, acceleration);
body.AddRelativeForce(thrust, ForceMode.Acceleration);
as the tank will never be pushed in the opposite direction of travel since I have no hills or ramps in the scene. With this in mind I tried the following portion of your posted code:
if (tankRigidbody.velocity.magnitude < maxSpeed)
{
// Move the tank forward and backward based on vertical input
tankRigidbody.AddRelativeForce(0, 0, speed * verticalInput, ForceMode.VelocityChange);
}
This still doesn’t achieve the desired effect and instead makes the tank rapidly jump to a velocity above the maxSpeed and back to the maxSpeed while increasing the current speed variable in the Inspector window during testing. Did you have this issue when you tested your code?
I mean your add force method is using ForceMode.VelocityChange which immediately jumps to a certain velocity, so I’m not sure why you’re surprised at your current behaviour.
If you want a gradual chance you want to use the other modes.
You may need to lower the speed variable in the inspector if it’s making the tank move too quickly. But I suspect the real issue is that you’ve done a typo and your script is changing the speed variable for some reason. The code I posted doesn’t change the speed variable.
still fail to limit the tank to the maxSpeed value even though each of these code snippets are the last line of code in both Update and FixedUpdate. Can you please test this with the following starting values for speed and maxSpeed?:
I’m trying to understand why the maxSpeed is ignored even though the very last thing done on each FixedUpdate loop is literally supposed to clamp the rigidbody’s vector to the maxSpeed value.
Strange… I’ve not had any problems clamping the velocity of a rigidbody
You may be struggling with a weird quirk of Unity in that if you make an initialized variable visible to the inspector by using public or [SerializeField] then later you decide to change the initialized value in script, Unity won’t register the new value until you reset the script in the Inspector. So perhaps you’re changing maxSpeed in the script and not in the Inspector?
I don’t even have to change maxSpeed in the inspector or the script during testing and I still run into this same problem where maxSpeed gets ignored. I only change the current speed during testing and changing it within the inspector during play mode or with code as mentioned above still doesn’t do anything different. Did the same above code work for you?
Do you know that when you add a force, the effects such as changing the linear or angular velocity don’t happen until the simulation runs? Adding a force or lots of forces are summed then simulated later. Adding a force then immediately clamping the velocity is pointless. You’ll be clamping the velocity from the previous simulation step. This is clearly documented in the docs here.
If you look at the docs for Rigidbody you’ll also find Rigidbody.maxLinearVelocity although it is possible to exceed this max temporarily until the next simulation step using this but that’s also documented.
If you absolutely must change the velocity there and then, you can directly add a value to the Rigidbody.velocity
After reading MelvMay’s messages I did some more tests and he’s right. If you add too much force to a rigidbody then clamping its velocity becomes unreliable.
Personally I wouldn’t try to restrict a rigidbody’s velocity directly but if you really feel its necessary then try being less forceful when moving your objects around. Try using ForceMode.Impulse.
It’s not about too much. If you’re referring to the maxVelocity property, if you read the docs you’ll see it says the clamping happens before the simulation step so post simulation, it can move beyond that, until the next simulation step.
All I see above is confusion on what things do and when it happens.