1

I'm writing a function that slowly turns exactly 90 degreees over the course of a period of time. So far everything is working fine, I just need to figure out how to make it rotate around a point instead of rotation around the transforms center.

protected bool _isRotating = false;
public IEnumerator RotateWithEasing(GameHelper.Axis axis, Vector3 isolatedAxisPoint, float inTime)
{
    if(_isRotating)
    {
        yield break;
    }

    _isRotating = true;

    var degrees = this.GetDegreesFromAxis(axis);

    Quaternion fromAngle = transform.rotation;
    Quaternion toAngle = Quaternion.Euler(transform.eulerAngles + degrees);
    for (float t = 0f; t < 1f; t += Time.deltaTime / inTime)
    {
        transform.rotation = Quaternion.Lerp(fromAngle, toAngle, t);
        yield return null;
    }

    _isRotating = false;
}

Can anyone help point me in the right direction, How can I modify this so it rotates around the specified isolatedAxisPoint?

johnny 5
  • 19,893
  • 50
  • 121
  • 195
  • Draw a circle around the point and move it along that circle? – Master117 May 13 '16 at 14:11
  • That sounds like it would work, I'm new to unity woudl I use a `Quaternion.Lerp` or a `Quaternion.Slerp` for that – johnny 5 May 13 '16 at 14:13
  • 1
    you might very well look into [rotate around](http://docs.unity3d.com/ScriptReference/Transform.RotateAround.html), combined with a `speed * deltatime` you can add ease and even timing – MX D May 13 '16 at 14:16
  • 1
    Try this: http://docs.unity3d.com/ScriptReference/Transform.RotateAround.html – Master117 May 13 '16 at 14:16
  • @MXD I've used rotate around but I'm confused on how to do that over make it move a finite 90 degree over a small period of time – johnny 5 May 13 '16 at 14:17
  • 1
    Ill see if I have some time later today for a more elaborative answer ;) – MX D May 13 '16 at 14:23
  • @MXD if you do that would be awesome thanks! – johnny 5 May 13 '16 at 14:33
  • Hi Johnny, never use quaternions for any reason, ever, at all. Just use "Rotate" or if you prefer simply set the eulerAngles. it's very easy. – Fattie May 13 '16 at 17:36

2 Answers2

5

Here's an actual example Unity tween that happened to be showing in a text editor another window!

If you want to rotate something, it is trivial just use deltaTime in a loop in an IEnumerator, and adjust the eulerAngles or just call Rotate. (Never use quaternions for any reason in Unity.) In this example I just call Rotate.

This is a very basic pattern in Unity.

Here is the basic pattern of a tween in Unity. You will do this 1000s of times in Unity!

  1. calculate the end time
  2. do a while loop until that time is reached
  3. each frame, slide (or whatever) by the appropriate fraction.

Note that of course you

 yield return null;

inside the loop, it means "wait until the next frame".

(Note that as with any tween, it's good practice to force-set the final values at the end so you know it's perfect. Notice in this example I simply set the eulerAngles, at the end when the loop is finished.)

private IEnumerator _roll( Vector3 delta )
    {
    rollBegins.Invoke();
    
    Vector3 begin = transform.eulerAngles;
    
    float rollSecs = 2f; // speed of the roll
    
    float startTime = Time.time;
    float endTime = startTime+rollSecs;

    while (Time.time < endTime)
        {
        Vector3 d = delta * ( Time.deltaTime / rollSecs );
        transform.Rotate(d, Space.World );
        yield return null;
        }
    
    transform.eulerAngles = .. perfectly correct end values;
    
    busy = false;
    
    rollComplete.Invoke();
    }

{Note, in that actual code example, "delta" is understood to be only on one axis; don't worry about this, it's just an example of a tween using a coroutine.}

Note - of course there are "two ways" to go inside the loop. You can calculate the small amount you should move it that frame. Or, you can just calculate what the new position should be at that time. It's up to you. Many programmers think the latter is more logical, if so, do that. Note too that you can do the while loop until a certain time has passed, or, you can do the while loop until you reach your destination! (Being very careful about float equalities.) The choice is yours.

