[BGE Module] Cycle statement improve (Execution of one istruction per frame, etc..)

To take a pause from a script i can’t find how to solve a bug, i created this little module.
Since it happened i needed a clean and easy way to do a cycle that iterates only one element per frame, i created a module that helps doing this.(this is for blender 2.5)
In addition, my “Simple_For” statement support iterators and generators as well as strings and lists :wink:
In the attached blend there is a script with 4 tests and the module, and here the module alone

class EachFrame:
    '''A class that manages the python's cycling statement, executing the code one time per each time the class is called'''
    
    class For:
        '''This class iterates over the item of the object_to_cycle one item per call, and run the function_to_run *ALWAYS* passing the just iterated item as first argument '''
        #storing_global_class is a "workaround" so that i don't have to import bge. When you call this script, always set storing_global_class as bge( unless you know what you are doing while you set it differently)
        def __init__(self, storing_global_class, name, object_to_cycle, function_to_run):
            #set an unique name to use for storing the data in the bge as global vars
            self._storingC = storing_global_class
            self._label = str(hash(str(name)))
            self._cycleobj = object_to_cycle
            self._func = function_to_run
            #if is the first time the class in called, it sets all it needs
            if not hasattr(self._storingC, self._label):
                setattr(self._storingC, self._label, 0)
        
        def execute(self, *args):
            #retrieve how many cycle have been already processed
            step = getattr(self._storingC, self._label)
            #retrieve the next object to cycle
            if step < len(self._cycleobj):
                s = (self._cycleobj)[step]
                #exectute the function with his arguments
                self._func(s, *args)
                setattr(self._storingC, self._label, step+1)
                return(step)
            else:
                return None
    
    class Simple_For:
        '''This class does the some thing the for statement does, but it can cycle even generators and iterator, and it cycle on element per call. It returns the cycled element'''
        def __init__(self, storing_global_class, name, object_to_cycle):
            self._storingC = storing_global_class
            self._label = str(hash(str(name)))
            self._cycleobj = object_to_cycle
            if not hasattr(self._storingC, self._label):
                setattr(self._storingC, self._label, 0)
        
        def execute(self):
            #retrieve how many cycle have been already processed
            step = getattr(self._storingC, self._label)
            #retrieve the next object to cycle
            if isinstance(self._cycleobj, str) or isinstance(self._cycleobj, list) or isinstance(self._cycleobj, range):
                if step < len(self._cycleobj):
                    setattr(self._storingC, self._label, step+1)
                    return (self._cycleobj)[step]
                else:
                    return None
            else:
                try:
                    for index in range(step-1):
                        (self._cycleobj).next()
                    setattr(self._storingC, self._label, step+1)
                    return (self._cycleobj).next()
                except StopIteration:
                    return None
    
    class Strict_While:
        '''This class will execute the function_to_run for each time it is called, but *ONLY* if the condition is true and have always been true(if you call this function one time with a false condition, then even if the next time the condition is true the function wont be executed'''
        #storing_global_class is a "workaround" so that i don't have to import bge. When you call this script, always set storing_global_class as bge( unless you know what you are doing while you set it differently)
        def __init__(self, storing_global_class, name, condition, function_to_run):
            self._storingC = storing_global_class
            self._label = str(hash(name))
            self._condition = condition
            self._func = function_to_run
            if not hasattr(self._storingC, self._label):
                setattr(self._storingC, self._label, True)
        
        def execute(self, *args):
            never_been_false = getattr(self._storingC, self._label)
            #controll if the condition is true and have never been false
            if self._condition and never_been_false:
                self._func(*args)
            else:
                setattr(self._storingC, self._label, False)
    
    class While:
        '''This class execute the function each time it is called if the condition is true'''
        def __init__(self, storing_global_class, name, condition, function_to_run):
            self._storingC = storing_global_class
            self._label = str(hash(name))
            self._condition = condition
            self._func = function_to_run
        
        def execute(self, *args):
            #controll if the condition is true and then execute
            if self._condition:
                self._func(*args)

With this, if you want to run do

for obj in objlist:
 myfunc(obj, myargs)

but go trough the list one object per frame, you can


-------------------------             -----------------------------
-----Always sensor-------     --->   ----Python Controller----
-True tick enabled at 0--            -------script:text----------

text:


function = EachFrame.For(bge, "my function", objlist, myfunc)
function.execute(myargs)

or

 function = EachFrame.Simple_For(bge, "my function", objlist)
iterated_object = function.execute()
myfunc(iterated_object, myargs)

and it will do the work for you :wink:
Hope it can help you

