Functions.Classes. Toggle functions.

Hello.
Got stuck with python a bit, how do i toggle between functions in a script?

 
import bge
 
class FOV:
cont = bge.logic.getCurrentController()
own = cont.owner
scene = bge.logic.getCurrentScene()
cam = scene.active_camera 
 
aim = cont.sensors['aim']
 
cam.lens = own['lens']
#own['lens'] = 18
 
if aim.positive:
FOVP()
 
else:
FOVM()
 
def FOVP(): 
 
own['lens'] = 20
 
 
 
 
def FOVM():
 
own['lens'] = 17

I know its all a mess, i am still at beggining, i just started with classes and multiple functions.

Thank you!

hard to read. Please use Code tags rather than quote tags :slight_smile:

Anyway you need to instantiate the class first.

e.g.


class MyClass(object):
  def myMethod(self):
    print ("myMethod")

def callableFunction(cont):
  myClass = cont.owner.get("myClass")
  if myClass is None:
    myClass = MyClass()
    cont.owner["myClass"]  = myClass 

  myClass.myMethod()

Thanks Moster, now i must understand how it works.

Basically, a class is a prototype of an object that you design, and you have to make a copy of it to actually use it (though you can access classes directly, if I recall). As an example:



class Bike(object):
     def __init__(self):
          self.wheels = 2
          self.motor = False

I just defined a Bike class. It has a wheels and a motor property. However, we need to actually create a unique bike - that’s where the object-oriented programming part of Python comes in. We create a new bike object by initializing an instance of the Bike class:


class Bike(object):
     def __init__(self):
          self.wheels = 2
          self.motor = False

mybike = Bike()

Now mybike is an instance of the Bike class, and changes to mybike doesn’t influence the Bike class. For example:


class Bike(object):
      def __init__(self):
           self.wheels = 2
           self.motor = False

mybike = Bike()
mybike.motor = True

hisbike = Bike()
hisbike.wheels = 4


We can edit a property of a unique instance of a class, and not have it influence other instances, as they’re all unique.

EDIT: For putting BGE Python into a class, it’s a bit different:



from bge import logic

class BGEObject():

     def __init__(self, cont):

          self.controller = cont
          self.obj = cont.owner

cont = logic.getCurrentController() 
bgeobj = BGEObject(cont) # You could also get the controller out of the logic module in the __init__() function itself.


But note that a class does nothing by itself - you have to actually instantiate and update the class, as can be seen above.

OK thanks solarlune, but I still don’t get it how do I make “sections” of scripts that run only if triggered.
DEF could be the thing that I need.
Like,let’s say I have the player which must have and idle state, running state etc.
A main DEF should activate other DEF (idle for example), and if it’s not positive anymore it should get back to main DEF and so on.

Hey BlendingBGE,

I think a finite state machine (FSM) might help you. Basically the idea behind a FSM you have a number of states (idle, running, jumping) and you have transitions between states. Transitions are like conditions such as “if in the idle state and the space bar is pressed, transfer to the jumping state”.

You can visualise a FSM as a flow map, e.g:

They are relatively easy to implement in python, the idea is you represent a state by an ID, such a string “idle”, “walking” and “jumping”. The actual state is a function, e.g:

def idle():
    # play idle animation
    pass

def walking():
    # catch keyboard input
    # apply force
    # play appropriate animation
    pass

def jumping():
    # apply upwards force if just entering state
    # remove/dampen controls
    pass

You want to run the current state each frame and check that if the conditions for any transitions are met then to switch the current state. Transitions require three things, a from state, a to state and the condition. Conditions can be done as functions also:

def if_wasd():
    keyboard = bge.logic.keyboard
    return (keyboard.events[bge.events.WKEY] == bge.logic.KX_INPUT_ACTIVE or
        keyboard.events[bge.events.AKEY] == bge.logic.KX_INPUT_ACTIVE or
        keyboard.events[bge.events.SKEY] == bge.logic.KX_INPUT_ACTIVE or
        keyboard.events[bge.events.DKEY] == bge.logic.KX_INPUT_ACTIVE)

ect

Heres a simple class to manage the states and transitions of an FSM:

class FiniteStateMachine:
    """Finite State Machine"""
    def __init__(self):
        self.states = {}  # A dictionary of states: identifier -> callable
        self.transitions = {}  # A dictionary of transistions: from state -> [to state, condition], ...]
        self.current_state = None  # Identifier of the current state

    def add_state(self, state, action=None):
        """Add another state
        state - identifier
        action - callable, to be run while state is active
        """
        self.states[state] = action
        self.transitions[state] = []

        # Make the new state the current state if its the first one added
        if self.current_state is None:
            self.current_state = state

    def add_transition(self, parent_state, child_state, condition):
        """ Add a transistion between states
        parent_state: the from state identifier
        child_state: the to state identifier
        condition: A callable which returns True or False
        """
        self.transitions[parent_state].append([child_state, condition])

    def main(self):
        for transition in self.transitions[self.current_state]:
            if transition[1]():
                self.current_state = transition[0]

        if callable(self.states[self.current_state]):
            self.states[self.current_state]()

