Rigidbody falling speed resets when moving midair - c#

So my rigidbody controller has a speed limiter with the following code:
Vector3 flatVel = new Vector3(rb.velocity.x, 0f, rb.velocity.z);
// limit velocity if needed
if (flatVel.magnitude > moveSpeed)
{
Vector3 limitedVel = flatVel.normalized * moveSpeed;
rb.velocity = new Vector3(limitedVel.x, rb.velocity.y, limitedVel.z);
}
but for some reason that last line of code:
rb.velocity = new Vector3(limitedVel.x, rb.velocity.y, limitedVel.z);
causes my character's falling speed to reset when moving in midair, when it reaches about -50 units of speed. I've tried modifiying rb.velocity.x and rb.velocity.z seperately, but Unity doesn't allow that. Is there any other way to cap rigidbody speeds without using rb.velocity?

According to the Unity documentation here.
In most cases you should not modify the velocity directly, as this can result in unrealistic behaviour - use AddForce instead Do not set the velocity of an object every physics step, this will lead to unrealistic physics simulation. A typical usage is where you would change the velocity is when jumping in a first person shooter, because you want an immediate change in velocity.
I'd say this is the reason you are encountering the behaviour that you are.
A Unity forum post, found here, suggests the correct way to do this is:
The 'proper' way would be to apply a force in the opposite direction of the rigidbody's velocity. The amount of force should be proportional to the extent to which the rigidbody is exceeding its speed limit.
Another Unity forum post suggests to make use of the ClampMagnitude method, the post can be found here.
private void FixedUpdate()
{
var force = new Vector3(input.x, 0f, input.y) * acceleration;
var velocity = playerRigidbody.velocity + force;
playerRigidbody.velocity = Vector3.ClampMagnitude(velocity, maxSpeed);
}
Hope this helps.

Related

How does the interaction between rigidbody2d.movePosition and rigidbody2d.AddForce works?

I'm trying to use the .AddForce code alongside the .movePosition one, but the .movePosition making the .AddForce do nothing. I think is because the line: " Vector2 movement = new Vector2 (moveHorizontal, 0);" but I really don't know how to fix it. The .AddForce code works as inteded by itself.
Edit: posted full code.
using UnityEngine;
using System.Collections;
public class CompletePlayerController : MonoBehaviour {
public float speed; //Floating point variable to store the player's movement speed.
public float jumpforce;
private Rigidbody2D rb2d; //Store a reference to the Rigidbody2D component required to use 2D Physics.
// Use this for initialization
void Start()
{
//Get and store a reference to the Rigidbody2D component so that we can access it.
rb2d = GetComponent<Rigidbody2D> ();
}
//FixedUpdate is called at a fixed interval and is independent of frame rate. Put physics code here.
void FixedUpdate()
{
Jump();
//Store the current horizontal input in the float moveHorizontal.
float moveHorizontal = Input.GetAxis ("Horizontal");
//Store the current vertical input in the float moveVertical.
//Use the two store floats to create a new Vector2 variable movement.
Vector2 movement = new Vector2 (moveHorizontal, 0);
//Call the AddForce function of our Rigidbody2D rb2d supplying movement multiplied by speed to move our player.
rb2d.MovePosition ((Vector2)transform.position + (movement * speed * Time.deltaTime));
}
void Jump(){
if (Input.GetButtonDown("Vertical")){
rb2d.AddForce(new Vector2(0, jumpforce), ForceMode2D.Impulse);
}
}
}
As the names say:
MovePosition
Moves the rigidbody to the specified position by calculating the appropriate linear velocity required to move the rigidbody to that position during the next physics update. During the move, neither gravity or linear drag will affect the body.
AddForce
Adds a force to the Rigidbody
The force is specified as two separate components in the X and Y directions (there is no Z direction in 2D physics). The object will be accelerated by the force according to the law force = mass x acceleration - the larger the mass, the greater the force required to accelerate to a given speed.
So according to the mass and friction of the object it causes it to move with a certain velocity according to the physics.
Mixing both makes not much sense since MovePosition is usually used for isKinematic rigidbodies, so ones that are fully controlled via script while AddForce only has effect on not isKinematic ones, thus fully controlled by physics alone; by mixing both you don't give AddForce a chance to work since you overwrite the linear velocity of the rigodbody by using MovePosition which adjusts an absolute velocity to the Rigidbody thus completely overruling the AddForce.
rb2d.MovePosition (transform.position + (movement * speed * Time.deltaTime));
this target position you want it to move towards has always the same Y as transform.position.y so you basically nail your object to that height.
However, if you think about it, all physics actions have one thing in common: In the end they are all different ways to under the hood apply a certain velocity to the rigodbody.
So instead of letting these methods calculate that velocity you could instead rather directly change the velocity yourself and thereby let AddForce fully control the Y velocity while you control only X component:
rb2d.velocity = new Vector2(movement * speed, rb2d.velocity.y);

Unity get mouselook to be smooth and framerate independent

