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.
Related
I have a FPS Faux gravity player controller it can do the basics Walk Run Jump Crouch but I'm now implementing Slide functionality to it and it works I have this Equation that calculates weather or not we should be decreasing in speed or increasing
moveSpeed = moveSpeed * ((slopeAngle / 2250) + .99);
it works, it works well but we have a issue that the equation doesn't care if we are gong Uphill or down so, I can actually slide accelerate up a hill so how can I implement a system that will lower my speed if my direction is facing up hill and increase it when I'm facing down hill
I'm already using terrain normals so I need a way to figure out what the up direction of a terrain normal is and then plug it into my equation so when I'm facing down I accelerate and facing up the terrain normal I decelerate
You need to use the geometry of the situation:
You have two known vectors: n, the normal to the surface you are currently on, and g, gravity (both direction and magnitude).
You also have one desired vector: a, your acceleration due to sliding (this would then be added to any other accelerations)
Neglecting friction (which you probably don't want to do, but that's separate), your acceleration is given by the projection of the gravity vector onto the surface plane:
a = g - n * Vector3.Dot (g, n) / Vector3.Dot (n, n)
If n is known to be a unit vector, you can skip the division as an optimization.
This will take care of acceleration direction regardless of the slope and your current velocity, or even the direction of gravity. It will also work equally well in 2D or 3D (or any other dimension, really).
You could raycast forward in a forward direction from your feet to check if they are going uphill. If it hits something, then we will not calculate the sliding.
The height value must be slightly less than half your player's height
Also, dist must be a low number, but not too low.
public float height = 0.99f; //--> Must be a little less than half the height
public float dist = 0.02f; //--> Must be a low number, but not too low
void Update()
{
Ray ray = new Ray(transform.position - height, transform.forward);
if (!Physics.Raycast(ray, dist))
{
moveSpeed = moveSpeed * ((slopeAngle / 2250) + .99);
}
}
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.
My camera jitters on rotation, but not movement. This script is the only asset in a new project, and it's attached to the camera - it's supposed to be a free-flying camera. I added a few cubes into the environment so I could check it was moving correctly, and it stutters several times a second whenever it's rotated. Movement works just fine.
I've tried changing camera clipping planes, I've tried moving either movement or rotation or both into Update() instead, removing the default camera and adding my own, and since it's an otherwise empty project I'm showing over 500fps so don't have any performance issues - nothing has had any effect, for better or worse.
Any suggestions welcome! If someone has a minute to open up a default project and slap this script on the camera and make sure it's not something on my end, that would at least rule that out and I'd really appreciate it. Thanks in advance.
public class CameraController : MonoBehaviour
{
public Vector2 mouseSensitivity = Vector2.one;
public Vector3 moveSensitivity = Vector2.one;
public Transform camTransform;
Vector3 camEulers = Vector3.zero;
void Start()
{
camTransform = FindObjectOfType<Camera>().transform;
}
void LateUpdate()
{
KeyboardMovement();
LockedMouseRotation();
}
void KeyboardMovement()
{
camTransform.position += Input.GetAxis( "Vertical" ) * camTransform.forward * Time.deltaTime * moveSensitivity.y;
camTransform.position += Input.GetAxis( "Horizontal" ) * camTransform.right * Time.deltaTime * moveSensitivity.x;
}
void LockedMouseRotation()
{
camEulers.x -= Input.GetAxis( "Mouse Y" ) * mouseSensitivity.y * Time.deltaTime;
camEulers.y += Input.GetAxis( "Mouse X" ) * mouseSensitivity.x * Time.deltaTime;
camEulers.x = Mathf.Clamp( camEulers.x, -90.0f, 90.0f );
camTransform.rotation = Quaternion.Euler( camEulers );
}
}
Don't use Time.deltaTime here.
Your goal here is to convert measurements of linear distance (GetAxis("Mouse X") and GetAxis("Mouse Y")) into a measurement of angular distance (the angular distance you are using to adjust the camera's Euler angles). For that you only need a constant factor, which can be your mouseSensitivity components.
But GetAxis gets multiplied by Time.deltaTime in other places and works perfectly fine. Why do those work like that?
Your goal is not converting to distance from "throttle position" like one might from GetAxis("Horizontal") or GetAxis("Vertical") or GetAxis("customKeyButtonOrThrottleAxis") . That is a situation where you would want to multiply by a time (Time.deltaTime) to calculate a distance if you are using the position of the button or throttle as velocity. This is because distance = speed * time.
In some cases you might multiply by Time.deltaTime to calculate a velocity or speed if the button or throttle is to represent acceleration, because speed = acceleration * time.
Okay, so why doesn't Time.deltaTime work here?
When you use GetAxis("Mouse X") or GetAxis("Mouse Y"), you get a measurement of pixel difference in position since the last frame.
The typical way a mouse driven camera works in games is that moving the mouse the same distance in the same direction should move the camera the same way (assuming you don't move the camera all they way up or down of course).
So what that means is that multiplying by Time.deltaTime does here is often actually the opposite of what you might expect - moving the mouse the same distance moves the camera's rotation differently depending on how many frames that movement occurred over and how quickly those frames were rendered.
The faster the frames were rendered (that means, a smaller Time.deltaTime), the less camera rotation the same mouse distance becomes. This results in unpredictable jitter especially when the very act of moving the camera can drastically change the framerate.
So how do I fix it then?
So, the only thing you need to do here is to remove the multiplication by Time.deltaTime:
camEulers.x -= Input.GetAxis( "Mouse Y" ) * mouseSensitivity.y;
camEulers.y += Input.GetAxis( "Mouse X" ) * mouseSensitivity.x;
A similar situation where you might want to include Time.deltaTime is if you want to calculate the speed or velocity of the mouse movement.
But, in that case, you would want to divide by Time.deltaTime. This is because speed = distance / time.
Vector2 mouseVelocity = Vector2.Scale(mouseSensitivity,
new Vector2(Input.GetAxis("Mouse X"),
Input.GetAxis("Mouse Y"))
) / Time.deltaTime;
In most cases, you probably don't want to be concerned with the speed/velocity of the mouse movement. But, for example, those interested in a super monkey ball type game using a track ball input might find this side bit useful!
I am working on a game where i need to shoot the ball at an angle and power defined by 2 sliders (1 angle slider, 1 power slider). I currently have this code to control the launching of the ball:
public void shoot()
{
float angle = angleSlider.GetComponent<Slider>().value;
float power = powerSlider.GetComponent<Slider>().value;
gameObject.SetActive(false);
ball.GetComponent<Rigidbody2D>().simulated = true;
Vector2 releaseVector = Quaternion.AngleAxis(angle, transform.up) * transform.forward;
ball.GetComponent<Rigidbody2D>().velocity = releaseVector * (power/3);
}
with this current code it works almost perfect apart from one thing. When the angle is like between 30 and 60, the ball is launched well but if i set it to 0 degrees the ball would barely move and on the contrary if i set it to 90 degrees, the ball launches with much more power. How can i set a constant speed for all degrees so that the speed is only affected by the power slider only please? Thanks.
Typically, you shouldn't set the velocity of a rigidbody directly. Per the Unity docs...
In most cases you should not modify the velocity directly, as this can result in unrealistic behaviour.
Instead, you usually want to impart a physical impulse to the ball using an API like AddForce or AddRelativeForce
That is easy.. You have to normalize the releaseVector.
ball.GetComponent<Rigidbody2D>().velocity = releaseVector.normalized * (power/3);
Then adjust the power to what you want. That way you will have the direction u wanted and speed depends on the power value.
If you want to know what normalize do, you can find more information here;
https://docs.unity3d.com/ScriptReference/Vector3.Normalize.html
In my game I have targets that spawn and move towards the player in a straight line. I use this code:
transform.LookAt(target.transform);
transform.position = Vector3.MoveTowards(transform.position, target.transform.position, speed);
Which makes the target home in on the player and move towards them.
I decided to try and make them move in a sin wave, so it looks less boring than going in a straight line, and I added a line of code, which now becomes:
transform.LookAt(target.transform);
transform.position = Vector3.MoveTowards(transform.position, target.transform.position, speed);
transform.position += transform.right * Mathf.Sin (Time.time * 3f) * 1f;
Now this works great, my enemies move left and right as they travel towards the player, except, they all follow the same path at the same time, resulting in a large group of targets moving left and right in a sort of choreographed way.
I believe it is because I use Time.time in the Mathf.Sin piece of code, which results in all the targets using the same value to generate a sine wave.
I've tried changing to Time.deltaTime and changing the Time.time variable to a Random.Range variable between 2 numbers, but they either don't work, stop my targets from moving, or make them look like they are vibrating. I've also tried using animations as well, but because of my MoveTowards code, this doesn't work either.
So, is there a way to make a GameObject move left and right in a sine wave and move forwards at the same time, but independently so they do not look choreographed? :-)
In order to offset the phase of the sine function (while keeping the frequency and amplitude) you have to add a constant to x in sin(x).
So you could try transform.position += transform.right * Mathf.Sin (Time.time * 3f + i) * 1f; where i is unique to the target moving towards the player.
See also: https://en.wikipedia.org/wiki/Sine_wave