Trying to rotate transform on z axis

Hi guys,

Trying to rotate an objects transform on the z axis using lerp.
Its rotating but not slowly, just one swift motion. Cant work out why its doing this.

heres my code:

void Update()
    {
        print("Barrel up = " + tc.raiseBarrel);
        if (tc.raiseBarrel)
        {
            var r = transform.eulerAngles;
            transform.rotation = Quaternion.Euler(r.x, r.y, Mathf.LerpAngle(0, 90, 0.5f));
        }
    }

the last parameter of every lerp is time, but not in a sense of ongoing time, but in a sense of time >projection<.
we call that an interpolant. it is the parameter according to which the lerp evaluates the final value.
if it’s 0 the result is the minimum, if it’s 1 the result is the maximum, and of course it can be anything in between (and even beyond this range, we call that extrapolation).

it works like percentage.

your t is 0.5, and then you’re doing this every frame.
so as many times per second as your FPS is, you evaluate the following:
50% of angle between 0 and 90 degrees.

in other words, that’s all you do, you just snap the whole thing to 45 degrees and that’s it.

Ok, cool, so how do i slow down the process to make it rotate smooth? Thats the part i was hoping for lol

Hi,

In first place, whatever value you use in your t parameter of the lerp function should depende on time. So, you should be multipliying you t by Time.deltaTime.

One quick way to adjust how much easing you want would be to create an exposed variable, rotateSpeed for example, and use it in your lerp function. You can try to set different rotateSpeed values to see what works best for you.

[SerializeField]
private float rotateSpeed = 1;

void Update()
    {
        print("Barrel up = " + tc.raiseBarrel);
        if (tc.raiseBarrel)
        {
            var r = transform.eulerAngles;
            transform.rotation = Quaternion.Euler(r.x, r.y, Mathf.LerpAngle(0, 90, rotateSpeed * Time.deltaTime));
        }
    }

that’s not a good way to use lerp.
if you want to do that, you ought to track a lapse of time, and compute t from that, but that turns fiddly most of the time.
there was a good bit of math doing lerps over time, but I forgot where I got it, and I forgot to actually play with it. damn.

anyway

using UnityEngine;

public class LerpingTest : MonoBehaviour {

  float _duration;
  float _lastTime = 0f;

  void Start() => this.enabled = false;

  void Update() {
    var currTime = Time.time;
    var t = Mathf.Clamp01((currTime - _lastTime) / _duration);
    var r = transform.eulerAngles;
    transform.rotation = Quaternion.Euler(r.x, r.y, Mathf.LerpAngle(0f, 90f, t));
    this.enabled = t < 1f;
    _lastTime = ct;
  }

  public void Restart(float duration) {
    _duration = Mathf.Max(0f, duration);
    if(_duration > 0f) {
      _lastTime = Time.time;
      this.enabled = true;
    }
  }

}

It’s possible to make a coroutine to keep the fluff inside, but I’m too lazy.

Thanks guys,

lol still the same, dont know what i have wrong.
please keep helping me

here is the entire class code:

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class RotateBarrel : MonoBehaviour
{
    public TruckController tc;

    private void Start()
    {
       
        tc = GameObject.Find("RocketTruck").GetComponent<TruckController>();
    }

    float _duration;
    float _lastTime = 0f;
    float ct;


    void Update()
    {
        Shoot();
        if (tc.raiseBarrel)
        {
            var currTime = Time.time;
            var t = Mathf.Clamp01((currTime - _lastTime) / _duration);
            var r = transform.eulerAngles;
            transform.rotation = Quaternion.Euler(r.x, r.y, Mathf.LerpAngle(0f, 70f, t));
            this.enabled = t < 1f;
            _lastTime = ct;
        }
    }

    public void Restart(float duration)
    {
        _duration = Mathf.Max(0f, duration);
        if (_duration > 0f)
        {
            _lastTime = Time.time;
            this.enabled = true;
        }
    }

    public float lastTimeShot = 0;
    public float shootingDelay; //time between shots in seconds
    public GameObject truckBulletSpawner;
    public GameObject truckBullet;

    public void Shoot()
    {
        if (tc.raiseBarrel)
        {
            if (Time.time > lastTimeShot + shootingDelay)
            {
                lastTimeShot = Time.time;
                GameObject newShell = Instantiate(truckBullet, truckBulletSpawner.transform.position, transform.rotation);
                 newShell.GetComponent<Rigidbody2D>().AddRelativeForce(Vector2.right * 2000);
            }
        }
    }
}

if it’s anything, it’s probably your reliance on eulerAngles, which are not supposed to be used like that.
I’m just tired of explaining it constantly, if you really wanted to know about it, you’d read about it in the docs.

1 Like

Wait, why are we lerpin’ at all here???

Use Mathf.MoveTowardsAngle() and be done with it!

Handles all directions around 0 to 360 correclty.

2 Likes

