Rotating an object around a plane to look towards a point not on the plane

I’m having a hell of a time trying to wrap my head around how to handle this… need suggestions.
I have a set of wheels on an axel attached to a mock-vehicle (you can probably think of this kinda like a trailer) by a strut in the middle of the axel. This wheel assembly should rotate ‘freely’ along it’s Y rotation.
I have another gameobject, a leveraging bar, that attaches to the axel between the wheels. This bar should rotate ‘freely’ around it’s attachment point on the Z and X axis.
These objects can’t be parented to one or the other as they each have separate rigidbodies.
I also have a third gameobject, a player-controlled vehicle, that will be leveraging this bar around, but that might be unrelated to the solution.
I need the wheel assembly to follow the attachment point along it’s Y axis only, so the bar can position itself properly on the other two axis, but I can’t find a working solution.

I thought ProjectOnPlane and LookAt/LookRotation would be what I needed, but those gave me seemingly random rotations.
I also tried a few variants of LocalEulerAngles, but those would inconsistently break with negative angles.
RotateAroundPoint seemed good until I realized it was the inverse of that I needed.
I feel like i’m missing something simple, but I have no idea what.

This video shows the closest-working thing, but it’s just a LookAt().
I need the wheel on the left to rotate so the strut coming out of the top does not move, just rotate.

This is the angle the strut should be at:

Part of me wishes you’d abstract the whole idea and illustrate it better, simply name the pieces A, B, C, and ask a straightforward question. These geometry problems are trivial for some of us, but here I go wondering if I’m going to waste time with a wrong answer.

and

My question is how does one rotate around a plane?

I’d argue that the vertical strut coming of the wheel doesn’t move, it only rotates, albeit around local Z and Y. In either case you really have a hard time defining the problem, which is probably the reason why you can’t define the solution as well.

So let’s see, it appears that you want to constrain the wheel rotation to happen only around the vertical axis (the strut), not around the wheel’s rotational axis. To achieve this, consider that each rotation has a corresponding direction.

If you imagine some vector being rotated around, what you basically want is for that vector to always stay on the plane, in this case it’s the ground plane elevated a little to meet the center of the wheel.

To build a direction from the wheel’s center C to some arbitrary point P, you do

var dir = (p - c).normalized;

This is the shortest way to it. To bring this back to the ground plane, simply ignore it’s Y component. This will possibly turn in into a non-unit vector, so you need to normalize when this is done.

var delta = p - c;
var dir = new Vector3(delta.x, 0f, delta.z).normalized;

Now to build a quaternion out of this, use FromToRotation between the prefab’s original forward and this newfound direction.

var rot = Quaternion.FromToRotation(Vector3.forward, dir);

This rotation will always orient [an object that was originally oriented toward global forward] towards P, but only around Y axis.

There are other ways to accomplish this, but this is one of the easiest when it comes to projecting to base planes.

Btw, FromToRotation can fail if dir is directly opposite the left-hand side vector, in this case opposite forward.

To prevent this you can do

var rot = dir == Vector3.back? Quaternion.AngleAxis(180f, Vector3.up)
                             : Quaternion.FromToRotation(Vector3.forward, dir);

Even though == is to be avoided when comparing floats, dir == Vector3.back here works flawlessly because floats can be exactly compared if they are exactly 0 or 1 and Vector3.back is just (0, 0, -1)

Finally assign this rotation to the wheel

wheel.transform.rotation = rot;

Thank you! This is very close to what I’m trying to do, and I was able to jump what seems like 99.9% the rest of the way myself with this:

    void Start() {
        wheelStrutReferenceDirection = objFrontWheel.transform.forward.normalized;
    }

// other code

// called once when the towbar connects, then every Update() that the connected towbar is in motion
    public void OnMovementUpdate(GameObject towPoint) {
        wheelToOffset = towPoint.transform.position - objFrontWheel.transform.position;
        wheelToDirection = new Vector3(wheelToOffset.x, 0f, wheelToOffset.z).normalized;
        if (wheelToDirection == Vector3.back) {
            // other parts of the code will override this part long before we reach a 180 degree rotation
            return;
        }
        wheelToRotation = Quaternion.FromToRotation(wheelStrutReferenceDirection, wheelToDirection);
        objFrontWheel.transform.rotation = wheelToRotation;
        // do movement stuff
    }

But there’s just one small problem, the side of the wheel points at the towbar. (But keeps the proper strut angle, so yay! progress!)
I was able to get the forward direction of the wheel to properly point toward the towbar by using objFrontWheel.transform.right.normalized but that gave the same results as the FromToRotation(Vector3.forward,dir), I assume because the .right reference direction is parallel to Vector3.forward.
The brainless solution would be to find a 90 degree offset for the direction vector, but I feel like I’m still not quite grasping exactly how or why the expected result isn’t happening with the objFrontWheel.transform.forward.normalized
Anyone have some insight?

My bad, I’ll do better next time. I’ll probably say something like
“I need an object to rotate along an arbitrarily angled plane so it points at an arbitrary point that is not on that plane.”
Yes, much more abstract. Definitely won’t get the ‘not enough info, please explain exact use case’ posts with that one…

Well I’m sorry I’m lesser than you, I guess. I’ll just become smarter. Easy Fix! :smile:

Yes, Probably. That might, just maybe, be a small part of the reason I’m asking for help.