Let’s say the left gear is rotating and the right gear is connected to him. How can i calculate the correct rotation for the right gear by using their positions in space, the rotational axis of them (which can be parallel or perpendicular on each other) and applied rotation on the powered(left) one?
I feel like i have to use sinus and cosinus for this in a way similar with iteration in points located on a circle circumference but i cannot think at a suitable solution.
I can use a lot of raycasts which start from the left gear center and extend around his theets; in this way i can find contact location of these two gears and based on direction teeth move/rotate in that location (the red arrows) i somehow can translate this direction to the other gear and rotate it but i want to avoid raycast route because it will mean i have to do like > 16 raycasts for each rotated gear and cpu will cry when there are a lot of gears in action
Also i don’t want to use physics for the same reason
Good. Physics would be insanely hard to get working on something like this.
Gear ratios are simple:
Technically it is the ratio of the circumference at the contact point of the gears.
Because circumference is related to radius, the radius can serve as a proxy.
float outputAngle = (inputAngle / inputGearRadius) * outputGearRadius;
That’s it. That’s how all non-frictional transmissions work.
Yes, that is correct, but is not enough for gears to work correctly in different conditions but (funny) it was enough for me to get motivated and just start work and think on implementation myself
.
Just applying that outputAngle to rotate other gear around his axis created multiple problems e.g. if rotation axis vector of two parallel gears were pointing in opposite direction (like vector.up and vector.down), gears got rotated in same way. On perpendicular gears problems were even worse and the gears would rotate correctly only in few particular relative positions.
After a lot of thinking i came with a more than enough working solution:
public SimpleGear connectedGear;
public float radius;
public bool isMotor;
public float angle;
private void Update()
{
if(isMotor)
{
Vector3 inFwd = transform.forward; // transform.forward represent gear's rotational axis
Vector3 inPos = transform.position;
Vector3 outFwd = connectedGear.transform.forward;
Vector3 outPos = connectedGear.transform.position;
Vector3 inContactDir = Vector3.ProjectOnPlane((outPos - inPos).normalized, inFwd).normalized;
Vector3 outContactDir = Vector3.ProjectOnPlane((inPos - outPos).normalized, outFwd).normalized;
Vector3 goodInFwd = inFwd;
Vector3 goodOutFwd = outFwd;
if (Vector3.Angle(inFwd, outFwd) > 10 && Vector3.Angle(inFwd, outFwd) < 170)
{
goodInFwd = Vector3.ProjectOnPlane((outPos - inPos).normalized, inContactDir).normalized;
goodOutFwd = Vector3.ProjectOnPlane((inPos - outPos).normalized, outContactDir).normalized;
}
else
{
if(Vector3.Angle(goodInFwd,goodOutFwd) >= 170)
{
goodInFwd = -goodInFwd;
}
}
float outputAngle = -(angle / radius) * connectedGear.radius * ((goodOutFwd.normalized == outFwd.normalized) ? 1:-1) * ((goodInFwd.normalized == inFwd.normalized) ? 1 : -1);
transform.Rotate(transform.forward * angle * Time.deltaTime, Space.World);
connectedGear.transform.Rotate(outFwd * outputAngle * Time.deltaTime, Space.World);
Debug.DrawLine(inPos, inPos + inContactDir.normalized, Color.red);
Debug.DrawLine(outPos, outPos + outContactDir.normalized, Color.green);
Debug.DrawLine(connectedGear.transform.position, connectedGear.transform.position + goodOutFwd*100);
}
}
The code is working but i feel like i overcomplicated it and it could be done fewer lines of code(for e.g. inContactDir and outContactDir variable might be replaced with gears transform.right or .up )
If physics isn’t required, then gears only need to rotate themselves, and tell other connected gears what speed they were rotated by and what ratio they are. Then all other connected gears can do the same.
Then it’s just a matter of rotating said gear on their axis with something like Quaternion.AngleAxis.
When I did something like this I found that you often want the relationship to be two-way, as in you should be able to rotate either gear (or any gear within a series) and they rotate each-other, so they need a reference to one another. This means you also need to add a few safe-guards, such as when a gear is rotated, it doesn’t try to rotate the gear that’s driving it, otherwise you end up with a stack-overflow.
Not sure if you were just looking for a simple example, but I came up with:
public class RotateTest : MonoBehaviour
{
public Transform otherTrans;
void Update()
{
float speed = 0.1f;
transform.Rotate(0, speed, 0);
float rotOffset = 5.0f; // teeth align value
otherTrans.rotation = Quaternion.Euler(
transform.rotation.eulerAngles.y + rotOffset, 0, 0);
}
}
I used 2 cubes with the same orientation, so otherTrans.rotation is on the X value, so if your other gear is just rotated 90, just set that for Y instead of X. Not sure if another way is more performant or not, but I assumed you just wanted something simple. ![]()
Just want to emphasise my point with an example. Again, gears don’t need to rotate other gears, they just need to tell other gears that they need to rotate, and they can rotate themselves.
For example:
using System.Collections.Generic;
using UnityEngine;
public class MechanicalGear : MonoBehaviour
{
#region Inspector Fields
[SerializeField]
private List<MechanicalGear> _connectedGears = new List<MechanicalGear>();
[SerializeField, Min(2)]
private int _ratio = 10; // ergo, the number of gears
[SerializeField]
private Vector3 _axis = Vector3.forward; // blue axis
#endregion
#region Properties
public int Ratio => _ratio;
#endregion
#region Driving Methods
public void DriveGear(float speed) // for non-gear sources to drive gears
{
RotateGear(speed);
foreach (var gear in _connectedGears)
{
gear.DriveGear(this, speed);
}
}
public void DriveGear(MechanicalGear driver, float speed)
{
float ratio = (float)_ratio / (float)driver.Ratio;
speed = -1 * (speed / ratio); // we multiply by negative 1 to reverse direction
RotateGear(speed);
foreach (var gear in _connectedGears)
{
// we don't want to drive the gear driving this one, otherwise we go infinite
if (gear != driver)
{
gear.DriveGear(this, speed);
}
}
}
private void RotateGear(float speed)
{
Quaternion rotation = Quaternion.AngleAxis(speed, _axis);
transform.rotation = rotation * transform.rotation;
}
#endregion
}
And of course, gears don’t drive themselves, so we just use another script to run them:
using UnityEngine;
public class GearDriver : MonoBehaviour
{
#region Inspector Fields
[SerializeField]
private float _driverSpeed = 10f;
[SerializeField]
private MechanicalGear _gear;
#endregion
#region Unity Callbacks
private void Awake()
{
if (!_gear)
{
this.enabled = false;
}
}
private void Update()
{
float speed = _driverSpeed * Time.deltaTime;
_gear.DriveGear(speed);
}
#endregion
}
And it works pretty well: https://i.gyazo.com/d5e89206bf41a26d72c13a50c52b872d.mp4
Notably this means you do not at all need to care about each gear’s orientation to one another.
Indeed, your solution is good and working. But it is applicable only if the gears position or rotation is not updated during runtime(because this happens: https://gyazo.com/608694473693a6983d5a72eaf131653b ). For use cases where we create the gears in unity editor and we don’t want to update positon or rotation at runtime, it is more than enough.
In my case, I want the gears to have some kind of “fake” physics such way dynamically changes at runtime will not broke gears logic and also creating custom gears at runtime (like a crafting system) will not involve manually setting up their rotation axis.
My solution work but is far from beeing finished: https://gyazo.com/b61716d69fa2229f6635a7a7c52c2106
Also, you are right, gears should not rotate other gears, and there should be clear separation of logic between what a gear should do and what driver/motor should do. I think, gears and motor should be in different scripts and their scripts alone should not do much on their own, instead, a single gear system/controller script which contains a list of gears and motors should be responsible for updating their rotations based on connected motors. In such way different cases can be handled easier , e.g. if motor1 send to gear3 rotation 50, and motor2 sent to gear3 rotation -25, gear rotation should be 25
I personally would make gears rotate other gears, as if you’re simulating the physics. And can also get more in depth if you were making a real-style gearing system, as far as scale or teeth in ratio.
But if your not simulating physics, then true, just make parent rotate all of them appropriately to “look” right.
That’s kind of a separate problem, and a level higher than the actual gear mechanics.
Whatever system you/the player uses to manipulate and place gears should be seperate - and on top of - the actual system that drives these gears. The lower level system should just have an appropriate API for establishing and breaking these connections.
It’ll be a lot more manageable than actually trying to simulate gears with any sort of fidelity.
