• Hey Guest! Ever feel like entering a Game Jam, but the time limit is always too much pressure? We get it... You lead a hectic life and dedicating 3 whole days to make a game just doesn't work for you! So, why not enter the GMC SLOW JAM? Take your time! Kick back and make your game over 4 months! Interested? Then just click here!

SOLVED For loops troubles

K00lman

Member
I am trying to make a clicker upgrade system that will loop through all actives upgrades and then print their upgrade text on them with this code:
GML:
for (var upgrade = 1; upgrade == array_length(upgrades); upgrades += 1){
    draw_text(upgrades[upgrade].x, upgrades[upgrade].y, upgrades[upgrade].name_text);
}
For some reason, this code never runs as when I check via the debugger it says that the upgrade variable is "Unable to evaluate". What am I doing wrong?
 

samspade

Member
I think he meant upgrades, Also, it should be upgrade < array_length not == assuming you have the variable upgrades (and it is an array) and the < change doesn't work, I would walk through it in the debugger to see what is going on.
 

K00lman

Member
I think he meant upgrades, Also, it should be upgrade < array_length not == assuming you have the variable upgrades (and it is an array) and the < change doesn't work, I would walk through it in the debugger to see what is going on.
When I stepped through the debugger for the first time it did not show any indication of what was going wrong other than the "Upgrade <Unable to evaluate>". After I made the change to a < it gives me an illegal array use error. Checking the debugger it gets to the draw_text this time but then crashes with the error there.
 

TheouAegis

Member
Do you mean to start the loop from one and not 0? If index zero has an upgrade, it will be skipped over.

Have you verified your upgrades array is set up and defined properly? I just want to make sure that is not an error referring to an improperly set up array. If anything, post the code that sets your array up in the beginning.
 

K00lman

Member
When I stepped through the debugger for the first time it did not show any indication of what was going wrong other than the "Upgrade <Unable to evaluate>". After I made the change to a < it gives me an illegal array use error. Checking the debugger it gets to the draw_text this time but then crashes with the error there.
Turns out I had forgotten to add the -1. Now it runs but does not draw the text. Looking through the debugger makes it clear that for some reason it never goes into the for loop. Putting a breakpoint on the for loop stops it, but putting one inside, it never stops.
 

Yal

🐧 *penguin noises*
GMC Elder
I am trying to make a clicker upgrade system that will loop through all actives upgrades and then print their upgrade text on them with this code:
GML:
for (var upgrade = 1; upgrade == array_length(upgrades); upgrades += 1){
    draw_text(upgrades[upgrade].x, upgrades[upgrade].y, upgrades[upgrade].name_text);
}
For some reason, this code never runs as when I check via the debugger it says that the upgrade variable is "Unable to evaluate". What am I doing wrong?
Since the "upgrade" variable is a var, it's temporary and only exists in the draw event. It'll not exist before the draw event runs, and will be gone after the draw event finishes, so it will never exist from the debugger's point of view.
 

K00lman

Member
Do you mean to start the loop from one and not 0? If index zero has an upgrade, it will be skipped over.

Have you verified your upgrades array is set up and defined properly? I just want to make sure that is not an error referring to an improperly set up array. If anything, post the code that sets your array up in the beginning.
GML:
upgrades[0] = 1;
upgrades = scr_newUpgrade(upgrades, instance_create_layer(0, 0, "Upgrades", obj_upgrade));
I mean to start it at 1 becasue I am using the first spot to indicate the first empty spot because I could not think of any better way to do it.
 

K00lman

Member
Since the "upgrade" variable is a var, it's temporary and only exists in the draw event. It'll not exist before the draw event runs, and will be gone after the draw event finishes, so it will never exist from the debugger's point of view.
I have removed the var and it still has the exact same problems.
 

Yal

🐧 *penguin noises*
GMC Elder
GML:
function scr_newUpgrade(upgrades, upgrade){
    upgrades[upgrades[0]] = upgrade;
    upgrades[0] += 1
    return upgrades
}
You can use array_length to get the length of the array (which is also the index of the next unused cell):
upgrades[array_length(upgrades)] = upgrade;

This means you can use the entire array for the same purpose (which makes it easier to avoid bugs)
 

K00lman

Member
You can use array_length to get the length of the array (which is also the index of the next unused cell):
upgrades[array_length(upgrades)] = upgrade;

