5
\$\begingroup\$

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

enter image description here

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;
    }
}
\$\endgroup\$
  • 1
    \$\begingroup\$ gamedev.stackexchange.com/questions/61981/… "You can easily rotate an arbitrary point around another arbitrary point with the following:..." \$\endgroup\$ – MichaelHouse Jun 26 '18 at 14:23
  • \$\begingroup\$ @MichaelHouse thank you. That answers part of my question, but how to rotate it over time, say 2 seconds using a coroutine is not answered. To put things a bit more into perspective, I have a Bezier curve, and I want to rotate one control point around another. \$\endgroup\$ – TenOutOfTen Jun 26 '18 at 14:43
  • \$\begingroup\$ @MichaelHouse I would appreciate an answer with how to move the point over time \$\endgroup\$ – TenOutOfTen Jun 26 '18 at 14:55
  • \$\begingroup\$ gamedev.stackexchange.com/questions/44881/… \$\endgroup\$ – MichaelHouse Jun 26 '18 at 16:08
  • \$\begingroup\$ @MichaelHouse I've updated the question \$\endgroup\$ – TenOutOfTen Jun 27 '18 at 14:55
1
\$\begingroup\$

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.

\$\endgroup\$
1
\$\begingroup\$

First of all, you need a couple of things. You need to store the angle you want to rotate the object by, the time it should take and the point you want to rotate it around obviously, but you'll also need to store when you started the rotation relative to a fixed point in time (e.g. the epoch, resource here).

Inside the coroutine you'll need the current time relative to the same point in time as before. Subtract the time you started from it and divide it by the time the rotation should last. This gives you a value between 0 and 1 that tells you how far you are currently. It's 0 when it begins, 1 when it ends, 0.5 when it's halfway there, etc. I'll call this t.

Based on t you can get the angle you need to rotate B relative to the original position by just multiplying it with it. The only thing left to do is to actually rotate the vector. Refer to the first part of this StackOverflow question if needed (This is a pretty useful formula, so try memorizing it).

So, to rotate the B vector around A, subtract A from B, rotate the resulting vector by the current angle and add A back to it.

So, in code

// Inside the coroutine
float t = (currentTime - startTime) / duration;
float angle = totalRotation * t;

// If the angle is in degrees, then convert it to radians by multiplying
// by Mathf.Deg2Rad
// A positive angle creates a counter-clockwise rotation

Vector2 ab = B - A;
Vector2 rotated = new Vector2(
    ab.x * Math.cos(angle) - ab.y * Math.sin(angle),
    ab.x * Math.sin(angle) + ab.y * Math.cos(angle)
);

return A + rotated;
\$\endgroup\$
  • \$\begingroup\$ I am a bit confused with what currentTime and startTime should be. Would really appreciate the complete coroutine. \$\endgroup\$ – TenOutOfTen Jun 30 '18 at 14:03
  • \$\begingroup\$ @TenOutOfTen startTime is the time the rotation starts, currentTime is the current time. Please read the paragraphs above it \$\endgroup\$ – Bálint Jun 30 '18 at 16:45

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.