Problem referencing instances from a For Each JSON loop

  • Hi Everyone!

    This is probably something simple that I'm overlooking, but...

    I have a JSON for my levels where I store the map, the starting X and Y and a list of enemies on the following format:

    		{
    			"map": [
    				[1,1,1,1,1,1,1,1,1,1,1,1,1,1,1],
    				[1,1,1,1,1,1,1,1,1,1,1,1,1,1,1],
    				[1,1,1,1,1,1,1,1,1,1,1,1,1,1,1],
    				[1,1,1,1,1,1,1,1,1,1,1,1,1,1,1],
    				[1,1,1,1,1,1,1,1,1,1,1,1,1,1,1],
    				[1,1,1,1,1,1,1,1,1,1,1,1,1,1,1],
    				[1,1,1,1,1,1,2,2,2,1,1,1,1,1,1],
    				[1,1,1,2,2,2,0,0,0,2,2,2,1,1,1],
    				[1,1,1,0,0,0,0,0,0,0,0,0,1,1,1],
    				[1,1,1,0,3,0,0,0,0,0,3,0,1,1,1],
    				[1,1,1,0,0,0,0,0,0,0,0,0,1,1,1],
    				[1,1,1,0,0,0,0,0,0,0,0,0,1,1,1],
    				[1,1,1,0,0,0,0,0,0,0,0,0,1,1,1],
    				[1,1,1,0,3,0,0,0,0,0,3,0,1,1,1],
    				[1,1,1,0,0,0,0,0,0,0,0,0,1,1,1],
    				[1,1,1,1,1,1,1,1,1,1,1,1,1,1,1],
    				[1,1,1,1,1,1,1,1,1,1,1,1,1,1,1],
    				[1,1,1,1,1,1,1,1,1,1,1,1,1,1,1],
    				[1,1,1,1,1,1,1,1,1,1,1,1,1,1,1],
    				[1,1,1,1,1,1,1,1,1,1,1,1,1,1,1],
    				[1,1,1,1,1,1,1,1,1,1,1,1,1,1,1],
    				[1,1,1,1,1,1,1,1,1,1,1,1,1,1,1],
    				[1,1,1,1,1,1,1,1,1,1,1,1,1,1,1]
    			],
    			"enemies": [
    				[5,8,1,32,4,0],
    				[9,8,1,32,4,0.5],
    				[7,8,0,0,0,0]
    			],
    			"x": 7,
    			"y": 7
    		}

    There are two types of enemies, stationary and moving (using sine). When I place the stationary enemies, I'm trying to get te nearest floor tile and destroy it, since it's below the enemy and players won't be able to change it (which is a victory condition).

    The problem is, when I try to access that tile from the for each loop it won't show. In fact, even "spFloor.count" will return 0.

    Here is the file. Any help is welcome.

    Cheers.

  • Not sure on the precise problem here but obviously it is managing to create the enemies first. If you slow it right down, pull the build enemy into a different function then call that function after a 'wait for previous actions to complete' on the floor function then it works.

  • No, It's not. I debugged that and you can see on the browser log that all tiles are created BEFORE starting to create enemies.

  • brunopalermo

    the problem is that when objects are created, they are not added to C3's internal object lists until after the top level event that creates them.

    in your sample file, that is the entire buildlevel function that starts at event 3.

    that means you can not pick them yet in event 14 where you want to destroy them.

    the easiest solution here is to add a Wait 0 action to event 10. That will defer running the "place enemies" loops until the end of the current tick - by then C3 has finished adding all the tiles and they can be picked the way you would expect - and destroyed as required.

  • No, It's not. I debugged that and you can see on the browser log that all tiles are created BEFORE starting to create enemies.

    The logging occurs yes but they're not created yet so I gave you a fix to ensure they are created before trying to reference them.

  • What I mean is that when the floors are created they CAN be counted. The log IS counting them.

    But when I try to count them from inside the For Each JSON loop it returns ZERO.

    That makes no sense. How come they can be counted (even if they're "not added to the internal list of objects") in one situation but cannot, later on, in another situation?

    By the way, I already have an workaround since my first post. I just want to understand why this happens and if it was actually supposed to be happening. :)

    Also, waits in loops are not good practice.

    Cheers.

  • So, no developments on this.

    I tried crating another function to create enemies and call it at the end of the function that builds the level. It still can't reference floor tiles. Any other thoughts?

    Cheers!

  • calling a function is no different than copy and pasting those actions into the place where the function is called (except the function doesn't know what objects were picked when it was called). But the actions in the function can still only pick objects that were available to be picked at the point it was called, so it is really still part of the same top level event even though you divided it into two functions.

    if you want the tiles to be fully created when you add the enemies, then the enemies must be created in a separate top level event.

    so, if you don't want to do:

    AJAX on complete - call function CreateLevel; wait 0; call function CreateEnemies;

    then use a GameState variable:

    AJAX on complete - Set GameState to "CreateLevel";

    System GameState = "CreateLevel" - call function CreateLevel; Set GameState = "CreateEnemies"

    System GameState = "CreateEnemies" - call function CreateEnemies; set GameState = "LevelReady"

  • Try Construct 3

    Develop games in your browser. Powerful, performant & highly capable.

    Try Now Construct 3 users don't see these ads
  • AJAX on complete - call function CreateLevel; wait 0; call function CreateEnemies;

    This is exactly my workaround. Even though I'm not using the wait, since it's not necessary.

    What I'm trying to figure out here is WHY should I need two functions when one should work perfectly well. :) In fact, if you check the log in the Browser console you'll notice that I can reference tiles normaly before, but, for some reason, when I'm creating enemies they seem to disapear, even though they are still there.

    Seems like a bug to me. If the problem were the order of creation, the Browser logs wouldn't work either but they do...

    Thanks anyway.

  • the wait 0 is critical to making it work. without the wait 0, the tiles will not have finished the process of being created when the CreateEnemies function is called. it is not a bug, Ashley has said it is a "feature" of the way objects gets created in Construct and to change it would cause too much trouble.

    with the wait 0, the tiles will be fully created and can be picked the way you want.

  • I'm doing it without the wait and it's working perfectly. As far as I know, the first function will be completely executed before calling the following one.

    There was some thread on that some weeks ago, let me check and find that for you.

    EDIT: Here's the thread I mentioned above.

    Cheers!

  • I remember following that thread :)

    the first function will completely execute before the second one, but since they are both called from the same top level event, the objects will not have completed the creation process.

    I took your sample file and added a couple buttons - one to call the two functions without a wait, and one to call with a wait. (and a third to clear the board)

    without a wait the spFloor.Count is zero, and the Pick All fails because there is nothing to pick yet.

    with the wait, calling the same functions, it works just the way you expect it to work.

    https://www.rieperts.com/games/forum/TileTest.c3p

  • the first function will completely execute before the second one, but since they are both called from the same top level event, the objects will not have completed the creation process.

    So you say, but I'm doing it, without the wait and it's working perfectly as it should. :)

    That would only be a problem if you were calling the second function from inside the first one.

    For instance. This would need a "wait":

    on Start of layout:
    ----Call createLevel
    
    on createLevel:
    ----(all code for the create level functions, positioning tiles and stuff)
    ----Wait 0
    ----Call loadEnemies
    
    on loadEnemies:
    ----(all code for creating enemies and destroying overlapping tile)
    

    This would not:

    on Start of layout:
    ----Call createLevel
    ----Call loadEnemies
    
    on createLevel:
    ----(all code for the create level functions, positioning tiles and stuff)
    
    
    on loadEnemies:
    ----(all code for creating enemies and destroying overlapping tile)
    

    That's what I'm saying. I used the second approach, no wait needed.

    But thank you for the discussion. It's great to have this exchange of ideas!

    Cheers.

  • I'm guessing you didn't take a look at the example I posted (based on your file).

    I do it the way you say should work - and it doesn't, until you add a wait. Try it - click the no wait button, then click the clear button, then click the wait button.

    in the pseudo code you posted, the "On Start Of Layout" is the top level event. Any objects created by actions under it will not be pickable until all the actions in that event are complete and the next top level event starts. The functions don't count as top level events because they are called from the same "on Start of Layout" event. However, having two "On Start of Layout" events would work:

    on Start of Layout - createLevel

    on Start of Layout - loadEnemies

    post a sample showing me that I am wrong. I don't think you can - I have seen this issue cause a lot of trouble, so fully understanding how objects are created and how the eventsheet works is really important.

  • Listen... I have a game currently with that and it's working. I'm not even debating IF this work. I can see it does. I have this working right now.

    But, since you're being so stubborn, here it is:

    sample showing you that you're wrong

    Just mouse over obstacles to see that tiles below them were succesfully destroyed.

    See, mom, no waits!

Jump to:
Active Users
There are 1 visitors browsing this topic (0 users and 1 guests)