This means you can use the entire array for the same purpose (which makes it easier to avoid bugs)
Yes, but I also need to be able to reuse a space once it gets freed up. If I were to just use the length when an old upgrade gets removed its spot will sit empty making everything less efficient. This way I can set that space to be the first free one.
 

Fielmann

Member
GML:
for (var upgrade = 1; upgrade == array_length(upgrades); upgrades += 1){
    draw_text(upgrades[upgrade].x, upgrades[upgrade].y, upgrades[upgrade].name_text);
}
upgrade == array_length(upgrades); means loop body will only run when upgrade equals the length of upgrades. You probably want upgrade <= array_length(upgrades); instead.
upgrades += 1 tries to add 1 to upgrades which is an array. You probably want upgrade += 1.
 

K00lman

Member
upgrade == array_length(upgrades); means loop body will only run when upgrade equals the length of upgrades. You probably want upgrade <= array_length(upgrades); instead.
upgrades += 1 tries to add 1 to upgrades which is an array. You probably want upgrade += 1.
Even after making both changes, it still does not draw the text.
 

Yal

🐧 *penguin noises*
GMC Elder
Yes, but I also need to be able to reuse a space once it gets freed up. If I were to just use the length when an old upgrade gets removed its spot will sit empty making everything less efficient. This way I can set that space to be the first free one.
There's so much wrong with this statement. You can at most keep track of one empty spot this way, what are you going to do if two upgrades get removed at once? Also, since you just increase the counter by 1, if you first remove an upgrade in the middle somewhere and then start adding new upgrades, you will overwrite existing upgrades instead of adding the new ones at the end.

It would be easier logic-wise to just loop through the entire array and check if there's any empty slots somewhere (e.g. undefined or -1 or whatever) and if so, put the new thing there.
 

K00lman

Member
There's so much wrong with this statement. You can at most keep track of one empty spot this way, what are you going to do if two upgrades get removed at once? Also, since you just increase the counter by 1, if you first remove an upgrade in the middle somewhere and then start adding new upgrades, you will overwrite existing upgrades instead of adding the new ones at the end.

It would be easier logic-wise to just loop through the entire array and check if there's any empty slots somewhere (e.g. undefined or -1 or whatever) and if so, put the new thing there.
I did not think of that, thanks.
 

Nidoking

Member
Even easier, use a ds_list - if you remove an upgrade from the list, the rest will slide to the left to fill in the gap, and you can just ds_list_add new upgrades.

Even with an array, you can manually shift things to fill gaps or just move the last entry in the array into the empty slot.
 

K00lman

Member
So I have changed my scr_newUpgrade code to:
Code:
function scr_newUpgrade(upgrades, upgrade){
    var location = array_length(upgrades);
    for (var locations = 0; locations > array_length(upgrades); locations += 1) {
        if (upgrades[locations] == 0){
            location = locations;
            break;
        }
    }
    upgrades[location] = upgrade;
    return upgrades
}
and I am having the same issues with this for loop as I was with the original for loop. Looking in the debugger the code runs just fine till it gets to one of these loops, then it just never runs the code inside, just skipping over it. What is happening?
 

samspade

Member
In this last case that is because the middle condition will never evaluate to true since you have set locations to 0 and then told the loop to run only if locations is greater than the length of the array (presumably a number greater than zero).

There are a number of other strange things about it. For example, if you've already got the length of upgrades saved in locations, I would use that rather than get the length again. Also, if you find an upgrade that is == to zero, why not immediately set it, rather than saving the position and then setting it?
 

FrostyCat

Redemption Seeker
You have the wrong idea about how for loops operate. They continue looping when the middle condition is true, not when it is false.

Memorize the basic book lines on for loops:
If you don't start learning how to count properly from 0, you'll keep screwing up like this.
  • With n entries starting from 0, the last index is n-1.
  • With n entries starting from 1, the last index is n.
Hence the standard forms to iterate n times using for:
Code:
// Zero-indexed (GML arrays, lists, grids)
for (var i = 0; i < n; i++)
for (var i = n-1; i >= 0; i--)

// One-indexed (GML strings)
for (var i = 1; i <= n; i++)
for (var i = n; i > 0; i--)
The last time I ever made the mistake you did was a decade ago. The only reason I didn't make it again since then is because I've memorized basic counting patterns like this.
 

K00lman

Member
You have the wrong idea about how for loops operate. They continue looping when the middle condition is true, not when it is false.

Memorize the basic book lines on for loops:
Thanks for this, I have only ever used python for loops and just assumed they worked the same way.
 
Top