Unity.Physics 0.3.2 joins into the scene!

Hey everyone! Hope this day finds you well!

Here’s a little something to spice up your week: Unity.Physics 0.3.0-preview.1 is now available to download via the Package Manager! Here are some of the key features you can expect to find in this release:

  • Authoring support for Legacy Joints. Old-school gameobject based joints will now be automatically converted to Unity.Physics based joints when a ConvertToEntity component is attached, or if they’re placed in a subscene.
  • The Physics Simulation can now be optionally stepped with in a single threaded mode or immediately. This is much more performant for simple simulations where job management costs more than the simulation step itself. See the ‘6. Use Cases/ImmediateMode’ sample for example changes.
  • New Collider Shape re-sizing handles. These will allow one to resize the collision shapes directly in the scene view, if that’s your cup of tea.
  • Dependencies have been updated. Unity.Physics now has explicit dependencies for Burst, Collections, Jobs and Mathematics, in addition to Entities.
  • Bug fixes!
  • Performance improvements!

For a more detailed list of changes, check out the change log.

The Physics Sample has some changes as well, including fixes for a few issues, as well as updated dependencies (LWRP, Input System, DOTS Editor, Hybrid Renderer).

As usual, the features introduced in this release are experimental, so please report any issues you find using the Unity Bug Reporter, or in this thread.

10 Likes

Not sure about what I think of static bodies being forced to participate in the transform system, if how I read the changelog is accurate. Even when the bug is fixed. We have static only collision worlds with 400k+ colliders. Bugs aside are we going to take a performance hit when rebuilding for this use case?

  • If a static body does not have Parent component, then it is presumed to be in world space, and Translation/Rotation are read directly as before. This is how things have been up to this point, and it is the preferred, optimal approach.
  • If a static body does have a Parent component, then we decompose world space position/orientation for the RigidBody representation from the entity’s LocalToWorld component. The perf hit here comes from a) data bandwidth to build rigid body positions/orientations (since we are reading in 64 bytes per body, rather than just the 28 bytes per body in the former case) and b) doing the calculation. The bug in the TRSToLocalToParent system only applies to this latter case, as it was basically always touching LocalToParent with read/write permissions, whether or not anything changed, which meant LocalToWorld was always being rebuilt even if nothing changed.
1 Like

Cant wait to try this!

Does this include the collector refactor? Can’t find it in notes

That will be coming next release

1 Like

Is there some other plan for joints coming up later or why there isn’t any authoring components for them in Unity Physics? I know there are some in the physics samples but they are in additional scripts there.

To be able to even use the legacy joint conversion, you’d need to setup all physics using old style components, meaning no physics body components but rather old rigidbody etc which I guess it expected as it’s legacy joint conversion, but I’m now wondering what’s coming up that will not be for “legacy” setup :slight_smile:

Btw when will those updated physics samples go live? The sample repo hasn’t been updated for this package yet :slight_smile:

Any ETA about to public EulerAngles and PhysicsShapeExtensions?
I don’t use GameObjectConversion, but I need the two file to update my hybrid systems to the new version per time.

The updated Samples should go live some time today, hopefully. Thanks for pointing it out!

1 Like

Yes, a new authoring component is being worked on that will be part of the package proper. The current plan is for it to work much like the new shape component (e.g., one component with some common preset types, as well as a fully custom configuration). We need to settle a couple points in the run-time though to validate how the authoring data should look. We just need to give it the right design time because it is a lot of work to make changes to authoring data after the fact.

It is true a legacy joint needs a legacy Rigidbody, but you can also put a PhysicsBodyAuthoring component on the GameObject. In this case, the conversion system will ignore the data on the Rigidbody and just use things defined on PhysicsBodyAuthoring.

1 Like

There are no specific plans for either of these types different from anything else. But it would be very helpful if you can provide some clearer information about what you are doing that you need these types to be public.

I want two method like this:

public static BlobAssetReference<Unity.Physics.Collider> Convert(
            this UnityEngine.Collider collider,
            Unity.Physics.Material material,
            CollisionFilter collisionFilter,
            float convexRadius,
            bool isBaked);

public static BlobAssetReference<Unity.Physics.Collider> Convert(this PhysicsShapeAuthoring shape, int groupIndex, bool isBaked);

Can you give me some more context what the use cases for these methods are? In what ways do the built-in conversion systems not meet your needs?