Is this a relatively new function?

For the record

eulerAngles are used when you want to maintain a specific global orientation and are most useful on a child. For example a rotating parent with a billboard sprite. Using localEuler in this instance causes the the desired billboard to inherit its parent orientation before applying it’s local angle. if the child has no parent the localEuler can be used to rotate the object 360 by += a single axis vector. But the eulerAngles will not. I believe every 180* rotation on the eulerAngle shifts the axis to a different axis.

No.

I have not observed this myself. Rotations are exactly that rotations. Euler angles will get converted to a Quaternion behind the scenes. World or local space doesn’t change how rotations work.

Rotating an object on the y axis 90 degrees causes the x and z axis to switch. Doing this on the x axis causes the y axis to switch with the z axis. Doing this with the z axis cause x and y to switch.

Euler angles are used to be more human readable as Quaternions are matrix like. Rotations are better calculated in radians yet Unity chooses to use degrees to make it easier. 2π is the same as 360 yet 360 is easier to understand. Unity likes to make things easier for the developer, this causes confusion in some cases as it doesn’t give the “correct” results like.

transform.rotations = Quaternion.Euler(0f, 270f, 0f);

In the transform inspector it will show (0, -90, 0) for rotation. Yet if I enter that number directly in the inspector, it will remain (0, 270, 0) even after restarting the editor.

Unity will go from 0 - 180 then -180 back to 0 when setting the rotation through script. The conversion from Quaternion to Euler is most likely the culprit, I’m not an expert so I don’t know how the conversion works.

Rotations don’t quite act the way you want them to, due to conversions and probably a lack of understanding what Unity is doing behind the scenes.

And for the love of Pete, PLEASE, if the start and/or end value of a lerp changes, don’t use lerp. Unity has multiple MoveTowards functions for this, you can even use SmoothDamp if so desired.

Good question!

^

@iLinaza
@Chris-Trueman
Btw I find this explanation (7:00+) very compelling.
You’d still need to track the time on your own, so it’s not that lerps are particularly fit for this usage scenario, I’m just expanding the topic a little.

Man, you always have this mythological explanations :slight_smile:

It is partly true what you’re saying. But your explanation is wrong. Euler angles can be reliable under certain circumstances. I’m expecting Bunny to sweep in to correct me in a highly detailed essay, but when eulerAngles work this is because you’re locked onto an axis-aligned plane. Under the hub, they are simply computed from the quaternions, and this computation is very lousy for two reasons: 1) the two mathematical models are very incompatible with each other, 2) there is a human bias in that code, and I’ve found discontinuities in it, moreso than what is normally expected from this kind of math. But the real issue with this computation I describe below under ‘the crux of the problem’.

I have an example lying on my hard disk, where I freely move an avatar on top of the globe and I’m tracking it’s forward and upward vectors, smoothly, without any problems or jitter. I am able to fully rely on its local Y rotation in eulerAngles to extract its local yaw rotation, and this works without any problem. But the reason I can do this is because I’ve locked it in only a few degrees of freedom by using quaternions. In local space, it only actually rotates about its local Y axis, and this is why the computation does work as expected.

The crux of the problem
The same rotation can be described with multiple angle combinations. Quaternions on the other hand, map 1 to 1. So when you inverse from quaternion → Euler you lack any context to know what result is actually correct, if there are several. I’m sure there is a way to do it, but it’s incredibly hard, and Unity simply didn’t want to do it, because it would still be unreliable and that would defeat the purpose of Quaternions.

I’ve done the full research on this when I made an ortho transform, where the goal was to simplify a rotation of the cube to only 90 degrees, and I analyzed whether axis flipping would introduce new original transformations (it looked like it would) and how many (this was a hard problem, completely non-intuitive). This analysis allowed me to formalize the problem, and make a compact solution without having to rely on quaternions or matrices, and also helped me determine once and for all how many unique rotations there are, and if you need a mirror, should you include a mirror on both X and Y, or just X, or to even include Z.

To do this, I had to make a prop with designated faces, otherwise it’s impossible to do. And I’d advise anyone to try this, because you’ll quickly learn how many ambiguous rotations you can make, ones that sound completely different on paper, but end up equal to each other.

All in all there are 24 unique ways to rotate a cube with 90 degrees rotations (including identity rotation), and you need just one X-axis mirror, to enable all kinds of mirrors imaginable. For example, imagine a game like Minecraft, where you can rotate a block in place, and you want to enable flipping the cube on all three axes. Well, you need only 48 transformations in total. I needed that because you can easily encode such transformation in only 6 bits, which is incredibly important if you’re making a voxel-based game. As a point of comparison, Quaternion on its own (no mirror) consumes a whopping 128 bits.

