Camera is following not smoothly - c#

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);
}

Related

Using MoveRotation in Unity 3D to turn player towards a certain angle

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.

Unity2d: SmoothDamp not moving to Lerp-ed calculated position

travelTime = 0.0f;
while (Vector3.Distance(Vector2.Lerp(startPos, endPos, travelTime), transform.position) >= maxDistance) {
locationToGo = Vector2.Lerp(startPos, Input.mousePosition, travelTime);
travelTime += 0.1f;
}
// SHOW
// Smoothly move the camera towards that target position
transform.position = Vector3.SmoothDamp(transform.position, locationToGo, ref velocity, smoothTime);
So, this code is meant to use Lerp to return a Vector2 a certain amount of 'steps' towards the mouse pointer. And then give that value to the SmoothDamp to move the square to the pos. But the Lerp is not calculating correctly. Does anyone know what is wrong, or any better working alternatives?
Since I don't really know what exactly you are trying to achieve i'm just gonna guess you want to move a GameObject to the MousePosition every Frame.
You could do it like this (it is out of my head, not compileproof):
GameObject ourGameobject;
Vector3 ourTarget;
void Update()
{
ourGameobject.transform.position = Vector3.lerp(ourGameobject.transform.position, ourTarget, Time.DeltaTime);
}
For faster or lower speed just multiply Time.DeltaTime with sth.
Also Note that Input.MousePosition returns a coordinate on the screen.

Smooth Lerp movement?