Ps: if the module will be appreciate i can write a little more documentation, there are few things that at first time can give a little problems (for example if you use the EachFrame.For class, the function you call must have as first parameter the iterate object :slight_smile:

Attachments

Each Frame Module.blend (64.4 KB)

If I understand you correctly:

Rather then process all items of a list in one frame,
frame 1: A, B, C, D

this method let you process you one item when the controller is executed. The next execution it processes the next item and so on.
frame 1: A
frame 2: B
frame 3: C
frame 4: D

Is that right?

Exactly!
The next item is processed when you call the execute method.
So you can put it into condition statements, and call it only in particular cases, or you can call it multiple times in a frame.
I think this can give a bit more freedom without caring too much of the management of the thing.

Using this in the bge script i’m working on, i needed to add a few methods
Both for For and Simple_For i added restart_counter() and set_counter_to(counter) methods
I also changed the privacy of some variables (now accessible from the class)


class EachFrame:
    '''A class that manages the python's cycling statement, executing the code one time per each time the class is called'''
    
    class For:
        '''This class iterates over the item of the object_to_cycle one item per call, and run the function_to_run *ALWAYS* passing the just iterated item as first argument '''
        #storing_global_class is a "workaround" so that i don't have to import bge. When you call this script, always set storing_global_class as bge( unless you know what you are doing while you set it differently)
        def __init__(self, storing_global_class, name, object_to_cycle, function_to_run):
            #set an unique name to use for storing the data in the bge as global vars
            self._storingC = storing_global_class
            self._label = str(hash(str(name)))
            self.object_to_cycle = object_to_cycle
            self.function_to_run = function_to_run
            #if is the first time the class in called, it sets all it needs
            if not hasattr(self._storingC, self._label):
                setattr(self._storingC, self._label, 0)
        
        def execute(self, *args):
            #retrieve how many cycle have been already processed
            step = getattr(self._storingC, self._label)
            #retrieve the next object to cycle
            if step < len(self.object_to_cycle):
                s = (self.object_to_cycle)[step]
                #exectute the function with his arguments
                self.function_to_run(s, *args)
                setattr(self._storingC, self._label, step+1)
                return(step)
            else:
                return None
        
        def restart_count(self):
            setattr(self._storingC, self._label, 0)
        
        def set_counter_to(self, counter):
            setattr(self._storingC, self._label, counter)
    
    class Simple_For:
        '''This class does the some thing the for statement does, but it can cycle even generators and iterator, and it cycle on element per call. It returns the cycled element'''
        def __init__(self, storing_global_class, name, object_to_cycle):
            self._storingC = storing_global_class
            self._label = str(hash(str(name)))
            self.object_to_cycle = object_to_cycle
            if not hasattr(self._storingC, self._label):
                setattr(self._storingC, self._label, 0)
        
        def execute(self):
            #retrieve how many cycle have been already processed
            step = getattr(self._storingC, self._label)
            #retrieve the next object to cycle
            if isinstance(self.object_to_cycle, str) or isinstance(self.object_to_cycle, list) or isinstance(self.object_to_cycle, range):
                if step < len(self.object_to_cycle):
                    setattr(self._storingC, self._label, step+1)
                    return (self.object_to_cycle)[step]
                else:
                    return None
            else:
                try:
                    for index in range(step-1):
                        (self.object_to_cycle).next()
                    setattr(self._storingC, self._label, step+1)
                    return (self.object_to_cycle).next()
                except StopIteration:
                    return None
        
        def restart_count(self):
            setattr(self._storingC, self._label, 0)
        
        def set_counter_to(self, counter):
            setattr(self._storingC, self._label, counter)
    
    class Strict_While:
        '''This class will execute the function_to_run for each time it is called, but *ONLY* if the condition is true and have always been true(if you call this function one time with a false condition, then even if the next time the condition is true the function wont be executed'''
        #storing_global_class is a "workaround" so that i don't have to import bge. When you call this script, always set storing_global_class as bge( unless you know what you are doing while you set it differently)
        def __init__(self, storing_global_class, name, condition, function_to_run):
            self._storingC = storing_global_class
            self._label = str(hash(name))
            self._condition = condition
            self.function_to_run = function_to_run
            if not hasattr(self._storingC, self._label):
                setattr(self._storingC, self._label, True)
        
        def execute(self, *args):
            never_been_false = getattr(self._storingC, self._label)
            #controll if the condition is true and have never been false
            if self._condition and never_been_false:
                self.function_to_run(*args)
            else:
                setattr(self._storingC, self._label, False)
    
    class While:
        '''This class execute the function each time it is called if the condition is true'''
        def __init__(self, storing_global_class, name, condition, function_to_run):
            self._storingC = storing_global_class
            self._label = str(hash(name))
            self._condition = condition
            self.function_to_run = function_to_run
        
        def execute(self, *args):
            #controll if the condition is true and then execute
            if self._condition:
                self.function_to_run(*args)