2D Physics in Unity 2021.2

2D Physics in Unity 2021.2

In Unity 2021.2 we’ve introduced two new features for 2D Physics that provide direct access to primitive shapes and unlock additional extensibility possibilities.

What’s new?

CustomCollider2D

  • A new collider with direct access for creating, updating and deleting actual primitive shapes.
  • It can use any combination of the available primitive shapes that the physics engine understands i.e. Circles, Capsules, Polygons or Edges.
  • As a 2D Collider, it automatically inherits all existing features such as physics materials, triggers, physics callbacks, effector compatibility, scene gizmos etc.
  • Physics shape properties can be modified at runtime as well.

PhysicsShapeGroup2D

  • Represents a simple collection of primitive shapes.
  • Physics shapes can be added, modified or removed and PhysicsShapeGroup2Ds can be merged.
  • Designed to define the shapes in a CustomCollider2D
  • A PhysicsShapeGroup2D can be populated by using Collider2D.GetShapes on any existing 2D collider or Rigidbody2D.GetShapes on any Rigidbody2D (to get all shapes on all colliders).

Samples
Check out the 2D Physics Samples github repo, especially the following scenes that show CustomCollider2D and PhysicsShapeGroup2D in action:

  • CustomCollider2D_Compound
  • CustomCollider2D_Logo

About 2D Physics
Read more about the current 2D Physics features here.

What can you do?
Try it out and let us know what you think of the additions and improvements. We want to know what works as expected, what doesn’t and what is missing. We’d love to see how you use them as well, so please show off all the cool things that you make with them!

1 Like

Hi @MelvMay I’ve got a possible use-case for this new physics functionality that I’d love to run by you and get your thoughts.

We use Spine for 2D animation in our game. One thing you can do in Spine is create a “bounding box” that is simply just a polygon shape (no visual component) that you can animate/position, and then in Unity you can use that bounding box information to do stuff. One of the primary things it is used for is Hitboxes.

The Spine-Unity runtime has some built-in functionality that can create a PolygonCollider2D out of the bounding box, and then position that PolygonCollider2D so that it follows the position of the bounding box in the Spine animation (aka if you had a bounding box that followed a Sword swinging in the spine animation, the PolygonCollider2D would also follow the sword when you play the animation in Unity).

A limitation with the current built-in functionality is that, although the vertices of the bounding box can be altered/animated in the Spine animations themselves, the size/shape of the PolygonCollider2D would not reflect those changes in Unity (obviously, since you couldn’t change the physics shapes previously!). Example: if you had a hitbox that was following a Sword as the sword swings, the current functionality works fine because the PolygonCollider2D doesn’t need to change size and only needs to be positioned/rotated. However if you had something more dynamic, like a flamethrower, where the size and shape of the hitbox should change during the animation to match the graphics, it wouldn’t work with the current implementation.

Would this new physics functionality allow us to update the shape of one of these hitbox-colliders each frame to match the shape in the Spine animation? Just to add a bit more info - when you “animate” one of these bounding boxes in Spine, you can’t change the number of vertices, only alter their positions.

(Side note that for my specific usage of this functionality it would be using the physics shapes as Triggers only, in case it makes a difference with changing shapes on the fly).

Thanks for any insight, and I’m excited to see if I can give this a try!

The main thing to consider with the PolygonCollider2D is that it’s a brute force but handy Collider. By this I mean it accepts paths that define (potentially) concave outlines (even holes with opposing windings) but those are not something the physics system understands at all. They need to be processed so constantly changing them means there’s a whole bunch of conversion that has to happen well before you even get to what the CustomCollider2D is doing. They have to be passed through LibTess to tessellate them down to (max) 8-sided convex polygons (identical to a Polygon PhysicsShape2D). This costs CPU time and is why you should stick to the primitive colliders like BoxCollider2D (still just a 4-sided polygon shape) or CircleCollider2D as they produce a single shape and have very little intermediary processing steps.

The CustomCollider2D lets you do whatever you like with raw access to the shapes themselves. The limitation is really down to the inherent limitations of the physics engine (Box2D). Like most physics engines, it doesn’t want shapes to deform but instead, their poses changing only which is defined by the body they are attached to. In summary, it prefers them to be immutable for best performance.

This collider lets you do whatever you like and we’ve tried to put the least amount of code in-between you modifying shapes and it being done. We only verify things that would otherwise cause potential crashes in the physics engine. We’ve proven it can handle significant changes deforming geometry.