I've been confusing myself about Update() vs FixedUpdate() and time.deltaTime vs time.fixedDeltaTime for this situation.
I want frame-rate independent movement for the camera mouse-look but at the same time I want it to be as smooth as possible for 60 fps. For some reason, 60 fps seems slightly jittery.
My question is, do I put the function below inside of Update() or FixedUpdate()? Should the deltaTime variable be time.deltaTime or time.fixedDeltaTime to achieve this? Or am I doing this all wrong?
Thanks for any input.
BTW the SmoothDamp function below is a smoothing function based on lerp that allows for the feedback
void MouseLook()
{
if (_isEscaped)
return;
if (!_isAllowedToLook)
return;
var deltaTime = Time.fixedDeltaTime;
var adjustedSensitivity = _lookSensitivity * 100f; //Keeps sensitivity between 0.1 and 10f but makes the sensitivity in math actually work
_lookInput.x = SmoothDamp.Float(_lookInput.x, Input.GetAxis("Mouse X"), _lookSmoothSpeed, deltaTime);
_lookInput.y = SmoothDamp.Float(_lookInput.y, Input.GetAxis("Mouse Y"), _lookSmoothSpeed, deltaTime);
// Mouse look
var newVerticalPitch = _verticalPitch + _lookInput.y * -adjustedSensitivity * deltaTime;
_verticalPitch = SmoothDamp.Float(_verticalPitch, newVerticalPitch, 15f, deltaTime);
_verticalPitch = Mathf.Clamp(_verticalPitch, -_maxVerticalLookAngle, _maxVerticalLookAngle); //Clamp vertical look
// Camera vertical rotation
var newCameraRotation = Quaternion.Euler(Vector3.right * _verticalPitch);
_camTransform.localRotation =
SmoothDamp.Quaternion(_camTransform.localRotation, newCameraRotation, 15f, deltaTime);
// Player horizontal rotation
var newPlayerRotation = _transform.rotation * Quaternion.Euler(_lookInput.x * adjustedSensitivity * deltaTime * Vector3.up);
_transform.rotation = SmoothDamp.Quaternion(_transform.rotation, newPlayerRotation, 15f, deltaTime); //Player rotation
}
Update and FixedUpdate are both callback functions that Unity invokes on your Monobehaviours at certain times during their lifetime. More specifically, Update is called every frame (assuming the Monobehaviour is enabled) and FixedUpdate is called every physics frame.
Physics frames are independent of graphical frame-rate and are set from within Unity to occur at a specific interval. The default is 50 times per second (in other words FixedUpdate runs at 50fps by default).
FixedUpdate is generally a function that you should reserve for your physics-based needs (such as using RigidBodies or ray-casting etc.) and most certainly not one to use for things such as getting user input.
Now that that is hopefully cleared up, let's address Time.deltaTime and Time.fixedDeltaTime:
Time.fixedDeltaTime returns the amount of time that has passed since the previous FixedUpdate() frame ran. Now, since we know from above that FixedUpdate() runs at a constant frame-rate (50 by default) this value will more or less always be the same between frames
Time.deltaTime similarly returns the amount of time that has passed since the previous Update() frame ran. Now, since Update() does not run at a constant frame-rate, but is dependent on the user's machine, this value is almost always going to be a little different each frame.
So with all of this in mind, should this go in Update() or FixedUpdate()? And should it use Time.deltaTime or Time.fixedDeltaTime?
If I've done a decent job at explaining the differences between these things, then I would expect you to now know that it ought to go in Update() and use Time.deltaTime.
I do not know what that SmoothDamp.Float() method is doing exactly, but by lerping between two positions using Time.deltaTime as the t parameter, you will ensure that no matter what the frame-rate of the machine running this is, that it will be smooth and consistent.

Accelerate object towards a certain direction

I am trying to make a game by myself and I encountered a difficulty.
I have this object and I need it to accelerate towards a vector 3 point.
I tried using the Vector3.MoveTowards command, but the object moves with a constant velocity and stops at the destination.
What I need to do is have the object accelerate from 0 velocity towards a vector 3 point and not stop at the point, but continue at the same direction after it went through the point.
Does anyone know how to do this?
Thank you!
Perform these steps in a method that is called in the Update or FixedUpdate method. FixedUpdate is recommended if you are using rigid bodies.
First, you need to find the direction from your position to the point, and define a velocity instance variable in your script if not using Rigid Bodies. If you are using a Rigidbody, use rigidbody.velocity instead. target is the Vector3 position that you want to accelerate towards.
// Use rigidbody.velocity instead of velocity if using a Rigidbody
private Vector3 velocity; // Only if you are NOT using a RigidBody
Vector3 direction = (target - transform.position).normalized;
Then you need to check if we have already passed the target or not. This check makes sure that the velocity remains the same
// If our velocity and the direction point in different directions
// we have already passed the target, return
if(Vector3.Dot(velocity, direction) < 0)
return;
Once we have done this we need to accelerate our Transform or Rigidbody.
// If you do NOT use rigidbodies
// Perform Euler integration
velocity += (accelMagnitude * direction) * Time.deltaTime;
transform.position += velocity * Time.deltaTime;
// If you DO use rigidbodies
// Simply add a force to the rigidbody
// We scale the acceleration by the mass to cancel it out
rigidbody.AddForce(rigidbody.mass * (accelMagnitude * direction));
I recommend that you use a Rigidbody since it makes much more sense when doing something like this.

