I have Lifters that "carry" an object. I want the object to move and rotate based on how those Lifters move. Requirements include:
- Supporting 1-4 Lifters at any side of the Object (e.g., both on one side, opposite, so on - can't make any guarantees about their positions relative to the object)
- Supporting any orientation of the Object (e.g. the Object could be flipped on its side, sideways, or upside down; can't make guarantees that Forward is always facing forward, etc)
As such I implemented this by having the object move by setting its velocity to the average of the Lifters' velocity.
Now, if not all of them move (or if there's a disparity in their velocities), I want the object to rotate, like so:
This is done by keeping track of how much an individual lifter has moved in a frame from the Object's position, and calculating the angle via
m_Angle += Vector3.SignedAngle(lastPos - transform.position, currentPos - transform.position).
This "movement angle" I add up for every Lifter and then at the end, Rotate the Object via Transform.Rotate(Vector3.up, m_Angle)
.
However, this approach has some problems:
- Calculations are done along the Y axis and therefore does not angle the Object up or down if the Lifters have different vertical positions,
m_Angle
actually overshoots, but I found simply dividingm_Angle
by thenumber of Lifters
made it perfectly accurate. I do not know why this works.
Because I want this to rotate the object in "3 dimensions", I have attempted to calculate the "euler angles" of each Lifters' movement via:
Vector3 oldDisplacement = lastPos - transform.position;
Vector3 newDisplacement = currentPos - transform.position;
Quaternion rotation = Quaternion.FromToRotation(oldDisplacement, newDisplacement);
m_EulerAngles += rotation.eulerAngles;
And then at the end, I rotate the Object based on these "movement angles":
transform.Rotate(m_EulerAngles, Space.World);
This works in three dimensions, however it is off in the same way that m_Angle
was before I divided it. It rotates the object too far. On top of that it seems to produce rotations along axes that have no displacement.
I want the object to only rotate exactly by the angle created by the Lifter's movement. This should keep it "synced" to Blue instead of rotating past as you see here.
When I check how much rotation Blue is adding per frame, I get an output like so:
Blue is adding (0.000, 359.507, 0.066); on movement (-0.015, 0.000, 0.000)
Despite moving solely on the x-axis, there is a z-euler angle being created, and the y-euler angle of 0.493 degrees is too much for -0.015f movement. I do not know why rotation.eulerAngles
is inaccurate in my usage.
The only issue I can perceive is that since the Object's position is also moved at the end, the angle calculation assumes it is not moving and generates a larger angle (e.g., perhaps newDisplacement
should be something like currentPos - (transform.position - m_Body.velocity * Time.deltaTime)
). However, removing the Object's movement from the finalPosition of the Lifter seemingly has no effect.
Or perhaps there is some finite precision in Quaternion.FromToRotation? Or my math is incorrect in some way?
I'd love a sanity check on the code or approach or if I should approach this problem from a different perspective.