1

I'm creating a puzzle game that generates random sized pieces with 2D meshes. The images contain transparent portions and sometimes a piece is completely transparent. I need to detect what percentage of a piece is transparent. One way I found to do this is to go pixel by pixel. I posted my solution to this HERE. However, this process adds a few seconds during loading which I'd like to avoid and I'm looking for other ideas

I've considered using the selection outline of a MeshCollider to somehow to get a surface area I can compare to the surface area of the mesh but everything I find is on the rendering of outline with specialized shaders. Does anyone have any ideas on to solve this?

Example puzzle piece with transparent pixels around top of head..

enter image description here

Cassova
  • 530
  • 1
  • 3
  • 20

1 Answers1

2

1) I guess you could add a PolygonCollider2D to your sprite and use its Path for the outline and calculation of the surface area. Not sure however if this will be faster.

PolygonCollider2D.GetPath:

A path is a cyclic sequence of line segments between points that define the outline of the Collider

Checking PolygonCollider2D.GetTotalPointCount or path length may be good enough to determine if the sprite is 'empty'.

Sprite.vertices, Sprite.triangles may also be helpful.

2) You could also improve performance of your first approach:

  • instead of calling GetPixel as you do now use GetPixels or GetPixels32 and loop through the array in one for loop.

Using GetPixels can be faster than calling GetPixel repeatedly, especially for large textures. In addition, GetPixels can access individual mipmap levels. For most textures, even faster is to use GetPixels32 which returns low precision color data without costly integer-to-float conversions.

  • check only every 2nd or nth pixel as it should be good enough for approximation
  • limit number of type casts
Dave
  • 2,684
  • 2
  • 21
  • 38
  • Regarding the PolygonCollider2D approach, I tried this but it always renders large pentagon over the image which is much larger the inside image. I figured this was because it's a mesh and not a sprite. Do you know how to make it hug the image like it does for sprites? – Cassova Jun 12 '20 at 00:24
  • As far I can see your image is a material not ui image? I think I would first try creating a sprite from your image to use build in functionality of the sprite class. – Dave Jun 12 '20 at 10:20
  • Take a look at [Sprite.Create](https://docs.unity3d.com/ScriptReference/Sprite.Create.html) – Dave Jun 12 '20 at 10:30
  • Still not sure about the performance. I believe custom shader would be the fastest as it uses GPU and you could do the calculation/check when rendering with almost no cost but I am not good with shaders and can't help much here. – Dave Jun 12 '20 at 10:36
  • When creating the sprite use `SpriteMeshType.Tight`. "Tight mesh based on pixel alpha values. As many excess pixels are cropped as possible." It will generate the mesh based on the pixel alpha values so it will check each pixel as you do in your first approach. It may use a better algorithm though. Please write if it's faster as it's interesting to know. – Dave Jun 12 '20 at 10:46
  • And then you can use just Sprite.triangles for the surface area without the PolygonCollider2D. – Dave Jun 12 '20 at 12:46
  • PolygonCollider2D is using sprite mesh for its shape. – Dave Jun 12 '20 at 12:52
  • I don't see how creating a sprite is going to work since the sprite requires a Rect and a texture. The texture I'm using is the complete image (in this case a dinosaur). The mesh is used to only render part of the texture. This is why the bottom edge isn't straight across like it would be if it were a Rect. – Cassova Jun 13 '20 at 05:09
  • I've added another picture that might help make this more clear. – Cassova Jun 13 '20 at 05:15
  • You could create smaller textures from the main texture to create sprites instead of your mesh. It wold require rewriting your code but it is definitely possible. This way you could use the advantages of the sprite and polygon2d classes. – Dave Jun 13 '20 at 09:40
  • If you don't want to do that then you have to analyze the texture with an algorithm of your choice. Sprite class is doing the same thing after all. – Dave Jun 13 '20 at 09:44
  • I can't see how it's possible to create new texture from a mesh and main texture after a lot of searching. Your suggestions on skipping pixels and using GetPixel32 have helped a lot though so thanks. – Cassova Jun 13 '20 at 13:03
  • It would require looping through pixels of the original texture, [creating new Texture2D](https://docs.unity3d.com/ScriptReference/Texture2D-ctor.html) and [setting the pixels](https://docs.unity3d.com/ScriptReference/Texture2D.SetPixels.html) of the new Texture to the portion of the original texture. – Dave Jun 13 '20 at 18:50
  • But your first approach with improved script is as fast or faster so I would stick to it. – Dave Jun 13 '20 at 18:52