i would like to calculate the jump height. but i don't know how
Example:
RightBody2D rb = this.GetComponent<RightBody2D>();
// Do jump
rb.velocity = new Vector2(rb.velocity.x, jump);
// Here is the question. how do i calculate jump height
float jumpHeight = CalculateJumpHeight(jump);
indicator.transform.position = new Vector2(transform.position.x, jumpHeight);
You can calculate this as follows, however, it's not going to be perfectly accurate in every instance because Unity's physics system isn't deterministic.
float timeToReachApexOfJump = Mathf.Abs(rb.velocity.y / Physics.gravity.y);
float heightOfJump = (0.5f * Physics.gravity.y * timeToReachApexOfJump) + (rb.velocity.y * timeToReachApexOfJump);
Debug.Log(timeToReachApexOfJump);
Debug.Log(heightOfJump);
Related
I'm doing a crossing system for a soccer game, and i managed to cross a ball to a target.
The current code ads a curve to the ball, but it only works if the crossSpeed variable is 5, and if the player y axis is at a certain height, if i change the player y axis i need to adjust the drag and angular drag of the ball for it to work correctly.
I took this formula from this unity question: https://answers.unity.com/questions/384515/addforce-to-go-a-specific-distance-and-height.html?sort=votes
vWSpeed, are supposed to be the offset for the ball to go to the left, for it to then get curve from the AddCurveToBall function.
Can anyone help make this code work for multiple cross speeds?
private void FixedUpdate()
{
//if we are crossing the ball add curve to it
if(addCurveToBall)
AddCurveToBall();
}
//this will cross the ball
public void StartCrossBall()
{
//get the direction from the ball to the player
Vector3 dir = playerHead.transform.position - transform.position;
//normalize the direction
dir.Normalize();
//balistic trajectory
float g = Physics.gravity.magnitude; // get the gravity value
float vSpeed = Mathf.Sqrt(2 * g * playerHead.transform.position.y + 2f); // calculate the vertical speed
//balistic trajectory
float gW = Physics.gravity.magnitude; // i use the gravity value for the curve
float vWSpeed = Mathf.Sqrt(2 * g * playerHead.transform.position.y + 2f); // calculate the curve speed
float totalTime = 2 * crossSpeed / g; // calculate the total time
float hSpeed = Vector3.Distance(transform.position, playerHead.transform.position) / totalTime; // calculate the horizontal speed
rb.velocity = dir * hSpeed;
rb.velocity += new Vector3(0, vSpeed + yOffset, vWSpeed + xOffset); // launch the projectile!
//rb.velocity += new Vector3(hSpeed, vSpeed, dir.z + 0.5f); // launch the projectile!
addCurveToBall = true;
}
void AddCurveToBall()
{
//rb.AddForce(Physics.gravity * rb.mass);
rb.AddForce(new Vector3(0,0,Physics.gravity.y) * rb.mass);
}
private void OnCollisionEnter(Collision collision)
{
addCurveToBall = false;
ResetRigidBodyPhysics();
}
I have been searching for an answer for hours, and I cant find the solution. I have a script that rotates a steering wheel when the car is turning. It has maximum rotation of 120 and a minimum rotation of -120. I have been trying to rotate with RotateTowards and other Quaternion methods but I can't figure them out. How can I make it when the turning angle is not zero and turning keys (a and d) are not pressed, it goes to its original rotation of 0, 0, 0, at a certain speed?
Here's my script for the steering wheel:
if (horizontalInput > 0)
{
wheel.transform.Rotate(Vector3.forward*Time.deltaTime*wheelspeed);
rotations -= wheelspeed;
}
else if (horizontalInput < 0)
{
wheel.transform.Rotate(-Vector3.forward * Time.deltaTime * wheelspeed);
rotations += wheelspeed;
}
And here is my (very bad) script for the minimum and maximum rotation of the steering wheel:
angle += Input.GetAxis("Horizontal") * Time.deltaTime*10;
angle = Mathf.Clamp(-120, angle, 120);
wheel.transform.localRotation = Quaternion.AngleAxis(angle, Vector3.forward);
angle += Input.GetAxis("Horizontal") * Time.deltaTime * 400;
angle = Mathf.Clamp(120, 0, angle);
wheel.transform.localRotation = Quaternion.AngleAxis(angle, Vector3.forward);
You should do something like this for the steering wheel script:
float rotateBack = 0f;
float angle = 0f;
void Update(){
angle += Input.GetAxis(“Horizontal”) * Time.deltaTime * 10;
angle = Mathf.Clamp(angle, -120, 120);
if (Input.GetAxis(“Horizontal”) == 0 && angle != 0f){
angle += rotateBack * Time.deltaTime;
if (angle > 0){
rotateBack = -10f;
}
else{
rotateBack = 10f;
}
}
transform.rotation = Quaternion.Euler(0f, 0f, angle);
}
What this does is setting a variable of angle to the input times a value to increase turning speed. Then it clamps if so it doesn’t go over 120, or under -120. Then, if there is no input and the angle isn’t zero: it will change the variable by how much to rotate back. The rotate back variable is set to either positive or negative based on which direction the steering wheel needs to be turned. The only problem with this script that I could see is the angle not being exactly 0, continuing the if statement. If this error affects your game, use the Mathf.Round(); function.
(If there are any errors, comment on this post.)
I would do what #ken is doing, but refactor the speed factors and other constants into fields so they are more easily changed and also use Mathf.MoveTowardsAngle to move the angle back to its neutral position.
[SerializeField] float rotateBackSpeed = 3f; // degrees per second
[SerializeField] float rotateSpeed = 10f; // degrees per second
[SerializeField] float angle = 0f; // degrees
[SerializeField] float minAngle = -120f; // degrees
[SerializeField] float maxAngle = 120f; // degrees
[SerializeField] float neutralAngle = 0f; // degrees
void Update()
{
angle = Mathf.Clamp(angle + Input.GetAxis(“Horizontal”) * rotateSpeed
* Time.deltaTime, minAngle, maxAngle);
if (Mathf.Approximately(0f, Input.GetAxis(“Horizontal”)))
{
angle = Mathf.MoveTowardsAngle(angle, neutralAngle,
rotateBackSpeed * Time.deltaTime);
}
transform.eulerAngles = angle * Vector3.forward;
}
I am using relative force on an object. The idea is
If I apply the up arrow it will add force to the direction it's moving.
When I apply the down arrow, it will apply negative force to the direction of motion slowing the object down.
The issue I am trying to solve is, when the down arrow is pressed enough that the object eventually slows down, I need the object to stop and not change directions.
In short what I need to do, is on a FixedUpdate pass work out if the objects velocity (or Direction) is going to be negative to the direction (or velocity) of movement. This seems possible, however I am not sure of the right calculation AddForce Uses.
My guess is I'll need to use some calculation and the Vector3.Dot method
Dot Product of two vectors.
The dot product is a float value equal to the magnitudes of the two
vectors multiplied together and then multiplied by the cosine of the
angle between them.
Something like this
var future = (forward) * _body.velocity.magnitude * _body.mass;
However, I am not sure what's needed.
Given
private Rigidbody _body;
public float Speed;
Physics calculation
private void FixedUpdate()
{
// get the controls
var hAxis = Input.GetAxis("Horizontal");
var vAxis = Input.GetAxis("Vertical");
if (_body.velocity == Vector3.zero)
{
// this is just to give some forward moment when stationary
// this will change at some stage and not my problem
var movement = new Vector3(hAxis, 0.0f, vAxis);
if (vAxis > 0)
_body.AddForce(movement * Speed);
}
else
{
// get the direction we are traveling
var direction = _body.velocity.normalized;
// calculate the vertical force from the controls
var forward = direction * vAxis * Speed;
// Calculate the side force from teh controls
var side = Vector3.Cross(direction, Vector3.up) * hAxis * -1f * Speed;
// This is equation is wrong.
var future = (forward) * _body.velocity.magnitude * _body.mass;
// what I am trying to say, is if the future velocity is negative
// i.e the user has pressed the back arrow so much it will go backwards
// then I want to come to a dead stop, and not apply any backwards force
if (Vector3.Dot(direction, future.normalized) < 0)
{
_body.velocity = Vector3.zero;
return;
}
_body.AddForce(forward + side);
}
}
I'm no real Physics pro but I'll try my best ;)
You could do the clamping int the next call of FixedUpdate so after the _body.velocity is updated
public Rigidbody _body;
public float Speed;
// store the last forward direction
private Vector3 lastForward;
private void FixedUpdate()
{
// Check and clamp the current velocity against the last one
// so after forces from last call have been applied
if (Vector3.Dot(lastForward, _body.velocity.normalized) < 0)
{
Debug.Log("Clamped negative");
_body.velocity = Vector3.zero;
}
lastForward = _body.velocity.normalized;
var hAxis = Input.GetAxis("Horizontal");
var vAxis = Input.GetAxis("Vertical");
// == for Vector3 has only a precision of 0.00001
// to be sure you coul use this instead
if (Mathf.Approximately(_body.velocity.magnitude, 0))
{
if (vAxis > 0) _body.AddForce(new Vector3(hAxis, 0.0f, vAxis) * Speed);
}
else
{
var forwardForce = Speed * vAxis * lastForward;
var sideForce = Speed * hAxis * -1f * Vector3.Cross(lastForward, Vector3.up);
_body.AddForce(forwardForce + sideForce);
}
}
Alternatively I guess what Immersive ment you could instead of using AddForce calculate the force yourself directly in velocity like
public Rigidbody _body;
public float Speed;
private void FixedUpdate()
{
var currentVelocity = _body.velocity;
var currentDirection = currentVelocity.normalized;
var hAxis = Input.GetAxis("Horizontal");
var vAxis = Input.GetAxis("Vertical");
if(Mathf.Approximately(_body.velocity.magnitude, 0))
{
// Directly calculating the velocity as Immersive said:
// newVelocity = currentVelocity + Time.deltaTime * Force / Mass
// where Force = direction * Speed
//
// Ofcourse you could also just use AddForce for this case but
// just for consistency I would use the same method for both cases
if(vAxis>0) _body.velocity += new Vector3(hAxis, 0.0f, vAxis) * (Time.deltaTime * Speed / _body.mass);
}
else
{
var forwardForce = Speed * vAxis * currentDirection;
var sideForce = Speed * hAxis * -1f * Vector3.Cross(currentDirection, Vector3.up);
// calculate the future velocity directly without using AddForce
// (see comment above)
var newVelocity = currentVelocity + (forwardForce + sideForce) * (Time.deltaTime / _body.mass);
// Only use this velocity if the new direction is still forward
// otherwise stop
_body.velocity = Vector3.Dot(currentVelocity, newVelocity) < 0 ? Vector3.zero : newVelocity;
}
}
Arrived at the PC and tested it now ;) both should basically work
Using AddForce and clamping in the next call
Using Calculated velocity
I wish to launch my Arrow GameObject at an angle of 30˚ and a velocity of 30 m/s. In a script I add a rigidbody to this Arrow. However, I am also trying to launch this Arrow in the direction of the player (away from the enemy) in a 3D scene. I cannot figure out how to plug in these variables to get the Vector3 for the "arrowRigidbody.velocity"
//THE VARIABLES REFERENCED ABOVE APPEAR LIKE SO:
Rigidbody arrowRigidbody;
Transform playerTransform;
Transform enemyTransform;
float angle = 30f;
float velocity = 30f;
//How do I use these variables in order to shoot the projectile at a speed
//of 30 m/s and an angle of 30˚ in the direction of the player in 3D scene
arrowRigidbody.velocity = /*????*/;
Thank you for your time and patience :)
Assuming you only shoot 'forward' you can use the simplified:
var targetDirn = transform.forward;
var elevationAxis = transform.right;
var releaseAngle = 30f;
var releaseSpeed = 30f;
var releaseVector = Quaternion.AngleAxis(releaseAngle, elevationAxis) * targetDirn;
arrowRigidbody.velocity = releaseVector * releaseSpeed;
If you need to shoot 'off-axis', you can replace the first two lines:
var targetDirn = (target.transform.position - transform.position).normalized;
var elevationAxis = Vector3.Cross(targetDirn, Vector3.up);
Using some geometry, knowing that vector will have a magnitude (m) of 1, the y-component will be m/2 and the x-component will be m*(3^.5)/2. Which will make your final value:
arrowRigidbody.velocity = new Vector2(Mathf.Pow(3, .5f)/2, 1/2) * velocity;
For a changing angle, you know that the x component will be m * cos(angle) and the y component will be m * sin(angle), leaving you with:
float velx = velocity * Mathf.Cos(angle * Mathf.Deg2Rad);
float vely = velocity * Mathf.Sin(angle * Mathf.Deg2Rad);
arrowRigidbody.velocity = new Vector2(velx, vely);
I have a Cube(Player) in my game scene. I have written a C# script to constrain the movement(using Mathf.Clamp()) of the Cube so it does not leave the screen.
Below is my FixedUpdate() method from the Script
private 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, x_min, x_max),
0.5f,
Mathf.Clamp(rb.position.z, z_min, z_max)
);
}
Values of x_min, x_max, z_min, z_max are -3, 3, -2, 8 respectively inputted via the unity inspector.
PROBLEM
The script is working fine but my player(Cube) can move up to -3.1 units in negative X-AXIS (if I keep pressing the left arrow button) which is 0.1 units more in negative X-AXIS(This behavior is true for other axes too). It obviously clamps -3.1 to -3 when I stop pressing the button.
Why is this happening? Why doesn't it(Mathf.Clamp()) restrict the Cube to -3 units in first place?
Why am I getting that extra 0.1 units?
And Why is this value 0.1 units only? Why not more or less?
Your issue could be caused because you are setting velocity and then position, but before the next frame unity adds the velocity to your objects position. Which is why it ends up 0.1 units off.
To fix this, try resetting the velocity of the object to zero if it's about to leave your boundaries.
This is happening because the internal physics update moves the cube after you have put it in its clamped position. Mathf.Clamp() does restrict the cube to the expected position in the first place, as you can verify by putting a Debug.Log directly after you assign the position
You are getting the extra .1 units because you give the object a speed that gets applied after you clamp
It is 0.1 units because of the speed and the setting for Fixed Timestep (in Project Settings > Time). If you would set a higher speed it would be more. If you would lower the Fixed Timestep it would also be a bit more.
You already got the reason why this happens.
In order to fix the problem, you can use this code to prevent the cube to go outside your boundaries:
private void FixedUpdate() {
Vector3 movement = new Vector3(Input.GetAxis("Horizontal"), 0f, Input.GetAxis("Vertical"));
// Get new position in advance
float xPos = rb.position.x + movement.x * speed * Time.fixedDeltaTime;
float zPos = rb.position.z + movement.z * speed * Time.fixedDeltaTime;
// Check if new position goes over boundaries and if true clamp it
if (xPos > x_max || xPos < x_min) {
if (xPos > x_max)
rb.position = new Vector3(x_max, 0, rb.position.z);
else
rb.position = new Vector3(x_min, 0, rb.position.z);
movement.x = 0;
}
if (zPos > z_max || zPos < z_min) {
if (zPos > z_max)
rb.position = new Vector3(rb.position.x, 0, z_max);
else
rb.position = new Vector3(rb.position.x, 0, z_min);
movement.z = 0;
}
rb.velocity = movement * speed;
}