(Possibly) Useful Python State Functions

Hi everyone,

I helped Cray out awhile back with some somewhat-more-user-friendly Python functions to play with states. Some of this post is copy-pasta-ed. Check that thread for a more detailed explaination of the state bitmask.

Basically, these functions simplify setting states through Python, and remove the need to have to create the state bitmask yourself.

Now for the functions (explanation afterwards)


import math

def copy_state(obj, states):
    i = 0
    for state in states:    
        k = 1
        k = k << (state - 1)
        i = i | k
    obj.state = i

def inv_state(obj, states):
    i = obj.state
    for state in states:    
        k = 1
        k = k << (state - 1)
        i = i ^ k
    obj.state = i

def add_state(obj, states):
    i = obj.state
    for state in states:    
        k = 1
        k = k << (state - 1)
        i = i | k
    obj.state = i

def sub_state(obj, states):
    i = obj.state
    for state in states:
        p = int(math.pow(2, state-1))
        if p & i == p:
            i -= p
    obj.state = i

def isActive(obj, states):
    for state in states:
        p = int(math.pow(2, state-1))
        if p & obj.state != state:
            return False
    return True

These functions basically recreate the things that the State Actuator does. They take 2 arguments, obj and states.

obj is an object (more specifically a KX_GameObject) and it’s the kind of object that you get from cont.owner or from scene.objects[‘bla’]. This is the object whose state you want to manipulate.
states is a list of states that you want the function to apply to obj. It’s of the form [1,5,9]. You can put any number of states in, but all of the numbers should be in between 1 and 32.

Now for a description of the functions:

copy_state(obj, states) - This function sets obj’s state to have only the states indicated in the states variable active. For example:


own = GameLogic.getCurrentController().owner
copy_state(own, [1,2,4,6])

States 1, 2, 4, and 6 would all now be active, and all other states will be deactivated.

inv_state(obj, states) - This function inverts the states indicated on obj (if the state indicated is active, it’ll be deactivated. If the state indicated is inactive, it’ll be set to active).


own = GameLogic.getCurrentController().owner
copy_state(own, [1,2,4,6])
inv_state(own, [1,4,8])

After this code is run, states 2, 6, and 8 will be active. States 1 and 4 were already active, so they were deactivated, and state 8 wasn’t active, so it was activated.

add_state(obj, states) - This function activates the desired states while also keeping all currently activated states enabled.


own = GameLogic.getCurrentController().owner
copy_state(own, [1,2,4,6])
add_state(own, [3,5,6])

States 1, 2, 3, 4, 5, and 6 would all now be active. Note that it didn’t matter that 6 was already active… no errors occured, the state just continued to be active.

sub_state(obj, states) - This function deactivates the desired states and doesn’t modify other states.


own = GameLogic.getCurrentController().owner
copy_state(own, [1,2,4,6])
sub_state(own, [1,4,8,20])

States 2 and 6 would still be active, and all other states will be deactivated. 1 and 4 were removed, and 8 and 20 are still deactivated.

isActive(obj, states) - This function checks to see if ALL of the states indicated by the states variable are active. If any of them are inactive, this returns False, if all of them are active, this returns True.


own = GameLogic.getCurrentController().owner
copy_state(own, [1,2,4])
if isActive(own, [1,4]):
    print "States 1 and 4 are active"  
if isActive(own, [1,6]
    print "States 1 and 6 are active"
else:
    print "Either state 1 or 6 are inactive" 

This will print “States 1 and 4 are active” and then “Either state 1 or 6 are inactive”

You can specify individual states by isActive(obj, [1]).

Note that the state lists don’t have to be in any particular order. You can do [1,5,9] or [5,9,1] or [9,1,5] and it’ll have the same results.

To include these in your scripts, put them either in a file (maybe call it states.py) in the same directory of your blend file, or in a script inside of your blend file. Whether or not it’s in an external file or inside of the blend file, you need to give it a .py extension. Otherwise the import command won’t know where to look.

You can then, in your own scripts, do either of the following:


import states

#bla bla rest of code

states.copy_state(own, [2])

OR


from states import *

#bla bla rest of code

copy_state(own, [2])

I hope that these are useful! Let me know if you have problems with them.

-Sam

own = GameLogic.getCurrentController().owner
copy_state(own, [1,2,4,6])
if isActive(own, [1,4]): print “States 1 and 4 are active”
if isActive(own, [1,6]): print “States 1 and 6 are active”
else: print “Either state 1 or 6 are inactive”

This will print “States 1 and 4 are active” and then “Either state 1 or 6 are inactive”

That doesn’t look right… you just declared both states 1 and 6 to be active, and then you’re saying that one of them is inactive.

Anyways, neat little function. I tried to do somethign similar a while back, although mine was a lot messier code.

Whoooops. You’re right, I fixed it. The function is fine, but my example was incorrect.

-Sam

This is very useful, Thanks!

One problem, the isActive function doesn’t work correctly.
For example if states 1, 2 and 3 are active, then obj.state = 7 (0111)
ANDing any number below 7 to 7 and you get the same number.
I’ve been trying to fix it for a while now, but no luck.

And here’s a small improvement that allows you to enter a single integer.

def copyState(obj, states):
    i = 0    
    try:
        for state in states:
            k = 1
            k = k << (state - 1)
            i = i | k
    except:
        k = 1
        k = k << (states - 1)
        i = i | k
        
    obj.state = i

I also have a suggestion that you make the functions like copyState() instead of copy_state() to be more consistent
with other stuff like setParent() or getCurrentController()
But that doesn’t really matter.

Edit:
I don’t know how you missed this, I just copied it from one of your other functions.
works now.:slight_smile:

def isActive(obj, states):
    for state in states:
        p = int(math.pow(2, state-1))
        if p & obj.state != p:
            return False            
    return True

Thanks

Ha, you’re right about isActive! I’d fixed it in my blend file, but forgot to update. Thanks for catching it!

That’s a cool fix to allow for single integers. You can, of course, just do [i] with i being any number, but it’s nice to allow for both a list and a single state. I assume that when you try and use the in function that it throws the exception?

You can always change the functions TO copyState() instead of copy_state()… I’ve just been writing in Pygame a lot lately, and that’s the convention that they use, so I got used to it :).

I’m sure that there are plenty of improvements to be had. I’ll update the main list when I have time and when I get more suggestions!

-Sam

Since you released it (two or three weeks ago?) I’ve been using it a lot in the next version of the template.
thank you :slight_smile:
I’m glad I asked for some help at that time, that was useful.