I'm making a basic 2D space shooter game in unity. I have the movements working fine and the camera follows the player. I've been scratching my head over how to give the camera a slight delay from the player moving to the camera moving to catch up to it without it teleporting. I was told to use a Vector3.Lerp and tried a few things from stackoverflow answers but none seemed to work with the way my code is set up. Any suggestions?
(myTarget is linked to the player)
public class cameraFollower : MonoBehaviour {
public Transform myTarget;
void Update () {
if(myTarget != null){
Vector3 targPos = myTarget.position;
targPos.z = transform.position.z;
transform.position = targPos;
}
}
}
If you linear interpolate (Lerp) you risk that Time.deltaTime * speed > 1 in which case the camera will start to extrapolate. That is, instead of following it will get in front if your target.
An alternative is to use pow in your linear interpolation.
float speed = 2.5f;
float smooth = 1.0f - Mathf.Pow(0.5f, Time.deltaTime * speed);
transform.position = Vector3.Lerp(transform.position, targetPos, smooth);
Mathf.Pow(0.5, time) means that after 1/speed second, half of the distance to the target point will be travelled.
The idea with Lerping camera movement is to gradually and smoothly have the camera make its way to the target position.
The further away the camera is, the bigger the distance it will travel per frame, but the closer the camera is, the distance per frame becomes smaller, making the camera ease into its target position.
As an example, try replacing your transform.position = targPos; line with:
float speed = 2.5f; // Set speed to whatever you'd like
transform.position = Vector3.Lerp(transform.position, targPos, Time.deltaTime * speed);
Related
I've been told that Rigidbody.MoveRotation is the best way in Unity 3D to rotate the player between fixed positions while still detecting hits. However, while I can move smoothly from fixed position to position with:
if (Vector3.Distance(player.position, targetPos) > 0.0455f) //FIXES JITTER
{
var direction = targetPos - rb.transform.position;
rb.MovePosition(transform.position + direction.normalized * playerSpeed * Time.fixedDeltaTime);
}
I can't find out how to rotate smoothly between fixed positions. I can rotate to the angle I want instantly using Rigidbody.MoveRotation(Vector3 target);, but I can't seem to find a way to do the above as a rotation.
Note: Vector3.Distance is the only thing stopping jitter. Has anyone got any ideas?
First of all MoveRotation doesn't take a Vector3 but rather a Quaternion.
Then in general your jitter might come from overshooting - you might be moving further than the distance between your player and target actually is.
You can avoid that bit by using Vector3.MoveTowards which prevents any overshooting of the target position like e.g.
Rigidbody rb;
float playerSpeed;
Vector3 targetPos;
// in general ONLY g through the Rigidbody as soon as dealing wit Physics
// do NOT go through transform at all
var currentPosition = rb.position;
// This moves with linear speed towards the target WITHOUT overshooting
// Note: It is recommended to always use "Time.deltaTime". It is correct also during "FixedUpdate"
var newPosition = Vector3.MoveTowards(currentPosition, targetPos, playerSpeed * Time.deltaTime);
rb.MovePosition(newPosition);
// [optionally]
// Note: Vector3 == Vector3 uses approximation with a precision of 1e-5
if(rb.position == targetPos)
{
Debug.Log("Arrived at target!");
}
Then you can simply apply this same concept also to rotation by going through the equivalent Quaternion.RotateTowards basically just the same approach
Rigidbody rb;
float anglePerSecond;
Quaternion targetRotation;
var currentRotation = rb.rotation;
var newRotation = Quaternion.RotateTowards(currentRotation, targetRotation, anglePerSecond * Time.deltaTime);
rb.MoveRotation(newRotation);
// [optionally]
// tests whether dot product is close to 1
if(rb.rotation == targetRotation)
{
Debug.Log("Arrived at rotation!");
}
You can go one step further and use a tweeting library to tween between rotations.
DOTween
With that you can call it like this:
rigidbody.DoRotate(target, 1f) to rotate to target in 1 second.
Or even add callbacks.
rigidbody.DoRotate(target, 1f).OnComplete(//any method or lambda you want)
If at some point you want to cancel the tween yuou can save it on a variable and then call tween.Kill();
So, you want to animate the rotation value over time until it reaches a certain value.
Inside the Update method, you can use the Lerp method to keep rotating the object to a point, but you will never really reach this point if you use Lerp. It will keep rotating forever (always closer to the point).
You can use the following:
private bool rotating = true;
public void Update()
{
if (rotating)
{
Vector3 to = new Vector3(20, 20, 20);
if (Vector3.Distance(transform.eulerAngles, to) > 0.01f)
{
transform.eulerAngles = Vector3.Lerp(transform.rotation.eulerAngles, to, Time.deltaTime);
}
else
{
transform.eulerAngles = to;
rotating = false;
}
}
}
So, if the distance between the current object angle and the desired angle is greater than 0.01f, it jumps right to the desired position and stop executing the Lerp method.
I am creating a Jump and run game but you are driving with a car. I use the the Wheel Joint 2D collider and I am also able to jump. Here is my code for the movement:
void Update()
{
movement = Input.GetAxis("Horizontal");
if (Input.GetButtonDown("Jump") && IsGrounded())
{
carRb.velocity = new Vector2(carRb.velocity.x, jumpForce);
}
if (Input.GetButtonUp("Jump") && carRb.velocity.y > 0f)
{
carRb.velocity = new Vector2(carRb.velocity.x, carRb.velocity.y * 0.5f);
}
}
private void FixedUpdate()
{
backTire.AddTorque(-movement * speed * Time.fixedDeltaTime);
frontTire.AddTorque(-movement * speed * Time.fixedDeltaTime);
carRb.AddTorque(-movement * carTorque * Time.fixedDeltaTime);
}
It works just fine but when I am fast and jump I rotate completely around my own axis and I land on my head and can't move anymore. Therefore I want to limit the rotation of the z-axis to a certain degree so that it won't happen anymore. I looked up how to do it but it doesn't fit for my car-context. Do you have any idea?
I would be very grateful
You have multiple options to solve this:
Freeze the rotation axis on the rigibody (very limiting option)
Detect the faulty position as "upside-down + grounded" and respawn the vehicle after 2s
Forcefully rotate the vehicle back to normal rotation when it's grounded (not in air anymore, so you still allow flips in the air)
Limit the angle via script.
Last thing could be done like this:
Vector3 eulerRot = rb.rotation.eulerAngles; // read current rotation
eulerRot.z = Mathf.Clamp(eulerRot .y, minRotation, maxRotation); // clamp it only on z axis.
rb.rotation = Quaternion.Euler(eulerRot); // set clamped rotation
Im scratching my head over this one, really cant figure it out.
Im trying to implement the player movement from this example:
https://www.youtube.com/watch?v=LNidsMesxSE
Starts at minute 4:25 and ends at minute 5:20
This script tries translating all that to unity.So I started all the way over from scratch and just want to make a simple movement script.
You can plug this into any Unity version, throw the script onto and object with a CharacterController component, add a child object with a mesh that will tilt, the main object will rotate around its Y axis and move.
I do recommend using a simple T pose character or atleast a long capsule so you can better see what is happening than when you would be using a cube to test this.
The weird glitches im having is that the object randomly spasms out even though im always only adding extremely small rotations and movements every frame. And 95% of the time it doesnt happen, so I havent been able to pinpoint exactly what is causing this.
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class CharacterMove : MonoBehaviour
{
public float speed = 200f;
public float rotationSpeed = 180f;
public float tiltFactor = 8f;
public bool normalizeVelocity = false;
CharacterController cc;
Vector3 velocity;
Transform armatureBody;
void Start()
{
cc = GetComponent<CharacterController>();
armatureBody = transform.GetChild(0);
}
void Update()
{
velocity = new Vector3(Input.GetAxis("Horizontal"), 0,
Input.GetAxis("Vertical"));
}
void FixedUpdate()
{
if ((velocity != Vector3.zero))
{
var ccVelocity = cc.velocity; // CharacterController velocity. This might be the problem, but without it I cannot rotate the object towards the actual forward velocity.
Quaternion toRotation;
toRotation = Quaternion.LookRotation(ccVelocity, Vector3.up);
transform.rotation = Quaternion.RotateTowards(transform.rotation, toRotation, rotationSpeed * Time.deltaTime);
if (Input.GetKey(KeyCode.S)) print("new toRotation: " + toRotation);
armatureBody.localEulerAngles = new Vector3(velocity.z * tiltFactor, 0, -velocity.x * tiltFactor); // Tilt Body towards velocity This causes the weirdest and most consistest twitching bugs when the tilt is backwards so the object is moving backwards, maybe because im using LookRotation to look backwards?
}
velocity = Quaternion.Euler(0, transform.eulerAngles.y, 0) * velocity; // forward is always forward
cc.SimpleMove(velocity * Time.deltaTime * speed);
}
}
This script does 4 things,
get the velocity from player input.
rotate the main Object(this script) towards the velocity
add tilt towards the velocity, to the child armatureBody, this is a t Pose character in my case.
move the main Object this script is on.
Any help would be appreciated.
I tried removing the vector3 != Zero check, I tried normalizing the vectors, and I tried using a different Quaternion rotation method, but all with the same faulty results.
Check in Update() if Input.forward > threshold if so rotate to velocity. Dont rotate to velocity when only going sideways or backwards, so we can now strafe sideways and walk backwards when standing still or run and rotate to our velocity with the AD keys when running.
void Update()
{
velocity = new Vector3(Input.GetAxis("Horizontal"), 0,
Input.GetAxis("Vertical"));
if (velocity.z > 0.2f)
{
forwardVelocityThreshold = true;
}
else forwardVelocityThreshold = false;
}
Then in our FixedUpdate() we check for that bool and apply the rotateToVelocity if true.
Adjust the following if statement:
if (ccVelocity != Vector3.zero && forwardVelocityThreshold)
{
toRotation = Quaternion.LookRotation(ccVelocity, Vector3.up);
transform.rotation = Quaternion.RotateTowards(transform.rotation, toRotation, rotationSpeed * Time.fixedDeltaTime);
}
Now it works as intended, I also changed the deltaTime to FixedDeltaTime as suggested by Voidsay in the comments, although I have heard conflicting things about this. Some say FixedUpdate always uses fixedDeltaTime even if you are using deltaTime, other say no, always use fixedDeltaTime in FixedUpdate.
The camera is supposed to follow a 2D character, here is code
void LateUpdate ()
{
var to = target.position;
to.z = transform.position.z;
var newPos = Vector3.Lerp(transform.position, to, speed * Time.deltaTime);
transform.position = newPos;
newPos.z = to.z;
Debug.DrawRay(newPos, Vector3.up, Color.green, 5);
}
I also draw positions of the character and the camera.
Red lines are character's positions, and green lines are camera's positions
What do I do wrong?
UPDATE:
I've figured out something interesting. On the picture below green lines are positions of the Camera that is moved by Vector3.Lerp inside a LateUpdate method. Yellow lines are positions of the character that I set to the character's Rigidbody2D inside FixedUpdate method, and the red lines are positions of the character's transform as they seen from inside the camera's LateUpdate.
What I want to say is that the actual position of the character is driven by its Rigidbody2D component. By changing Rigidbody2D's "Interpolate" option we can get different results.
The problem is that even if I add to the Rigidbody2D's position the same value every FixedUpdate tick, the result isn't so consistent. Sometimes the distance between new and old position is bigger than it should be.
Add to that the we set position of the camera in the LateUpdate method, which has different update rate than FixedUpdate, so even if we set the new position to the character's transform, and not to the Rigidbody2D, the movement of the camere still won't be smooth, because speed of the character will be different every frame.
For now I have only one solution.
Set the new position of the character to its transform, and not to the Rigidbody2D
Change position of the camere in its FixedUpdate, and not in the LateUpdate.
Then the positions will look like this
But since position of the camera is set in the FixedUpdate, it won't be so smooth as it might be, and also i'm not sure whether collision detection of the character will work good, since we set its position directly to its transform.
the problem could be coming from how you are using interpolation to determine how far to move the camera.
I don't know if Vector3.Lerp's behavior would be to extrapolate if the third parameter (its fraction) is higher than 1.0, but i suspect this could be the problem (specifically if there is a bit more time between frames, and speed * Time.DeltaTime becomes higher than 1.0)
A better way (eliminating the lerp) could be to do the interpolation of distance over speed and time yourself ;
void LateUpdate ()
{
var to = target.position;
to.z = transform.position.z;
//you can just multiply a Vector3 with a float
//so we can do the interpolation maths ourselves like this :
var distanceToMove = (to - transform.position) * speed * Time.deltaTime;
var newPos = transform.position + distanceToMove;
transform.position = newPos;
newPos.z = to.z;
Debug.DrawRay(newPos, Vector3.up, Color.green, 5);
}
(if that got confusing, here is cleaned up version to make it more concise)
void LateUpdate ()
{
var to = target.position;
to.z = transform.position.z;
transform.position += (to - transform.position) * speed * Time.deltaTime; ;
Debug.DrawRay(transform.position, Vector3.up, Color.green, 5);
}
I am having a problem with the camera following the player. When the player moves, the camera shakes and this effect is more noticeable when the player is in the crouched position. I am using root motion for the player with animations by mixamo.
Player Script:
Transform cameraT;
void Start () {
cameraT = Camera.main.transform;
}
void FixedUpdate ()
{
float sideMotion = Input.GetAxis("SideMotion");
float straightMotion= Input.GetAxis("StraightMotion");
if (Mathf.Abs(sideMotion) > 0 || Mathf.Abs(straightMotion) > 0)
{
transform.eulerAngles = new Vector3(transform.eulerAngles.x,
cameraT.eulerAngles.y);
}
}
Camera Script:
public float distanceFromPlayer=2f;
public float mouseSensitivity=6;
public Transform player;
public Vector2 pitchConstraint= new Vector2(-30,80);
Vector3 rotationSmoothVelocity;
Vector3 currentRotation;
public float rotationSmoothTime = 0.2f;
float yaw; //Rotation around the vertical axis is called yaw
float pitch; //Rotation around the side-to-side axis is called pitch
private void LateUpdate()
{
yaw += Input.GetAxis("Mouse X") * mouseSensitivity;
pitch -= Input.GetAxis("Mouse Y") * mouseSensitivity;
pitch = Mathf.Clamp(pitch, pitchConstraint.x, pitchConstraint.y);
currentRotation = Vector3.SmoothDamp
(currentRotation, new Vector3(pitch, yaw), ref rotationSmoothVelocity, rotationSmoothTime);
transform.eulerAngles = currentRotation;
transform.position = player.position - transform.forward * distanceFromPlayer;
}
For public transform I just added an empty game object to player chest level and parented it to the player. I got the rotating camera smoothly trick from an online tutorial but that exact thing won't work on player rotation though.
A character controller is attached to player without any rigidbody or collider.
You are going to want to use Lerp on the camera in order to smooth its motion from one position to another. The Scripting API has a good example of Vector3.Lerp.
So in your case, you could add a public variable for smoothing position as well. Something like positionSmoothTime. Then make a "Desired Position" variable, we'll call it destPosition.
Vector3 destPosition = player.position - transform.forward * distanceFromPlayer;
transform.position = Vector3.Lerp(transform.position, destPosition, positionSmoothTime);
This should effectively smooth it to where the stuttering should go away. Another thing that will help that someone else mentioned is using a part of the body that moves less (Like the head) as the target. This combined with the Lerp function will give you a smooth camera movement.
Another large help would be to move things into the Update() function. The reason being is FixedUpdate() doesn't get called every frame. So you could potentially get stutter as a result. If you move everything into Update() it will update every frame and help smooth things out. If you do this though you will need to multiply all movement by Time.deltaTime as shown in the example below.
Vector3 destPosition = (player.position - transform.forward * distanceFromPlayer) * Time.deltaTime;
For more examples, check the links to each function to see Unity's documentation on it. It has examples of everything I've shown here.