I have a gameobject and I would like to find out if the object is moving upward (positive) or downward (negative). How do I get to do this?
Assuming that the object has a rigidbody, you can use this in the update method (or anywhere for that matter) of a MonoBehavior attached to your GameObject.
Rigidbody rb = GetComponent<Rigidbody>();
float verticalVelocity = rb.velocity.y;
If you want the velocity along any axis, you can use the dot product:
Rigidbody rb = GetComponent<Rigidbody>();
Vector3 someAxisInWorldSpace = transform.forward;
float velocityAlongAxis = Vector3.Dot(rb.velocity, someAxisInWorldSpace);
The above code would give you the velocity along the GameObject's forward axis (the forward velocity).
If the object doesn't have a rigidbody, you can save its old position in a variable and then compare it to the current position in the update loop.
Vector3 oldPosition;
private void Start() {
oldPosition = transform.position;
}
private void Update() {
Vector3 velocity = transform.position - oldPosition;
float verticalVelocity = velocity.y / Time.deltaTime; // Divide by dt to get m/s
oldPosition = transform.position;
}
Related
I am trying ground collision of kinematic bodies using rigidbody2D.cast. Player and ground collider overlap with each other.
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class ForceMethod : MonoBehaviour
{
[SerializeField] Vector2 worldGravity;
[SerializeField] float downCastDistance = 0.05f;
Vector2 startingVelocity = new Vector2(0, 0);
RaycastHit2D[] hits = new RaycastHit2D[2];
Rigidbody2D rb;
void Start()
{
rb = GetComponent<Rigidbody2D>();
}
void Update()
{
rb.Cast(Vector2.down, hits, downCastDistance);
}
void FixedUpdate()
{
rb.MovePosition(rb.position + startingVelocity * Time.deltaTime);
startingVelocity += worldGravity * Time.deltaTime;
if (hits[0].collider.name == "Ground")
{
rb.MovePosition(rb.position);
}
}
}
Before
After
rb.MovePosition(rb.position);
does nothing, since moves the rb in its current same position.
What you want to do probably is check before moving the rb if the cast hits the ground, but cast in the fixed update as well, and not of a fixed downCastDistance but cast by the same distance the rb will travel downward (which is startingVelocity * Time.deltaTime). If it doesn't hit then go down normally as you did, if it does, move the rb to the highest position between the normal fall update and the position of the point where the raycast hit (which is at ground level):
//FixedUpdate()
float currTravelDistance = startingVelocity.y * Time.deltaTime;
rb.Cast(Vector2.down, hits, Mathf.Abs(currTravelDistance));
if (hits[0].collider.name == "Ground")
{
rb.position.y = Mathf.Max((rb.position + currTravelDistance).y, hits[0].point.y);
}
else {
rb.MovePosition(rb.position + currTravelDistance);
}
also since this moves the rb exactly on the ground, if the rb's center is in the middle of the sprite, move it up by the distance from the sprite's "feet" to its center.
Finally if the player hits the ground you also need to reset to 0 startingVelocity
I'm trying to realize moving on an object round sphere (walk on it), but when it gets to the equator of the sphere, it stops moving.
public Transform planet;
public bool AlignToPlanet;
public float gravityConstant = -9.8f;
void FixedUpdate()
{
Vector3 toCenter = planet.position - transform.position;
toCenter.Normalize();
GetComponent<Rigidbody>().AddForce(toCenter * 9.8f, ForceMode.Acceleration);
if (AlignToPlanet)
{
Quaternion q = Quaternion.FromToRotation(-transform.up, -toCenter);
q = q * transform.rotation;
transform.rotation = Quaternion.Slerp(transform.rotation, q, 1);
}
Debug.Log(CrossPlatformInputManager.GetAxis("Vertical"));
GetComponent<Rigidbody>().AddForce(transform.forward * 2, ForceMode.Impulse);
}
First off, you should avoid calling GetComponent more than necessary. Just call it once to get the Rigidbody in Awake then refer to the result in FixedUpdate.
Second, once you determine the object's up direction, you can use cross products to determine what would be the forward that is closest to the previous forward while still maintaining that up.
Then, you can use Quaternion.LookRotation to set that up and forward.
Finally, you should use Rigidbody.MoveRotation to set the rotation.
Altogether:
private Rigidbody rb;
public Transform planet;
public bool AlignToPlanet;
public float gravityConstant = -9.8f;
void Awake()
{
rb = GetComponent<Rigidbody>();
}
void FixedUpdate()
{
Vector3 toCenter = planet.position - transform.position;
toCenter.Normalize();
rb.AddForce(toCenter * 9.8f, ForceMode.Acceleration);
if (AlignToPlanet)
{
Vector3 newUp = -toCenter;
Vector3 newRight = Vector3.Cross(newUp, transform.forward);
Vector3 newForward = Vector3.Cross(newRight, newUp);
Quaternion newRot = Quaternion.LookRotation(newForward, newUp);
rb.MoveRotation(q);
}
Debug.Log(CrossPlatformInputManager.GetAxis("Vertical"));
rb.AddForce(transform.forward * 2, ForceMode.Impulse);
}
I set up a new project with your script, and it works fine for me.
The only things I changed (beside the AddForce on the forward to test it) are :
The FromToRotationin which you pass opposite vectors (one to the up, and one to the down)
The Slerp I removed, it is useless since you pass a value of 1.
So, it looks like this :
if (AlignToPlanet)
{
// removed the minus in front of toCenter
Quaternion q = Quaternion.FromToRotation(-transform.up, toCenter);
transform.rotation = q * transform.rotation;
}
EDIT
I agree with #Ruzhim : the LookRotation is a better way to compute your new rotation, since it takes also the forward of your object.
(And for the fact to not use the GetComponent in any Update)
I want to move my player character(human) on a curved surface. But at the same time character shall stay perpendicular to the surface normals and it should face in the movement direction and can handle collisions(if there is a wall ahead, shall not be able to go through it).
I tried to make a parent stay over normals and change the child local rotation towards direction of motion of its parent. But it has several limitations as of now.
Here is the code what i was using:
[SerializeField] float raycastLength = 1f;
bool canPlayerMove = true;
public float speed = 2f;
public Vector3 offset; //object's position offset to ground / surface
public Quaternion childDirection;
private void Update()
{
float moveHorizontal = SimpleInput.GetAxis("Horizontal");
float moveVertical = SimpleInput.GetAxis("Vertical");
Ray ray = new Ray(transform.position, -transform.up);
RaycastHit hitInfo;
if (Physics.Raycast(ray, out hitInfo, raycastLength))
{
transform.rotation = Quaternion.LookRotation(Vector3.up, hitInfo.normal);
transform.position = hitInfo.point + offset;
Debug.DrawLine(ray.origin, hitInfo.point, Color.red);
}
if (canPlayerMove)
{
Vector3 movement = new Vector3(moveHorizontal, 0, moveVertical);
if (movement != Vector3.zero)
{
childDirection = Quaternion.Slerp(transform.GetChild(0).localRotation, Quaternion.LookRotation(movement), 0.15F);
transform.GetChild(0).localRotation = childDirection;
}
transform.Translate(movement * speed * Time.deltaTime, Space.Self);
}
}
first to not make your player go thru walls you want to add a collider to your walls and not set it as trigger, you will also need a rigidbody on your player and this will help in the next steps.
Secondly you will need to acces the rigidBody in code using this: (if you Check Use Gravity it will also stay on your terrain that you made)
private Rigidbody rb;
private float speed = 7.5f;
private void Start()
{
//this gets the rigidbody on the gameObject the script is currently on.
rb = this.GetComponent<Rigidbody>();
}
private void Update()
{
float hor = Input.GetAxis("Horizontal");
float vert = Input.GetAxis("Vertical");
//this will move your player frame independent.
rb.MovePosition(this.transform.position + new Vector3(hor, 0, vert) * speed *
Time.deltaTime);
}
Also make sure that you have a rigidBody on your player, else it will throw an error.
By using method transform.translate(Vector3.left * 5f * Time.Deltatime; does this change the velocity of a gameobject with a rigidbody ,because in my case it doesnt work.Is there a way i can move an object so the velocity changes .If not is there any way to measure velocity of an object without having a Rigidbody attached to it. Thanks.
You can measure the velocity vector like this:
Vector3 pos, velocity;
void Awake()
{
pos = transform.position;
}
void Update()
{
velocity = (transform.position - pos) / Time.deltaTime;
pos = transform.position;
}
You can use Transform.hasChanged
if (!this.transform.hasChanged)
{
print("Player is not moving");
}
transform.hasChanged = false;
Is it possible to move a gameobject to click position in unity 2D while gameobject is a rigidbody 2d with gravity and movement looks like jump from gameobject's position to click position. Any help reference will be really helpful :)
something a bit like this?
Transform projectile;
float speed = 5f;
void Update()
{
if(Input.GetMouseButtonDown(0))
{
Vector3 target = Camera.main.ScreenToWorldPoint(Input.mousePosition);
target.z = projectile.position.z;
Vector3 offset = target - projectile.position;
Vector3 direction = offset.normalized;
float power = offset.magnitude;
projectile.GetComponent<RigidBody2D>().AddForce(direction * power * speed);
}
}