I’ve made an OrthoTransform struct in the end, which is easily convertible back to Quaternion or Matrix, but you rotate that however you want, and it gives you a code, a simple integer between 0 and 64 (2^6), but also detects ambiguities, so the last 16 combinations aren’t actually used. You need to track X and Y rotations fully (4 rotations x 90 degrees; so that’s 2 bits each), however Z can be tracked partially (just 0 and 90), to cover the full scope of 24 possible transformations, and that’s five bits. You then add one X mirror as a sixth.

In other words, our 3D reality is a lie, at least when it comes to rotations. You end up with less degrees of freedom than the space itself seemingly allows for.

1 Like

All you gotta do is put two objects on screen

+= a vector axis to one of the objects Euler
+= a vector axis to one of the objects localEuler

then try the same thing under parent.

i currently use a Euler = 0, 45, 0 on a child of a parent who rotates. And the child maintains its perspective to the camera whatever rotation or angle the parent takes.

such a thing save me calculating transform.forward = negative of camera position - transform position and then adjusting the skew offset you would get as your camera scrolls.

You could never check to see if my object Euler axis changed by 45* using the == comparison: but you can count the angles assuming you iterate the by +1 and so when you reach 360 or 45 you know he has done the required revolution.

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class EULERTEST : MonoBehaviour
{
    public Transform OBJ1;
    public Transform OBJ2;
    // Update is called once per frame
    void Update()
    {
        OBJ1.eulerAngles += new Vector3(0, 1, 1);
        OBJ2.localEulerAngles += new Vector3(0, 1, 1);
    }
}

after try this one

OBJ1.eulerAngles += new Vector3(1, 0, 1);
OBJ2.localEulerAngles += new Vector3(1, 0, 1);

When you’re feeling brave try this one

OBJ1.eulerAngles += OBJ1.right + OBJ1.up;
OBJ2.localEulerAngles += OBJ2.right + OBJ2.up;

Doesnt work on X, only works Y and Z; Y gives you an X rotation, Z gives you a Y rotation and X and Y give you a diagonal rotation.

OBJ1.eulerAngles += new Vector3(0, 1, 0);
OBJ2.localEulerAngles += new Vector3(0, 0, 1);

the fun is endless.

OBJ1.eulerAngles += new Vector3(0, 1, -1);
OBJ2.localEulerAngles += new Vector3(0, -1, 1);

when you finally get it

OBJ1.forward += (OBJ1.right * Time.deltaTime);
OBJ2.forward += (OBJ2.right * Time.deltaTime);

We shouldnt talk about it but issue stems from floating point
this fixes X axis rotation.

    int AXIS_A;
    void Update()
    {
        AXIS_A = AXIS_A + 1;
        if (AXIS_A > 360)
            AXIS_A = 0;
        OBJ2.eulerAngles = new Vector3(AXIS_A,0,0);
    }

Here you may have all axis

        AXIS_A = AXIS_A + 1;
        if (AXIS_A > 360)
            AXIS_A = 0;
        AXIS_B = AXIS_B + 1;
        if (AXIS_B > 360)
            AXIS_B = 0;
        AXIS_C = AXIS_C + 1;
        if (AXIS_C > 360)
            AXIS_C = 0;
        OBJ2.eulerAngles = new Vector3(AXIS_A,AXIS_B,AXIS_C);

8513363--1134635--807150DE-1755-4C32-81A4-C00E3F4799F7.jpeg

@AnimalMan You have only highlighted the issue with euler angles, your misunderstanding of rotations in general and Unity’s conversion from a matrix/quaternion rotation to a euler rotation.

The last two examples “fix it”, not because it’s an integer but that you are storing the value outside of the conversion that would happen with the previous examples. Replacing int with float in those examples produces the exact same results.

This is how to “fix” the rotation in your examples.

OBJ1.Rotate(1f, 1f, 1f, Space.World);
OBJ2.Rotate(1f, 1f, 1f, Space.Self);
//or
OBJ1.rotation *= Quaternion.Euler(1, 1, 1);
OBJ2.localRotation *= Quaternion.Euler(1, 1, 1);
//And to prove the point
OBJ1.rotation *= Quaternion.Euler(OBJ1.right + OBJ1.up);
OBJ2.localRotation *= Quaternion.Euler(OBJ2.right + OBJ2.up);

This shows the rotation the way it should be. There is no conversion with these methods as they use what Unity is using behind the scenes, quaternions that easily convert to a matrix, which is what directx/opengl/vulcan/metal want to set the transform(position/rotation/scale).

You are right, float’s do suck. Just like euler rotations.

[quote=“orionsyndrome, post:14, topic: 897040”]
In other words, our 3D reality is a lie, at least when it comes to rotations. You end up with less degrees of freedom than the space itself seemingly allows for.
[/quote]This is false, math is flawed not reality.

Ignored ^^

have a good day floating point.

Really? Nothing constructive to say? Wow. Good luck in your future endeavours.

I am not writer of this thread I just provide information. Orion has already addressed floating point.
This isn’t about me.

stop hi jacking thread.