I’m trying to build a 2D space game similar in looks to Asteroids. My ships are CharacterBody2D and based on polygons and I want players to be able to design their own ships. That works great with Line2D with .closed = true. However, I run into trouble with the CollisionPolygon2D where I get this error when using the same vertices that make up the Line2D:
I found a few GitHub issues on this (#57789, #59425, and #91607), and it seems this is a thing that can fail when the polygon to create the collision shape it too complex or if it has duplicated points in it. A suggested workaround if to set .BuildMode = BUILD_SEGMENTS for the collision shape, but this makes the physics behave in a very weird way for me.
This is a recording of a ship with BUILD_SEGMENTS without any player inputs:
So, that is not ideal! It seems to sink into the tilemap a bit and then overcompensate. And I would like to use BUILD_SOLIDS instead which does have proper physics.
Has anyone encountered this issue before? Even being able to detect when a decomposition fails would be helpful. Currently, it’s just an error in the log, but nothing I can detect from within GDscript.
To avoid those errors clean up your input outlines and make sure that they do not have crossed edges or cause overlap in any way.
That error is always a geometry input error. It happens because the input geometry is invalid in the sense that it is geometry impossible to slice it into non-overlapping, convex polygons.
The common reason for this is geometry overlap, aka duplicated points, crossed edges, intersecting outlines, or points just so stacked near each other that precision errors will make them overlap anyway even while visually looks like they could be fine. Because float precision is part of the problem you see more of those errors on low precision platforms like web builds.
The reason why the segment-version works is because what it does is just stupidly-simple in comparison to the convex decompose. The segment-version just adds a collision edge between 2 points for every point in the input array. Hence why the resulting input collision is super-low quality, subject to tunneling problems, and with many edges creates the worst physics performance imaginable.
The trouble in this specific case is that I want people to be able to create their own polygon shapes. So in order for that to work, I would have to create a function to clean that input and hope it doesn’t trigger the “decomposition failed” error? There is no way to know from GDscript if that error occurred, right?
Well, I suppose I could create a mini-simulation in the background somehow that launches the ship through a wall and sees if it makes it through. That doesn’t seem very elegant!
Hmm, maybe another way could be to just make the hull points itself collision shapes, like CircleShape. I have a max of 24 vertices that make up the hull so maybe that wouldn’t be too bad. Accuracy would probably depend on the radius of my CircleShape. Too small and I would probably be subject to tunnelling problems and the likes since it sounds similar to the segmenting.
It would be better f I could detect when a decomposition fails, since then I would be able to fall back on a system like that.
For a single outline with up to 24 vertices you can check every segment, aka vertex1 + vertex2, vertex2 + vertex3, … if it crosses or overlaps another existing segment. If that is the case it is broken input. Or check if any of the vertices are placed too close to each other by snapping them to a small grid. If you do those two things and fix or discard such inputs you rule out 99% of the broken cases.
Right, that could work. Thanks! I’ll try that out. For the 1% edge cases, I could probably just place a small circular collision shape at the centre. Otherwise it might be possible for people to design ships that can fly through everything and not get hit.