Search Unity

  1. Welcome to the Unity Forums! Please take the time to read our Code of Conduct to familiarize yourself with the forum rules and how to post constructively.
  2. We have updated the language to the Editor Terms based on feedback from our employees and community. Learn more.
    Dismiss Notice
  3. Join us on November 16th, 2023, between 1 pm and 9 pm CET for Ask the Experts Online on Discord and on Unity Discussions.
    Dismiss Notice

how to control connected body joint orientation?

Discussion in 'Physics' started by bentama, Nov 8, 2017.

  1. bentama

    bentama

    Joined:
    Jan 5, 2014
    Posts:
    5
    It appears to be impossible to setup a new joint without moving/rotating the two bodies into the desired relative rest position/rotation. It would be nice to be able to setup the joint and then let the simulation move the bodies to a solved state if needed.

    I am aware of the 'anchor' and 'connected anchor' parameters (along with the auto-configure checkbox), but this isn't enough since it doesn't address the relative orientations of the two sides of the joint.

    For example, consider a door with a single hinge. The hinge can be assigned to the door and the anchor and axis parameters (partially) define the joint orientation on the door side, but there is only 'connected anchor' to define the orientation on the door frame side. Furthermore, the axis parameter isn't enough to define the rest angle of the joint.

    The workaround is of course to position the two bodies in their rest positions when the joint is initialized, and this is what we're currently doing to get by. But it has a number of problems, and here is just one: there is no good way to serialize/deserialize a simulation in progress (e.g., load a saved game) since the joint bodies need to be in their rest position on the first frame for joint configuration to work right. I guess the bodies could be moved to their correct saved position/rotation on the second frame (and hide the first frame so the user doesn't see it). Is that what others do to work around this?

    It would be really great to ditch the axis (and secondaryAxis parameters of CJ) and replace with a Quaternion for joint rotation (relative to the body) and another Quaternion for connected joint rotation (relative to the connected body).
     
  2. KeithKong

    KeithKong

    Joined:
    May 31, 2015
    Posts:
    73
    You are mostly correct but here's some tips to make your work around more workable.

    1) Distance between anchors does not matter since they are explicitly set (unless you are using autoConfig, a confusing approach if being done in script), so all you need to do is orient the connected body rotation wise.

    2) You cannot ditch the axis/secondary axis. This actually establishes the joint orientation for the main body AND limits. Otherwise, you would actually need two cached rotations or axis pair (one for relative orientation and one for limits). This is because the limits require an additional relation outside of the "rotation difference" between the objects.

    3) You don't need to hide a frame or anything like that. Simply create the joint, cache and orient connected body rotation, set all joint properties, then immediately restore connected body rotation using cached variable.

    4) For serialization, you simply need a utility mono behavior which permanently stores the desired relative rotation at rest.

    Hope this helps :)
     
    Last edited: Nov 9, 2017
  3. bentama

    bentama

    Joined:
    Jan 5, 2014
    Posts:
    5
    Sorry for the slow reply, I forgot about this post for a bit.
    And thank you very much KeithKong for your reply! I really appreciate you taking the time to share your insights!

    Unfortunately I don't think I described the issue clear enough in my original post. The crux of the problem is that I want to initialize a joint such that the rest orientation is different from the orientation at the start of the simulation and I don't see how that is possible with the current set of options that Unity makes available.

    Here's an example: consider a hinge used to model a swinging door. The hinge is setup with angle limits so the rest angle (angle==0) should be the state when the door is in the closed position. This means the simulation must start with the door in the closed position. But what if I want it to start with the door open? If I start with the door open, then the open angle is what gets defined as the rest angle. That is the issue.

    Similar example: imagine a character rig with the rest position in a T-pose, but we want the simulation to start with the character in a different (natural) position. We want the T-pose to define joint rest angles, but we don't want joints to start at the rest angle.

    Your comment #3 seems to indicate that I can setup everything in its rest position, create the joint, and then immediately move everything to their simulation start positions. Are you sure about that? In my experience that will result in the joints incorrectly configured - they will use the simulation start position rather than the initial position.
     
  4. KeithKong

    KeithKong

    Joined:
    May 31, 2015
    Posts:
    73
    I totally understand the limitation you're describing, perhaps it was me who was unclear about the solution to this :p

    To use your example of the door being open initially but limits set relative to the closed state–When setting up the joint via script, you would temporarily rotate the door to the closed state, then set all properties (not sure which ones matter but probably the connected body/axis/secondary axis/limit properties), then in the same exact method (so in the same frame/fixed frame) you rotate the door back to open. My tests have shown that the limits are relative to the rotation at the moment of setting, not some later "before first frame" check.

    So it's a little hacky feeling, but it works and you can have your initial state be whatever you want.

    Note: In my tests I am creating the ConfigurableJoint mono behavior as well. I should probably see if it works "resetting" all properties without creating the joint.
     
    Last edited: Nov 20, 2017
  5. KeithKong

    KeithKong

    Joined:
    May 31, 2015
    Posts:
    73
    Just made an alteration to my test behavior that allows for resetting the properties without destroying the ConfigurableJoint and it works!

    This test script assumes that no rotation is the correct resting relation. You can then rotate it to any position and press play. When playing, you can change the limits and then set resetWaitFrames true to see the limits properly update no matter the current rotation.

    Here's the code if you want to play with it:

    Code (CSharp):
    1. using UnityEngine;
    2.  
    3. public class ScriptConfigJointTest : MonoBehaviour {
    4.  
    5.     public int waitFrames = 10;
    6.     public bool resetWaitFrames = false;
    7.     public bool destroyJoint = false;
    8.  
    9.     public Rigidbody connectedBody = null;
    10.  
    11.     public Vector3 anchor;
    12.     public Vector3 axis;
    13.     public Vector3 secondaryAxis;
    14.  
    15.     public Vector3 connectedAnchor;
    16.  
    17.     public float primaryHighLimit = 30f;
    18.     public float primaryLowLimit = -30f;
    19.     public float secondaryLimit = 30f;
    20.     public float tertiaryLimit = 0f;
    21.  
    22.     private int mWaitFrameCount;
    23.     private ConfigurableJoint mConfigJoint;
    24.  
    25.     void Awake () {
    26.         mWaitFrameCount = 0;
    27.         mConfigJoint = null;
    28.     }
    29.  
    30.     void FixedUpdate () {
    31.  
    32.         if (destroyJoint) {
    33.             if (mConfigJoint != null) {
    34.                 Destroy (mConfigJoint);
    35.                 mConfigJoint = null;
    36.             }
    37.             destroyJoint = false;
    38.         }
    39.  
    40.         if (resetWaitFrames) {
    41.             mWaitFrameCount = 0;
    42.             resetWaitFrames = false;
    43.         }
    44.  
    45.         mWaitFrameCount++;
    46.         if (mWaitFrameCount == waitFrames) {
    47.  
    48.             if (mConfigJoint == null) {
    49.                 mConfigJoint = this.gameObject.AddComponent<ConfigurableJoint> ();
    50.             }
    51.  
    52.             Quaternion prevRot = this.transform.rotation;
    53.             this.transform.rotation = Quaternion.identity;
    54.  
    55.             mConfigJoint.connectedBody = connectedBody;
    56.  
    57.             mConfigJoint.anchor = anchor;
    58.             mConfigJoint.axis = axis;
    59.             mConfigJoint.secondaryAxis = secondaryAxis;
    60.  
    61.             mConfigJoint.autoConfigureConnectedAnchor = false;
    62.             mConfigJoint.connectedAnchor = connectedAnchor;
    63.  
    64.             mConfigJoint.xMotion = ConfigurableJointMotion.Locked;
    65.             mConfigJoint.yMotion = ConfigurableJointMotion.Locked;
    66.             mConfigJoint.zMotion = ConfigurableJointMotion.Locked;
    67.  
    68.             mConfigJoint.angularXMotion = ConfigurableJointMotion.Limited;
    69.             mConfigJoint.angularYMotion = ConfigurableJointMotion.Limited;
    70.             mConfigJoint.angularZMotion = ConfigurableJointMotion.Locked;
    71.  
    72.             SoftJointLimit softJointLimit = mConfigJoint.highAngularXLimit;
    73.             softJointLimit.limit = primaryHighLimit;
    74.             mConfigJoint.highAngularXLimit = softJointLimit;
    75.  
    76.             softJointLimit = mConfigJoint.lowAngularXLimit;
    77.             softJointLimit.limit = primaryLowLimit;
    78.             mConfigJoint.lowAngularXLimit = softJointLimit;
    79.  
    80.             softJointLimit = mConfigJoint.angularYLimit;
    81.             softJointLimit.limit = secondaryLimit;
    82.             mConfigJoint.angularYLimit = softJointLimit;
    83.  
    84.             softJointLimit = mConfigJoint.angularZLimit;
    85.             softJointLimit.limit = tertiaryLimit;
    86.             mConfigJoint.angularZLimit = softJointLimit;
    87.  
    88.             this.transform.rotation = prevRot;
    89.         }
    90.     }
    91. }
    Notice that the joint creation is also taking place BEFORE temporarily changing the rotation, so it doesn't appear to be important.
     
    Last edited: Nov 21, 2017
  6. bentama

    bentama

    Joined:
    Jan 5, 2014
    Posts:
    5
    thanks again for your very helpful response! And thank you for verifying this behavior with a script.

    It appears I was wrong about how joints are initialized. As you point out, the critical point is when certain parameters of the joint are set. I just constructed a simple test with a hinge joint and found that when the 'connectedBody' parameter is set is when the relative orientation between the two bodies is defined. I'm guessing the state of 'connectedBody' also has an impact on joint limits too.

    I presumed joint configurations were finalized on the next frame because something I read somewhere I think, and because I thought that was the behavior I was observing. I must have been jumping to conclusions and didn't understand the importance of the order in which joint parameters are set. Thanks again for helping to clear this up!
     
  7. KeithKong

    KeithKong

    Joined:
    May 31, 2015
    Posts:
    73
    No problem! I've been working almost exclusively with joints for the last month building a dynamic runtime "object plugging" system. It's required me to do extensive testing to figure out exactly how the darned thing works. Good to know the connectedBody property is the key ingredient, thanks for that insight!