'Need help Slerping positions and rotations
I have 2 scripts on my camera, one for following the player ( FollowTarget.cs ), and one which is supposed to, over time, Slerp the camera's transform position and rotation around the player on death ( DeathCam.cs ).
With DeathCam.cs I am trying to get a nice smooth movement to the [desired/set in inspector] position and rotation values over time on death but currently the camera jumps to straight to the position and rotation set in inspector. Which I think maybe down to the transform.position = camPos; in Update.
I have tried to set the position and rotation in the PlayDeathCam() function but that fails to work and is currently commented out and replaced by a bool which checks in Update to see if the death cam is active
Code: FollowTarget.cs
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class FollowTarget : MonoBehaviour{
[Header("----- Target To Follow --------------------------------------------------")]
public GameObject target;
[Header("----- Adjustments To Make ------------------------------------------")]
public Vector3 adjustVal;
void Update(){
pos = new Vector3( target.transform.position.x + adjustVal.x,
target.transform.position.y + adjustVal.y,
target.transform.position.z + adjustVal.z);
transform.position = pos;
}
}
Code: DeathCam.cs
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class DeathCam : MonoBehaviour{
[Header("----- Duration -----------------------------------------------------")]
[SerializeField] float duration = 5.0f;
[Header("----- Positions ----------------------------------------------------")]
[SerializeField] float finalPosX;
[SerializeField] float finalPosY;
[SerializeField] float finalPosZ;
[Header("----- Angles -------------------------------------------------------")]
[SerializeField] float tiltAngleX;
[SerializeField] float tiltAngleY;
[SerializeField] float tiltAngleZ;
[Header("--------------------------------------------------------------------")]
[SerializeField] Vector3 camPos;
[SerializeField] Quaternion camRot;
[SerializeField] bool deathCamActive;
///[ Start]
void Start(){
}
///[ Update]
void Update(){
if (deathCamActive) {
camPos = new Vector3((transform.position.x + finalPosX),
(transform.position.y + finalPosY),
(transform.position.z + finalPosZ));
transform.position = camPos;
}
}
///[ PlayDeathCam]
public void PlayDeathCam(){
float tiltAroundX = camPos.x * tiltAngleX;
float tiltAroundY = camPos.y * tiltAngleY;
float tiltAroundZ = camPos.z * tiltAngleZ;
// camPos = new Vector3((transform.position.x + finalPosX),
// (transform.position.y + finalPosY),
// (transform.position.z + finalPosZ));
camRot = Quaternion.Euler(tiltAroundX, tiltAroundY, tiltAroundZ);
transform.position = Vector3.Slerp(transform.position, camPos, Time.deltaTime * duration);
deathCamActive = true;
transform.rotation = Quaternion.Slerp(transform.rotation, camRot, Time.deltaTime * duration);
StartCoroutine("DeathCamSlow");
}
///[ DeathCamSlow]
IEnumerator DeathCamSlow(){
Time.timeScale = 0.3f;
yield return new WaitForSeconds(1.6f);
Time.timeScale = 1.0f;
}
}
Solution 1:[1]
There is a few things wrong with your code, it should be fixable, but first let's try to disassembly some confusion that seems to be going on.
Interpolating position with slerp looks wrong for starters.
But you seem to have placed code that you believe is going to do an animation in a method that is only going to be called once.
I like to separate my thinking about triggering animations by using a pattern similar to
void StartAnimation()
{
StartCorouting(LerpDriverRoutine());
}
IEnumerator LerpDriverRoutine()
{
float x=0;
while (x<1)
{
ApplyLerp(x);
x+=Time.deltaTime*speed;
yield return null;
}
ApplyLerp(1);
}
void ApplyLerp(float lerpAmount) // this will get called with varying amount as time passes
{
// use lerp amount here
}
While lerping x=lerp(x,other,Time.deltaTime) makes sense sometimes, more often it does not. Here you are not lerping between values A and B here, instead lerping between current value and target, which is leads to a similar result but often will not feel smooth. It's often much better to use Mathf.SmoothDamp if you want to gently settle onto a target value. But if Linear interpolation is what you want, and learning to code animations you might want to start with that, you need to store your start position somewhere and use it for lerp (for the time the animation is running), not just use the current value, at least not before you know what you are doing
IEnumerator LerpDriverRoutine()
{
float x=0;
Vector3 startPosition=transform.position;
Quaternion startRotation=transform.rotation;
while (x<1)
{
transform.position=Vector3.Lerp(startPosition,camPos,x);
transform.rotation=Quaternion.Slerp(startRotation,camRot,x);
x+=Time.deltaTime*speed;
yield return null;
}
}
Edit as requested: Here's an example using smoothdamp. It has a few advantages: It will alwas start and end slowly, reach peak velocity exactly mid-way and start slowing down as it approaches the target. You don't need to know the time it's going to take an animation to settle on the new state in advance. You can also change the target value while the animation is in progress, without any visible jerks as long as you don't loose velocity values. SmoothDamp uses an external helper value that holds current velocity (The downside is that you need to check for your desired error at the movement will converge to the target value but might not actually reach it) When the distance to travel is longer, the animation will take longer too, but it will preserve the general dynamics as expressed by the smoothTime parameter. Please note smoothTime is rogughly equivalent of time it takes the animation to go half-way, not all-the way, as it never fully goes all the way. Default value of .75 means (very roughly) around 1.5 secs for the movement to settle on the desired value.
IEnumerator SmoothDampMovement(Vector3 target, float smoothTime=0.75f, float epsilon=0.01f)
{
float velocityX=0;
float velocityY=0;
float velocityZ=0;
var position=transform.position;
while (transform.position-target).Vector3.sqrMagnitude >epsilon)
{
position.x = Mathf.SmoothDamp(position.x, target.x, ref velocityX, smoothTime);
position.y = Mathf.SmoothDamp(position.y, target.y, ref velocityY, smoothTime);
position.z = Mathf.SmoothDamp(position.z, target.z, ref velocityZ, smoothTime));
transform.position=position;
yield return null;
}
}
For rotations there is Mathf.SmoothDampAngle which handles the edge case where 360 degrees go back to 0, I am sure you can work out the details based on the above example
Sources
This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.
Source: Stack Overflow
Solution | Source |
---|---|
Solution 1 |