Move GameObject back and forth - c#

I got an Object that I want to move up to Point A and when it reaches Point A it should move to Point B. When it reaches Point B it should move back to Point A.
I thought I could use Vector3.Lerp for this
void Update()
{
transform.position = Vector3.Lerp(pointA, pointB, speed * Time.deltaTime);
}
But how can i move back then? Is there an elegant way to archieve this? Obviously I would need 2 Lerps like this way:
void Update()
{
transform.position = Vector3.Lerp(pointA, pointB, speed * Time.deltaTime); // Move up
transform.position = Vector3.Lerp(pointB, pointA, speed * Time.deltaTime); // Move down
}
Could someone help me out?

There are many ways to do this but Mathf.PingPong is the easiest and the simplest way to accomplish this. Use Mathf.PingPong to get number between 0 and 1 then pass that value to Vector3.Lerp. That's it.
Mathf.PingPong will automatically return value will that will move back and forth between 0 and 1. Read the linked documentation for more info.
public float speed = 1.19f;
Vector3 pointA;
Vector3 pointB;
void Start()
{
pointA = new Vector3(0, 0, 0);
pointB = new Vector3(5, 0, 0);
}
void Update()
{
//PingPong between 0 and 1
float time = Mathf.PingPong(Time.time * speed, 1);
transform.position = Vector3.Lerp(pointA, pointB, time);
}

Related

Drone Controller in Unity: movement issues with the unity inputSystem

I'm trying to build a controller for a unity gameobject to move it similary to a normel quadrotor drone. So the drone should be able to ascend and descend by mobing the left stick up and down, turn around by moving the left stick sidewards and move horizontally by using the right stick.
I tried implementing it with the unity inputSystem, but unfortunately, it doesn't really work the way it is supposed to.
Movements are mostly not smooth and the rotation causes the horizonzal movement to move in wrong directions.
Here is my code:
public void OnMove(InputValue inputValue)
{
//horizontal movement
Debug.Log(inputValue.Get());
Vector3 move = new Vector3(inputValue.Get<Vector2>().x * 10 * Time.deltaTime, 0, inputValue.Get<Vector2>().y * 1 * Time.deltaTime);
move = Quaternion.Euler(0, rotation, 0) * move;
//playerDrone.transform.position += new Vector3(inputValue.Get<Vector2>().x * 10 * Time.deltaTime, 0, inputValue.Get<Vector2>().y * 10 * Time.deltaTime);
playerDrone.transform.Translate(move, Space.World);
}
public void OnClockwiseRotation()
{
//rotation of drone clockwise
playerCam.transform.Rotate(0, 0.5f, 0, Space.World);
rotation += 0.5f;
}
public void OnCounterclockwiseRotation()
{
//rotation of drone counterclockwise
Debug.Log("Rotation");
playerCam.transform.Rotate(0, -0.5f, 0, Space.World);
rotation += 0.5f;
}
public void OnAscend1()
{
//ascend drone
playerDrone.transform.position += new Vector3(0, 0.1f, 0);
Debug.Log("Ascend");
}
public void OnDescend()
{
//descend drone
Debug.Log("Descend");
playerDrone.transform.position += new Vector3(0, -0.1f, 0);
}
Does anyone know why the movement is so problematic with that implementation?
Thanks in advance
There are a few mistakes I'd say
Your rotation and your ascending are frame-rate dependent. Here you didn't use Time.deltaTime
In both OnClockwiseRotation and OnCounterclockwiseRotation you do
rotation += 0.5f;
You are using once playerDrone and another time playerCam .. you should probably stick to one as it sounds like you are only rotating a camera but not the drone.
In general I would not hardcode the values into your code but rather expose some class fields so you can adjust the velocities vis the Inspector without having to recompile
You probably would rather do something like
// Adjust these in the Inspector!
// in angle per second
public float rotationSpeed = 45;
// in meter per second
public float ascendingSpeed = 1;
public float forwardSpeed = 1;
public float sidewardsSpeed = 1;
public void OnMove(InputValue inputValue)
{
// In this case probably not a big deal but in general use getters only once
var input = inputValue.Get();
var move = new Vector3(input.x * sidewardsSpeed * Time.deltaTime, 0, input.y * forwardSpeed * Time.deltaTime);
move = Quaternion.Euler(0, rotation, 0) * move;
playerDrone.transform.Translate(move, Space.World);
}
public void OnClockwiseRotation()
{
var angle = rotationSpeed * Time.deltaTime;
playerDrone.transform.Rotate(0, angle, 0, Space.World);
rotation += angle;
}
public void OnCounterclockwiseRotation()
{
var angle = rotationSpeed * Time.deltaTime;
playerDrone.transform.Rotate(0, -angle, 0, Space.World);
rotation -= angle;
}
public void OnAscend1()
{
playerDrone.transform.position += Vector3.up * ascendingSpeed * Time.deltaTime;
}