In the latter signature I can see for example you want to assign a groupIndex, which we have no built-in workflow for yet, but in that case I would recommend a system that just runs after the built-ins and modifies the component data. For example:

using Unity.Collections;
using Unity.Entities;
using Unity.Mathematics;
using Unity.Physics;
using Unity.Physics.Authoring;
using UnityEngine;

// add this component to a hierarchy root to make all converted bodies ignore each other
[DisallowMultipleComponent]
class IgnoreBodiesAuthoring : MonoBehaviour
{

}

[UpdateAfter(typeof(LegacyRigidbodyConversionSystem))]
unsafe class ConvertIgnoreBodies : GameObjectConversionSystem
{
    EntityQuery m_IgnoreBodiesQuery;

    protected override void OnCreate()
    {
        base.OnCreate();
        m_IgnoreBodiesQuery = GetEntityQuery(ComponentType.ReadOnly<IgnoreBodiesAuthoring>());
    }

    protected override void OnUpdate()
    {
        if (m_IgnoreBodiesQuery.CalculateEntityCount() == 0)
            return;

        using (var entities = m_IgnoreBodiesQuery.ToEntityArray(Allocator.Persistent))
        {
            for (var i = 0; i < entities.Length; ++i)
            {
                var ignore = EntityManager.GetComponentObject<IgnoreBodiesAuthoring>(entities[i]);
                var groupIndex = -1 * math.abs(ignore.GetHashCode());

                foreach (var body in ignore.GetComponentsInChildren<PhysicsBodyAuthoring>())
                {
                    var bodyEntity = GetPrimaryEntity(body);

                    if (!DstEntityManager.HasComponent<PhysicsCollider>(bodyEntity))
                        continue;

                    var collider = DstEntityManager.GetComponentData<PhysicsCollider>(bodyEntity);
                    var filter = collider.ColliderPtr->Filter;
                    filter.GroupIndex = groupIndex;
                    collider.ColliderPtr->Filter = filter;
                }
            }
        }
    }
}

I don’t use the built-in conversion systems,I have a custom conversion workflow seem as GameObjectEntity(For using UnityEngine.Animator, ParticleSystem and so on).So I need to convert UnityEngine.Collider and PhysicsShapeAuthoring to BlobAssetReference<Unity.Physics.Collider> in runtime.
And sometime I don’t want to bake the collider such as lockstep server and client, it maybe induces flopping errors.
This is my code to use the two method:

        private bool __Build()
        {
            if (__physicsColliders == null || __physicsColliders.length < 1)
            {
                bool isBaked = (_flag & Flag.Baked) == Flag.Baked;
                var colliderBlobInstances = new NativeList<CompoundCollider.ColliderBlobInstance>(Allocator.Temp);

                Transform transform = base.transform;

                __colliders = new List<UnityEngine.Collider>();
                transform.GetComponentsInChildren<UnityEngine.Collider>(__colliders.Add, typeof(PhysicsShapeComponent), true);

                CollisionFilter collisionFilter = default;
                collisionFilter.CollidesWith = collidesWith;
                collisionFilter.BelongsTo = (uint)(int)_belongsTo;
                collisionFilter.GroupIndex = _groupIndex;

                Unity.Physics.Material material;
                material.Flags = 0;
                material.FrictionCombinePolicy = Unity.Physics.Material.CombinePolicy.Minimum;
                material.RestitutionCombinePolicy = Unity.Physics.Material.CombinePolicy.Minimum;
                material.CustomTags = 0;
                material.Friction = 0.0f;
                material.Restitution = 0.0f;
                __colliders.Convert(
                    colliderBlobInstances.Add,
                    transform,
                    material,
                    collisionFilter,
                    0.0f,
                    isBaked
#if UNITY_EDITOR
                , false
#endif
                );

                int index = 0;
                foreach (var collider in __colliders)
                {
                    if (collider.isTrigger)
                    {
                        if (__triggerIndices == null)
                            __triggerIndices = new List<int>();

                        __triggerIndices.Add(index);
                    }

                    //Destroy(collider);

                    ++index;
                }

                __shapes = new List<PhysicsShapeAuthoring>();
                transform.GetComponentsInChildren<PhysicsShapeAuthoring>(__shapes.Add, typeof(PhysicsShapeComponent), true);

                __shapes.Convert(
                   colliderBlobInstances.Add,
                   transform,
                   _groupIndex,
                   isBaked
#if UNITY_EDITOR
                , false
#endif
                );


                foreach (var shape in __shapes)
                {
                    if (shape.IsTrigger)
                    {
                        if (__triggerIndices == null)
                            __triggerIndices = new List<int>();

                        __triggerIndices.Add(index);
                    }

                    //Destroy(shape);

                    ++index;
                }

                __physicsColliders = PhysicsColliders.Create(colliderBlobInstances, true);

#if UNITY_EDITOR
                __physicsColliders.name = transform.root.name;
#endif

                colliderBlobInstances.Dispose();

                return true;
            }

            return false;
        }

