- Home /
Set a HingeJoint's Target Position to point toward an object?
I have a hinge joint and I want it's spring's TargetAngle to be to where it will point toward an object. I don't know how I could go into any more description, so I drew a simple picture.
Thanks!
HingeJoint can be quite a pain to use. Your application may work ok, but frankly, I avoid them for many situations. If there are other reasons for using a hinge, let me know, but if you're just pointing an object toward a target, you'll find it a lot easier to simply use an empty GameObject as a pivot (placed where the hinge joint would be) and just rotate as required.
If you really want to use the HingeJoint, first to note is that the spring in the joint doesn't have a target angle. The spring is just a bounce against the hinge's limits, and I doubt you want to use a spring, but that's up to you. You'll want to use the motor, apply a force in the appropriate direction, monitor as it moves, set the limit angles such that one (in the direction of rotation) points to your target, then when it reaches that, close the other limit to hold the joint in that position and turn off the motor. Not simple.
I'll convert this into an answer as we advance our conversation, if you like.
Your first objective, however, is to deter$$anonymous$$e the angle. Are you familiar with $$anonymous$$athf.ArcTan2 and how it works (or, more generally, how the inverse tangent function is used)?
I really need it to be a hinge joint. I am making a physics based character made up of hinge joints, so I need it to be a hinge joint
Well....have you got any of it working?
I ask because I have some experience in doing that, and frankly, it doesn't work out well. In my case it was a robotic arm on a robot, so maybe it isn't exactly the same, but the joints just don't work correctly. Indeed, I implemented the robotic arm without hinge joints, just using GameObjects as pivot points, and it worked quite well.
PhysX has articulated joints, but Unity doesn't expose them yet. At some point they'll be available in a future Unity version, and they work as expected, but not HingeJoints. They are especially horrible on an arm - the wrist is just nuts when moving the elbow, and both the elbow and wrist go nuts moving the shoulder joint.
That said, it doesn't much matter, both have similar issues which brings me to repeat my closing question. Do you know how to use the inverse tangent to get an angle to a target?
All of the attempts so far I have tried have worked decently well, the joints are not acting up, and are responding well. Right now I have 4 empties that act like target points. Each limb tried to go to it, and I already have an I$$anonymous$$ set up for the elbow/shoulder bend. Since hinge joints are 1 axis, I set it up to where each part has a duplicate. I do not know how to use the inverse tangent to get an angle. Thank you for going into detail to help me!
If it works, good - at some point I suspect you'll see odd behaviors you can't quite explain, and if that does happen, remember what I've said about them - the articulated joints deal with all of this well, but hinges have issues - the can start to work and get touchy.
That said, how to you prefer to move the HingeJoint? Are you familiar with using the target limits and positioning the joint as you require? If so, all you really need is the target angle, and I'll put that in an answer (for the score on the board, you know)...
Answer by JVene · Aug 22, 2018 at 03:30 AM
First, one must calculate the angle to the target. I'll assume rotation on the Z axis for the moment, because it uses the standard X, Y coordinates common to 2D discussion, but you can choose any axis (or two if you need to create a Quaternion accounting for a 3D angle in other situations).
First, you require a vector to the target point. This is simply something like:
Vector3 v = target - subject;
Where target and subject are Vector3 positions, subject is the position of the hinge pivot point, target is where you want to point. The result is v, a vector3, from which we'll use the x and y members for Atan2 (next).
In trig, tangent is (in an oversimplified statement) y / x. There's an implied triangle involved and these are the two legs of that triangle in the y and x axis, where the point we're 'getting tangent' for is at the end of the hypotenuse of this implied right triangle (the other end is at the origin). The inverse trig function gives the angle, in radians, of this tangent. For C# in Unity that is something like:
float a_rad = Mathf.Atan2( v.y, v.x );
Note that some math libraries use x, y, but this one uses y, x, so attend the order with care.
This is in radians, but you'll probably require degrees, so:
float a_deg = a_rad * Mathf.Rad2Deg;
Converts to degrees. Note, however, this may be an odd orientation to you. It is from straight math, and the Mathf library follows. The angle returned from Atan2 assumes that pointing to the right (a point on the x axis) is zero degrees. Positive rotation results in counter clockwise rotation. You may have to invert this for your application if you assume a rotation that differs. Some assume a 'clock' like orientation, where noon is zero and clockwise is positive. You may have to invert with something like:
float a_deg_clockwise = 180 - a_deg;
This assumes you would use Atan's natural tendency to produce a range of 180 to -180 return values. This inverts counter clockwise into a clockwise rotation, but now 0 degrees is pointing to the negative x axis, which you would then orient as you might require by incrementing or decrementing the angle by 90 degrees, depending on the orientation you prefer. Experiment a bit, because you may have the hinge inverted from what you expect - I can't see your project from here, but merely recognize that the orientation of Atan2 is standard math, and you may have an alternate view that is related by possibly mirrored or offset by 90 degrees.
The result should be the TargetAngle you require.
Hmm, Ive tried tweaking it a little bit, and I can't seem to fix this problem. It is working for the lower 90 degrees, but when I try to go up past 0, it shoots straight down to roughly -90 degrees. Here is the code:
using System.Collections; using System.Collections.Generic; using UnityEngine;
public class EmptyTranslation : $$anonymous$$onoBehaviour { public HingeJoint Hinge1; public HingeJoint Hinge2; public HingeJoint Hinge3; public HingeJoint Hinge4; public HingeJoint Hinge5;
public GameObject targets;
public bool positive;
public bool negative;
public GameObject target;
public GameObject handfoot;
public GameObject upperlimb;
public float maxDistance;
public float $$anonymous$$Distance;
public float currentDistance;
public float totalDistance;
public float distance;
public float DistancePercent;
public GameObject maxDistanceObject;
public GameObject $$anonymous$$DistanceObject;
public Vector3 v;
public float a_rad;
public float a_deg;
// Use this for initialization
void Start () {
maxDistanceObject = new GameObject("Empty");
maxDistanceObject.transform.position = handfoot.transform.position;
maxDistanceObject.transform.parent = targets.transform;
$$anonymous$$DistanceObject = new GameObject("Empty");
$$anonymous$$DistanceObject.transform.position = upperlimb.transform.position;
$$anonymous$$DistanceObject.transform.parent = targets.transform;
maxDistance = maxDistanceObject.transform.position.x;
$$anonymous$$Distance = $$anonymous$$DistanceObject.transform.position.x;
}
// Update is called once per frame
void Update () {
totalDistance = maxDistance - $$anonymous$$Distance;
distance = target.transform.localPosition.x - $$anonymous$$Distance;
DistancePercent = distance / totalDistance;
DistancePercent = DistancePercent * 90 + -90;
DistancePercent = $$anonymous$$athf.Clamp(DistancePercent,-90, 0);
v = target.transform.localPosition - upperlimb.transform.localPosition;
a_rad = $$anonymous$$athf.Atan2(v.z,v.x);
a_deg = -a_rad * $$anonymous$$athf.Rad2Deg;
a_deg = -180 + a_deg;
JointSpring spring2 = Hinge2.spring;
spring2.targetPosition = a_deg;
Hinge2.spring = spring2;
JointSpring spring1 = Hinge1.spring;
spring1.targetPosition = DistancePercent;
Hinge1.spring = spring1;
JointSpring spring3 = Hinge3.spring;
spring3.targetPosition = -DistancePercent * 2;
Hinge3.spring = spring3;
Debug.Log(Hinge2.spring.targetPosition);
}
}
I'm about 90% certain the problem is here:
a_rad = $$anonymous$$athf.Atan2(v.z,v.x);
a_deg = -a_rad * $$anonymous$$athf.Rad2Deg;
a_deg = -180 + a_deg;
I put this into a spreadsheet for some quick checking. All results for a_deg return negative values, which I don't think is correct. That is, the range of a_deg is -360 to zero. I don't think (without checking) the hinge accepts angles < -180 or > 180.
The fact it works for a quadrant means you are close. What I can't see from here are what values of a_deg are working. You're saying the lower 90 degrees, I'm not exactly sure what that means, but since my observation is that a_deg is co$$anonymous$$g out -360 to zero, I would need to better understand what a_deg values are working, as in -90 to zero, or -180 to -90 to be of more help here.
$$anonymous$$aybe this gif can help you understand the problem better.
https://giphy.com/gifs/3d-physics-unity3d-2t9yiL0lqd7na0pGIO