Search Unity

  1. Megacity Metro Demo now available. Download now.
    Dismiss Notice
  2. Unity support for visionOS is now available. Learn more in our blog post.
    Dismiss Notice

Question Simple Spring Constraint Between Entities Along X Axis

Discussion in 'Physics for ECS' started by oldhighscore, Dec 24, 2020.

  1. oldhighscore

    oldhighscore

    Joined:
    Nov 28, 2020
    Posts:
    79
    I am starting to dive into physics constraints and am trying to setup a simple example. I would like to have the sphere in the attached picture act as the static body in which the dynamic cube body will try to move towards. Since the cube is on the right of this image, I would want it to fling to the left, back to the right, back in, at lower and lower frequencies finally coming to a rest at the spheres center.

    When playing around I've gotten the cube to act as expected towards the world origin but I've been unable to replicate it for a different point. I don't really recall what that setup was exactly either but it was something like (you will have to see reference code for this to make sense) and I only used a single body and entity.

    physicsJoint.BodyAFromJoint = BodyFrame.Identity


    For reference, I've looked through the physics joint samples, read pretty much all the physics documentation and have the physics package source code open but I'm still at a loss. I know there are helper APIs for creating constraints, but I'm really trying to get a handle of how to just setup a simple use case from scratch and from what I can tell there is not a constraint for this particular example, maybe it would combine two constraints LimitDOFJoint and something else...

    I feel like I'm having difficulty setting the proper BodyAFromJoint and BodyBFromJoint parameters, and when it comes to linear algebra all though I'm well read up on it I don't have that much practical experience so I often am doing a lot of trial and error instead of fully understanding what perhaps some of the example code is doing when its performing things like

    // what exactly does the inverse of a RigidTransform represent?
    // what exactly does a multiplication between two RigidTransforms represent?
    RigidTransform bFromA = math.mul(math.inverse(worldFromB), worldFromA);

    Can someone please explain it to me like I'm 5 on how to get a proper spring setup along the X axis between two points? My next problem would be to implement a similar spring except for an angular constraint instead of a linear one, kinda like a seesaw that comes to equilibrium when starting from a 30ish degree offset.

    Code (CSharp):
    1. public class JointTesting : MonoBehaviour, IConvertGameObjectToEntity
    2. {
    3.     public GameObject TargetBody;
    4.  
    5.     public void Convert(Entity entity, EntityManager entityManager, GameObjectConversionSystem conversionSystem)
    6.     {
    7.  
    8.         var jointEntity = entityManager.CreateEntity(new ComponentType[]
    9.         {
    10.             typeof(JointTest),
    11.             typeof(PhysicsJoint),
    12.             typeof(PhysicsConstrainedBodyPair)
    13.         });
    14.         entityManager.SetName(jointEntity, "PhysicsJoint X Pivot Spring");
    15.  
    16.         var physicsJoint = new PhysicsJoint();
    17.         {
    18.             physicsJoint.JointType = JointType.Custom;
    19.             physicsJoint.SetConstraints(new FixedList128<Constraint>
    20.             {
    21.                 new Constraint
    22.                 {
    23.                     Type = ConstraintType.Linear,
    24.                     ConstrainedAxes = new bool3(false,true,true),
    25.                     SpringDamping = Constraint.DefaultSpringDamping,
    26.                     SpringFrequency = Constraint.DefaultSpringFrequency
    27.                 },
    28.                 new Constraint
    29.                 {
    30.                     Type = ConstraintType.Angular,
    31.                     ConstrainedAxes = new bool3(true,true,true),
    32.                     SpringDamping = Constraint.DefaultSpringDamping,
    33.                     SpringFrequency = Constraint.DefaultSpringFrequency
    34.                 },
    35.             });
    36.  
    37.             var worldFromTarget = Math.DecomposeRigidBodyTransform(TargetBody.transform.localToWorldMatrix);
    38.             var worldFromEntity = Math.DecomposeRigidBodyTransform(this.transform.localToWorldMatrix);
    39.  
    40.             // the code below is what I'm trying to wrap my head around
    41.             // I know theres a relationship between the cube -> sphere
    42.             // I want the sphere (TargetBody) to be the pivot point
    43.             // And have the cube (this) to try and spring to equilibrium towards the sphere
    44.  
    45.             // I copy and pasted this from some other examples, not really any logical or critical thinking going on my end
    46.             // some basic questions
    47.             // what exactly does the inverse of a RigidTransform represent?
    48.             // what exactly does a multiplication between two RigidTransforms represent?
    49.             RigidTransform bFromA = math.mul(math.inverse(worldFromTarget), worldFromEntity);
    50.             var positionLocal = this.transform.position;
    51.             var positionInConnectedEntity = math.transform(bFromA, positionLocal);
    52.             var bodyAFromJoint = BodyFrame.Identity;
    53.             bodyAFromJoint.Position = positionInConnectedEntity;
    54.  
    55.             // what do I set these to? I'm just guessing right now
    56.             physicsJoint.BodyAFromJoint = bodyAFromJoint;
    57.             physicsJoint.BodyBFromJoint = BodyFrame.Identity;
    58.  
    59.             entityManager.SetComponentData(jointEntity, physicsJoint);
    60.         }
    61.      
    62.  
    63.         var targetEntity = conversionSystem.GetPrimaryEntity(TargetBody);
    64.         var constraintPair = new PhysicsConstrainedBodyPair(entity, targetEntity, false);
    65.         entityManager.SetComponentData(jointEntity, constraintPair);
    66.  
    67.     }
    68. }
     

    Attached Files:

  2. oldhighscore

    oldhighscore

    Joined:
    Nov 28, 2020
    Posts:
    79
    I updated the first constraint to actually be constrained by the x. I'm able to get it to swing to the world origin but not the target point, trying to figure out what to set the joint bodies to atm. The B body (the target/sphere) I feel like should be the identity since that's where the joint is and the A body (the entity/cube) should be the offset from B -> A in relation to B's space?
     
  3. oldhighscore

    oldhighscore

    Joined:
    Nov 28, 2020
    Posts:
    79
    This seems to have worked, but I suppose I'm not exactly sure why setting both A & to be to Identity makes sense, are these the ideal resting positions?

    Code (CSharp):
    1.  
    2.  
    3. public void Convert(Entity entity, EntityManager entityManager, GameObjectConversionSystem conversionSystem)
    4.     {
    5.  
    6.         var jointEntity = entityManager.CreateEntity(new ComponentType[]
    7.         {
    8.             typeof(JointTest),
    9.             typeof(PhysicsJoint),
    10.             typeof(PhysicsConstrainedBodyPair)
    11.         });
    12.         entityManager.SetName(jointEntity, "PhysicsJoint X Pivot Spring");
    13.  
    14.         var physicsJoint = new PhysicsJoint();
    15.         {
    16.             physicsJoint.JointType = JointType.Custom;
    17.             physicsJoint.SetConstraints(new FixedList128<Constraint>
    18.             {
    19.                 new Constraint
    20.                 {
    21.                     Type = ConstraintType.Linear,
    22.                     ConstrainedAxes = new bool3(true,true,true),
    23.                     SpringDamping = 1,//Constraint.DefaultSpringDamping,
    24.                     SpringFrequency = 1//Constraint.DefaultSpringFrequency
    25.                 },
    26.                 new Constraint
    27.                 {
    28.                     Type = ConstraintType.Angular,
    29.                     ConstrainedAxes = new bool3(true,true,true),
    30.                     SpringDamping = Constraint.DefaultSpringDamping,
    31.                     SpringFrequency = Constraint.DefaultSpringFrequency
    32.                 },
    33.             });
    34.  
    35.             physicsJoint.BodyAFromJoint = BodyFrame.Identity;
    36.             physicsJoint.BodyBFromJoint = BodyFrame.Identity;
    37.  
    38.             entityManager.SetComponentData(jointEntity, physicsJoint);
    39.         }
    40.      
    41.  
    42.         var targetEntity = conversionSystem.GetPrimaryEntity(TargetBody);
    43.         var constraintPair = new PhysicsConstrainedBodyPair(entity, targetEntity, false);
    44.         entityManager.SetComponentData(jointEntity, constraintPair);
    45.  
    46.     }
     
  4. oldhighscore

    oldhighscore

    Joined:
    Nov 28, 2020
    Posts:
    79
    Was able to get the angular example working as well

    Code (CSharp):
    1. public class ZAngularSpring : MonoBehaviour, IConvertGameObjectToEntity
    2. {
    3.     public GameObject TargetBody;
    4.  
    5.     public void Convert(Entity entity, EntityManager entityManager, GameObjectConversionSystem conversionSystem)
    6.     {
    7.         var jointEntity = entityManager.CreateEntity(new ComponentType[]
    8.         {
    9.             typeof(JointTest),
    10.             typeof(PhysicsJoint),
    11.             typeof(PhysicsConstrainedBodyPair)
    12.         });
    13.         entityManager.SetName(jointEntity, "PhysicsJoint Z Angular Spring");
    14.  
    15.         var physicsJoint = new PhysicsJoint();
    16.         {
    17.             physicsJoint.JointType = JointType.Custom;
    18.             physicsJoint.SetConstraints(new FixedList128<Constraint>
    19.             {
    20.                 new Constraint
    21.                 {
    22.                     Type = ConstraintType.Linear,
    23.                     ConstrainedAxes = new bool3(true,true,true),
    24.                     SpringDamping = 0,
    25.                     SpringFrequency = 0,
    26.                 },
    27.                 new Constraint
    28.                 {
    29.                     Type = ConstraintType.Angular,
    30.                     ConstrainedAxes = new bool3(true,true,true),
    31.                     SpringDamping = 1,
    32.                     SpringFrequency = 1
    33.                 },
    34.             });
    35.  
    36.             entityManager.SetComponentData(jointEntity, physicsJoint);
    37.         }
    38.        
    39.         var targetEntity = conversionSystem.GetPrimaryEntity(TargetBody);
    40.         var constraintPair = new PhysicsConstrainedBodyPair(entity, targetEntity, false);
    41.         entityManager.SetComponentData(jointEntity, constraintPair);
    42.     }
    43. }
     
  5. milos85miki

    milos85miki

    Joined:
    Nov 29, 2019
    Posts:
    197
    Hi @oldhighscore, let me address your questions about transforms:
    1. Inverse of a transform matrix is a transform matrix that does the inverse (opposite) transformation.
    2. Multiplication of transform matrices is their composition, so math.mul(AFromB, BFromC) actually produces AFromC.
    Note that nomenclature is a bit unintuitive, as the transformation goes right to left (BFromC means C transform in B's local space) and that's why the signature of math.transform has transform first and then vector: vecInA = math.transform(AFromB, vecInB).

    In the example you provided (RigidTransform bFromA = math.mul(math.inverse(worldFromB), worldFromA)), math.inverse(worldFromB) produces BFromWorld and math.mul(BFromWorld, worldFromA) gives the final BFromA.
     
    Last edited: Dec 30, 2020
  6. oldhighscore

    oldhighscore

    Joined:
    Nov 28, 2020
    Posts:
    79
    Thank you for the reply!

    Looking back on the problem now this makes sense. I spent the last several days studying up on linear algebra and implementing basic use cases to develop a better grasp on transforms using vectors, matrices, euler representations and quaternions. Opening up the math package served educational as well to understand what the api has to offer and how it saves me from having to memorize/implement all the conversion formulas.
     
    Sab_Rango and milos85miki like this.