Creating 2d image map of objects based off 3d space colliders Unity C#

I have been trying the last few days to generate a .jpeg/.png image colour tile map based off of a unity scene where it is mapping objects with the layer “block”, anything that it collides with including inside of, gets marked in a colour. Anything it doesnt is marked in black. Imagine a 1025 grid with the below format as an example, an “x” is a area with an object / series of objects with the layer as “block”, and a mesh collider.

    xxx             x                x

    xxx                      xxx        x

    xxx      xx              xxx

Assuming everything has the y value 0, I have been using two for loops to iterate through space x and z and I have tried raycasting 1 point forwards which works, except it wont map the inside of objects. I cant get a cube with a collider to generate the map either, OnCollisionEnter doesnt trigger.

What I am aiming for is ideally using a point of scale 1,1,1 to iterate through each point on the map using the for loops and saying basically I am inside of a collider with tag or not inside collider and it moves like so

1 - inside collider




    0xx             x                x

    xxx                      xxx        x

    xxx      xx              xxx

2 - inside collider




    x0x             x                x

    xxx                      xxx        x

    xxx      xx              xxx




3 - inside collider




    xx0             x                x

    xxx                      xxx        x

    xxx      xx              xxx




4 - not inside collider




    xxx0            x                x

    xxx                      xxx        x

    xxx      xx              xxx

For the raycasting, it almost works fine except for mapping the inside of objects, and it seems to have weird clipping around objects where its placing ray collisions a few points away from the object mesh colliders.

I created the mapMarker game object to visually display where the rays were colliding to help my debugging, and it is on the edges of objects. Which is how i discovered the weird clipping where its a good few points out.

     for (float i = 0; i < 1025; i++)

        {

            for (float j = 0; j < 1025; j++)

            {

                height = getHeightOfTerrain(j, i);

     

                // -1 as we are casting 1 point forward

                Ray r = new Ray(new Vector3(j-1, height, i), new Vector3(1, 0, 0));

                RaycastHit hit;




                // if ray hits collider with the layer mask

                if (Physics.Raycast(r, out hit, 1, LayerMask.GetMask("block")))

                {

                    obj = hit.transform.gameObject;




                    // add the colour to the 2d map

                    texture.SetPixel((int)hit.point.x, (int)hit.point.z, new Color(255, 0, 0));




                    GameObject mapMarker = GameObject.CreatePrimitive(PrimitiveType.Cube);

                    mapMarker.name = obj.name;

                 

                    mapMarker.GetComponent<Renderer>().material.color = new Color(255, 0, 0);

                    mapMarker.transform.position = new Vector3(hit.point.x + 1f, height + 2f, hit.point.z);

                }

                else

                {

                    texture.SetPixel((int)j, (int)i, new Color(0, 0, 0));

                }

            }

        }

How would you approach this? Is my method just wrong? I tried iterating a box collider through the above code and doing collision tests on the box collider but it never triggered anything.

This certainly seems like a reasonable way to automap a level, but you won’t be able to performantly do this every frame. As long as you realize this is a “when the level loads up” type of approach, this should work.

Some other tips:

Raycast will only hit the very first thing and nothing else. RaycastAll will return a bunch of things, everything the ray passes through.

Raycast only hits colliders, modulo setting the extra flag queriesHitTriggers. If you have a tiny object occupying a 1x1 square not precisely at whatever your math considers to be the “center” where the ray goes through, it won’t be hit.

You won’t be able to iterate a box collider because that only triggers during the physics phase of updating, which occurs NOT when your script is running. Here is how Unity operates each frame:

EDIT: edited out the edit

Thanks for the speedy reply, I am running this script once to “export” the map. I think what you have described with queriesHitTriggers is exactly my problem for it missing certain objects that the ray goes past, or perhaps why there are strange boxes appearing where there are no objects nearby. I’ll give that a google.

Good to know I shouldn’t focus on using a box collider, I probably spent the best part of 2 days getting frustrated at that!

In respect of doing something like texture.SetPixel((int)hit.point.x, (int)hit.point.z, Color(255, 0, 0)); inside of a collider, what is the best way to determine this as the ray wont touch it when during the iteration the point has moved inside of an objects collider?

Instead of raycasting, what if you just gave your “block” objects a child Cube GameObject on a “map” layer with the appropriate color. Hide the “map” layer on your main camera. Then you could use a camera with its culling mask set to only render the “map” layer, and use a technique like this: How to save manually save a PNG of a camera view to just render that camera’s view out to a PNG. It should be a lot faster than doing raycasts.

After you’re done, you can delete all the unneeded GameObjects and cameras etc…

1 Like

That’s why I was suggesting using RaycastAll… coupled with your “block” layer mask, this should return to you a list of items that you are interested in, and you can iterate over them:

https://docs.unity3d.com/ScriptReference/Physics.RaycastAll.html

Now if one collider you hit should be drawn RED and one should be GREEN, that’s sort of up to you to decide what to do in that case, and very specific to your game’s needs.

ALSO… to avoid missing stuff, there is this approach:

https://docs.unity3d.com/ScriptReference/Physics.SphereCastAll.html

It is similar to raycast but actually sweeps out a “tube” of space. I would also lift myself up really high above your map, like perhaps above the highest mountain, and raycast down a long way, like twice the highest mountain height.

And since it is an export step, you can also make it run super-fine-grained checks if you don’t care that it might take a long time to build the map. That’s another way to make sure you hit everything you’re interested in, by just doing a bazillion sub-checks per pixel area. I would make a tiny map to test it first, just to keep the iteration time short!