Editor Script - Setting a Render Asset As Mesh Collider

Hello friends,

I am attempting to create a small editor tool that will help me later on with environment setup. The tool will perform the following:

  1. Iterate through the children of the clicked entity in the scene.

  2. If the entity has a render component on it, the script adds a Rigidbody static and a Collision mesh.

  3. Fetch the render asset from the asset folder name found in the render component.

  4. Set the render asset to the mesh collider.

This will ensure that every entity has a mesh collider on it. It especially helpful for large scenes with a lot of assets.

The problem:

The system is working well when it comes to adding the components and fetching the render assets.

Screenshot 2023-09-14 094700

As you can see the render asset is set to the collision, the same asset as the one in render.

However, I keep getting this error when I run the game.

viewport-error-console.js:159 TypeError: Cannot read properties of undefined (reading 'length')
    at CollisionMeshSystemImpl.createPhysicalShape (system.js:402:51)
    at CollisionMeshSystemImpl.doRecreatePhysicalShape (system.js:470:31)
    at Asset.<anonymous> (system.js:449:22)
    at Asset.ready (asset.js:480:22)
    at CollisionMeshSystemImpl.loadAsset (system.js:447:19)
    at CollisionMeshSystemImpl.recreatePhysicalShapes (system.js:429:22)
    at CollisionMeshSystemImpl.afterInitialize (system.js:57:14)
    at CollisionComponentSystem.initializeComponentData (system.js:669:14)
    at CollisionComponentSystem.addComponent (system.js:55:14)
    at SceneParser._openComponentData (scene.js:107:24)

When i comment out this line of code that is setting the render asset to the collision entity.set('components.collision.asset', renderAssetId); the game launches normally.

When I set the render asset manually in the editor and not from the script, the game launches normally as well.

Here is the link to the project: here

Here is my code:

(function () {
    async function listChildrenAndAddComponents(entityId, indentation = '') {
        const entity = editor.entities.get(entityId);

        if (!entity) {
            console.warn("Entity not found.");
            return;
        }

        console.log(`${indentation}Processing entity: ${entity.get('name')}`);

        if (entity.has('components.render')) {
            const renderAssetId = entity.get('components.render.asset');
            const renderAsset = editor.assets.get(renderAssetId);

            console.log(`Render Asset ID: ${renderAssetId}`);

            if (!entity.has('components.collision')) {
                entity.addComponent('collision', { type: 'mesh' });
            }

            if (!entity.has('components.rigidbody')) {
                entity.addComponent('rigidbody', { type: 'static' });
            }

            if (renderAsset) {
                console.log("Render Asset Name:", renderAsset.get('name'));
                console.log("Render Asset Type:", renderAsset.get('type'));
                if (renderAssetId) {
                    // Set the collision asset to the render asset
                    entity.set('components.collision.asset', renderAssetId);
                } else {
                    console.log('Asset ID or Asset is not available');
                }
            } else {
                console.log("Render asset not found.");
            }
        }

        const children = entity.get('children');
        if (children && children.length > 0) {
            for (const childId of children) {
                await listChildrenAndAddComponents(childId, indentation + '  ');
            }
        }
    }

    function createButton() {
        const btn = new pcui.Button({ text: 'List Children and Add Components' });
        btn.style.position = 'absolute';
        btn.style.bottom = '10px';
        btn.style.right = '10px';
        editor.call('layout.viewport').append(btn);

        btn.on('click', async () => {
            const selectedEntities = editor.call('selector:items');
            if (selectedEntities.length === 0) {
                editor.call('status:text', 'No entity selected.');
                return;
            }

            for (const entity of selectedEntities) {
                await listChildrenAndAddComponents(entity.get('resource_id'));
            }
        });
    }

    createButton();
})();

Thank you for your help!

Hi @Firas_Al_Jerdy_Brigh,

The issue is you are adding a render asset in the model asset property of the collision component, it should be in the render asset field instead:

image

Normally you wouldn’t be able to do that, but here you are exploiting the editor API to force it. Update your code to this:

entity.set('components.collision.renderAsset', renderAssetId);

And for anyone interested here is a short code snippet that will iterate on all selected entities and add a collision/rigidbody (static) component using the render asset from any render component found:

editor.selection.items.forEach(o => {
    const renderAssetID = o.get('components.render.asset');

    if(!renderAssetID) return true;

    o.addComponent('collision', {
        type: 'mesh',
        renderAsset: renderAssetID
    });

    o.addComponent('rigidbody', {
        type: 'static',
        friction: 0.5
    });
});
1 Like

Hello Leonidas,

Thank you so much for this reply, this worked brilliantly!

I have been stuck on it for the last hour or so.

Cheers!

1 Like