My little rant on colliders

Look, why it’s such a huge pain to add a collider to a tilted 2D object in a 3D environment? Like there’s a Box Collider, why can’t I simply make it flat and add a few extra points to it? Well, I can make it flat at least.

It seems like I need a mesh collider for this but finding how to generate a mesh from a sprite is like looking for some secret hidden info. And apparently it can be done only with code. Maybe there’s lots technical reasons against it, but having a simple polygon collider but for 3D would be so helpful. Also when importing a sprite, there’s an option for mesh type, why can’t I use that for a collider?

My apologies, but I’ve trying to figure this out for over month now, it’s starting to get to me.

I remember your other post(s). Here’s the same info again:

Unity 2D physics ONLY takes place in a 2D plane in the X/Y, and Z is always zero.

ONLY 2D versions of physics classes (such as colliders and rigidbody and joints) may be used with 2D.

By “used” with I mean “appear on the same GameObject and interoperate via the Physics2D system.”

That’s all there is to it.

So if you contemplate being fancy with 3D going on around your 2D physics simulator, that’s great.

Just know that absolutely nothing about the 2D physics system is going to change or care:

100% of EVERYTHING in 2D will ALWAYS be in the X/Y plane at Z == 0.

If YOU want to move data from the 2D simulated objects to the 3D world, GREAT! Do it. Moving 3D info back to 2D? GREAT! Just know that Z is always gonna be lopped off and be zero.

For instance, I made a game called TankCombat2D and a game called TankCombat3D.

They BOTH operate in 2D, but I am copying data from 2D to 3D for my 3D version.

It is identical game logic. The 2D game is running in the 3D world and EVERYTHING is invisible, and I am copying the 2D coordinates and projecting them in 3D.

Here’s the 2D → 3D projection code:

namespace TankCombat3D
{
    using System.Collections;
    using System.Collections.Generic;
    using UnityEngine;
    using TankCombat2D;

    public partial class TankCombat2D_Visualize3D : MonoBehaviour
    {
        const float OurCellSize = 2.0f;

        // proxy transform to turn the 2D world into something that can be seen in 3D
        public static Vector3 TransformPosition( Vector3 position2D)
        {
            // DO NOT USE source.z - it is meaningless

            float x = position2D.x;
            float z = position2D.y;

            const float CellSize = 2.56f;

            x /= CellSize;
            z /= CellSize;

            x *= OurCellSize;
            z *= OurCellSize;

            var position = new Vector3(
                x,
                100,
                z);

            return position;
        }

        // proxy transform to map euler.z in 2D into euler.y in 3D
        public static float TransformZRotation( float angle)
        {
            return -angle;
        }
    }
}

You can see it yourself at: Kurt Arcade by Kurt Dekker

And yet, none of this has to do with the question asked. He’s in a 3D world.

If you want a 3D mesh suitable for a collider in 3D, you need thickness. You already discovered you can make a 3D collider really thin, which is sufficient, but the resulting shape is not what you want. That’s fine. You can add multiple colliders on your 3D object, and you can add child objects with additional colliders to allow for rotating those colliders. In this way, you can approximate your sprite’s shape. No code required.

However, here’s someone who made a “2d sprite to extruded 3d mesh” converter. Looks like what you want: take an arbitrary 2D sprite with polygon outline, and extrude the polygon into a 3D mesh suitable for collision.

https://gist.github.com/valryon/f7b7b7d40e3fe2e12e6290a48ec439c6

1 Like

You’re missing the point. I get that 2D colliders are always in XY, in fact I changed my ground tilemap to be in Y not in Z, so I can have proper tilemaps 2D colliders.

What I’m going nuts about is why can’t there be a bit more complicated 3D collider?? I can add Box Collider (3D), make it flat, tilt it anyway I want, but it’s still only a box. Why can’t I add couple more points to it? Probably because there are some engine limitations I’ve no idea about. Or why it’s so complicated to get a mesh of a sprite, edit it and use that as mesh collider? This is what grinds my nerves.

I mean there are not a whole lot of games that are top down and use sprites and have 3D camera, but there’s no way I’m the only one with that problem. Also this probably shouldn’t be in 2D section, my apologies.

For now I’ve decided to chill and use simple 3D box collider as a trigger for highlighting. Maybe one day I’ll figure something out.

Sprites don’t generally have a conformal mesh. Sprites usually have a single giant quad (by far the most common default), or if extra steps have been done at import, sprites may have a mesh that is close to their shape, but generally it won’t match every pixel in the sprite.

Once you obtain a flat 2D mesh, you can extrude it into a 3D mesh and pass that to your MeshCollider. If you contemplate putting that MeshCollider on a Rigidbody, then it must ALSO be marked convex.

If you just have a collection of perimeter points going around, you’re welcome to extrude it with this:

https://bitbucket.org/kurtdekker/makegeo/src/master/makegeo/Assets/makeextrusion/Extrude2DShape.cs

There’s a test scene and script in the same folder:

https://bitbucket.org/kurtdekker/makegeo/src/master/makegeo/Assets/makeextrusion/testmakeextrusion.cs

1 Like

I’m not sure if it’s thickness exactly, more like the ability to tilt a collider to match the sprite.

I tried adding multiple 3D colliders, but they are still boxes and if you imagine a pine tree sprite, it got really awkward fast. And as you said, to rotate them I’ll have to add child objects and sometimes I’ll need to animate colliders slightly to match the sprite, so it gets more and more inefficient. It’s still an idea though.

