Scripting the PlayCanvas Editor

Reviving the thread just to show how scriptable the PlayCanvas editor can be.

Using the Tiled Terrain Manager to generate a runtime terrain from heightmaps … I was amazed how simple it was to re-use the same codebase in the editor, with minor changes, and generate the exact same terrain for the level designers to use.

The open nature of Javascript combined with the awesomeness the PlayCanvas guys have built in the editor is powerful.

6 Likes

Here is an example script that will position all of your selected entities in the X axis in a sequence.

  1. Select a number of entities in the editor.
  2. Open the browser console.
  3. Adjust the step in the first line, paste the code and execute.
translateChildrenToGrid(3);

function translateChildrenToGrid(step) {
  const type = editor.call("selector:type");

  if (type !== "entity") {
    return false;
  }

  const items = editor.call("selector:items");

  if (!items || items.length === 0) {
    return false;
  }

  items.forEach((item, index) => {
    editor.call("selector:set", "entity", [item]);
    item.set("position", [index * step, 0, 0]);
  });

  console.log("--- Finished execution ---");
}

3 Likes

And another one, I found a missing action in editor: I am unable to assign a material on a Mesh Instance of multiple model assets at the same time. I would have to assign them one after the other.

  1. Select a number of Model assets.
  2. Open the browser console.
  3. Set your Material asset id and Mesh Instance slot, paste the code and execute.

assetModelSetMaterial(15325478, 0);

function assetModelSetMaterial(assetId, slot) {
  const type = editor.call("selector:type");

  if (type !== "asset") {
    return false;
  }

  const items = editor.call("selector:items");

  if (!items || items.length === 0) {
    return false;
  }

  items.forEach((item, index) => {
    item.set("data.mapping." + slot + ".material", assetId);
  });

  console.log("--- Finished execution ---");
}
3 Likes

It’s never too dead to bump the thread.

Generating the terrain in editor is exactly what we’re looking for! Would you still happen to have the modified script?

Not exactly, but I may have something to help you out with that. I’ll get back to you tomorrow on that.

2 Likes

So here is a very easy way to get any script to execute and run inside the PlayCanvas editor. This is an open source framework that enables editor scripting:

With a minimal change in the default runtime terrain script we get it to run and render in the editor:

3 Likes

Marry me!

I was wondering how collision work out with that. Is it created by default? Or does it throw you the map in to assets so you can put it in mesh collider ?

The Uranus Editor SDK loads and executes Ammo.js if it’s included in the project. If the entity uses a collision and rigidbody component, physics will work in editor as well:

2 Likes

Do anyone know how to add a script (not the script component) to an entity via the Editor API?

My question was in thread Scripting the PlayCanvas Editor. I was asking about how to use the editor api to add a script to an entity. for just the script component, i have figured out it’s “editor.call(‘entities:addComponent’, (entity), ‘script’)”, but i can’t figure out how to add script

Hi @SunnyChow, I have some code to showcase that. I am not on desk currently, I’ll post it later today.

Here you go, the following code will add a script component to the selected, in the hierarchy, entity, attach a script type (orbitCamera) and at the same time set the value of an attribute:

var selectedEntity = editor.call('entities:selectedFirst');

editor.call('entities:addComponent', [selectedEntity], 'script');

var script = 'orbitCamera';
selectedEntity.set(`components.script.scripts.${script}`, {
    enabled: true,
    attributes: {
        distanceMax: 90
    }
});
selectedEntity.insert('components.script.order', script);
1 Like

Hi Leonidas,
I’m trying to set the entity attribute through editor code in a script attached to the current selected item.
But I’m getting observer errors, how can i set the entity into my entity attribute field/variable?
For context: in this example i wanted the script to link to itself just for testing, but I’m not sure what type of value it is expecting.

async function setLinkedEntity() {
      const type = editor.call("selector:type");

      if (type !== "entity") {
        return false;
      }

      const items = editor.call("selector:items");

      if (!items || items.length === 0) {
        return false;
      }
      //console.log('items:', editor.call('selector:items')[0].json());

      items.forEach(async (item, index) => {
         editor.call("selector:set", "entity", [item]);
        item.set('components.script.scripts.linkEntity',{
          enabled: true,
          attributes: {
            linked: item
          }
        });    
      });

      console.log("--- Finished execution 7 ---");
    }

Hi @fcsa,

I gave it a try on a project, to reference an entity to an attribute you only need to pass the entity guid.

Example code for a script type named myScript and an attribute named linkEntity:

item.set('components.script.scripts.myScript.attributes.linkEntity', item.get('resource_id'));

This will reference itself.

1 Like

Thank you!

Is there a way to use “drawLine” in the editor viewport?
I have installed violentmonkey’s extension and I find it quite usefull, I was wondering if there was a way to draw helper lines, like we do using this.app.drawLine but in the editor.

You can use that same method, the editor runs a regular pc.Application instance. The only difference is it doesn’t update the scene automatically (autoRender is set to false).

So to render a line per frame you will have to write your own update method. For quick setup a setInterval can do that:

window.setInterval( () => {
    pc.app.drawLine(new pc.Vec3(0,0,0), new pc.Vec3(0, 10, 0), pc.Color.WHITE);
}, 0); 

1 Like

hehe i kinda figured this out testing, but still thank you for your prompt response!
Here is a much complex one, I haven’t being able to figure out.

If anybody is interested here is how i was able to add rotation to my selected item

var lookMat = new pc.Mat4();
var rotation = new pc.Quat();
var pos1 = new pc.Vec3(items[0].get('position')[0], items[0].get('position')[1], items[0].get('position')[2]);
var pos2 = new pc.Vec3(items[1].get('position')[0], items[1].get('position')[1], items[1].get('position')[2]);    
var direction = new pc.Vec3().sub2(pos1, pos2);
lookMat.setLookAt(pc.Vec3.ZERO, direction, pc.Vec3.UP);
rotation.setFromMat4(lookMat);
var r = rotation.clone().getEulerAngles();
items[0].set('rotation',[r.x, r.y, r.z]); 

I’d say that since this is a regular PlayCanvas coding question post a new thread about it, so we don’t pollute this old but super useful topic.

1 Like