Delay in While Loop

Hello,

I would like to know if there is a way to add a delay while a while loop is running. I tried adding a delay sensor and inside of the while loop and i wrote: if cont.sensors[‘delay’].positive: and the I continued the script. Whenever I do this Blender just crashes. Please help me. :frowning:

Don’t use a while loop


for i in range(numOfPartsToProccessThisFrame) :
     Data = list[own['index']] 
     DoStuff(data) 
     own['index'] +=1
     if len(list) < own['index'] :
         own['index'] =0
   

Im sorry but I do not understand what to do with that. This is the code that doesn’t work:

while number < 21:
    if cont.sensors['delay'].positive:
         This is where stuff happens
         number = number + 1
          

So if the number is less than 21 i want it to continuously execute the code 20 times with a delay in between the executions using a delay sensor. Im sorry if i just do not get your code because Im kind of a noob at python but can you just explain it to me a little better :slight_smile:

While does not mean over time, while is all in 1 frame:

Delay duration 28-----python (do stuff 1 time)

Sounds more like what you need

well, since i dont know what the application for the code is, try this, its simpler:

## Requires Game Object to have a property "Timer" as type "Integer" and a property "Count" as type "Integer" ##

import bge

ctrl = bge.logic.getCurrentController()
owner = ctrl.owner

COUNT = 20 # in frames
DELAY = 5 # in frames

if owner["Count"] <= COUNT: # the sign can be just "<" if you want to exclude the 20th
    owner["Timer"] += 1

    if owner["Timer"] >= DELAY:
        ## DO STUFF ##
        owner["Count"] += 1
        owner["Timer"] = 0


connect the script to an “Always” sensor set to True pulse (first little button with dots). now you can set the properties to debug(little “I” next to property) and see the numbers change. make sure debug and framerate/profiler are on in the render settings.

loops dont work in the bge since each frame need to finish the code. a while loop that doesnt exit will get caught running that frame forever, appearing as a crash. for loops are better, but its best to avoid these where possible, since it might take a little time, causing stutters in framerate.

There are some times when it’s useful to use while. But usually it’s best to steer clear of it.
If you do use “while” it’s best to have an escape clause.

I usually incluse an “error” property which is +1 every loop. The while statement can then say:

error=0

while condition and error <1000:

That way if the main condition isn’t met, for example there’s a bug in your code, the while statement won’t get stuck in an infinite loop, forcing you to ctr-alt-delete your way out.

That way if the main condition isn’t met, for example there’s a bug in your code, the while statement won’t get stuck in an infinite loop, forcing you to ctr-alt-delete your way out.

Some other ways to escape that may be interesting:

  • In Linux, you can just go to Blender’s terminal (assuming you’re running it from one) and hit ctrl+C, this will raise an exception and kill the while loop. If you’re unlucky, it will just plunge back into the while loop on the next frame, but if alternate between hitting the escape button and killing the python script you can often avoid having to kill blender. (This is useful when you have unsaved work).
  • Or if you tweak the python exception hook you can get it to automatically quit the game engine on a python exception (eg ctrl+C). I do this for most of my projects…
  • Or you could bind the sigint to exit bge, so most errors behave the normal way (BGE keeps running), but a ctrl+C exits.

The end two options assume you don’t put big try-except statements through your code. If you are, you should probably reconsider why your code is generating exceptions. <rant>For some reasons, throwing and catching exceptions is considered ‘pythonic’, but in my mind, exceptions should be reserved for when there is unexpected behaviour. Pretty much: if you can check first, do, and don’t be afraid to throw exceptions yourself.</rant>

It is the opposite, you catch expected errors.

Unexpected errors are errors you do not catch = your code is not ready to deal with them.

In other words: When you catch an error it means the error is expected behavior of the surrounded code. The surrounding code knows how to deal with it.

Finally it comes to the same point: do not be afraid to throw exceptions when your code does not follow the “good case”. It is sort of a return value too.

Throwing exceptions is sin!!

As many have mentioned, all your python scripts run in 1 frame, so all while loops run in 1 frame. To “loop”, you should use either a delay sensor (execute script every X ticks) or a timer (execute script every X seconds).

For the latter use, an always sensor that executes the python script every tick, but if X time has not passed then return from the script immediately.

i find it rather amusing that everyone is throwing around complex concepts in a thread where the poster is clearly not looking for technical things.

Yeah honestly. It is not hard to explain a while loop. When using a while loop, you are not looping in real time, over the course of many frames. You are looping all within only one frame. that is why the game freezes, because BGE is waiting for the loop to finish before going on to the next frame. If you use a tighter while loop, only restricting it to a few loops, or use a for loop, you will be better off.

Wow, thanks everyone for the responses. I ended up finding a way just by avoiding the while loop because I realize that it is not the right use of it. I ended up using an if statement with some more variables and I got it working. :smiley:

I think people are trying to explain when you actually would use a while loop, and problems to look out for if you do.
When I first started out with python in blender the tutorial I read said “Don’t use while loop”.
That’s not very helpful.

yeah I avoid while at all costs. (hangup is no fun)

I would say:

“avoid while [without end] at all costs”

I do not use while that often because of the potential endless looping. This is more a style thing rather than really needed.

The recommendation is: avoid long running python code.

A loop is just something that can easily stack up processing costs, but it is a really good construct to deal with repeating operations.

While can be very useful in some cases. You shouldn’t avoid it just because it can cause an infinite loop, but you should avoid it when it’s not the correct thing to use.

When working with a stack while is important. You want to keep going until you’ve processed the entire stack (which might grow while the loop is working). A good example of this is a flood fill:


def fill(canvas, mouse_position):
    current = mouse_position
    stack = [current]

    while stack:                 
        x, y = stack.pop()
        current_tile = current_dict.get((x, y))
        if current_tile.empty:
             current_tile.empty = False
             search_array = [(-1, 0), (0, -1), (1, 0), (0, 1)]   
             for n in search_array:
                 nx, ny = n
                 stack.append((x + nx, y + ny))

mouse_position = (5, 12)
fill(canvas, mouse_position)

In all cases you should make sure that either there is a natural limit to the stack size (in a flood fill it would be the edge of the canvas) or add an counter to stop the loop if it goes on too long.
Recursion in python is a kind of stack and while loop:



def add_to_list(number_list, current):
         current += 1
     if current &lt; 1000:
         if current%2==0: # if the number is even, keep it
             number_list.append(current)
         add_to_list(number_list, current)

number_list = []
add_to_list(number_list, 0)

There are other uses for the while loop, such as solving a problem (packing rectangles in to a grid, maze navigation, etc…) through brute force.
Most often it is used where you can’t use a for loop because the contents of the list can change during the operation.

If the list remains the same then the best practice is to use a for loop. If you want the operation to carry on over several ticks then you should use an index counter or pop items from the start of the list.