I think I’ve come across this github page, also I found this tool on itch.io:
https://angelonit.itch.io/polygoncollider2dtomeshcollider

It’s not free unfortunately, plus I’m not ready to learn about meshes yet (seems a bit too complicated). I’ve also tried using sprite.bounds but it has it’s own quirks (like transparent pixels are still being detected).

But thanks anyway; as I said in the previous post I’ll stick to a simple 3D box collider as a temp solution.

If I set shading mode in a Scene window to wireframe, does this represent sprite meshes? If so, setting Mesh Type to Tight while importing a sprite looks good enough.

Thanks for the info, I’ll check into meshes once my brain is back to normal.

EDIT: also, do I understand correctly that meshes can only be built at runtime?

Meshes are normally not created at runtime. Procgen does create them at runtime.

When you import an FBX (for instance), a whole cascade of assets may be created:

GameObjects (with Transforms obviously)
Meshes
Materials
maybe even Textures?
AnimationClips

etc

These are all just Assets which may be created. A Mesh is also just an asset.

In that same MakeGeo repository there is code to write a Mesh .asset file to disk, which would allow you to drag it into any slot expecting such a thing, such as a MeshRenderer or a MeshCollider.

1 Like

Ya, it’s an engine thing (all engines do that). There are quite a few math algorithms to test simple-simple collider geometries that are immensely more efficient than a mesh test which requires a crazy number of triangle-triangle tests. Unfortunately, once you add a vertex and no longer make it a box, it’s a collider mesh even if it looks like a box.

Because it’s such a big jump in performance, it’s been made an explicit ‘make mesh in DCS’ → ‘import as Mesh Asset’ → ‘attach mesh to MeshCollider’

1 Like

I kinda found the solution, it’s so obvious I feel like a total idiot now. What I should’ve mentioned is that lots of objects I’m dealing with consist of many child objects with sprite renderers. So instead of trying to add one collider to all of them I added simple box collider to each one. When I was trying to implement sprite.bound method, I already checked sprite bounds on every child object, I’ve no idea why it took me so long to use the same logic with colliders.

One little problem: lots of warnings “BoxColliders does not support negative scale or size”. Which I can’t do much about since all the objects are tied to animations. So I guess I’ll hide warnings for now. I will have to tweak some colliders in animations, but man, it’s as good as it gets, it’s more or less accurate and colliders are moving along with objects.

Once again, my apologies and thanks for all advice, I’ll definitely keep all the mesh info in mind.

So you know moving forward, you should use the Physics forum to talk about 3D colliders rather than the 2D forum. Especially if you want to rant about them. :wink:

Thanks.

There are other ways of doing this but given that you can create a planar mesh from any 2D Collider, you can create meshes as below. Line 44 shows you how to do it in a single line of code.

using System;
using UnityEditor;
using UnityEngine;
using UnityEngine.UIElements;
using UnityEditor.UIElements;

public class CreateMeshFromPolygonEditor : EditorWindow
{
    private ObjectField m_ColliderObject;
    private TextField m_MeshAssetName;
  
    [MenuItem("Window/2D/ColliderToMesh")]
    public static void ShowEditorWindow()
    {
        var wnd = GetWindow<CreateMeshFromPolygonEditor>();
        wnd.titleContent = new GUIContent("Collider2DToMesh");
    }  
  
    public void CreateGUI()
    {
        // Create the UI.
        m_MeshAssetName = new TextField { label = "Asset Name"};
        m_ColliderObject = new ObjectField { objectType = typeof(Collider2D) };
        var generateButton = new Button { text = "Create Mesh" };
        generateButton.clicked += OnGenerate;
   
        var root = rootVisualElement;
        root.Add(m_ColliderObject);
        root.Add(m_MeshAssetName);
        root.Add(generateButton);
    }

    private void OnGenerate()
    {
        // Fetch the output config.
        var assetName = m_MeshAssetName.text;
        var collider = m_ColliderObject.value as Collider2D;
        if (collider == null || string.IsNullOrEmpty(assetName))
            throw new InvalidOperationException("Bad Collider/Path!");

        var assetPath = $"Assets/{assetName}.asset";

        // Create a Mesh Asset.
        var mesh = collider.CreateMesh(useBodyPosition: false, useBodyRotation: false);
        mesh.hideFlags = HideFlags.None;
        AssetDatabase.CreateAsset(mesh, assetPath);
        AssetDatabase.SaveAssetIfDirty(mesh);
    }
}
2 Likes

Yeah sorry, I was feeling a bit mental while posting. What I didn’t get into account that, as datacoda said, as soon as more points are added to the 3D box collider, things get very different from an engine point of view.

Hey, thanks a bunch! It took me a while to figure out what this is (I’m still learning C#). I can put any 2D collider in it and have a mesh generated right in the editor. And someone dared to charge 5 euros for this. I’ll use it to make colliders for simpler objects, they gonna be extra precise. I don’t know if it was already available somewhere or you made specifically for my case, but thanks anyway.

EDIT: I noticed that generated mesh colliders act as triggers even though IsTrigger is off the editor. And I need them to be triggers, so it’s perfect for me. Seems a bit weird though.
Plus if I turn IsTrigger on, it has to be convex and when it’s convex it’s basically a box collider.

1 Like

I wrote it for you, pretty quick to put together TBH. :slight_smile:

1 Like