Search Unity

  1. Unity 2019.2 is now released.
    Dismiss Notice

Error when attempting to destroy entities

Discussion in 'Project Tiny' started by Ferran_SP, Jun 3, 2019.

  1. Ferran_SP

    Ferran_SP

    Joined:
    Jul 9, 2018
    Posts:
    27
    Hi,

    I'm trying to delete some entities I've previously instantiated with the following code:
    Code (JavaScript):
    1. let lineEntitiesToDestroy: ut.Entity[] = new Array();
    2. this.world.forEach([ut.Entity, game.LineTag], (entity) => {
    3.     lineEntitiesToDestroy.push(entity);
    4. });
    5.  
    6. for(let i = 0; i < lineEntitiesToDestroy.length; ++i) {
    7.     ut.Core2D.TransformService.destroyTree(this.world, lineEntitiesToDestroy[i], true);
    8. }
    But this will cause the following error:

    ut.World.destroyEntities(): Attempted to delete invalid entity NONE (passed at index 0 in the array to destroyEntities() function)


    In the forEach loop, I've made sure that the entities I'm pushing into the lineEntitiesToDestroy array exist. I can even log their name.
    But in the for loop in which I'm trying to destroy the entities, it's as if whatever's inside the lineEntitiesToDestroy array is not valid.
    I can't destroy the entities, I can't get their name...

    Does anybody know what might be going on?
     
  2. Rupture13

    Rupture13

    Joined:
    Apr 12, 2016
    Posts:
    78
    Is it possible that one (or more) of the entities in the lineEntitiesToDestroy array is a child entity of another entity in that same array?

    If that's the case, due to the "destroyTree" function, an entity could be deleted from the destroyTree function from its parent rather than when you want to destroy it from when the loop goes over it. Then, because the entity is already deleted (and therefore the reference in code points to ut.NONE entity), when your loop arrives at this entity, it's the NONE entity.

    If all this is the case, you'd either have to make sure the list doesn't contain trees in of itself, or (and I think this is easier), before you destroy the entity and its tree, check if the entity is the NONE entity with
    if (!lineEntitiesToDestroy[i].isNone()) { ... }
     
  3. Zionisias

    Zionisias

    Joined:
    Apr 23, 2019
    Posts:
    40
    Hey Ferran_SP,

    The problem lies with the way you are storing your entities in a list. For reasons unknown, Unity Tiny clears the parameters of the anonymous function you provide to a .forEach() at the very and/clean up of this .forEach(). This means that any reference from outside the .forEach loop to a parameter defined in the anonymous function will refer to an empty entity or component after the .forEach() has executed. This is what causes all entities in the list to become the none entity (index and version of 0), and ultimately causing the error you are encountering.

    The fix is pretty easy though! Because Unity Tiny internally works with just index and version of an entity, rather than references, you can simply create a copy of the entity within the .forEach and store that copy instead:
    Code (CSharp):
    1. let lineEntitiesToDestroy: ut.Entity[] = new Array();
    2. this.world.forEach([ut.Entity, game.LineTag], (entity) => {
    3.     lineEntitiesToDestroy.push(new ut.Entity(entity.index, entity.version));
    4. });
    5. for(let i = 0; i < lineEntitiesToDestroy.length; ++i) {
    6.     ut.Core2D.TransformService.destroyTree(this.world, lineEntitiesToDestroy[i], true);
    7. }
     
  4. Ferran_SP

    Ferran_SP

    Joined:
    Jul 9, 2018
    Posts:
    27
    Thanks for your answers.
    The issue is exactly what Zionisias described (and the fix works, as well! :) ).

    I have two follow-up questions.

    1) You say Unity Tiny works with the index and version of an entity. Is there any documentation about this? What it means, and how it works, I mean.
    I assume all entities are stored in a sort of pool, and you can access them unequivocally with these index and version params? What do these two params mean?

    2) I looked up how entities are being destroyed in the sample projects, and in the MatchThree game they do exactly what I posted above. It's in DinosaurService.ts, performAttackOnObjectType:
    Code (JavaScript):
    1.             let entitiesToDestroy: ut.Entity[] = new Array();
    2.             world.forEach([ut.Entity, targetObjectType, game.Destructible, ut.Core2D.TransformLocalPosition],
    3.                 (entity, type, destructible, transformPosition) => {
    4.                     if (Math.abs(transformPosition.position.x - hitCenter) < hitRadius) {
    5.                         if (destructible.SpriteStates.length > 1) {
    6.                             this.damageDestructible(destructible);
    7.                         }
    8.                         else {
    9.                             let entityToDestroy = new ut.Entity();
    10.                             entityToDestroy.index = entity.index;
    11.                             entityToDestroy.version = entity.version;
    12.                             entitiesToDestroy.push(entityToDestroy);
    13.                         }
    14.  
    15.                         this.spawnExplosions(world, destructible, transformPosition);
    16.  
    17.                         if (shakeOnHit) {
    18.                             this.shakeCamera(world);
    19.                         }
    20.                     }
    21.                 });
    22.  
    23.             for (let i = 0; i < entitiesToDestroy.length; i++) {
    24.                 ut.Core2D.TransformService.destroyTree(world, entitiesToDestroy[i], true);
    25.             }
    For some reason, this seems to work (I checked, and that code is being called).
    Any clue why?

    3) Finally, I didn't realize I could destroy the entities inside the initial forEach loop. There's no need to store them in a separate array and then call destroy in a second loop. I'll just leave this written here if it ever helps somebody else. :p

    (oh, man, these emojis are terrible : ( : P)
     
    Zionisias likes this.
  5. Zionisias

    Zionisias

    Joined:
    Apr 23, 2019
    Posts:
    40
    Hey Ferran_SP,

    Good questions!
    1. There is as far as I know no documentation about Unity Tiny working purely on index and version internally, but this example case, is proof that even with a selfmade copy of an entity you can alter the actual entity. There is documentation about the index and version itself, and at the documentation of the version they mention that the combination of these two is always unique. They also hint under "index" how the version is determined. This seems to happen whenever a new entity is assigned the index of a previously destroyed entity. The version will then be 2, and make for a unique combination. Here is the link!
    2. Within the else-scope of the if-statement is the only place they add an entity to the array as far as I can see, and you can see that even they make a copy of the entity. You can do it in a single step using the constructor of the entity, as I had provided in my previous post, or you can first create an entity, and assign the index and version after creation, like done in the MatchTree game sample you provided.
    3. Yeah, destroying entities can mostly be done within the forEach loop. I have only very rarely encountered a usecase where I couldn't do this, but those usually included doing a new loop after the deletion in the same OnUpdate() or something similar, nothing to worry about in most cases.
    If you have any more questions, be sure to ask! ;)
     
  6. Ferran_SP

    Ferran_SP

    Joined:
    Jul 9, 2018
    Posts:
    27
    Ah, damn, I didn't read the new ut.Entity line in the MatchThree game chunk of code. I got too focused on the other part of the code. :/ It makes sense now.

    Thanks a ton for the super helpful answers!
     
    Zionisias likes this.