Note that very very often you use Lerp or even better SmoothStep with such tweens. It is ubiquitous in Unity. Once you master the basic pattern, experiment with that.

Note that in my example the code is using UnityEvent to flag the beginning and end of the animation. This is extremely common. After all it's usually the case that once some movement finishes, you then have to go on and do something else. Another good example is, while something is animating, the user is blocked from say steering or whatever is the case in your game.

Excellent intro to UnityEvent https://stackoverflow.com/a/36249404/294884 pls vote it up :)


For the record.

Tweeng in Unity.

The truly advanced way to tween in Unity is to tweeng:

Basic Tweeng code base appears in this question.

It takes a while to get the hang of that but it is incredibly powerful. It's mindboggling that the tweeng extension is only a few lines of code.

Community
  • 1
  • 1
Fattie
  • 27,874
  • 70
  • 431
  • 719
  • Thank you this is what I was looking for, I'll test this out when I get home, just wonder is rollComplete a built in delegate? Or some kind of event system which prevents it from being called mutliple times until its complete? – johnny 5 May 13 '16 at 17:59
  • Thanks, for the tip on setting the end euler angles I noticed before my angles we off by a slight bit probably because of rounding floating point decimal errors – johnny 5 May 13 '16 at 18:00
  • hi Johnny, right this is exactly the correct pattern in Unity. It is like the most basic thing, sometimes it feels like it is all you ever program in Unity, heh! "rollComplete" is a "unityEvent" I have included a full explanation. coroutine tweens like this is a great example of where you use UnityEvent. – Fattie May 13 '16 at 18:00
  • 1
    right! nicely spotted. it is essential to "force-set" the final correct destination. Especially with angles. – Fattie May 13 '16 at 18:00
  • I'm not home yet but I'm going to mark this as completed because I can logically compile the code in my head and it makes sense – johnny 5 May 13 '16 at 18:02
  • I wish I could up vote this twice, this is such a well written useful answer – johnny 5 Aug 26 '16 at 04:30
  • @Fattie I would be very grateful if you could take a look at my question, it's related this question https://stackoverflow.com/questions/51066807/issues-rotating-an-arbitrary-point-around-another-arbitrary-point – TenOutOfTen Jun 29 '18 at 08:31
  • hi @TenOutOfTen - done, the solution is **very simple** ! – Fattie Jun 29 '18 at 12:04
0

Use something like, assuming rotate in 1 sec by 90 degree:

int rotatedDegrees; 
int desiredRotation = 90;
float startTime = null;
float rotationDuration = 1f;

public void rotate()
{    
  if(startTime == null)
    startTime = time.Now;

  while((time.Now - starttime) / rotationDuration > (((float) rotatedDegrees) / desiredRotation))
  {
    transform.RotateAround (isolatedAxisPoint, Vector3.up, 1);
    rotatedDegrees++;
  }
}

At startTime + 0 it has rotated 0 degrees.

Now assume time.Now is something like startTime + 0.1 (+100ms). Then time.Now - startTime is 0.1, rotate until (rotatedDegrees / desiredRotation) is 0.1 which is 9, or 1/10 of the total rotation.

Master117
  • 660
  • 6
  • 21
  • hm interesting, but how does time.Now relate to rotatedDegrees / desiredRotation, isn't time.now always increasing, or shouldn't this be in a coroutine so that variable doesn't modify? – johnny 5 May 13 '16 at 14:32
  • @johnny5 edited, time.Now - startTime = time since the rotation started. – Master117 May 13 '16 at 14:48
  • Thanks, this looks solid Ill test it when I get back home. If it works i'll mark it as complete – johnny 5 May 13 '16 at 14:51
  • I have no idea what this code is. Johnny, just use an IEnumerator as you say, but forget about quaternions. simply use Rotate or eulerAngles depending on how you prefer. – Fattie May 13 '16 at 17:38