I need to rotate a point around another point (in 2D) by a given angle over a given duration, independent of game object (meaning that I've manually created the points and they're not positions from a game objects transform ) as shown in the image below where point B
rotates to C
by and angle e
around A
by with radius AB
. Using a coroutine would be preferable.
UPDATE
Based on links provided by @MichaelHouse I wrote the following methods to rotate a control point over time by a certain angle but it rather moves to a point (which is not the desired point) immediately to another point, not by the right angle. I'm not sure what I've done wrong.
public static Vector3 RotatePointAroundPivot(Vector3 point, Vector3 pivot, Quaternion angle)
{
return angle * (point - pivot) + pivot;
}
IEnumerator RotateControlPointWithDuration(Vector3 point, Vector3 pivot, float duration, float angle) {
float currentTime = 0.0f;
float ourTimeDelta = 0;
ourTimeDelta = Time.deltaTime;
float angleDelta = angle / duration; //how many degress to rotate in one second
while (currentTime < duration)
{
currentTime += Time.deltaTime;
ourTimeDelta = Time.deltaTime;
Vector3 newPos = RotatePointAroundPivot(point, pivot, Quaternion.Euler(0, 0, angleDelta * ourTimeDelta));
points[0] = new Vector2(newPos.x, newPos.y);
yield return null;
}
}
IEnumerator runMovement() {
yield return new WaitForSeconds(2.0f);
Vector3 point = new Vector3(points[0].x, points[0].y, 0);
Vector3 pivot = new Vector3(points[3].x, points[3].y, 0);
StartCoroutine(RotateControlPointWithDuration(point, pivot, 2.0f, 45.0f));
}
UPDATE 2
I've tried another approach using trig functions, but the point moves erratically before finally arriving at a point that seems to be the right point. Please see the code below:
IEnumerator runMovement() {
yield return new WaitForSeconds(2.0f);
Vector2 pivot = points[3];
StartCoroutine(RotateControlPointWithDuration(pivot, 2.0f, 90.0f));
}
IEnumerator RotateControlPointWithDuration(Vector2 pivot, float duration, float angle)
{
float currentTime = 0.0f;
float ourTimeDelta = 0;
Vector2 startPos = points[0];
ourTimeDelta = Time.deltaTime;
float angleDelta = angle / duration; //how many degress to rotate in one second
while (currentTime < duration)
{
currentTime += Time.deltaTime;
ourTimeDelta = Time.deltaTime;
points[0] = new Vector2(Mathf.Cos(angleDelta * ourTimeDelta) * (startPos.x - pivot.x) - Mathf.Sin(angleDelta * ourTimeDelta) * (startPos.y - pivot.y) + pivot.x,
Mathf.Sin(angleDelta * ourTimeDelta) * (startPos.x - pivot.x) + Mathf.Cos(angleDelta * ourTimeDelta) * (startPos.y - pivot.y) + pivot.y);
yield return null;
}
}
Answer
Your first "Update" code is mostly right, but it's using the wrong time delta.
Since "delta" just means difference, we shouldn't automatically reach for any old time variable with "delta" in the name, but instead consider which two endpoints we want a difference between.
Here the delta taken by the quaternion constructor wants the time difference between now and the start of the animation, so it can construct a rotation accounting for the full arc that should have been traversed over that interval. But Time.deltaTime gives you the difference between this frame and the previous frame - this will always hover close to your framerate (eg. 33 or 17 ms), rather than progressing smoothly from zero to your full duration.
So, we can correct and simplify the code a bit like this:
IEnumerator RotatePointAroundPivot(Vector3 pivot, float angle, float duration) {
Vector3 startPoint = points[0];
float progress = 0f;
float rate = 1f/duration;
while(progress < 1f) {
progress += Time.deltaTime * rate;
Quaternion rotation = Quaternion.Euler(0f, 0f, angle * Mathf.Min(progress, 1f))
// If your output is a Vector2, this will implicitly omit the z.
points[0] = pivot + (rotation * (startPoint - pivot));
yield return null;
}
}
Now we have a progress
variable that increases from zero to one over our duration, and we can use this to blend the angle from zero up to the full desired arc.
No comments:
Post a Comment