The great thing about this is that the actual deformation is done by you using whatever method you like so no black-boxed processing or fixed features. You might be taking your flamethrower outline and decomposing it into a bunch of polygons in a Job for instance or might be retrieving it from stored physics shapes you’ve pre-calculated. You can set the physics shapes using a PhysicsShapeGroup2D or using NativeArray of shapes/vertices using CustomCollider2D.SetCustomShapes. You can also read a PhysicsShapeGroup2D from any existing Collider2D (GetShapes) if you wanted to create a tool that (say) read the shapes from a PolygonCollider2D and wanted to sequentially fire them at a CustomCollider2D.

The fact that it’s a trigger doesn’t change performance or anything else. The only difference that makes is during the simulation step where if it sees it’s a trigger, it knows it doesn’t have to solve anything or produce contact points. it won’t change physics shape configuration etc.

NOTE: It can do all this with no GC waste or at least that would be true with the exception of this bug that’s fixed and waiting to land in the next release. :slight_smile:

1 Like

I’ll reply to this directly in a separate post here.

In this case, if you’re not deforming the collider then each of these BoxCollider2D should be attached to a Kinematic Rigidbody2D. You then only need to set the Rigidbody2D position/rotation either directly or via its MovePosition/MoveRotation. Moving a body means the collider and its shapes don’t need to be modified because they live in the space of the body.

This is how the physics engine wants to work by changing the poses of the bodies; the colliders are along for the ride. This is the least CPU work.

1 Like

Thanks a lot for the detailed response @MelvMay ! If you think I should create a separate thread for this specific topic just let me know (I definitely have some follow up questions).

Just so you know my standpoint, I have a pretty good understanding of Box2D (been using it in Unity, and also used the Box2D java library directly for a previous project). I’m also looking at this specific implementation from two perspectives - my own for my specific project, and also for anyone that uses Spine in case it can be added directly to the Spine-Unity runtime.

Yeah, this is exactly how the current built-in Spine-Unity runtime implementation works - it creates the (immutable) PolygonCollider2D based off of the initial shape of the boundingbox from Spine, which is then positioned/rotated along with the animation (but the individual vertices themselves are not altered). It’s great and efficient, but I’m wondering if I could add the polygon-deforming functionality if it isn’t too CPU-consuming.

Makes sense, totally agree that using the primitives would be faster. With this specific use-case, I’m wondering if the number of vertices on the ‘bounding box’ polygon is low enough that the time it takes to calculate the physics shape would be small enough to still use them.

When creating a bounding box in Spine, you are creating (and animating) it manually, so I would venture to say that most bounding boxes would have less than 10 vertices. In our game specifically, none of our bounding boxes have more than 10 vertices, and most of them have around 4. Here is a picture of what the bounding box would look like for a flamethrower type attack (the character spits fire). The green polygon is the bounding box, it has 5 vertices.

I haven’t used LibTess or manually created Physics Shapes, so I am not sure how long it takes to calculate the polygons even for a relatively simple shape. For my project specifically, these are the numbers we’d be looking at:

  • All boundingbox polygons would have less than 10 vertices, and most of them would be convex (and if not fully convex, then relatively simple to recreate with few convex shapes, like the flamethrower above).

  • A maximum of ~8 deforming boundingboxes active at a time. Our game is a boss-rush, so it is usually just the 1 player and 1 boss character fighting.

From my understanding, this would be the start to finish process for what I’m looking to do, and would be updated each frame while the boundingbox is active/enabled:

  • (Keeping a list of vertices for the boundingbox) Update the positions of each vertex using the data from the Spine animation.
  • Use LibTess to generate the convex polygon(s) shape.
  • Provide that convex polygon(s) shape to the CustomCollider2D.

If we were to pretend that step #2 (LibTess) took a trivial amount of time to compute, are there any other parts of the process that would be bottlenecks? Also would contacts be ‘maintained’ while the CustomCollider2D is modified (namely OnTriggerEnter / OnTriggerExit not firing for every contact when the CustomCollider2D is modified)?

At this point I’m mostly just trying to feel out whether or not I should look further into testing this out - AKA if you replied saying “No, you should never use LibTess live during runtime” or “The CustomCollider2D isn’t meant to be modified/deformed every frame”, I would probably drop the idea :p. But if this could work, it would be awesome!

Gotcha, thanks! I think I just wanted to mention that so that you know I’m not trying to do something like modify the shape of a bunch of marbles while they are stacked up on each other (since I’m only modifying Triggers, it wouldn’t cause any janky issues with the physics simulation, lol).

I guess the term “bounding box” was throwing me. More generally we’re talking concave/convex bounding polygon region. We’re on the same page now though!

