Loading JSON scene hierarchy during runtime

I have this project with multiple scenes. In these scenes you’re able to design an intersection through enabling or disabling certain entities. When you’ve designed an intersection, the player should be able to save this intersection as a JSON file and load it in whenever they want so they will have the exact same layout.

Right now it’s saving a JSON file with scene and boolean information of each entity in the scene. When I load it back in, it just opens the correct scene but does not enable the correct entities. How can I tackle this?

This is my code for loading the JSON file back in:

function loadJsonScene() {
    const fileInput = document.createElement("input");
    fileInput.type = 'file';
    fileInput.accept = 'application/json';
    fileInput.click();
    document.body.appendChild(fileInput);

    fileInput.onchange = (e) => {
        const file = e.target.files[0];
        if (!file) return;

        const reader = new FileReader();

        reader.onload = (er) => {
            const jsonContent = JSON.parse(reader.result);

            if (jsonContent && jsonContent.sceneName) {
                console.log(jsonContent.sceneName);
                
                //Destroy old scene
                var rootChildren = self.app.root.children;
                while(rootChildren.length > 0) {
                    rootChildren[0].destroy();
                }

                //Load new scene
                console.log("Before loading scene");
                self.app.scenes.loadSceneHierarchy(jsonContent.sceneName, function () {
                    console.log("Scene loaded: " + jsonContent.sceneName);
                    console.log("Entity count:", self.app.root.children.length);
                });
                

                //Load scene booleans
                console.log("Loaded entities:", jsonContent.entities);
                setSceneBooleans(jsonContent.entities.children, self.app.root);

            } else {

                console.error("Invalid JSON structure or missing 'sceneName' property");
            }
        };

        reader.readAsText(file);
    };

};


function setSceneBooleans(entities, parentEntity) {
    entities.forEach((entityData) => {
        var entity = findEntityByName(parentEntity, entityData.name);
        if (entity) {
            console.log('Setting entity:', entity.name);
            console.log('Current enabled status:', entity.enabled);
            
            entity.enabled = entityData.enabled;
            
            console.log('New enabled status:', entity.enabled);

            if (entityData.children && entityData.children.length > 0) {
                setSceneBooleans(entityData.children, entity);
            }
        } else {
            console.log('Entity not found:', entityData.name);
        }
    });
}



function findEntityByName(parentEntity, name) {
    if (parentEntity.name === name) {
        return parentEntity;
    }

    for (var i = 0; i < parentEntity.children.length; i++) {
        var childEntity = parentEntity.children[i];
        var foundEntity = findEntityByName(childEntity, name);
        if (foundEntity) {
            return foundEntity;
        }
    }

    return null;
}

https://playcanvas.com/project/1163660/overview/situatie-maker

SaveAndLoadScript.js line 12 should be:

    function exportSceneAsJSON() {
        var rootEntity = self.app.root;

looking at this code section:


    function loadJsonScene() {
        const fileInput = document.createElement("input");
        fileInput.type = 'file';
        fileInput.accept = 'application/json';
        fileInput.click();
        document.body.appendChild(fileInput);

        fileInput.onchange = (e) => {
            const file = e.target.files[0];
            if (!file) return;

            const reader = new FileReader();

            reader.onload = (er) => {
                const jsonContent = JSON.parse(reader.result);

                if (jsonContent && jsonContent.sceneName) {
                    console.log(jsonContent.sceneName);
                    
                    //Destroy old scene
                    var rootChildren = self.app.root.children;
                    while(rootChildren.length > 0) {
                        rootChildren[0].destroy();
                    }

                    //Load new scene
                    console.log("Before loading scene");
                    self.app.scenes.loadSceneHierarchy(jsonContent.sceneName, function () {
                        console.log("Scene loaded: " + jsonContent.sceneName);
                        console.log("Entity count:", self.app.root.children.length);

you can see that although you load the jsonContent, only the jsonContent.sceneName is being passed to Playcanvas via loadSceneHeirarchy().
see:
https://github.com/playcanvas/engine/pull/3557

specifically at the bottom,

var self = this;
this.scene = this.app.scenes.find(this.sceneName);

// Load the scene data via HTTP
this.app.scenes.loadSceneData(this.scene, function (err, sceneItem) {
    if (err) {
        console.error(err);
    } else {
        // Get a reference to the scene's root object
        var oldHierarchy = self.app.root.findByName('Root');
        oldHierarchy.destroy();

        // Load both the hierarchy and scene settings
        self.app.scenes.loadSceneHierarchy(self.scene, function() {});
        self.app.scenes.loadSceneSettings(self.scene, function() {});

        // Unload the data
        self.app.scenes.unloadSceneData(self.scene);
    }
});

if you implement this functionality into your codebase, i think it should work.

@Timo see above
try replacing the this.scene variable with jsonContent

Thank you for your reply! I tried implementing this but unfortunately I couldn’t figure it out. I’m gonna try a totally different approach with loading the boolean data back in.

1 Like