You add states and transitions to the FSM using the add_state and add_transition methods. The main method checks all conditions on the current state and transitions states if needed, otherwise calls the function assigned to the current state.

To set up your FSM you would just do something like:

own = bge.logic.getCurrentController().owner

def init():
    fsm = FiniteStateMachine()
    fsm.add_state('idle', idle)
    fsm.add_state('walking', walking)
    fsm.add_state('jumping', jumping)

    fsm.add_transition('idle', 'walking', if_wasd)
    fsm.add_transition('idle', 'jumping', if_space)
    fsm.add_transition('walking', 'idle', lambda: not if_wasd())
    fsm.add_transition('walking', 'jumping', if_space)
    fsm.add_transition('jumping', 'idle', if_grounded)

    own['fsm'] = fsm


def main():
    own['fsm'].main()


if 'fsm' in own:
    init()
else:
    main()

Hey, thanks for the info about classes, I modified my A* script to use classes instead of lists, makes it much easier to follow what’s happening as the class properties are named.

So I can see:

node.parent

instead of:

node[3]

I’ll be using them a lot more in future, should make it easier to edit my code and to catch bugs.

Guys sorry,but still i dont get it.
How do i actually toggle functions?
All i really need to know is how i can toggle between multiple functions(def)

Yes, the easiest and simplest way would be to toggle a property on the gameobject, using an if else statement to execute function a else function b

Tried to do some.
Here is a code:

 
 
import bge
 
def main():
    cont = bge.logic.getCurrentController()
    own = cont.owner
    actu = cont.actuators['rt']
 
    if own['pp'] == 1:
        run()
 
def run():
 
    cont.activate(actu)
    own['pp'] = 0
    return main()
main()
 

If i run the game >

 cont is not defined...

I dont know how to get that stuff to be recogniezed in other functions…

I don’t see why you’re using functions. Just use an if / else statement for now. it takes far more code than neccesary.


import bge
 
def main():
    cont = bge.logic.getCurrentController()
    own  = cont.owner
    actu = cont.actuators['rt']
    
    def run():
        own['pp'] = 0
        cont.activate(actu)
        
    
    if own['pp'] == 1:
        run()
 
main()

anyway agree with agoose, the choose must be only to have a script more clean, not more dirty :stuck_out_tongue_winking_eye:


import bge
def main(cont):
    own  = cont.owner
    actu = cont.actuators['rt']
    
    if own['pp'] == 1:
        own['pp'] = 0
        cont.activate(actu)


@ Andrew-101

Thanks for that well crafted post. It’s nice to see someone put in the effort, and explain things in such detail (I would say the same for SolarLune - you guys are great).

However, I do have some qualms about the content. Specifically, I would question the need for an FSM class, and the additional level of (what I would perceive to be) needless complexity that is introduced.

From my perspective, the following approach is significantly less complex, and offers equal, if not higher benefits:


main = state_A

def state_A():
    # whatever state A should do

    if keyHit(events.BKEY):
        main = state_B
    elif keyHit(events.CKEY):
        main = state_C

def state_B():
    # whatever state B should do
    
    if keyHit(events.AKEY):
        main = state_A

def state_C():
    # whatever state C should do

    if keyHit(events.AKEY):
        main = state_A

main()

What do you think?

Well, in andrew’s case, He has taken his code from the Novus Terra source, and in an animation / movement context an FSM makes plenty of sense. However, in BlendingBGE’s case it is True that it isn’t neccesary to go to such effort.

No one is questioning the benefits of a Finite State Machine. Actually, if you look at my example code carefully, you should notice that it is in fact an implicit Finite State Machine.

Goran;

The purpose of the class is the remove the need for the conditional statements to control transitions. With a small 2 or 3 state FSM I think something like you code is appropriate however with something such as a complex movement system or AI it might be better to use the class to control it. It just depends on what you are trying to achieve really. Also, its nice to know my posts are appreciated :slight_smile:

re reads change the last bit about fsm to fsm class.

In principle (ignoring BGE specifics), you are right.
To program the logic for one player, functions (“def” statements) would be enough.

But, now, think of two players, each with its own state (idle, running, etc.).

This is where classes come in.

People are so used to classes, that they are used even for a single player (and you never know: maybe you will make it mulltiplayer later… and anyway, NPCs/monsters are some kind of “players” too!).

But in principle, you are right.

At the BGE you already get a new instance when you add a new game object with the same logic. If you use classes make sure not to create a singleton by accident ;). Otherwise all game objects would use the same class instance and you wonder about the mysterious results.

A Warning:

I strongly advice not to use this code.
It contains a potential endless recursion (cyclic function calls).
main() calls run()
run() calls main()

Even if this implementation goes not into the endless recursion this issue is hidden.

Use recursion only if you know what you are doing. Better use a function call hierarchy (the function calls create a tree without loops).

But that’s just replacing conditional statements with functions like if_wasd, which basically evaluate the same set of required conditions.

It doesn’t really seem like a better alternative.