Just installed this preview, first time using ECS but there’s already this weird thing happening.
I created two cubes, with one sitting on the other, both dynamics. They are positioned over a third cube without Physics Body (it’s static). When I hit play, the cube on top starts to slowly slide of the other like on ice, even if there are no external forces applied.
Sliding doesn’t happen between dynamic and static, only between dynamics objects.
I tried to set friction in Physics Shape to 1 but nothing changed.

you’ll want to use Havok for this type of scenario. Stable stacks of rigidbodies are not a good match for “Unity Physics” package because it’s a stateless physics engine (which has its own advantages; most notably it allows for easy and low-bandwidth networked physics)

3 Likes

Can you please provide some more information? Though it’s not the recommended workflow, the built-in conversion systems can work at run-time now. Basically: Why do you need to have your own custom run-time conversion instead of using the ones we provide?

I think I also need some more information to understand the problem here. What exactly do you mean by “bake the collider”? What in the built-in approach is not compatible with a lockstep networking model?

Thanks!

Unless I’m missing something, this currently conflicts with Entities 0.7.0, so, 0.6.0 is the current supported version which is a shame, is this due to change any time soon?

Can you elaborate, @Oh-Carson ? You mean it depends on older entities? That doesn’t automatically mean it’s not compatible with newer one. I’m running latest entities + physics package here without package conflicts

1.I have some custom worlds and need to set physics systems to my custom system group in FixedUpdate,like this:
5583775--576685--upload_2020-3-13_10-11-35.png
My many systems can’t and no need to run in default world,so I set DefaultGameObjectInjectionWorld to empty:

#if !GAME_RESOURCE_EDIT
public class GameBootStrap : ICustomBootstrap
{
    public bool Initialize(string defaultWorldName)
    {
        var world = new World(defaultWorldName);
        World.DefaultGameObjectInjectionWorld = world;

        return true;
    }
}
#endif

2.I have multiple worlds and need to set a entity to a specify world,but current version built-in conversion systems can’t do this well(maybe it’s my misunderstand), so I custom conversion systems seem as GameObjectEntity,like this:
5583775--576691--upload_2020-3-13_10-31-32.png
3.I believe unity can improve these issues in the future. but it’s so long for my game.I think the two method can give me more possibility.

This is my current Implementation:

