I've been using Construct for YEARS and I still don't understand when to use For Each...

Not favoritedFavorited Favorited 0 favourites
  • 12 posts
From the Asset Store
Casino? money? who knows? but the target is the same!
  • I've read some other posts on this subject but I'm still confused. Could someone explain it to me?

    Let's say I have a sprite called mySprite, with an instance variable called "big". There are multiple instances on the screen.

    This works:

    if mySprite.big = 1 then Set Size to 25x25.

    It's my understanding that "mysprite.big = 1" iterates through all the mySprite instances and "picks" the ones where big = 1. In that code block anything I do to mysprite will only affect the picked sprites. Is that correct?

    So when do I need to do this:

    For Each mySprite

    mySprite.big = 1

    Then [Do Something]

    Tagged:

  • Conditions can do several things. For an object type it filters the list of picked objects. The actions of an object type will run for each instance.

    For example

    Sprite: big=1
    — sprite: set size to 25x25

    Is equivalent to:

    // fist pick sprites
    Var Sprite = list of all sprite instances
    Var Picked = [] // empty list
    For (var i=0; i<sprite.length; i+=1)
    __if(sprite[i].big==1)
    ____picked.append(sprite[i])
    For (var i=0; i<picked.length; i+=1)
    __picked[i].setSize(25,25)

    And oddly enough adding a for each after the condition will do the same thing.

  • Try Construct 3

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

    Try Now Construct 3 users don't see these ads
  • Thanks ROJOhound, I think I'm starting to get it.

    Is there a difference between For Each mySprite and Pick All mySprite? (I know the latter allows me to check PickedCount...)

  • Probably best to read up on picking and the event system works. The manual and tutorials can help. Also what has helped me is experimenting with the events to pin down what specific conditions do and whatnot.

    Generally events work with lists of object instances.

    Conditions, such as comparing instance variables, will filter down the picked instances.

    Actions will then run for each of the picked instances.

    Expressions will reference the currently picked instance, or if more than one is picked it will pick the first picked instance.

    “For each” will pick one instance from the picked instances one by one. That’s why sprite.pickedcount=1 after a “for each sprite”.

    “Pick all” will pick all the instances again. That’s why after a “pick all sprite” the pickedcount=count.

    At least that’s some basics. I’m unable to effectively cover all cases. It also doesn’t help that some conditions do something else entirely.

  • I've never figured it out either. I for each whenever I am in doubt. I am not sure what the overhead is.

    The problem gets more dangerous since it just takes the first one if you don't for each. For example if you are looking at "on path found" for a sprite you may easily think it is just one sprite. In fact it could be more than one sprite so you always have to for each it anyway.

    yours

    winkr7

  • Here's an example where it makes a difference:

    In event 2 the value of forEach equals the number of Sprites that are isSprite.

    In event 3 the event will only trigger once and noForEach equals 1 (assuming that there is at least one Sprite that is isSprite).

    So in general if you want to repeat actions that are based on the number of instances that don't directly affect the instances themselves then using for each is the way to go.

  • Action and conditions do an internal for each over each picked instance of the object type the Action or Condition is on. (i.e. system is only one instance, so using the create object system action will only ever create one object, while Sprite spawn object will spawn multiple objects if multiple Sprites are picked).

    There are outliers like system picking conditions, which again do an internal for each over picked instances of the object type selected in that picking condition.

    If you add an event sheet for each the whole event is repeated for each picked instance, with only one of those instances picked.

    (this may be a bit of a simplification of the actual engine internals, but thinking about it this way holds true)

  • In most cases, you don’t need For Each because the engine automatically loops through instances. For example,

    Enemy: Set HP to random(50,100)

    This will assign random HP values to each enemy instance, not the same value to all.

    However, you do need For Each if you specifically need to process each instance separately. For example:

    For Each Enemy → Call Function DealDamage(Enemy.UID)

    Without For Each, the function would only be called once for the first enemy instance.

    Another interesting fact I only recently learned: when multiple objects with multiple instances are referenced in the same event, the engine tries to process them in pairs.

    For instance:

    Enemy: Set size to Box.size

    This works fine if there’s only one Enemy or one Box. But if there are multiple instances of both, the engine will match them like this:

    • Enemy(0) → Box(0)
    • Enemy(1) → Box(1)
    • Enemy(2) → Box(2)

    Once it runs out of Box instances, it loops back to the beginning, meaning Enemy(15) might get Box(0)’s size.

    To avoid this, you’d need a For Each loop or another way to pick the correct instances.

    .

    Here’s a more obscure example of the same issue. Since the "On Timer" event picks both Tree instances, the engine compares Leaf.treeID with Tree.ID in pairs. As a result, some of the leaves are not picked.

    A "For Each Tree" loop is needed here:

  • I make a quick example to demonstrate the use of For-each.

    You will observe that the "red box" all have the same angle.

    When you operate directly on the multiple instance object, you don't need to use For-each.

    Sprite: Set Angle to random(360)

    But if you want to use function/action for multiple instance, you need to use For-each.

    If not, although each object will be executed function, but the results of their execution are the same.

    Sprite: Call Function RotateAction(random(360))

    So, you need to use a For-each to make each sprite to rotate a different Angle.

    For Each Sprite → Call Function RotateAction(random(360))

  • Since you asked when to use them.. I'll make it as simple as possible from my POV.

    "Pick all" -> "mySprite.big = 1" OR simply "mySprite.big = 1"

    - will do exactly what you want... which is to pick and do something to the sprite. It is faster when you have lots of the same objects and have to do something to them... except it won't give you the UID or loopindex for each sprite in case you need it for something.

    "for each" -> "mySprite.big = 1" OR "mySprite.big = 1" -> "for each" (which is more optimised by adding the for each after the pick) will actually loop on all the picked object and you will have all the properties of the sprite since construct is processing each 1 of them, For e.g mySprite.UID, mySprite.instanceVariable, the loopindex, etc

    The "pick" is faster from what I tested and I use it everytime for simple actions whereas I use the "for each" when I have to make use of their properties like the UIDs or the loopindex for some purposes; this is suitable when I need to call a function for each 1 of the sprite and make use of their properties there.

  • The way I see it, a For Each condition simply copies/repeats the event once for each instance of the object, while picking that instance per each copy.

    If you don't use For Each, all instances are picked by default (notwithstanding other conditions).

    This generally comes up when an instance refers to itself in an expression or variable.

    However, as Dop mentioned above, there are several "crutches" where Construct tries to be smart about it, by picking/referring to the correct instance in expressions automatically without a for each. So a lot of times you can get away without using it.

  • dop2000

    Actually it does more than match them by pairs

    For example

    Enemy: set width to box.width

    Becomes:

    for(let n=0; n<enemy.pickedCount; n+=1)
    — enemy[n].width = box[n%box.pickedCount].width

    So yeah if the count of both is the same they are paired. But if there are more enemies than boxes then it will loop over the boxes again.

    Anyways related to the original question. For each is needed whenever you want to do things to each object individually when more than one object is picked. That’s the easiest rule of thumb to me. Although you can get away without a for each in some cases.

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