I have a script that moves an item towards a transform in a coroutine, but I want the item to slowly increment on its local Vector3.forward while simultaneously rotating towards the item, until it reaches its destination. Right now using Vector3.MoveTowards it moves directly to the item in a straight line, and I wish to get more of a gradual curved effect wherever the transform point is placed.
I’ve tried to lerp between the item’s position and the desired position slowly incrementing. Can’t seem to get this one to work.
You may find it’s smoother to use Quaternion.Slerp instead of RotateTowards.
It seems a little odd that you’re accessing the rotation through beltItem.transform.rotation and then accessing the position through beltItem.item.transform.position.
It might be tidier to make beltItem reference the transform directly and then you can do something like:
Yes, Kinda. I want behaviour where an object will move smoothly with “homing” behaviour between point A to point B, within a defined timeframe. If point B is closer, it will move slower to target, if it’s further away it will move quicker, always ending up at the last point defined in the allotted timeframe.
I guess the behaviour of MoveTowards but not directly in a straight line.
That’s a good point. Not sure why I was doing that. I’ve cleaned it up and added some improvements, but as it stands it’s still too “floaty” towards the point, rather than tight movement and rotation directly to the point, finishing on its target rotation.
private IEnumerator MoveItem() {
isSpaceTaken = true;
var beltItemLocation = beltItem.transform;
if(beltItem.item != null && nextBeltInSequence != null && nextBeltInSequence.isSpaceTaken == false) {
nextBeltInSequence.isSpaceTaken = true;
var moveStep = beltManager.Speed * Time.deltaTime;
var rotationStep = beltManager.RotationSpeed * Time.deltaTime;
while (Vector3.Distance(beltItemLocation.position, nextPosition.position) > 0.25f) {
Vector3 targetDirection = nextPosition.position - beltItemLocation.position;
//Quaternion targetRotation = nextBeltInSequence.itemQueueSpot.transform.rotation;
beltItemLocation.rotation = Quaternion.Slerp(beltItemLocation.rotation, Quaternion.LookRotation(targetDirection), rotationStep);
//beltItemLocation.rotation = Quaternion.Slerp(beltItemLocation.rotation, targetRotation, rotationStep); // item ends up with target rotation but if it misses its mark the while codition is not met and it keeps going.
beltItemLocation.position += beltItemLocation.forward * moveStep;
yield return null;
}
//beltItem.item.transform.rotation = nextPosition.rotation; // Clean up rotation at the end. Works but its too 'jerky' if the object hasnt had time to rotate
isSpaceTaken = false;
nextBeltInSequence.beltItem = beltItem;
beltItem = null;
}
}
To provide more realistic behavior of the missile, I would (assuming the missile is going from ground to air):
Use SmoothDamp for the position and rotation
Force the missile to move and face upwards only until a certain amount of time has elapsed since it was launched, then start turning and moving to the target.
Set the pivot point of the missile to be the very top of the missile.
private IEnumerator MoveItem ()
{
var beltItemLocation = beltItem.transform;
moveStartTime = Time.time;
while (Vector3.Distance (beltItemLocation.position, nextPosition.position) > 0.25f) {
Vector3 targetPosition = nextPosition.position;
Vector3 targetDirection = targetPosition - beltItemLocation.position;
if (Time.time - moveStartTime < 0.50f) {
//Keep the missile moving straight up for a short duration of time
targetPosition.x = beltItemLocation.position.x;
targetPosition.z = beltItemLocation.position.z;
targetDirection = Vector3.up;
}
beltItemLocation.rotation = SmoothDampQuaternion (beltItemLocation.rotation, Quaternion.LookRotation (targetDirection), ref smoothRotation, smoothRotationTime);
beltItemLocation.position = Vector3.SmoothDamp (beltItemLocation.position, targetPosition, ref smoothPosition, smoothPositionTime);
yield return null;
}
}
//Source: https://gist.github.com/maxattack/4c7b4de00f5c1b95a33b
public static Quaternion SmoothDampQuaternion (Quaternion rot, Quaternion target, ref Quaternion deriv, float time)
{
if (Time.deltaTime < Mathf.Epsilon) {
return rot;
}
// account for double-cover
var Dot = Quaternion.Dot (rot, target);
var Multi = Dot > 0f ? 1f : -1f;
target.x *= Multi;
target.y *= Multi;
target.z *= Multi;
target.w *= Multi;
// smooth damp (nlerp approx)
var Result = new Vector4 (
Mathf.SmoothDamp (rot.x, target.x, ref deriv.x, time),
Mathf.SmoothDamp (rot.y, target.y, ref deriv.y, time),
Mathf.SmoothDamp (rot.z, target.z, ref deriv.z, time),
Mathf.SmoothDamp (rot.w, target.w, ref deriv.w, time)
).normalized;
// ensure deriv is tangent
var derivError = Vector4.Project (new Vector4 (deriv.x, deriv.y, deriv.z, deriv.w), Result);
deriv.x -= derivError.x;
deriv.y -= derivError.y;
deriv.z -= derivError.z;
deriv.w -= derivError.w;
return new Quaternion (Result.x, Result.y, Result.z, Result.w);
}
You can play with smoothRotationTime and smoothPositionTime till it looks right.
The smoothDamp position causes missile to slow down as it approaches the target. Although, I’m not sure why you want that behavior.