Strange issue when i edit a json resource in a script

I create an json asset array in a script and tried to get all this files into a single array with an ID appended to them dynamically…but when try to see the final result the property id my script show the same value…
image

image

// swap method called for script hot-reloading

// inherit your script state here

TypeBScript.prototype.swap = function (old) {

    this.enabled = true;

    old.app.keyboard.off("keydown");

    this.app.keyboard.on("keydown", (e) => {

        switch (e.key) {

            case pc.KEY_1:

                let finalJson = [];

                this.jsonAssets.forEach((asset, index) => {

                    let resource = asset.resource;

                    resource.id = index;

                    console.log(index);

                    console.log(resource);

                    finalJson.push(resource);

                });

                console.log(finalJson);
        }

    });

};


While it is executing is ok…but when i print the finalJson array all those ids properties are now the same

Is this a bug or did i make something wrong?

I’m not following what’s going here.

Can you post an example project, the repro steps of the issue and what you expect to happen please?

Repro steps:

  1. Create an json asset with any data you want.

  2. Add it to an script attribute thats is an array

  3. In your code loop each copy of this json asset

  4. Inside your loop try to change or add any property inside this jsonAsset resource (i tried to create a new property called “id” and put the index of the loop interaction as its value)

  5. Add each instance to an array

  6. Print the array

Result Expected: I will have the same json info I created in assets panel but each one of them will have a different id because of the loop interaction

Result i have now: Instead of getting different “id” values I’m getting the last of index of the array in all elements

I think the problem is the getter function from an asset object

//myScript.js
let resource = asset.resource;

//definition in asset.js
    get resource() {
        return this._resources[0];
    }

This getter is not returning a copy from this.resources[0] but the actual object…since i’m using the same asset in attribute array it must be referencing the same “pointer” and I suspect any modifications to this resource object, even in my local variable, will apply this changes to all places where this resource is used

I got this working by adding theses lines before add it to my array…

//attempt to create a copy from the modified resource before adding to array
let tempStringParsed = JSON.parse(JSON.stringify(resource));
let fullJson = [];

this.jsonAssets.forEach((r,i) => {
let resource = r.resource;
resource.id = i;
let tempString = JSON.stringify(resource);
let tempStringParsed = JSON.parse(tempString);
fullJson.push(tempStringParsed);
});

console.log(fullJson)
/*[
    {
        "name": "Random Name",
        "age": 25,
        "hobby": "Play VideoGames",
        "id": 0
    },
    {
        "name": "Random Name",
        "age": 25,
        "hobby": "Play VideoGames",
        "id": 1
    },
    {
        "name": "Random Name",
        "age": 25,
        "hobby": "Play VideoGames",
        "id": 2
    },
    {
        "name": "Random Name",
        "age": 25,
        "hobby": "Play VideoGames",
        "id": 3
    }
]*/

…but i know i won’t be able to call JSON.stringify() on entities if I decide to include entities in this resource because of the circular references that this object has

image

Assuming this all reference the same JSON asset, then yes, theses are all references to the same asset.

//myScript.js
let resource = asset.resource;

This does not make a copy, it is just a reference. If you modify resource, you are modifying the asset resource so any other scripts/references to the asset will also be affected because they are all referencing the same object

Yes, here you are making a ‘deep’ copy of the JSON data and have a new object.

This is not a bug, it’s how it was designed.

1 Like

Good is not a bug!
The bad part i’m out of ideas on how to create a json attribute with multiple schemas exposed to the inspector panel like I used to do in UnrealEngine 4 with C++

struct Address {
  string street;
  int houseNumber;
  string zipCode;
  string city;
  string state;
  string country; 
 
}

struct User {
  string name;
  string age;
  string email;
  struct Address address;
}

struct User newUser;

I will try to append entities with scripts to update its parent data, but this solution is far from ideal in my case

With C++ and C# structs, they are copied by value.

I don’t understand what you mean by ‘json attribute with multiple schemas’. Can you give a concrete example with how it looks in Unreal and usage please?

Oh wait, I see the nested structs in the example now

sure…i will post a image from what i would get in Unreal.

Just a second!

In Unreal i have an actor (this is same as an entity) with an exposed variable (attribute) called newUser
image

Since user is a structure (the same as a jsonAttribute) i can see all its properties like this…but i have one subproperty that accepts new data

Outside the structure editor and the entity script this is what i wanted to see in the entity inspector panel

If this NewUser must be an array i can change it too!

image

image

Thats why i thought creating a “schema asset” like we have the “json” or “text” asset would help in this feature…because in a single jsonAtribute we could just place the schemasAssets as sub-attributes and the parser just needed to draw this models on the inspector

Instead of creating the schema inside the script attributes we do it somewhere else, in another menu, then we just plug it in the attributes…

The usage is be able to config everything from the editor in a single script without needing to create “i don’t know how many sub-entities and sub-scripts” just to be able to concatenate a full json with every data from sub-entity’s script attributes to do something like newUser.address.zipcode with info from the editor

Unfortunately, you are stuck with flat JSON schemas for script attributes. It’s not just the GUI/Editor that needs to be updated but the backend and how that data is stored as well.

So either you have to have some structured entity hierarchy to have ‘nested’ JSON data and build that up on startup or if the data doesn’t reference an PlayCanvas asset/entity, you can have this data in Google Sheets and export as JSON that can be imported into PlayCanvas. (which quite a few people do)

There’s no real good answer here to have nested data structures as script attributes at the moment.

struct Address {
  string street;
  int houseNumber;
  string zipCode;
  string city;
  string state;
  string country; 
 
}

struct User {
  string name;
  string age;
  string email;
  struct Address address;
}

struct User newUser;

Using the nested entity solution, this would be pretty straightforward to do I think

Thank you for your time and patience. I will try to do something with the entity hierarchy for now. You can close this thread.