How can I smooth rotation which is based on cursor position?

private float rotationZ;
Vector3 difference = Camera.main.ScreenToWorldPoint(Input.mousePosition) - transform.position;
difference.Normalize();
float rotationZ = Mathf.Atan2(difference.y, difference.x) * Mathf.Rad2Deg;
transform.rotation = Quaternion.Euler(0f, 0f, rotationZ);
So far my code will get the cursor position and instantly perform a transform. However, if I move the cursor near the center of the pivot, the rotation will be instantaneous and choppy. How can I add a slight smooth to this, to make it travel from point A to point B slower?
You would store the actual target value in a field and then move towards it
private Quaternion targetRotation;
private Camera camera;
private void Awake ()
{
// Camera.main is quite expensive so rather store the reference
camera = Camera.main;
// Initially set your current rotation as target
// especially important if you don't always update the target rotation but e.g.
// only if a certain button is pressed
targetRotation = transform.rotation;
}
private void Update ()
{
UpdateTargetRotation();
RotateTowardsTarget();
}
private void UpdateTargetRotation ()
{
var difference = (camera.ScreenToWorldPoint(Input.mousePosition) - transform.position).normalized;
var targetRotationZ = Mathf.Atan2(difference.y, difference.x) * Mathf.Rad2Deg;
targetRotation = Quaternion.Euler(0, 0, targetRotationZ);
}
Now for the RotateTowardsTarget you have many options.
One of them would indeed be using Quaternion.Lerp with a certain interpolation factor. Here you interpolate smoothly towards the target value but very fast at the beginning and get slower and slower the closer you get to the target value regardless of how much you rotate in total. This also might never really reach the exact target value.
// Adjust in the Inspector
public float interpolationFactor = 5f;
private void RotateTowardsTarget ()
{
transform.rotation = Quaternion.Lerp(transform.rotation, targetRotation, interpolationFactor * Time.deltaTime);
}
Or another option would be to rather use Quaternion.RotateTowards which rotates with a fixed speed
// Adjust in the Inspector
public float anglePerSecond = 45;
private void RotateTowardsTarget ()
{
transform.rotation = Quaternion.RotateTowards(transform.rotation, targetRotation, anglePerSecond * Time.deltaTime);
}
You're looking for Mathf.Lerp
Example of how you could use it:
float rotationZ;
//Changes how quickly will rotate
const float rotationSpeed = 0.58f;
Vector3 difference = Camera.main.ScreenToWorldPoint(Input.mousePosition) - transform.position;
difference.Normalize();
rotationZ = Mathf.Atan2(difference.y, difference.x) * Mathf.Rad2Deg;
float lerpRotation = Mathf.Lerp(transform.rotation.z, rotationZ, Time.deltaTime * rotationSpeed);
transform.rotation = Quaternion.Euler(0f, 0f, lerpRotation);
You should use Quaternion.Slerp() in a coroutine.
You check the cursor position every FixedUpdate then start a coroutine that interpolates and sets the rotation every frame until the next FixedUpdate comes. The created lag is basicly unnoticable and it looks silky smooth.
For the interpolation t value you should use a variable that stores the time elapsed since the last FixedUpdate (you add that up from time.deltatimes), and to get the t you just divide it by 0.02secs.
Then when the coroutin finishes, set the rotation to the final value.

When I make an object face a mouse cursor in Unity it eventually offsets