public static BlobAssetReference<Unity.Physics.Collider> Convert(this PhysicsShapeAuthoring shape, int groupIndex, bool isBaked)
        {
            quaternion rotation;
            var transform = shape.transform;
            var localToWorld = transform.localToWorldMatrix;
            var shapeToWorld = shape.GetShapeToWorldMatrix();
            var blob = BlobAssetReference<Unity.Physics.Collider>.Null;
            switch (shape.ShapeType)
            {
                case ShapeType.Box:
                    var boxGeometry = shape.GetBoxProperties();
                    if (isBaked)
                    {
                        var orientation = EulerAngles.Default;
                        orientation.SetValue(boxGeometry.Orientation);
                        boxGeometry = boxGeometry.BakeToBodySpace(localToWorld, shapeToWorld, orientation);
                    }

                    blob = Unity.Physics.BoxCollider.Create(
                        boxGeometry,
                        GetFilter(shape, groupIndex),
                        GetMaterial(shape));
                    break;
                case ShapeType.Capsule:
                    var capsuleGeometryAuthoring = shape.GetCapsuleProperties();
                    if (isBaked)
                        capsuleGeometryAuthoring = capsuleGeometryAuthoring.BakeToBodySpace(localToWorld, shapeToWorld);

                    blob = Unity.Physics.CapsuleCollider.Create(
                        capsuleGeometryAuthoring.Get(),
                        GetFilter(shape, groupIndex),
                        GetMaterial(shape));
                    break;
                case ShapeType.Sphere:
                    var sphereGeometry = shape.GetSphereProperties(out rotation);
                    if (isBaked)
                    {
                        var orientation = EulerAngles.Default;
                        orientation.SetValue(rotation);
                        sphereGeometry = sphereGeometry.BakeToBodySpace(localToWorld, shapeToWorld, ref orientation);
                    }

                    blob = Unity.Physics.SphereCollider.Create(
                        sphereGeometry,
                        GetFilter(shape, groupIndex),
                        GetMaterial(shape));
                    break;
                case ShapeType.Cylinder:
                    var cylinderGeometry = shape.GetCylinderProperties();
                    if (isBaked)
                    {
                        var orientation = EulerAngles.Default;
                        orientation.SetValue(cylinderGeometry.Orientation);
                        cylinderGeometry = cylinderGeometry.BakeToBodySpace(localToWorld, shapeToWorld, orientation);
                    }

                    blob = CylinderCollider.Create(
                        cylinderGeometry,
                        GetFilter(shape, groupIndex),
                        GetMaterial(shape));
                    break;
                case ShapeType.Plane:
                    float3 v0, v1, v2, v3;
                    shape.GetPlaneProperties(out var center, out var size, out rotation);
                    {
                        var orientation = EulerAngles.Default;
                        orientation.SetValue(rotation);
                        if (isBaked)
                        {
                            PhysicsShapeExtensions.BakeToBodySpace(
                                center,
                                size,
                                orientation,
                                localToWorld,
                                shapeToWorld,
                                out v0,
                                out v1,
                                out v2,
                                out v3);
                        }
                        else
                        {
                            PhysicsShapeExtensions.CalculatePlanePoints(
                                center,
                                size,
                                orientation,
                                out v0,
                                out v1,
                                out v2,
                                out v3);
                        }
                    }

                    blob = PolygonCollider.CreateQuad(
                        v0,
                        v1,
                        v2,
                        v3,
                        GetFilter(shape, groupIndex),
                        GetMaterial(shape));
                    break;
                case ShapeType.ConvexHull:

                    using (var pointCloud = new NativeList<float3>(65535, Allocator.Temp))
                    {
                        shape.GetConvexHullProperties(pointCloud);
                        if (isBaked)
                            shape.BakePoints(pointCloud);

                        ConvexCollider.Create(
                            pointCloud,
                            shape.ConvexHullGenerationParameters,
                            GetFilter(shape, groupIndex),
                            GetMaterial(shape));
                    }
                    break;
                case ShapeType.Mesh:
                    const int defaultVertexCount = 2048;
                    using (var pointCloud = new NativeList<float3>(defaultVertexCount, Allocator.Temp))
                    using (var triangles = new NativeList<int3>(defaultVertexCount - 2, Allocator.Temp))
                    {
                        shape.GetMeshProperties(pointCloud, triangles);
                        if (isBaked)
                            shape.BakePoints(pointCloud);

                        blob = Unity.Physics.MeshCollider.Create(
                            pointCloud,
                            triangles,
                            GetFilter(shape, groupIndex),
                            GetMaterial(shape));
                    }
                    break;
                default:
                    break;
            }
            return blob;
        }

Bake collider need the transform.localToWorldMatrix,sometime my game have the same collider but in different hierarchy between server and client.
it give me two different colliders even if i make sure they have the same transform in world space.

Just upgraded, it seems like running collision event jobs before spawning any physics objects results in errors. If you add the code below to an empty project I get

CollisionEventJob.EventReader.m_InputVelocities has not been assigned

After spawning a unit the errors stop. Is this a bug or should I prevent this somehow?

code

[UpdateAfter(typeof(StepPhysicsWorld))]
public class CollisionEventSystem : JobComponentSystem
{
    protected override JobHandle OnUpdate(JobHandle inputDeps)
    {
        var step = World.GetExistingSystem<StepPhysicsWorld>();
        var build = World.GetExistingSystem<BuildPhysicsWorld>();

        return new CollisionEventJob
            {
            }
            .Schedule(step.Simulation, ref build.PhysicsWorld, inputDeps);
    }

    struct CollisionEventJob : ICollisionEventsJob
    {
        public void Execute(CollisionEvent c)
        {
        }
    }
}
1 Like