Yes although I’d say more specifically for point 2:

  • If convex and vertex count is within the range 3-8 then create a single polygon shape

  • If not convex or vertex count is > 8 but still within a known small limit like 12 then use something like the ear-clipping algorithm (fairly quick on small things). You can even calculate all of them in parallel in a job. This will produce triangles though.

  • Use Libtess C# port only if you want potentially larger unconstrained tessellation. LIbtess can produce n-sided polygons which reduces shape-count. It even supports Delaunay (this option is supported for PolygonCollider2D/CompositeCollider2D/TilemapCollider2D in 2022.2)

Yes it should be fine but this should be true for all 2D colliders. If you modify the path in (say) a PolygonCollider2D or the size of a BoxCollider2D then the shapes are regenerated too. Whilst it’ll instantly cause existing contacts to be destroyed, as long as it sees contacts during the next simulation step then an “Exit” (collision or trigger) won’t be produced. For “Exit” to be called, there needs to be no contacts after the simulation step has complete.

Note also that you don’t need to rely on callbacks either. You can easily do Collider2D.OverlapCollider to explicitly perform hit-testing.

Another point of note is that if you place all your hit-boxes in a single CustomCollider2D then you won’t be able to differentiate between which hit box. In future versions of Unity, you’ll be able to get around this by providing custom-data per-shape that allows you to identify each shape. Additionally, regarding triggers, you’ll be able to have not only mixed shapes but the ability to set each shape as a trigger or not as opposed to it being a collider-wide setting as it is now.

1 Like

I’ll add that this is a C# port of LibTess2 tessellating an arbitrary outline. It’s very fast but might be overkill if you can constrain those hitboxes:

Thanks for the additional info @MelvMay !

Ah - ok I see what you’re getting at here. For my project specifically, it might make sense to try out the ear-clipping algorithm, but I’m wondering if the Libtess method might be a better “across the board” solution if other Spine-users want to also try this? When you say that Libtess “might be overkill”, do you mean that it would be much slower for low-vertex-count polygons, versus ear-clipping?
Basically wondering if I was to set this up using Libtess for scalability for other users who might have higher vertex-counts, would I be shooting myself in the foot for my own project (if Libtess is much slower than the ear-clipping algorithm?). Sorry, I know its annoying asking about performance questions without specifics, just trying to get a ballpark feeling based on your suggestion :wink:

If Libtess is ok to proceed with, would this be the correct version to use? https://github.com/speps/LibTessDotNet