Set orientation of moving object towards it movement direction without using rigidbody

I have a GameObject (camera) which is moving on the spline (using a library). I want to set its orientation towards its movement. What I mean is, the object should be looking in the direction that it is moving. I tried some things to achieve this, but I was unsuccessful. Here are my attempts:
transform.rotation = Quaternion.LookRotation(transform.position);
I have also tried this
transform.rotation = Quaternion.LookRotation(transform.forward);
Neither transform.position (a point in space) nor transform.forward (the direction an object is already facing) will necessarily give you the direction an object is moving in.
For an object with a RigidBody, you'd access this through the velocity variable - but since it sounds like you're using a proprietary spline library and have no access to the object's actual movement direction, you may need to keep track of it yourself.
The simplest solution (if the library really doesn't provide movement information), is to store the object's present position and previous position each frame, and manually calculating the delta position (aka. The distance travelled between the frames) to find the velocity vector. For example:
public class MovingObject : MonoBehaviour {
private Vector3 prevPosition;
void Start() {
// Starting off last position as current position
prevPosition = transform.position;
}
void Update() {
// Calculate position change between frames
Vector3 deltaPosition = transform.position - prevPosition;
if (deltaPosition != Vector3.zero) {
// Same effect as rotating with quaternions, but simpler to read
transform.forward = deltaPosition;
}
// Recording current position as previous position for next frame
prevPosition = transform.position;
}
}
Hope this helps! Let me know if you have any questions.
Try
transform.rotation = Quaternion.LookRotation(transform.position - currentPosition);
where currentPosition is the position on the spline where your game object is right now (before applying the transformation transform).
Keep in mind that Quaternion.LookRotation() does not expect the position to look at but the direction where to look instead. See the documentation and FAQ.
You are on the right track, but you should use your movement direction: transform.rotation = Quaternion.LookRotation(movementDirection), as transform.forward is already the objects' orientation.
If you do not know/have access to the objects' current movement direction but you know the target position, you can rotate towards the target position: transform.rotation = Quaternion.LookRotation(targetPosition - transform.position)

Realistic turns for vehicle

I am trying to add realistic looking driving behavior to a simple vehicle. Currently I use the rigidbody MovePosition, and I only apply MoveRotation when I have detected forward/reverse axis from Input.
Instead I would like to apply direction/rotation even if the car is coasting, but do not want it turning when standing still, as if it were a tracked vehicle. I would also like the turn to appear to come from the front of the vehicle where the wheels (not actual objects in the game) would be located.
What I have so far is mostly borrowed from the tanks tutorial:
m_MovementInputValue = Input.GetAxis(m_MovementAxisName);
m_TurnInputValue = Input.GetAxis(m_TurnAxisName);
// Create a vector in the direction the tank is facing with a magnitude based on the input, speed and the time between frames.
Vector3 movement = transform.forward * m_MovementInputValue * m_Speed * Time.deltaTime;
// Apply this movement to the rigidbody's position.
m_Rigidbody.MovePosition(m_Rigidbody.position + movement);
if (m_MovementInputValue > 0)
{
float turn = m_TurnInputValue * m_TurnSpeed * Time.deltaTime;
// Make this into a rotation in the y axis.
Quaternion turnRotation = Quaternion.Euler(0f, turn, 0f);
// Apply this rotation to the rigidbody's rotation.
m_Rigidbody.MoveRotation(m_Rigidbody.rotation * turnRotation);
}
I think you can save yourself some time by looking at the Wheel Collider components of Unity API : http://docs.unity3d.com/Manual/class-WheelCollider.html , I saw a demo some times ago which used it to make a full racing game so it should do the trick to simulate realistic vehicles (Demo in question).
Other than that here is how you could do it :
For starters if you want your vehicle to turn only when it is moving forward or backward you can multiply your turn force by the velocity forward vector of your vehicle
float turn = m_TurnInputValue * m_TurnSpeed * Time.deltaTime * (m_Rigidbody.velocity.magnitude / MaxVehicleSpeed);
The idea of (m_Rigidbody.velocity.magnitude / MaxVehicleSpeed) is to have a value between 0 and 1 depending on the vehicle speed.
then for the turn to appear from the front of the vehicle you need to change your point of rotation, and I can't find a solution using MoveRotation. You can do that by using the AddForceAtPosition function of the Rigidbody but it'll become more complicated to compute it.
Here is the idea :
Vector3 force = transform.right * turn;
m_Rigidbody.AddForceAtPosition(force, MiddleOfWheelAxis);
With MiddleOfWheelAxis the point between your two front wheels.

Categories