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
  4. Dismiss Notice

DOTS physics for 2D games: Apply constraint on z axis?

Discussion in 'Physics for ECS' started by Vacummus, Dec 9, 2019.

  1. Vacummus

    Vacummus

    Joined:
    Dec 18, 2013
    Posts:
    191
    I am trying determine the feasibility of using DOTS physics for a 2D side scrolling game. Is there a way to apply a constraint on the z-axis so that Rigidbodies don't fall off the edge?
     
    Vectrex likes this.
  2. steveeHavok

    steveeHavok

    Joined:
    Mar 19, 2019
    Posts:
    481
  3. Vacummus

    Vacummus

    Joined:
    Dec 18, 2013
    Posts:
    191
    For those who want to do this via code (instead of the editor), here is how you can create a joint for a rigidbody entity that will apply constraints against the z-axis for both rotation and translation:

    Code (CSharp):
    1. public static Entity CreateLockedZAxisJointEntity(EntityManager entityManager, Entity entity)
    2. {
    3.     var worldFromA = new RigidTransform(
    4.         entityManager.GetComponentData<Rotation>(entity).Value,
    5.         entityManager.GetComponentData<Translation>(entity).Value
    6.     );
    7.     RigidTransform bFromA = math.mul(math.inverse(RigidTransform.identity), worldFromA);
    8.     BlobAssetReference<JointData> jointData = CreateLockedZAxisJoint(bFromA);
    9.  
    10.     var componentData = new PhysicsJoint
    11.     {
    12.         JointData = jointData,
    13.         EntityA = entity,
    14.         EnableCollision = 1,
    15.     };
    16.  
    17.     Entity jointEntity = entityManager.CreateEntity(typeof(PhysicsJoint));
    18.     entityManager.AddComponentData(jointEntity, componentData);
    19.  
    20. #if UNITY_EDITOR
    21.     entityManager.SetName(jointEntity, $"Joining {entityManager.GetName(entity)} + PhysicsWorld");
    22. #endif
    23.  
    24.     return jointEntity;
    25. }
    26.  
    27. public static BlobAssetReference<JointData> CreateLockedZAxisJoint(RigidTransform offset)
    28. {
    29.     Constraint[] constraints = new Constraint[2] {
    30.         new Constraint
    31.         {
    32.             ConstrainedAxes = new bool3(false, false, true),
    33.             Type = ConstraintType.Linear,
    34.             Min = 0,
    35.             Max = 0,
    36.             SpringFrequency = Constraint.DefaultSpringFrequency,
    37.             SpringDamping = Constraint.DefaultSpringDamping
    38.         },
    39.         new Constraint
    40.         {
    41.             ConstrainedAxes = new bool3(false, false, true),
    42.             Type = ConstraintType.Angular,
    43.             Min = 0,
    44.             Max = 0,
    45.             SpringFrequency = Constraint.DefaultSpringFrequency,
    46.             SpringDamping = Constraint.DefaultSpringDamping
    47.         }
    48.     };
    49.  
    50.     return JointData.Create(
    51.         new Math.MTransform(float3x3.identity, float3.zero),
    52.         new Math.MTransform(offset),
    53.         constraints
    54.     );
    55. }
    Usage is to simply pass in your entityManager and the entity you wish to apply the constraint against to this function:

    Code (CSharp):
    1. CreateLockedZAxisJointEntity(entityManager, entity);
     
  4. anatomi

    anatomi

    Joined:
    Jan 9, 2016
    Posts:
    7
    When I try to use the above code, Unity crashes (no error given). This is how I've tried to implement it:
    Code (CSharp):
    1.  
    2. using UnityEngine;
    3. using Unity.Entities;
    4. using Unity.Mathematics;
    5. using Unity.Transforms;
    6. using Unity.Physics;
    7. using Unity.Collections;
    8.  
    9. public sealed class Bootstrap : MonoBehaviour
    10. {
    11.     public int count;
    12.     private EntityManager entityManager;
    13.     public GameObject cubePrefab;
    14.  
    15.     private void OnEnable()
    16.     {
    17.      
    18.     }
    19.  
    20.     private void Start()
    21.     {
    22.         if (!enabled) return;
    23.  
    24.         entityManager = World.DefaultGameObjectInjectionWorld.EntityManager;
    25.  
    26.         var settings = GameObjectConversionSettings.FromWorld(World.DefaultGameObjectInjectionWorld, null);
    27.         Entity cubePrefabEntity = GameObjectConversionUtility.ConvertGameObjectHierarchy(cubePrefab, settings);
    28.  
    29.         CreateLockedZAxisJointEntity(entityManager, cubePrefabEntity);
    30.  
    31.         var positions = new NativeArray<float3>(count, Allocator.Temp);
    32.  
    33.         int yPos = 2;
    34.         for (int i = 0; i < positions.Length; i++) {
    35.             // Instantiate a bunch of cubes on top of each other.
    36.             positions[i] = new float3(UnityEngine.Random.Range(0f, 1.5f), yPos, UnityEngine.Random.Range(0f, 1.5f));
    37.             var instance = entityManager.Instantiate(cubePrefabEntity);
    38.             entityManager.SetComponentData(instance, new Translation { Value = positions[i] });
    39.             yPos += 2;
    40.         }
    41.         positions.Dispose();
    42.     }
    43.  
    44.     public static Entity CreateLockedZAxisJointEntity(EntityManager entityManager, Entity entity)
    45.     {
    46.         var worldFromA = new RigidTransform(
    47.             entityManager.GetComponentData<Rotation>(entity).Value,
    48.             entityManager.GetComponentData<Translation>(entity).Value
    49.         );
    50.         RigidTransform bFromA = math.mul(math.inverse(RigidTransform.identity), worldFromA);
    51.         BlobAssetReference<JointData> jointData = CreateLockedZAxisJoint(bFromA);
    52.  
    53.         var componentData = new PhysicsJoint
    54.         {
    55.             JointData = jointData,
    56.             EntityA = entity,
    57.             EnableCollision = 1,
    58.         };
    59.  
    60.         Entity jointEntity = entityManager.CreateEntity(typeof(PhysicsJoint));
    61.         entityManager.AddComponentData(jointEntity, componentData);
    62.  
    63. #if UNITY_EDITOR
    64.         entityManager.SetName(jointEntity, $"Joining {entityManager.GetName(entity)} + PhysicsWorld");
    65. #endif
    66.  
    67.         return jointEntity;
    68.     }
    69.  
    70.     public static BlobAssetReference<JointData> CreateLockedZAxisJoint(RigidTransform offset)
    71.     {
    72.         Constraint[] constraints = new Constraint[2] {
    73.             new Constraint
    74.             {
    75.                 ConstrainedAxes = new bool3(false, false, true),
    76.                 Type = ConstraintType.Linear,
    77.                 Min = 0,
    78.                 Max = 0,
    79.                 SpringFrequency = Constraint.DefaultSpringFrequency,
    80.                 SpringDamping = Constraint.DefaultSpringDamping
    81.             },
    82.             new Constraint
    83.             {
    84.                 ConstrainedAxes = new bool3(false, false, true),
    85.                 Type = ConstraintType.Angular,
    86.                 Min = 0,
    87.                 Max = 0,
    88.                 SpringFrequency = Constraint.DefaultSpringFrequency,
    89.                 SpringDamping = Constraint.DefaultSpringDamping
    90.             }
    91.         };
    92.  
    93.         return JointData.Create(
    94.             new Math.MTransform(float3x3.identity, float3.zero),
    95.             new Math.MTransform(offset),
    96.             constraints
    97.         );
    98.     }
    99. }
    If I don't use CreateLockedZAxisJointEntity the prefab cubes (fitted with Physics Body and Shape) instantiate properly. Any idea what's going on?
     
  5. Vacummus

    Vacummus

    Joined:
    Dec 18, 2013
    Posts:
    191
    I am not familiar with using Entities as prefabs, and tend avoid the GameObjectConversion workflow process in favor of building my game via code (gives me more control and dramatically simplifies content creation on complex projects for me).

    However, instead of applying the joint constraint on the prefab entity, try to instead to apply it to the instance inside the for loop that you created, like so:

    Code (CSharp):
    1. int yPos = 2;
    2. for (int i = 0; i < positions.Length; i++) {
    3.     // Instantiate a bunch of cubes on top of each other.
    4.     positions[i] = new float3(UnityEngine.Random.Range(0f, 1.5f), yPos, UnityEngine.Random.Range(0f, 1.5f));
    5.     var instance = entityManager.Instantiate(cubePrefabEntity);
    6.     entityManager.SetComponentData(instance, new Translation { Value = positions[i] });
    7.     CreateLockedZAxisJointEntity(entityManager, instance);
    8.     yPos += 2;
    9. }
    Also, it's important that the joint is created after the translation is set. Doing it before the translation would not necessarily cause unity crash, but more so that any updates to the Translation component after a joint has been created will be ignored. Well not exactly ignored, more so the physics joint system will override Translation component with the connected position (relative to the connected body) of the joint. So the creation process is like so:
    Code (CSharp):
    1. - Create Rigidbody Entity
    2. - Set Translation
    3. - Create Joint entity (Use current Translation and Rotatation of RigidbodyEntity to set the connected position on the joint)
    4. - Phsysics Joint Systems OnUpdate: Update Translation of RigidbodyEntity with connected position from the Joint Entity.
     
    glenneroo and anatomi like this.
  6. anatomi

    anatomi

    Joined:
    Jan 9, 2016
    Posts:
    7
    Makes perfect sense. I'm trying to transition into a more code-centric process, but I'm admittedly a weak programmer.
    And this did the trick! Thank you.
     
  7. sdvoynikov

    sdvoynikov

    Joined:
    Nov 24, 2012
    Posts:
    7
    I found another solution that works for me. Set infinity inertia tensor for x and y axis like that:

    Code (CSharp):
    1.  
    2. em.AddComponentData(entity, PhysicsMass.CreateDynamic(new MassProperties {
    3.         Volume = ...,
    4.         MassDistribution = new MassDistribution {
    5.                 Transform = ...,
    6.                 InertiaTensor = new float3(float.PositiveInfinity, float.PositiveInfinity, 1),
    7.         }
    8. }, 1));
    9.  
     
    Last edited: Dec 15, 2019
    Vacummus and florianhanke like this.
  8. Vacummus

    Vacummus

    Joined:
    Dec 18, 2013
    Posts:
    191
    ^ Oh man, that one is great. Thanks for sharing!
     
  9. BenjaminBachman

    BenjaminBachman

    Joined:
    Feb 1, 2017
    Posts:
    29
    This seems to work for the rotation but the Z translation is not fixed and slightly moves away from 0 over time.
    My (hacky feeling) approach is to have a system running after
    ExportPhysicsWorld
    which sets the Z translation, X rotation, Y rotation as well as the linear velocity in Z direction and the angular velocity in X and Y directions to 0 every frame:

    Code (CSharp):
    1. using Unity.Entities;
    2. using Unity.Jobs;
    3. using Unity.Mathematics;
    4. using Unity.Physics;
    5. using Unity.Physics.Systems;
    6. using Unity.Transforms;
    7.  
    8. [UpdateAfter(typeof(ExportPhysicsWorld))]
    9. public class Physics2dSystem : JobComponentSystem
    10. {
    11.     protected override JobHandle OnUpdate(JobHandle inputDeps)
    12.     {
    13.         return Entities
    14.             .WithBurst()
    15.             .ForEach((
    16.                 ref PhysicsVelocity velocity,
    17.                 ref Translation position,
    18.                 ref Rotation rotation) =>
    19.             {
    20.                 velocity.Linear = new float3(velocity.Linear.x, velocity.Linear.y, 0);
    21.                 velocity.Angular = new float3(0, 0, velocity.Angular.z);
    22.                 position.Value = new float3(position.Value.x, position.Value.y, 0);
    23.                 rotation.Value = quaternion.Euler(0, 0, rotation.Value.ToEuler().z);
    24.             })
    25.             .Schedule(inputDeps);
    26.     }
    27. }
     
    florianhanke likes this.
  10. TRS6123

    TRS6123

    Joined:
    May 16, 2015
    Posts:
    246
    @BenjaminBachman if you want to constrain a body around all axes, you can just set all axes of the InertiaTensor to Infinity
     
  11. BenjaminBachman

    BenjaminBachman

    Joined:
    Feb 1, 2017
    Posts:
    29
    But wouldn't that also fix the rotation around the Z axis?
    Maybe I misunderstood OP. In my example I try to constraint the physics to the XY plane for an "Asteroid" type game. So only allow rotation around the Z axis but restrict the Z translation to 0. Like this:
    upload_2019-12-16_17-27-37.png
    Can this even be expressed with a float3 inertia tensor?
     
  12. TRS6123

    TRS6123

    Joined:
    May 16, 2015
    Posts:
    246
    Setting InertiaTensor to Infinity doesn't really lock rotation around an axis. It just prevents the simulation from modifying the angular velocity around the axis. You can still set the PhysicsVelocity to whatever you want.
     
  13. YiboInsane

    YiboInsane

    Joined:
    Jul 22, 2019
    Posts:
    16
    Bump. I wonder what is the right way to do the same in ECS physics 1.0.
     
    KANIYO and likk like this.