int[] ints = new int[12];
int midPoint = ints.Length / 2;
int index = midPoint + 1;
do
{
// do stuff
index = (index + 1) % ints.Length;
}
while (index != midPoint);
A for loop can have more than one variable as long as you define the type outside of the loop. With that in mind we can have a separate variable tracking the conditional while the variable we want to be a certain range can just be initialized and modified. I wouldn’t necessarily call this a simpler approach though.
int i, j;
for (i = startIndex, j = 0; j != array.Length; i = (i + 1) % array.Length, j++)
Wow this actually perfect! It will take me a while to figure out the exact logic, but simple debug shows it does indeed wrap around nice and neat. Thank you very much. I think it’s simpler than while loops, I always manage to crash Unity with those.
I would say it even stronger: keep the loop control logic dead simple: one variable, ideally going from zero to max one number at a time.
Then do things at the top of the loop like use modulus (%) operator to get your values, pull up table entries, scale the stepping, etc.
YES there are exceptions, but for the simple “start from middle, go to just before middle” thing you specifically asked about, I would use the offset → modulus approach that mgear and Spiney posted.
All for() loops are just while() loops underneath. Really.
Here’s why your code locks up:
Unity will lock up 100% of the time EVERY millisecond your scripting code is running.
Nothing will render, no input will be processed, no Debug.Log() will come out, no GameObjects or transforms will appear to update.
Absolutely NOTHING will happen… until your code either:
returns from whatever function it is running
yields from whatever coroutine it is running
As long as your code is looping, Unity isn’t going to do even a single frame of change. Nothing.
No exceptions.
“Yield early, yield often, yield like your game depends on it… it does!” - Kurt Dekker
You can cause an infinite loop with a for too. I ended up creating a couple while testing that line and crashing the online code tester (https://try.dot.net/).
Here’s another common approach using a recursive method. While I’m not a fan of recursion I think this is easier to read than the for loop I provided earlier but partially because it’s not crammed into a single line. The catch with recursion is that it’s possible to run out of stack space and cause a crash that way.
void LoopFromIndex(int[] array, int start, int stop)
{
// Check if start index is out of bounds.
if (start < 0 || start >= array.Length)
return;
Console.WriteLine(start);
// Move to the next index, wrapping around if necessary.
int nextIndex = (start + 1) % array.Length;
// Stop recursion if we've reached the stop value.
if (start == stop)
return;
LoopFromIndex(array, nextIndex, stop);
}
I tested the method with 2 variables by @Ryiah and it worked perfectly for what I need. Now I have to admit I didn’t quite understand @mgear method, it seems to start with 0 (which isn’t what I need) and I don’t know enough of loops to figure it out.
The reason I don’t wanna do more complicated things is there are already 2 for loops and I don’t need more complications.
If anyone is interested in what I’m trying to do: imagine an inventory system in Stardew Valley (or Minecraft), where you can switch current active hotbar. When an item is added, it first searches for any slot with this particular item (1st loop), if there isn’t any it searches for an empty slot (2nd loop).
If the first hotbar is active then searching from 0 makes sense, however if another hotbar is active, it has to start searching from slots that belong to the current active hotbar.
Since method with 2 vars works and it’s basically a one line, I don’t see a problem with it.
In their version, the “i” variable is just used as a counter to loop through the size of the array. You would then use “k” as the index of the array within the loop.
That should probably read k = (k+1) % array.Length;. The use of the incrementor (++) simultaneously with an assignment is not appropriate. The assignment happens with k’s original value, after the increment happens, so no increment is kept. I didn’t notice that in @mgear 's code previously.
It’s returning the amount left over from the division of the left value by the right value.
Debug.Log(5 / 3); // returns 1 because 3 goes into 5 once
Debug.Log(5 % 3); // returns 2 because 5 divided by 3 leaves 2
Here’s a method version of it if you want to see the formula.
public int Modulo(int a, int b)
{
return a - ((a / b) * b);
}
Modulo’s primary advantage is being able to create a looping effect without a conditional like an if which is an optimization that in some cases can be significant.
for (int i = 0; i < 15; i++)
{
Debug.Log(i % 5);
}
I feel like the standard method is to compute the adjusted value. This would be the version with int i2=(i+6)%A.length inside the loop. That makes it easier to understand that there’s only 1 moving value, going through the array, and you’re just using it a little differently. Or put another way, we can quickly see that i2 isn’t just doing it’s own thing - it’s 100% based on the main loop variable.
Yes, that would be my approach as well. Computationally we’re doing the same thing anyways but we do not need to track the variable separately, so it can be a local variable. Instead of adding 1 each iteration and wrapping it around, we can simpy add the offset (6) to the current for loop variable and wrap it around to get our current shifted index.
I’ve also seen people using a foreach loop and tracking their own index in parallel. For me this is a complete anti-pattern as it’s not always guaranteed that however the foreach loop actually iterates, that it matches with the parallel tracked index.
That said, a struct enumerable could also solve this
public struct ShiftedIterator
{
public static ShiftedIterator Get(int aOffset, int aRange) => new ShiftedIterator(aOffset, aRange);
int m_Offset, m_Index, m_Range;
public ShiftedIterator GetEnumerator() => this;
public int Current => (m_Index + m_Offset) % m_Range;
public bool MoveNext() => ++m_Index < m_Range;
public ShiftedIterator(int aOffset, int aRange)
{
m_Offset = (aOffset<0)?(aRange+aOffset%aRange):aOffset;
m_Range = aRange;
m_Index = -1;
}
}
See it here in action (.NET Fiddle).
The offset can be any positive or negative “shift”. So at a shift of “-1” and a range / count of 10 it would iterate 9, 0, 1, 2, 3, 4, 5, 6, 7, 8. So the output will always be in the range 0 to range-1 but shifted & rotated by the given amount.
I was thinking about creating an extension method for integer, however it’s difficult to choose a good “direction” and name for the method. So should it be
array.Length.Iter(6)
or should it be
6.Iter(array.Length)
I guess the first one would make most sense, but choosing a meaningful name is kinda difficult in order to not be too verbose. So for the sake of clarity it’s probably best to not go there. Here something like a Rotate would make more sense
for(int i = 0; i < array.Length; i++)
{
int idx = i.Rotate(6, array.Length);
}
Though I don’t think that’s worth it when you could just do
for(int i = 0; i < array.Length; i++)
{
int idx = (i+6) % array.Length;
}
Yes, you can not as easily rotate in the negative direction, but I don’t think that’s required that frequently anyways
I can’t prove it, but I think the “foreach with hacked-in index” is from people who started with foreach, especially in some other language that may not even have a for(;;). They learn foreach for lists, while for weird stuff (like finding x^y), and anything else is rare. For the OP’s problem they might prefer to put the first 6 items on the end, then use a simple foreach.
Or maybe they’ve seen the trick with for (x,i) in addIndexs(L): (a foreach loop with an extra part to get the index), but they can’t find it in C#. They have to manually do int i=0; i++ and are like “geez, doesn’t anyone in C# ever need to use indexes? There’s no language support!”