Search Unity

Question Tagging Hybrid Objects (Colliders/Triggers) and Collision Filters

Discussion in 'Physics for ECS' started by jashan, Aug 20, 2020.

  1. jashan

    jashan

    Joined:
    Mar 9, 2007
    Posts:
    3,307
    I'm still fairly new to DOTS and Unity Physics and have one type of object that needs to exist both in the "old GameObject/MonoBehaviour world" as well as the "new DOTS world". I have already managed to make the entity follow the GameObject-position by adding CopyTransformFromGameObject in IConvertGameObjectToEntity.Convert(...).

    What I'm trying to figure out now is how to best assign the Collision Filters for the DOTS-trigger. Obviously, I can't assign it from the old-school Collider component. So, should I add PhysicsShape-components, basically duplicating the old-school Colliders but adding the Collision Filters? If so, is the system smart enough to then ignore the old-school Colliders completely in the DOTS-world?

    Or would I have to create my own authoring components for that purpose, that set up the Collision Filters during conversion?

    Or is there already some component that is designed for this specific purpose?

    The PhysicsCollider data-component doesn't give me much information in the entity debugger (it just looks like a tag, without any properties), so I'm not sure how to best test this. Also, PhysicsDebugDisplay doesn't seem to work for me.

    (I'm on Unity 2019.4.8, Entities 0.11.1, Unity Physics 0.4.1)
     
  2. petarmHavok

    petarmHavok

    Joined:
    Nov 20, 2018
    Posts:
    461
    So the old-school collider's filter is actually passed on to the PhysicsCollider that gets attached to the entity that gets created for you. There is a legacy collider conversion code in place to make sure even these colliders can be used for new DOTS physics. Now there are limitations in terms of features, but collision filtering is supported. It basically maps the layer of the game object to a category in the collision filter. You can check BaseLegacyColliderConversionSystem.ProduceCollisionFilter for how it actually works.
     
    jashan likes this.
  3. MostHated

    MostHated

    Joined:
    Nov 29, 2015
    Posts:
    1,235
    I believe I might be doing something fairly similar, but doing a bit of back and forth between copying the Entities position to a Legacy collider, and then vice versa to let legacy ragdoll stuff happen.

    I have ECS vehicles in which all pathfinding, movement, etc is handled in DOTS systems. The vehicles interact with each other and nearly all things using ECS colliders, but I wanted to use PuppetMaster which is still on legacy physics, so I built out my vehicles fully in ECS first and set the collision filters, did all the conversions, etc and then made an ECS prefab out of it. Then when I go to spawn the vehicle I attached a legacy collider and CopyTransformToGameObject, that just follows around the vehicle so that, that collider can interact with PuppetMaster, which are just using the legacy layer system/collision matrix.

    The characters pathfinding and movement, etc are done in DOTS as well, but then using a triggerjob I check for distance and collision in ECS and when that happens I disable the ECS movement and associated things and then let the PuppetMaster ragdoll take over for a moment until the character regains its composure and is ready to move again and then I use CopyTransformFromGameObject to get the ECS components to the new position of where the ragdoll ended up and then let ECS take over to drive the position and such again.
     
    petarmHavok likes this.
  4. jashan

    jashan

    Joined:
    Mar 9, 2007
    Posts:
    3,307
    This seems tricky. One of the awesome things about Unity Physics is that I no longer have to deal with the old layers. Those are used for a lot of other things than physics, so there are plenty of them in our project.

    Is there a way to disable the automatic conversions of colliders? That way, I could keep the old colliders for when I'm not using DOTS / Unity Physics and wouldn't have them get in the way when I'm using it. This would, of course, mean that I'll have to add the Unity Physics components where necessary - but in my case, that would be much easier than having Unity Physics create lots of entities / components that I don't really need (and that might in fact cause trouble).
     
  5. jashan

    jashan

    Joined:
    Mar 9, 2007
    Posts:
    3,307
    Hm ... yes ... please don't auto-convert stuff for me. Turns out that this was the reason I've had a 1.7 seconds hiccup now that I started using DOTS, that happens in GameObjectsConversionGroup, and a significant part is PhysicsShapeConversionSystem. That alone takes 1346 milliseconds and generates 150 MB of "stuff" that I don't need. BuildCompoundCollidersConversionSystem adds another 319ms and 32MB.

    EDIT: Turns out most of that time is JIT compilation. Hopefully, that only happens in the editor. Still need to do some testing to confirm that's the case. Then at least that part is not an issue ... because PhysicsShapeConversionSystem actually is necessary for me (it's the legacy stuff that I need to get rid of).
     
    Last edited: Aug 28, 2020
  6. MostHated

    MostHated

    Joined:
    Nov 29, 2015
    Posts:
    1,235
    Yeah, that was why I manually did my conversions first through code and then added the legacy collider after the fact. Automatic conversion is fine for some things I am sure, but so far I have needed a bit more control of the "what and when" things happen.
     
    jashan likes this.
  7. jashan

    jashan

    Joined:
    Mar 9, 2007
    Posts:
    3,307
    How did you disable the automatic conversion? Did you remove / comment out the conversion systems that come with Unity Physics? Or is there a more elegant way?

    The issue I have with making changes in the packages is that you can't use the versions from the package registry because Unity will overwrite any changes you made on each startup. So I have to turn it into an embedded package, or one in a separate folder (this would be much less hassle, if UT kept the packages on GitHub, like they did with ScriptableRenderPipeline ... but apparently, that repository is also no longer updated on GitHub ... meh! :-/ )
     
  8. MostHated

    MostHated

    Joined:
    Nov 29, 2015
    Posts:
    1,235
    My apologies, I more meant that I didn't just take a prefab in the editor and enable/place a "Convert to Entity" component on it and call it a day. I tried to handle it as seen below. Based on your most recent post, though, it sounds like you are looking for something a bit more in-depth than I was doing?

    There was a bit more to the prep, but this is the general idea.

    Code (CSharp):
    1. using (var vehicleGameObject = new LoadPrefabContentsScope(path))
    2. {
    3.     // ---------------------------------------------------------------- Wheel Setup
    4.     // -- Wheel Setup -------------------------------------------------------------
    5.     var wheels = vehicleGameObject.Prefab.Descendants()
    6.         .Where(x => WheelStrings.Contains(x.name) ||
    7.                     x.name.Contains("Wheel_")
    8.                     && !x.name.Contains("Steering")).ToList();
    9.  
    10.     for (int w = 0; w < wheels.Count; w++)
    11.     {
    12.         wheels[w].GetOrAddComponent<VehicleWheelComponent>().prefab = wheels[w].gameObject;
    13.     }
    14.  
    15.     // -------------------------------------------------------------- Physics Shape
    16.     // -- Physics Shape -----------------------------------------------------------
    17.     var physicsShapeAuthoring = vehicleGameObject.Prefab.AddComponent<PhysicsShapeAuthoring>();
    18.     physicsShapeAuthoring.SetConvexHull(ConvexHullGenerationParameters.Default);
    19.  
    20.     var categoryTags = new PhysicsCategoryTags { Value = 9 };
    21.     var customMaterialTag = physicsShapeAuthoring.BelongsTo;
    22.     customMaterialTag.Value = categoryTags.Value;
    23.     physicsShapeAuthoring.BelongsTo = customMaterialTag;
    24.  
    25.     // --------------------------------------------------------------- Physics Body
    26.     // -- Physics Body ------------------------------------------------------------
    27.     var physicsBodyAuthoring = vehicleGameObject.Prefab.GetOrAddComponent<PhysicsBodyAuthoring>();
    28.     var physTags = new PhysicsCustomTags { Value = 2 };
    29.  
    30.     var customTag = physicsBodyAuthoring.CustomTags;
    31.     customTag.Value = physTags.Value;
    32.  
    33.     physicsBodyAuthoring.CustomTags = customTag;
    34.     physicsBodyAuthoring.Mass = VehicleDataDefaults.vehicleDefaults[vehicleType]["mass"];
    35.     physicsBodyAuthoring.OverrideDefaultMassDistribution = true;
    36.     physicsBodyAuthoring.CustomMassDistribution = new MassDistribution
    37.     {
    38.         Transform = new RigidTransform(quaternion.identity, new float3(0f, -0.1f, 0f)),
    39.         InertiaTensor = new float3(2f / 5f)
    40.     };
    41.  
    42.     vehicleGameObject.IsSave = true;
    43.     Debug.Log($"✓ {vehicleGameObject.Prefab.name} Prefab Asset Created");
    44. }

    Code (CSharp):
    1. public NativeList<Entity> CreateVehicleEntity()
    2. {
    3.     blobAssetStore = new BlobAssetStore();
    4.     var settings = GameObjectConversionSettings.FromWorld(World.DefaultGameObjectInjectionWorld, blobAssetStore);
    5.     var entityManager = World.DefaultGameObjectInjectionWorld.EntityManager;
    6.     for (int i = 0; i < vehicleData.Count; i++)
    7.     {
    8.         var vehicleGo = vehicleData[i].activeGameObject;
    9.         var vehEntity = GameObjectConversionUtility.ConvertGameObjectHierarchy(vehicleGo, settings);
    10.         vehEntities.Add(vehEntity);
    11.         entityManager.AddComponentData(vehEntity, new VehicleEntity
    12.         {
    13.             entity = vehEntity
    14.         });
    15.  
    16.         entityManager.AddBuffer<WaypointPathList>(vehEntity);
    17.         entityManager.AddComponentData(vehEntity, new VehicleTag());
    18.         entityManager.AddComponentData(vehEntity, new MovableTag());
    19.         entityManager.AddComponentData(vehEntity, new MoveSpeed());
    20.         entityManager.AddComponentData(vehEntity, new WaypointData());
    21.         entityManager.AddComponentData(vehEntity, new WPPathStatus { PathStatus = VehicleController.Instance.pathStatus });
    22.         entityManager.AddComponentData(vehEntity, new WPGoalStatus { ReachedGoal = VehicleController.Instance.goalStatus });
    23.         entityManager.AddComponentData(vehEntity, new WPMoveStatus { movementStatus = VehicleController.Instance.moveStatus });
    24.         entityManager.AddComponentData(vehEntity, new SettingData
    25.         {
    26.             currentIndex = 1,
    27.             CurrentTurnSpeed = vehicleTurnSpeed,
    28.             DistanceFromWaypoint = distanceFromWaypoint
    29.         });
    30.  
    31.         entityManager.AddComponent(vehEntity, typeof(Prefab));
    32. #if UNITY_EDITOR
    33.         entityManager.SetName(vehEntity, $"Vehicle_{i.ToString()}");
    34. #endif
    35.         vehicleDatas.Add(vehEntity, vehicleData[i]);
    36.     }
    37.     return vehEntities;
    38. }

    At this point I would then instantiate the ECS vehicle when needed, and once it is spawned I would then add the collider to it, or whatever other legacy/managed things were needed just depending:

    Code (CSharp):
    1. public void SetupVehicleColliders(Entity entity, Entity prefab)
    2. {
    3.     var reference = Instantiate(VehicleConverter.Instance.vehicleDatas[prefab].vehicleCollider);
    4. #if UNITY_EDITOR
    5.     Debug.Log($"Collider: {reference.name}");
    6.     var objName = $"Veh_{math.abs(reference.GetHashCode()).ToString()}";
    7.     entityManager.SetName(entity,objName);
    8.     reference.name = objName;
    9. #endif
    10.     reference.transform.position = new Vector3(0f, 0f, 0f);
    11.     vehicleColliders.Add(entity, reference.Entity);
    12.     entityManager.AddComponent(entity, typeof(CopyTransformToGameObject));
    13.     entityManager.AddComponentObject(entity, reference.transform);
    14. }
     
    jashan likes this.
  9. jashan

    jashan

    Joined:
    Mar 9, 2007
    Posts:
    3,307
    Actually, less in-depth ... in a way ;-)

    From what I'm seeing now, there already is a define LEGACY_PHYSICS, that automatically is added when the resource/package com.unity.modules.physics is available. Now, all I'd need is a way to override that, so that LEGACY_PHYSICS is not there, even though we do have legacy physics.

    But it looks like I'll still have to get the current version of the package, copy it over to a local folder and add it back as local folder to be able to make the change I need to make.
     
  10. MostHated

    MostHated

    Joined:
    Nov 29, 2015
    Posts:
    1,235
    Sounds like that should be a simple enough solution, as long as it doesn't end up poking a bear that no one saw, lol.

    I saw someone in the forum here mention putting a package currently in your project into "dev mode" if I am not mistaken, but I have yet to have the need to do so myself, so I don't have any additional details on that, but perhaps that particular phrase/term might help get you where you need to be. :thumbsup:
     
    jashan likes this.
  11. jashan

    jashan

    Joined:
    Mar 9, 2007
    Posts:
    3,307
    So far, things look good ;-)

    Yeah, they've had different names for that, I guess. You can either put it right into the packages folder, which overrides packages.json and I guess that's what they call "dev mode". What I don't like about that approach is that it puts the whole package into my project's version control. There are benefits to that, too, of course, but for now I opted for the other approach:

    You can also just put it anywhere on the file system (outside the project), and then put the path into packages.json.

    For configurations like that, I wish there was a better way (some override in packages.json, for example) but I don't think that's an option, yet.
     
  12. MostHated

    MostHated

    Joined:
    Nov 29, 2015
    Posts:
    1,235
    Yeah, I just actually came across that a few minutes ago (the format for using local).

    At a minimum, it would be nice if packages.json would use jsonc or the like, similar to Houdini, so comments could be used within the json to more easily switch back and forth in a more simple manner between versions or whatever.

    Code (CSharp):
    1.     "com.unity.dataflowgraph": "0.14.0-preview.2",
    2.     // "com.unity.entities": "file:../dots/Samples/Packages/com.unity.entities",
    3.     "com.unity.entities": "0.14.0-preview.2",
    4.  
    It would be pretty great to have Houdinis package config system in general.
    You can set the paths based on env variables, conditionals, versions, use comments, etc.

    Code (JavaScript):
    1. {
    2.     "path" :
    3.         [
    4.             {"houdini_os != 'windows'" : "/user/bob/libs"},
    5.             {"houdini_os == 'windows'" : "$HOME/bob_win_libs"},
    6.             {"$use_tom_libs == '1'" : "$HOME/tom_libs"}
    7.         ]
    8. },
    9. {
    10.     "requires": { "houdini_version > '18.0'": "package.xyz" }
    11. }
    One of these days, maybe. I also cant wait to see how much more they end up integrating Python, though, just getting off track now, lol.

    As for the prior discussion, glad to see you were able to get on the right path though. Figuring out where to even start with certain functions and features is half the fun of DOTS. :p
     
    jashan likes this.
  13. jashan

    jashan

    Joined:
    Mar 9, 2007
    Posts:
    3,307
    Hehe, yup. If Unity Physics now works as I need it, I have most things that I need working in principle. So that's kind of a big milestone ... and I just hope the bears don't wake up and start h(a)unting me ;-)
     
    MostHated likes this.
  14. snacktime

    snacktime

    Joined:
    Apr 15, 2013
    Posts:
    3,356
    Few notes based on my own experience with hybrid.

    Sync data one way only, from entities to gameobjects. Not having one side with authority will case you much pain down the road. It's ok to go the other way if the gameobject must drive movement,if there is an actual good reason for that beyond well that's how the existing system works.

    It's fairly straight forward to convert LayerMask to CollisionFilter in code. Substitute BitArray32 with whatever bit array implementation you want Collections has that now.

    Code (csharp):
    1.  
    2. public static CollisionFilter CreateCollisionFilter(int belongsTo, LayerMask collidesWith)
    3.         {
    4.             BitArray32 mask = new BitArray32();
    5.             mask[belongsTo] = true;
    6.             CollisionFilter filter = new CollisionFilter()
    7.             {
    8.                 BelongsTo = (uint)mask.Bits,
    9.                 CollidesWith = (uint)collidesWith.value
    10.             };
    11.             return filter;
    12.         }
    13.  
    If you use conversion don't try to dual purpose stuff. Just create separate gameobjects. Ie PhysicsShape/PhysicsBody but no Physx collider. If it's convex or mesh link the mesh directly don't use a MeshFilter. This creates duplication but the separation of concerns is more beneficial long term.

    Conversion is not designed to be used at runtime. If you plan on using conversion in production plan on using sub scenes. Alternatively a custom implementation over world serialization (which can be surprisingly compact although requires a deeper understanding of a few things).
     
    jashan and MostHated like this.
  15. jashan

    jashan

    Joined:
    Mar 9, 2007
    Posts:
    3,307
    Thanks for chiming in!

    In our case, we have gameplay objects that basically only exist in ECS/DOTS, and player objects that are driven by VR controllers. Those primarily exist as and are driven by game objects (that are connected to and driven by the actual controller hardware). There is quite a bit of complexity in those "player objects", and it's only a few (maximum 5 per player, maximum 4 active players), so moving that over to ECS feels like a lot of pain for very little gain.

    I'm quite happy to no longer have to deal with LayerMasks for physics and I believe by disabling the LegacyConversions, that issue is now solved for us. I reviewed BaseLegacyColliderConversionSystem.ProduceCollisionFilter(...) and its context as @petarmHavok suggested. That part is easy to understand - but I wasn't able to figure out how I could make the system ignore those automatically created CollisionFilters (from LayerMask) and instead use my own implementation.

    Just out of curiosity, how would that work? In other words, how would I make the system actually use my own implementation of CreateCollisionFilter, without having to disable the default systems and completely rewrite them?

    I have considered this but just adding PhysicsBody and PhysicsShape to my controller objects and making sure Rigidbody and legacy Colliders are not converted seemed like the simplest solution in my specific scenario.

    There's a lot of moving control back and forth between physics and the physical controllers (which I believe would be a nightmare in ECS) but interaction with the gameplay objects (that live in ECS) only happens when control is at the physical controllers (not when control is in the physics engine, in this case PhysX, not Unity Physics).

    If this was a new project, doing everything in ECS may actually work but it seems like in this specific case, the benefits of ECS are outweighed by development costs.

    Thank you - I guess I need to learn more about sub scenes, then. I haven't really looked into those so far. And it sounds like custom implementation over world serialization might actually be what I want. At the moment, I have gameplay objects (i.e. objects that the player actually interacts with), which can be many, living completely in ECS. There's one unpleasant case there where for specific particle effects, I need the mesh data in the gameobjects/monobehaviour world (because currently, particles cannot exist in ECS, at least not without a custom implementation). But other than that, it looks like this will be nice and clean.

    Then, there's the objects representing the player, which primarily live in the gameobjects/monobehaviour world and just need to be hooked in with ECS-physics.

    With the environments, those are currently only in gameobjects/monobehaviours, plus I might add a collider or two for those into ECS. But I might also have ECS-based environments (I guess that would be a classic use case for sub scenes).