How to add representation of pickup mesh to Pickup Spawner in the Editor?

In out prior approach to weapon pickups I had it so certain pickups were deliberately oriented in the world. E.g. a bow pickup leaning against a wall. Sword in a stone, that sort of thing.

Now obviously the PickupSpawner has no mesh data to show until it spawns the child pickup from its InventoryItem at runtime.

It looks fairly straightforward to draw a mesh or wireframe using OnDrawGizmos(); so I was wondering if/how it might be possible to grab the mesh data in the pickup prefab in the editor. Obviously this is pretty trivial at runtime with an object reference to an instantiated object (and also unecesary since the object is then instantiated). I imagine a more direct approach of finding the correct file to read the data would be necessary but I have no idea how.

But, yeah, tl;dr it would be nice to have a way to access the mesh data contained in it’s pickup prefab and then use that data to draw it in OnDrawGizmos in the editor to aid placement / orientation; assuming the PickupSpawner orientation would translate correctly to the spawned object at runtime.

It’s not really that simple. Since it can be anything, you really don’t know what is in the prefab so you have to ‘feel’ your way around, hoping to hit something. In addition, the pickup can be made up of several meshes meaning you have to find them all, determine their orientation, etc. and try to render them.

If this is your goal then just drop the prefab in the pickup, orient the pickup like you would want it, and delete the prefab again.

You can get a reference to the Mesh if you have a reference to the Pickup prefab in code. Obviously, this would need to be done in OnDrawGizmos, and it’s expensive to read the prefab every frame (performance!).

I would try something like

        private void OnDrawGizmosSelected()
        {
            if (item == null) return;
            Pickup pickup = item.GetPickup();
            if (pickup == null) return;
            MeshFilter filter = pickup.GetComponentInChildren<MeshFilter>();
            Gizmos.DrawMesh(filter.mesh, transform.position + filter.transform.localPosition);
        }

Mind you, I can’t test this code at the moment.
One problem with this approach is that even restricting it to DrawGizmosSelected, while it’s selected the routine will keep grabbing the item and pickup every frame, which will slow you down like a hurricane.

So we could try this:

        [HideInInspector] [SerializeField] private string itemId;
        [HideInInspector] [SerializeField] private MeshFilter filter;
        private void OnValidate()
        {
            if (item.GetItemID() == itemId) return;  //nothing has changed
            if (item == null)
            {
                itemId = null;
                filter= null;
                return;
            }
            Pickup pickup = item.GetPickup();
            if (pickup == null)
            {
                filter = null;
                return;
            }
            filter = pickup.GetComponentInChildren<MeshFilter>();
            if (filter == null)
            {
                return;
            }
            itemId = item.GetItemID();
        }

        private void OnDrawGizmosSelected()
        {
            if (filter == null) return;
            Gizmos.DrawMesh(filter.mesh, transform.position + filter.transform.localPosition);
        }

You would also need to add a method in Inventory Item for any of this to work

public Pickup GetPickup() => pickup;

Ah ok, thanks for thinking this out! I nearly had something along your first example working though I got some exception dealing with the child game objects (the ones with the meshdata) so I assumed it was non viable in the editor. I was trying to index the child transform rather than use GetComponentInChildren. Looks sensible what you have in OnValidate, as you say you only need to get the reference once unless the spawned item is changed. I’ll give this a try as often I find it hard, even with the editor icon set, to see if the pickup is under the navmesh or floating above. Though I suppose it all depends on how often items are being manually placed vs. script generated.

It would seem also a change is needed to the pickup spawner too as I tried to do this manually by setting the transform at runtime and copy/pasting the values. When the item is spawned it places using a vector3 then childs the pickup prefab, requiring a reset to the transform for it to inherit the parent transform and be in the right orientation.

Would there be any benfit wrapping it in a #if UNITY_EDITOR? As there’s no need for the code outside that context.

Yes, as long as you don’t need OnValidate() for other things… (it’s not actually called by Unity in a Player). If you have other items to validate, you could wrap this code in a block.
It does occur to me that this may not actually work at all, as I’m not sure if GetComponentInChildren works in Prefabs. Writing on the road, so no compiler, all in my head.

Privacy & Terms