Currently it is my script for movement the player to the around the scene. How can i make it smoothly move?
void FixedUpdate()
{
bool running = Input.GetKey(KeyCode.LeftShift);
float h = Input.GetAxisRaw("Horizontal");
float v = Input.GetAxisRaw("Vertical");
bool isWalking = Mathf.Abs(h) + Mathf.Abs(v) > 0;
movement = ((running) ? runSpeed : walkSpeed) * new Vector3(h, 0.0f, v).normalized;
if (isWalking)
{
transform.position += movement * Time.fixedDeltaTime;
transform.LookAt(transform.position + movement);
}
}
Create a velocity vector:
Vector3 velocity = Vector3.zero;
Add your movement vector to velocity:
velocity += movement;
Add velocity to the actual position:
transform.position += velocity;
Smooth out velocity by reducing it over time:
velocity *= 0.975f;
The FixedUpdate() function is ran every fixed interval as defined by Time.fixedDeltaTime (which you can either set via the TimeManager (Fixed Timestep) or at runtime by directly setting Time.fixedDeltaTime to a new value.
Because you are moving the character position and rotation on a fixed interval, depending on framerate the character will either move a lot with a lower framerate or only move every few frames with a higher framerate.
For example with a fixed timescale of 0.02 seconds and the game running at a framerate of 30 frames per second (aka rendering every 0.033 seconds) your game will be doing this:
- [Time: 0.020s] Character position += 0.02
- [Time: 0.033s] Render frame with character at position 0.02
- [Time: 0.040s] Character position += 0.02
- [Time: 0.060s] Character position += 0.02
- [Time: 0.066s] Render frame with character at position 0.06
- [Time: 0.080s] Character position += 0.02
- [Time: 0.099s] Render frame with character at position 0.08
- [Time: 0.100s] Character position += 0.02
- [Time: 0.120s] Character position += 0.02
- [Time: 0.133s] Render frame with character at position 0.12
So in this example you can see how the character would jump forward at different amounts per frame and you can't guarantee what framerate the game will be running at either so it could end up being worse.
There are a few ways to make your character move smoothly though.
Put your code in an Update() loop instead of a FixedUpdate() loop, this will move the position of the character each rendered frame. Along with this you can multiply the movement speed by Time.deltaTime which is the time since the previous frame was rendered (aka time since Update() was last ran and the character was moved)
Use Vector3.Lerp(..)/Quaterion.Lerp(..) or Vector3.MoveTowards(..)/Quaternion.RotateToward(..) using a time/step value multiplied by Time.deltaTime to interpolate the character movement keeping it moving smoothly in relation to the game framerate.
If your character has a rigidbody component then you can simply just set the rigidbody interpolation to interpolate then move your character by calling:
characterRigidbody.MovePosition(wantedPosition);
characterRigidbody.MoveRotation(wantedRotation);
As a replacement to your existing transform movements (keeping your code inside of the FixedUpdate() loop)
But note that continuing to have your Input.* calls inside a FixedUpdate() is polling them more than needed so you might want to move them over into an Update() splitting the movement code and input listening code if you decide to do this. (I develop android games so maybe on PC this isn't worth worrying about as much, but probably still worth changing)
A direct code block answer to the question though could be to just try this, which is a combination of explanation 1 and 2:
// How long in seconds will it take the lerps to finish moving into position
public float lerpSmoothingTime = 0.1f;
private Vector3 targetPosition;
private Quaternion targetRotation;
void Update()
{
bool running = Input.GetKey(KeyCode.LeftShift);
float h = Input.GetAxisRaw("Horizontal");
float v = Input.GetAxisRaw("Vertical");
bool isWalking = Mathf.Abs(h) + Mathf.Abs(v) > 0;
movement = ((running) ? runSpeed : walkSpeed) * new Vector3(h, 0.0f, v).normalized;
if (isWalking)
{
targetPosition += movement * Time.deltaTime;
targetRotation = Quaternion.LookRotation(targetPosition - transform.position);
}
// Always lerping as if we suddenly stopped the lerp as isWalking became false the stop would be sudden rather than smoothly moving into position/rotation
transform.position = Vector3.Lerp(transform.position, targetPosition, Time.deltaTime / lerpSmoothingTime);
transform.rotation = Quaternion.Lerp(transform.rotation, targetRotation, Time.deltaTime / lerpSmoothingTime);
}
If you want to read up in more detail about smoothly moving objects, learning about lerps or just want more examples then check out this guide on how to fix movement stutter in Unity.
First of all you need to put your code into Update() method if u want it to execute each frame. FixedUpdate is called at fixed intervals depend on project settings (you can change it in Edit -> Project Settings -> Time -> Fixed Timestep). Usualy FixedUpdate() is used for physics related stuff.
Moving it in FixedUpdate should be very smooth. Sure, Vector3.Lerp could help with your movement, but why in the first place isn't it smooth?
I can only guess that you have your Camera in a normal Update Script or that there is some Rigidbody interpolation. All that you need to know about smooth motion is explained here:
http://www.kinematicsoup.com/news/2016/8/9/rrypp5tkubynjwxhxjzd42s3o034o8
You can use Vector3.Lerp(...).
Try this code:
float smoothTime = 0.125f;
Vector3 newPos;
...
void FixedUpdate()
{
bool running = Input.GetKey(KeyCode.LeftShift);
float h = Input.GetAxisRaw("Horizontal");
float v = Input.GetAxisRaw("Vertical");
movement = ((running) ? runSpeed : walkSpeed) * new Vector3(h, 0.0f, v).normalized;
//Set the new position
if(movement.magnitude > 0)
newPos = transform.position + movement;
// Use Vector3.Lerp(...)
transform.position = Vector3.Lerp(transform.position, newPos, smoothTime);
transform.LookAt(transform.position);
}
I hope it helps you.

character keeps shaking while in topdown shooter

ok well i am working on a top down shooter i have made my character look at the mouse using screentoworld view and i made my camera follow my players x, and z axis but anytime my character moves parallel from the mouse it starts shaking I think its because it looks at the mouse an the mouse real world position is dependent on the cameras position which is, but i am not sure.
here is the playerscript the many backslashes showed other things I've tried
gameObject.GetComponent<Rigidbody>().velocity = new Vector3(Input.GetAxisRaw("Horizontal")*movementspeed,0,Input.GetAxisRaw("Vertical")*movementspeed);
if (gameObject.GetComponent<Rigidbody> ().velocity.x != 0 && gameObject.GetComponent<Rigidbody> ().velocity.z != 0) {
gameObject.GetComponent<Rigidbody>().velocity = new Vector3(Input.GetAxisRaw("Horizontal")*movementspeeddiag ,0,Input.GetAxisRaw("Vertical")*movementspeeddiag);
}
// makes vector 3 type target equall to mouse position on pixial cordinates converted in to real world coordniates
target = camera.ScreenToWorldPoint (new Vector3(Input.mousePosition.x,Input.mousePosition.y,camera.transform.position.y - transform.position.y));
//Vector3 newtarget = target - transform.position;
//lerptarget = Vector3.Lerp (transform.eulerAngles,newtarget, 100);
// states the vector 3 values of target
// makes object local z face target and iniziates up axis
//Quaternion rotation = Quaternion.LookRotation (newtarget, Vector3.up);
//transform.eulerAngles = Vector3.up * Mathf.MoveTowardsAngle (transform.eulerAngles.y, rotation.eulerAngles.y, rotatespeed * Time.deltaTime);
transform.LookAt (target,Vector3.up);
and camera script
void LateUpdate(){
position = new Vector3 (player.transform.position.x, 10, player.transform.position.z);
transform.position = position;
//transform.localPosition = Vector3.Lerp (transform.position, position, speed *Time.deltaTime);
transform.eulerAngles = new Vector3 (90,0,0);
}
Probably, the problem is that you change the velocity of rigidbody. Becouse velocity changes applied each fixed update time interval, it doesnt match with frame updating time, and may lead to shaking. According to unity docs you should try to avoid directly changes of velocity. (http://docs.unity3d.com/ScriptReference/Rigidbody-velocity.html)
I'd suggest you to you use MovePosition alongside with Lerp function to implement player movement.
See this:
http://docs.unity3d.com/ScriptReference/Rigidbody.MovePosition.html
http://docs.unity3d.com/ScriptReference/Vector3.Lerp.html

Vector3.Lerp with code in Unity

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);

Categories

Resources