Procedural Noflip Shopping Cart Wheel in Unity
At Boulder I designed a wheel rig component that was used on several vehicles. The component included an auto-roll system using expressions; very similar to this setup.
One of the vehicles however went through a design change so that it's wheels could roll and twist on a vertical axis, like a shopping cart. I had seen this demo a few years ago so I implemented it as parent component for the standard wheel component.
This gave us results that were right for a shopping cart but didn't look right for our vehicle, because of two things.
Number one, our wheels twist axis was directly above the rolling axis. A real shopping cart wheel has the twist axis off-center. The reason for this is so if you were to move perpendicular to the direction the wheels are facing you'd get some torque on the twist axis because of the friction of the wheel. If the axis are aligned you get no torque though. With the aligned axis the twisting looked physically implausible.
Number two, when you push a shopping cart forward and then backward the wheels spin 180 degrees when you switch directions. This works fine for little wheels but our vehicle had big wheels so that fast twist didn't look right either.
I solved this problem by creating another history-dependent expression to solve for the twisting.I can't post that model or expression here since it belongs to Boulder, but I can do something even better.
Since I'm brushing up on Unity, and Unity is much better for history-dependent behaviors I can re-impliment it (with some improvements) in Unity!
Here you can the that the "wheel" gradually aligns to the direction of travel so that it doesn't jitter when the driving transform makes tiny movements. It also doesn't flip around once the driver starts travelling in the opposite direction, it just orients behind the driver instead of in-front. It's a very niche behavior and I'm glad that it can be set up in Unity in just a few minutes.
Here's the code:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class wheeelTwist : MonoBehaviour
{
private Vector3 oldPosition;
private Vector3 currentPosition;
private Quaternion currentQuaternion;
public float turningSpeed;
public Transform child;
public bool noFlip = true;
// Start is called before the first frame update
void Start()
{
oldPosition = transform.position + transform.forward.normalized;
currentPosition = transform.position;
currentQuaternion = transform.rotation;
}
// Update is called once per frame
void Update()
{
//update the old position
oldPosition = currentPosition;
//get the current position
currentPosition = transform.position;
//get the movement vector
Vector3 movementVector = currentPosition - oldPosition;
if (movementVector.magnitude > .01)
{
//get the angle of movement as a quaternion
float angle = Vector3.SignedAngle(currentQuaternion * new Vector3(0,0,1), movementVector, Vector3.up);
if (noFlip && Mathf.Abs(angle) > 90)
{
angle = angle - 180;
}
Quaternion targetQuaternion = Quaternion.Euler(0, angle, 0) * currentQuaternion;
//blend towards the targetQuaternion
currentQuaternion = Quaternion.RotateTowards(currentQuaternion, targetQuaternion, turningSpeed * movementVector.magnitude);
child.rotation = currentQuaternion;
child.position = currentPosition;
}
}
}
Comments