Create & Edit Templates at Runtime

I am working on making an editor in my project. I want to save the model-group made in the editor in the project in “template” format. Also, I want to save the templates that have been added to the editor and edited. Is it possible? I am wondering the best way to do this.

This is an interesting problem. To clarify, you have your own editor where users can modify the hierarchy and you want to be save it in the same Template resource format that the PlayCanvas Engine uses.

Technically, this is possible but we don’t have any examples on how to do this or made the format public.

Ultimately, the Template format is just JSON. Here’s an example:

{
    "entities": {
        "f54785a7-9220-4682-bb45-8c148096afec": {
            "name": "Spot Light",
            "tags": [],
            "enabled": true,
            "resource_id": "f54785a7-9220-4682-bb45-8c148096afec",
            "parent": null,
            "children": [
                "8d141fee-949d-49b1-a655-124b588fe647"
            ],
            "position": [
                -4.4195356369018555,
                9.643611907958984,
                2.664121795636931
            ],
            "rotation": [
                20.419859990805435,
                -8.410411591049507e-8,
                -16.219498256019275
            ],
            "scale": [
                1,
                2.1438879150408674,
                1
            ],
            "components": {
                "light": {
                    "enabled": true,
                    "type": "spot",
                    "bake": true,
                    "bakeArea": 0,
                    "bakeNumSamples": 1,
                    "bakeDir": true,
                    "affectDynamic": false,
                    "affectLightmapped": true,
                    "color": [
                        1,
                        1,
                        1
                    ],
                    "intensity": 1.78,
                    "castShadows": true,
                    "shadowUpdateMode": 1,
                    "shadowType": 0,
                    "vsmBlurMode": 1,
                    "vsmBlurSize": 11,
                    "vsmBias": 0.01,
                    "shadowDistance": 16,
                    "shadowResolution": 1024,
                    "numCascades": 1,
                    "cascadeDistribution": 0.5,
                    "shadowBias": 0.2,
                    "normalOffsetBias": 0.05,
                    "range": 12,
                    "falloffMode": 0,
                    "innerConeAngle": 10,
                    "outerConeAngle": 30,
                    "shape": 0,
                    "cookieAsset": null,
                    "cookieIntensity": 1,
                    "cookieFalloff": true,
                    "cookieChannel": "rgb",
                    "cookieAngle": 0,
                    "cookieScale": [
                        1,
                        1
                    ],
                    "cookieOffset": [
                        0,
                        0
                    ],
                    "isStatic": true,
                    "layers": [
                        0
                    ]
                }
            }
        },
        "8d141fee-949d-49b1-a655-124b588fe647": {
            "name": "Child one",
            "tags": [],
            "enabled": true,
            "resource_id": "8d141fee-949d-49b1-a655-124b588fe647",
            "parent": "f54785a7-9220-4682-bb45-8c148096afec",
            "children": [],
            "position": [
                0,
                0,
                0
            ],
            "rotation": [
                0,
                0,
                0
            ],
            "scale": [
                1,
                1,
                1
            ],
            "components": {}
        }
    }
}

So in theory, you can use the same format to create your own asset data for your Editor and it can be used by the Engine as a Template asset

I see, so how can I access this list from my own script? Because I want to update the templates in this list and create a new template.

I don’t understand what you mean from this? There’s no list, the above JSON is a single template.

I gather the designs designed with the editor with several primitive objects under one main object. And I want to save this object as a template when requested in the runtime. My question was for this, I want to save the objects produced in runtime as a template in assets>template.

You will have to save the Template JSON somewhere on your services (eg a database) and be able to retrieve it via an endpoint. (You can load it into the engine at runtime via the asset registry API)

You won’t be able to able to save it into the PlayCanvas Editor project if that’s what you are asking?

The data itself is just JSON. We don’t have have the format/properties of the JSON documented but you should be able to work it out from looking at the Template JSONs created by the Editor from downloaded builds or via the browser Chrome console/network tab.

1 Like

Got it, I thought there might be a direct way to save or update the project as an asset. I will try to solve it this way. Thanks @yaustar

I’m afraid not at this time, no.

If your users are part of the development team, it would be best for them to edit in the PlayCanvas Editor and perhaps have specific scenes for them to use.

If you need extra functionality, consider working with the Editor API to add that. Editor API | Learn PlayCanvas

1 Like

Hi @yaustar , I need help with one more thing.

As you said, I can update properties such as shapes, positions, scales of objects in the template. But when I want to add a new object into the template, a random ID I give creates a problem and I get such an error.

“Uncaught TypeError: Cannot read properties of undefined (reading ‘getGuid’)”

I tried copying an element in “_data.entities” and adding the physical properties of the object generated in my ingame editor. How can I solve this problem, or do you have any suggestions to add new entities in the template and update the template? I couldn’t solve this problem because I couldn’t generate the “resource_id” / “_guid”.

Looking at the code in the devtools

Looks like the template isn’t instantiating a new Entity and return null/undefined.

You will need to debug into the instantiate function and see where it’s failing.

Actually there is no value that is null. Because I am creating by copying all the values of the entity I will add from the existing value in “_data.entities”. But I think I got an error because I couldn’t generate the “resource_id” / “_guid” value. Could you show me a short example of adding an object to the existing template?

Ah, I thought this was two separate code executions:

image

This is pretty much in the realm of private API so we don’t have examples for this.

If you could post an example project, I can try to see if I can help but it’s at the risk that this could break in the future with engine releases.

I simply showed what I wanted to do in two if blocks. I want to be able to add objects to the existing template asset or make changes on properties repeatedly.

Example project: PlayCanvas | HTML5 Game Engine

I forked the project and focused on making key 1 work where a clone of the cone 3 is made and added to the template data: https://playcanvas.com/project/1006817/overview/f-template-test

TemplateGenerator.prototype.onKeyDown = function (event) {
    if (event.key === pc.KEY_1) {
        // The difficulty here is I'm not sure what order the entities are in in the template data or if it even matters
        // The first entry is the template parent entity ('parent-entity') not the cones.

        // For simplicity, lets look for cone 3 to add to the template as it has no children to handle 
        let cone3EntityData = null;
        for (const key in this.parentTemplateData) {
            if (this.parentTemplateData[key].name === 'cone-3') {
                cone3EntityData = this.parentTemplateData[key];
                break;
            }
        }

        // Create a clone of this
        let cloneEntityData = { ...cone3EntityData };

        // Each Entity has a unique GUID so let's generate one
        const guid = pc.guid.create();
        cloneEntityData.resource_id = guid;

        // Add this guid to the children of the parent as each parent has an 
        // array of its children guids and each child has guid to it's parent
        this.parentTemplateData[cloneEntityData.parent].children.push(guid);
    
        // Change some data on the clone
        cloneEntityData.position = [0, 1.5, 0];
        cloneEntityData.name = 'clone of cone 3';

        // Add this clone to the existing template
        this.parentTemplateData[guid] = cloneEntityData;

        console.log(this.parentTemplateData);

        let instance = this.parentTemplate.resource.instantiate();
        this.entity.addChild(instance);
    }

    event.event.preventDefault();
};
1 Like

Probably creating a unique guid and using it that way in the template asset will solve the problems. I will try it in my main project. Thank you @yaustar I will help!