Search Unity

  1. Unity support for visionOS is now available. Learn more in our blog post.
    Dismiss Notice

What a proper way to scale SphereCollider?

Discussion in 'Physics for ECS' started by Yuriy_Sevastyanov, Feb 25, 2020.

  1. Yuriy_Sevastyanov

    Yuriy_Sevastyanov

    Joined:
    Apr 9, 2017
    Posts:
    25
    Hello everyone!
    I have a 3d model with
    Code (CSharp):
    1. PhysicsShape
    2. {
    3.     ShapeType = Sphere,
    4.     Radius = 0.335,
    5.     Center = 0.29,
    6. }
    and it works fine until I change scale of the collider

    To change the scale I grabbed code from the
    UnityPhysicsSamples project,
    5b. Change Collider Size
    and modified it:
    Code (CSharp):
    1.  
    2. using Unity.Entities;
    3. using Unity.Jobs;
    4. using Unity.Physics;
    5. using Unity.Physics.Systems;
    6. using Unity.Transforms;
    7. using UnityEngine;
    8. using Math = System.Math;
    9. using SphereCollider = Unity.Physics.SphereCollider;
    10.  
    11. [UpdateBefore(typeof(BuildPhysicsWorld))]
    12. public class ChangeVolumeSystem : JobComponentSystem
    13. {
    14.     protected override JobHandle OnUpdate(JobHandle inputDependencies)
    15.     {
    16.         var deltaTime = Time.DeltaTime;
    17.         JobHandle jobHandle = Entities.ForEach(
    18.             (
    19.                 ref PhysicsCollider collider,
    20.                 ref Scale scale,
    21.                 in FoodAcceptorComponent foodAcceptor
    22.             ) =>
    23.             {
    24.                 ChangeVolume(
    25.                     deltaTime,
    26.                     ref collider,
    27.                     ref scale,
    28.                     in foodAcceptor);
    29.             }).Schedule(inputDependencies);
    30.  
    31.         return jobHandle;
    32.     }
    33.  
    34.     private static unsafe void ChangeVolume(
    35.         float deltaTime,
    36.         ref PhysicsCollider collider,
    37.         ref Scale scale,
    38.         in FoodAcceptorComponent foodAcceptor
    39.     )
    40.     {
    41.         var actualVolume = Conversions.ConvertSizeToVolume(scale.Value, foodAcceptor.VolumeFactor);
    42.         if (Math.Abs(1 - foodAcceptor.Volume / actualVolume) < 0.001)
    43.             return;
    44.  
    45.         // make sure we are dealing with spheres
    46.         if (collider.ColliderPtr->Type != ColliderType.Sphere)
    47.             return;
    48.  
    49.         var targetSize = Conversions.ConvertVolumeToSize(foodAcceptor.Volume, foodAcceptor.VolumeFactor);
    50.         var lerpStep = deltaTime * foodAcceptor.SizeGrowingSpeed;
    51.         var linearChangeFactor = (double) Mathf.Lerp(scale.Value, targetSize.ToFloat(), lerpStep) / scale.Value;
    52.  
    53.         // tweak the physical representation of the sphere
    54.         // grab the sphere pointer
    55.         SphereCollider* scPtr = (SphereCollider*) collider.ColliderPtr;
    56.         var newRadius = (float) (scPtr->Radius * linearChangeFactor);
    57.         var newCenter = (scPtr->Center * (float) linearChangeFactor);
    58.  
    59.         // update the collider geometry
    60.         var sphereGeometry = scPtr->Geometry;
    61.         sphereGeometry.Radius = newRadius;
    62.         sphereGeometry.Center = newCenter;
    63.         scPtr->Geometry = sphereGeometry;
    64.         scale = new Scale() {Value = (scale.Value * linearChangeFactor).ToFloat()};
    65.     }
    66. }
    67.  
    The problem is that the code doesn't affect MassProperties what I can see by the PhysicsDebugDisplay component. And then the model behavior becomes weird by the eccentricity, it can even walk through a wall.
    How can I manage the scale?
     
  2. Rory_Havok

    Rory_Havok

    Joined:
    Jun 25, 2018
    Posts:
    70
    The problem here is that rigid body is still using the original mass properties of the sphere - stored in
    the PhysicsMass component. There is currently nothing to detect that you have changed the collider and update the PhysicsMass of all the rigid bodies using it.

    But you should be able to workaround it by passing PhysicsMass into your function above and recomputing it based on your sphere's new MassProperties - use PhysicsMass.CreateDynamic() as a reference for the math needed.
     
    Yuriy_Sevastyanov likes this.
  3. Yuriy_Sevastyanov

    Yuriy_Sevastyanov

    Joined:
    Apr 9, 2017
    Posts:
    25
    @Rory_Havok Thank you very much. It works great!
    That is the final result:

    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. using UnityEngine;
    8. using Math = System.Math;
    9. using SphereCollider = Unity.Physics.SphereCollider;
    10.  
    11. [UpdateBefore(typeof(BuildPhysicsWorld))]
    12. public class ChangeVolumeSystem : JobComponentSystem
    13. {
    14.     protected override JobHandle OnUpdate(JobHandle inputDependencies)
    15.     {
    16.         var deltaTime = Time.DeltaTime;
    17.         JobHandle jobHandle = Entities.ForEach(
    18.             (
    19.                 ref PhysicsCollider collider,
    20.                 ref PhysicsMass physicsMass,
    21.                 ref Scale scale,
    22.                 in FoodAcceptorComponent foodAcceptor
    23.             ) =>
    24.             {
    25.                 ChangeVolume(
    26.                     deltaTime,
    27.                     ref collider,
    28.                     ref physicsMass,
    29.                     ref scale,
    30.                     in foodAcceptor);
    31.             }).Schedule(inputDependencies);
    32.  
    33.         return jobHandle;
    34.     }
    35.  
    36.     private static unsafe void ChangeVolume(
    37.         float deltaTime,
    38.         ref PhysicsCollider collider,
    39.         ref PhysicsMass physicsMass,
    40.         ref Scale scale,
    41.         in FoodAcceptorComponent foodAcceptor
    42.     )
    43.     {
    44.         var actualVolume = Conversions.ConvertSizeToVolume(scale.Value, foodAcceptor.VolumeFactor);
    45.         if (Math.Abs(1 - foodAcceptor.Volume / actualVolume) < 0.01)
    46.             return;
    47.  
    48.         // make sure we are dealing with spheres
    49.         if (collider.ColliderPtr->Type != ColliderType.Sphere)
    50.             return;
    51.  
    52.         var targetSize = Conversions.ConvertVolumeToSize(foodAcceptor.Volume, foodAcceptor.VolumeFactor);
    53.         var lerpStep = deltaTime * foodAcceptor.SizeGrowingSpeed;
    54.         var linearChangeFactor = (double) Mathf.Lerp(scale.Value, targetSize.ToFloat(), lerpStep) / scale.Value;
    55.  
    56.         // tweak the physical representation of the sphere
    57.         // grab the sphere pointer
    58.         SphereCollider* scPtr = (SphereCollider*) collider.ColliderPtr;
    59.         var newRadius = (float) (scPtr->Radius * linearChangeFactor);
    60.         var newCenter = (scPtr->Center * (float) linearChangeFactor);
    61.  
    62.         // update the collider geometry
    63.         var sphereGeometry = scPtr->Geometry;
    64.         sphereGeometry.Radius = newRadius;
    65.         sphereGeometry.Center = newCenter;
    66.         scPtr->Geometry = sphereGeometry;
    67.         scale = new Scale() {Value = (scale.Value * linearChangeFactor).ToFloat()};
    68.         physicsMass = PhysicsMass.CreateDynamic(scPtr->MassProperties, math.rcp(physicsMass.InverseMass));
    69.     }
    70. }
    71.  
     
    Rory_Havok likes this.