I made a script that makes the player point towards the mouse cursor, but recently I discovered a bug. When I move the mouse cursor too much (An example being when I spin the mouse around the object in circles, causing the object to move around.), the object ends up pointing a bit off of where the mouse should be. As in, the cursor would signal the object to look at it, and the object ends up looking the slightest bit off, making it feel quite odd after maneuvering quickly. How can I make it so the object always faces the cursor, with no offsets, even when I move the cursor as much as possible.
private void LateUpdate()
{
Vector3 lookAtPoint = Camera.main.ScreenToWorldPoint(Input.mousePosition);
Vector3 mousePoint = new Vector3(lookAtPoint.x, lookAtPoint.y, 0);
float angle = getAngle(transform.position, mousePoint);
transform.rotation = Quaternion.Lerp(transform.rotation, Quaternion.Euler(0, 0, angle), 9f * Time.deltaTime);
float getAngle(Vector3 currentLocation, Vector3 mouseLocation)
{
float x = mouseLocation.x - currentLocation.x;
float y = mouseLocation.y - currentLocation.y;
return angle = Mathf.Rad2Deg * Mathf.Atan2(y, x);
}
}
Looks like it's down to the way that you are using Quaternion.Lerp(). In particular, the last parameter - it is meant to be a value between 0f and 1f which you supply, it does not auto-increment for you.
So to fix this issue, what you want to do is save off a float somewhere. When you move the mouse (mouse position has changed since last frame) then start that value at 0f again. Then increment it at some value every frame until it is equal to or greater than 1f.
Doing this will not only fix your bug. It will, depending on how fast your increment, give you a smooth rotation effect. Below is an example.
internal class SomeClass : MonoBehaviour
{
private float lerpAmount = 0f;
private Vector3 cachedMousePosition = Vector3.zero;
private void LateUpdate()
{
var mousePosition
= Camera.main.ScreenToWorldPoint(Input.mousePosition)
* new Vector3(1, 1, 0);
bool recalculateRotation = false;
if (this.cachedMousePosition != mousePosition)
{
this.lerpAmount = 0f;
recalculateRotation = true;
}
if (this.lerpAmount < 1f)
{
this.lerpAmount = Mathf.Min(this.lerpAmount + Time.deltaTime, 1f);
recalculateRotation = true;
}
if (recalculateRotation)
{
float angle = getAngle(transform.position, mousePoint);
transform.rotation = Quaternion.Lerp(transform.rotation, Quaternion.Euler(0, 0, angle), this.lerpAmount);
}
float getAngle(Vector3 currentLocation, Vector3 mouseLocation)
{
float x = mouseLocation.x - currentLocation.x;
float y = mouseLocation.y - currentLocation.y;
return angle = Mathf.Rad2Deg * Mathf.Atan2(y, x);
}
}

Issue When Moving Object to Specific Position Unity 3D

I have an object that I want to move slowly & smoothly to a specific position when clicking, so I used this code:
currpos = transform.position;
Vector3 NewPos = new Vector3(- 10, currpos.y, currpos.z + 2);
Rigidbody.MovePosition(Vector3.Lerp(transform.position, NewPos, Time.deltaTime* MoveSpeed)) ;
The problem I have is when the MoveSpeed is low the object move a little bit and didn't reach the specific position, and when increasing the MoveSpeed he reach the specific position but quickly! do you have any suggestion?
public static Vector3 Lerp(Vector3 a, Vector3 b, float t);
Interpolates between the vectors a and b by the interpolant t. The
parameter t is clamped to the range [0, 1]. This is most commonly used
to find a point some fraction of the way along a line between two
endpoints.
As the Doc says, it is some fraction instead of distance. The logic isn't right.
So the code could be revised as follows:
currpos = transform.position;
Vector3 NewPos = new Vector3(- 10, currpos.y, currpos.z + 2);
Vector3 Dir= (NewPos-transform.position).normalized;
Vector3 velocity= Dir*MoveSpeed;
Rigidbody.MovePosition(transform.position+velocity);
If you insist using Lerp:
currpos = transform.position;
Vector3 NewPos = new Vector3(- 10, currpos.y, currpos.z + 2);
float dist= (NewPos-transform.position).magnitude;
if(dist!=0)
{
Rigidbody.MovePosition(Vector3.Lerp(transform.position, NewPos, Time.deltaTime* MoveSpeed)) ;
}
else
{
//do nothing new postion arrived
}
According to Unity3D documentation on Rigidbody.MovePosition you need to enable Rigidbody interpolation for smooth transition between the two positions. This should also be done in the FixedUpdate like this:
void FixedUpdate()
{
rb.MovePosition(transform.position + transform.forward * Time.deltaTime);
}
The Rigidbody.MovePosition takes just one parameter which is the destination Vector3. You should just specify destination point without the Lerping so try this instead:
Rigidbody.MovePosition(NewPos + Time.deltaTime * MoveSpeed);
And do it in the FixedUpdate. Also:
If the rigidbody has isKinematic set false then it works differently. It works like transform.position=newPosition and teleports the object (rather than a smooth transition).
Lerp Function gives a point between 2 Vectors everytime you call it, so just like the turtle problem, it will never reach to destionation. It will get slower and slower each time.
You can eliminate this behaviour with fixing travel speed to a constant but it will not look as good as slowing gradually as approach.
Also you can eliminate this behaviour with stopping the object after it gets too close to object, (that is how i use it usually)
Also note that, if you want to animate someting with code and you want it to be smooth you have to calculate it on "Update" Function.
So the code can be something like
void GoToTarget(){
var currpos = transform.position;
NewPos = new Vector3(- 10, currpos.y, currpos.z + 2);
traveling = true;
}
void Update () {
if (traveling){
rigidbody.MovePosition(Vector3.Lerp(transform.position, NewPos, Time.deltaTime* MoveSpeed));
if((transform.position-NewPos).magnitude < 0.1f){
//Current position is too close to Target, teleport to target and stop
transform.position = NewPos;
traveling = false;
}
}
}
Rather than lerping, why not just move towards it?
currpos = transform.position;
Vector3 NewPos = new Vector3(- 10, currpos.y, currpos.z + 2);
var position = Vector3.MoveTowards(transform.position, NewPos, Time.deltaTime* MoveSpeed);
Rigidbody.MovePosition(position);

Unity bouncing when colliding with object

I have made a script with movement, that finally works as i want it to, except one thing... I want it to be a first person game, but the way the movement works now, is on the global axis, which means that W is always torwards one specific direction, no matter what direction my camera is turning... How do i fix this? i want the movement to stay how it is, but with the W key to always be forward depending on the way the camera or player is looking.
Please let me know how my script would look edited, or atleast what part i have to change.
I would also like to add that i would love to be able to do a wall jump, but i am not sure how to add that behavior.
Here is my movement script:
public class MovementScript : MonoBehaviour {
public float speed;
public float jumpforce;
public float gravity = 25;
private Vector3 moveVector;
private Vector3 lastMove;
private float verticalVelocity;
private CharacterController controller;
// Use this for initialization
void Start () {
controller = GetComponent<CharacterController> ();
//Låser og gemmer musen
Cursor.lockState = CursorLockMode.Locked;
}
// Update is called once per frame
void Update () {
//Låser musen op
if (Input.GetKeyDown ("escape"))
Cursor.lockState = CursorLockMode.None;
moveVector = Vector3.zero;
moveVector.x = Input.GetAxis("Horizontal");
moveVector.z = Input.GetAxis("Vertical");
if (controller.isGrounded) {
verticalVelocity = -1;
if (Input.GetButton("Jump")) {
verticalVelocity = jumpforce;
}
} else {
verticalVelocity -= gravity * Time.deltaTime;
moveVector = lastMove;
}
moveVector.y = 0;
moveVector.Normalize ();
moveVector *= speed;
moveVector.y = verticalVelocity;
controller.Move (moveVector * Time.deltaTime);
lastMove = moveVector;
}
}
You need to go from local to world space:
for example when you want to move on the x-axis regarding the player's orientation, the local vector is (1, 0, 0), which you get from your input axis, but the CharacterController need a world based direction (see the doc of the Move function)
A more complex move function taking absolute movement deltas.
To get this, use Transform.TransformDirection like this
worldMove = transform.TransformDirection(moveVector);
controller.Move (worldMove * Time.deltaTime);
EDIT regarding your issue with the controller moving a bit after releasing the input:
That's because GetAxis gives you a smoothed value. Replace GetAxis by GetAxisRaw and it should work
Modify the final moving code to
controller.Move(moveVector.z * transform.forward * Time.deltaTime);
controller.Move(moveVector.x * transform.right * Time.deltaTime);
controller.Move(moveVector.y * transform.up * Time.deltaTime);
Alternatively, suppose your character is only rotated about the Y axis, you can look into rotating your moveVector by your character's Y rotation so moveVector's forward points parallel to your chracter's forward.
I found a good explaination of rotating a vector here: https://answers.unity.com/questions/46770/rotate-a-vector3-direction.html
Rotating a vector:
moveVector = Quaternion.Euler(0, transform.eulerAngles.y, 0) * moveVector;

Categories