You mentioned Libtess2, which I see here (https://github.com/memononen/libtess2) but it is C not C#, is there a public C# port?

Awesome! Thanks for all of that information. That upcoming functionality sounds great!

It’ll certainly be better at scale. The main reason I even mentioned ear-clipping (or other simple algo) on much smaller polygons is that you can do all the areas in parallel in a job. We don’t have that luxury in Unity because its use (say) in the PolygonCollider2D means it has to cope with 3 vertices to an unlimited amount. Also, we have to support holes; something simpler algorithms don’t support whereas LibTess2 (and others) do. We do have some experimental multi-threaded tessellation code working though.

Yes, that one would be good although recently I discussed this on the forums and someone else kindly recommended an alternative ( poly2tri) but I’ve personally not used it (see forum post here ).

Yes, we use LibTess2 in our native C++ code. I’ve used LibTessDotNet (and others) which is pretty much the same thing. Looking at it, poly2tri is actually linked as a reference in the LibTessDotNet GitHub page.

Good luck!

Also, if you need to discuss more then feel free to create a thread and tag me in it. Happy to help.

Thanks a ton for the info @MelvMay , excited to try it out and I’ll let you know of my progress!

1 Like

A quick update regarding a GC bug found for the PhysicsShapeGroup2D as reported here:

The fixes have now landed in the yet-to-be-released 2021.2.3f1 and 2022.1.0a15. Eventually, prior to release, this will be reflected in the public case tracker above.

2 Likes

Hey @MelvMay , I’m getting close to completion on this (probably will start a separate thread once I have it finished, to get some feedback if you don’t mind), but just noticed something - if you have a CustomCollider2D without a Rigidbody2D, and you move its gameobject around, the collider doesn’t move along with the gameobject. Is that intentional? The other 2D collider types move with the gameobject even without the RB. And if I remove the RB from the CustomCollider2D after having moved it around, it pops back to the original location.

(edit: Just to add more context, I was moving the gameobject by clicking and dragging it in Scene view)

Yes because that’s implicitly Static and the shapes you specify explicitly (unlike geometry you specify in, say, CircleCollider2D or BoxCollider2D etc) are in the space of the body they’re attached to. When you don’t specify a Rigidbody2D you’re saying Static which means NOT moving ever and the implicit Static ground-body never moves.

I’ve never liked the Unity idea of not adding a Rigidbody2D because this implicit Static is confusing and leads us to have to then support Transforms changing and all the horrible performance implications that entails.

If it moves in 2D physics, always move a Rigidbody2D and NEVER drive it with the Transform. This applies to all of 2D physics and especially when you’ve not added a Rigidbody2D because that, in other non-raw colliders, means all the shapes need to be recreated and the performance tanks because of it.

If you need it to be Static but, for some reason, you then need to move it (which means it should really use Kinematic) then add a Rigidbody2D and set the body-type appropriately.

The auto-magic of recreating shapes is NOT implemented here because it’s all about performance and like lots of parts of Unity, if we then support that, users do it and then again performance tanks. The CustomCollider2D is raw access and it’s fast. :slight_smile:

1 Like

Hey @MelvMay - thanks for all that additional info!

Hmm, so this means that if I use Timeline to tween a characters position (which would be directly setting the Transform), it is recreating the shape every frame? If so, maybe I could modify it to move the rigidbody instead.

And one more quick question regarding this - I have a lot of plants in my game that have a CircleCollider2D as a Trigger, with no Rigidbody. Its used so that if a character runs by the plant, it plays an animation on the plant like it got pushed to the side (doesn’t actually move the transform or anything). The plant is 100% stationary always. Should I be adding a rigidbody to it anyway, or is it not needed in this case (you mentioned it would act as though it is Static, which would be fine)?

Thanks a lot!

(And regarding my original topic, I got a proof of concept of it working, and am planning on working on it some more this weekend :slight_smile: )

No, it means if it moves you need to add a Rigidbody2D. Whilst directly modifying the Transform isn’t recommend, with a Rigidbody2D there the body will get updated, not the collider. It doesn’t matter if it’s “Timeline” or your scripts, they both use the same public API you do. There are no “special” rules for internal Unity features, 2D physics is in a module and it’s hidden from everything else; the only access is via the public API. The rule for you is the rule for every Unity feature too. :slight_smile:

Static (non-moving) trigger then. Yeah, it’s kind of weird because I answer this kind of thing all the time with various scenarios put to me but this collider doesn’t change the fundamentals of physics engine and there’s not a bunch of different rules, just one: If it doesn’t move, you don’t need to add a Rigidbody2D or add one and set it to Static body-type. If it does move however, add one, either Dynamic or Kinematic. it’s as simple as that.

Nice. Feel free to DM me on the forum if you get stuck with the new physics stuff.

1 Like

Struggling to find anything which is useful in the forums or documentation, but it is very relieving to see someone so active on the forums from the Unity team, your presence everywhere is noted and appreciated @MelvMay !

I am unsure if this is the correct place for this - but could you advise on the following issues we are having?

We have a thousand or so objects moving towards a target transform. We have jobified the calculations for this and are passing the calculated velocities directly into the RB2D.

We also have several hundred triggers moving through these - again, the calculations for these have been jobified and velocities are being passed in. No direct transform movements anywhere.

The physics2d step in the profile is (unsurprisingly) the most expensive operation we’re running. The jobification of the calculations has provided us a significant performance increase, but I feel like there’s still ground to gain.

I’ve been playing with the settings for the experimental jobs on the Physics2D settings, but it seems as though nothing is having an effect at all - including using multithreading and not. I’ve adjusted the jobs settings to what I assumed would be appropriate, but again, no real difference.

Each of the colliders is a circle collider, and the accuracy of the simulation isn’t a high priority. Could you advise on what we should be prioritising in the jobs settings, and also just confirm that the multi threading toggle is even working? Are we missing something obvious?

Unity 2021.2.8f

This isn’t the best place to ask the question no, that’d be the Physics forum.

I certainly cannot tell you to look at X for performance issues, it could simply be the limitations of Box2D. Whilst the MT can help, it doesn’t always help given how Box2D works. It’d take a major rewrite of that to achieve it and this is where the DOTS 2D physics was going.

If you post on the physics forum with some details from the profiler on what in the step is taking the time I might be able to advise what to look at. It might be finding new contacts, solving contacts, island traversal, contact callbacks to Unity etc.

I added a lot of profiler tags in the step method so it’s pretty detailed. I’d do that with MT off because it’ll more clearly show the time on the main-thread.

Hello @MelvMay , Do you have any news about contact modification for 2D? Anything to share with the community? :slight_smile:

1 Like

Not much news apart from it is being worked on, amongst other stuff. Note that Box2D has a very messy contact set-up in terms of providing contacts externally which makes it much harder to deal with than 3D (PhysX). Specifically here related to how it deals with Continuous contacts. This is causing a lot of pain points but as I said, it’s being worked on.

2 Likes