Better to use one long script or separate in a func.py

I have a few python questions… I have a python script for movement (movement.py) that is ~170 lines, and will be getting much larger very quickly as I add in different firing options and more key press options.

I have about 15-20 lines so far that are exact copies of code elsewhere in my script. I am considering creating another python script called movement_functions.py.

I would create functions for all of my repetitive code (also make it easier to read). My questions are as follows:

  1. Would performance be better if it was all in one script?
  2. Will the variables all have to be reset in the movement_functions.py, or will they manage to carry over? (IE own = cont.owner, etc)
  3. On the same vein as #2, will I have to reconnect every single logic block to my function script as well?
  1. It might be, since function calls do take some processing, but the difference should be incredibly minimal. I’m not 100% sure about it, though.

  2. You should set the variables when necessary in each function call from your common, movement_functions script. That way, each object can refer to the correct variables (i.e. each object’s controller and sensors). If you place them at the top of the script, but not in a function, they’ll be set when you import the module, which means that they will always refer to one object.

  3. I think you should import movement_functions at the top of your player script and call the function you need. That way, you don’t need to add another Python controller and reconnect the sensors.

General python rules are that following the DRY principle (don’t repeat yourself) common code should be a function or class.
It does add a little overhead, but you can negate this by writing it in C anyway.
Again, Solarlune makes equally relevant points.

Okay, that helped but I’m still having some issues…
From my main movement.py:

from bge import logic as GameLogiccont = GameLogic.getCurrentController()
own = cont.owner #getting the owner of the current controller
#getting our sensors
g1 = cont.sensors["g1"].positive #These 4 are our floor sensors
from move_functions import *
Main()

from move_functions.py

def Main():    print("IMPORTED")
    if g1:
        print("sensor found!")
    else:
        print("NOT FOUND")

However, I get the error “NameError: global name ‘g1’ is not defined”. If I readd all the sensors at the top of move_functions.py things break (no movement on my char)

Is there an easy way to get that working? Or should I just put all of the modules in movement.py (at the bottom maybe?). That way it still has the benefits of not repeating myself and the main program loop will still be nice?

Here’s my rules:

Separate different things into different scripts:
- Menu’s should be in a different module to players logic

Keep things together:
- Keep related scripts in the same module (ie different AI types in an AI script)

  • If functions are used only in one function make them sub-functions

    Make a module of common functions:
  • If you have several functions that every single script uses, then make a module so you can just import it. Like Agoose says, DRY.

The aim is to write for readibility. You only write it once, but you’ll read it a couple of times. Name functions properly, group things nicely, get ‘junk’ out of the way in a function, even if it does use a tad more processing power.

I’ll just run through how I’ve divided up my players logic, as I think it may be of interest.

Player object:
Always----
Mouse-----|----------PYTHON ‘everyFrame’ Script --------- Move actuator etc.
etc --------/

Pseudocode Script:


def everyFrame:
    movement(cont)
    weapons(cont)
    damage(cont)

def movement(cont):
    #Controllers is a module dedicated to input
    vec1 = controllers.getMouseMovement(cont)
    vec2 = controllers.getKeyboardMovement(cont)
    vec3 = controllers.getJoystickMovement(cont)

    finalVec = vec1+vec2+vec3
    #Then apply movement vec through servo motion

def damage(cont):
    damageSens = cont.sensors['Collision']
    #And so on

def weapons(cont):

    def lasers():
        '''laser-specific logic'''
    def plasma():
        '''plasma-specific logic'''

    weaponSelected = cont.owners['wepSel']
    mouseShoot, keyShoot, joyShoot = controllers.getShooting(cont)

    if mouseShoot == True or keyShoot == True or joyShoot == True
        
        if weaponSelected == 1:
             lasers()
        if weaponSelected == 2:
             plasma()

Everything in this script could be put into a single function. There are no sections of code called more than once, but I still used functions. I did this for readibility and debugging: if theirs a problem with the mouse input glitching, I know it will be in a single function in the controllers module. If the laser isn’t working, then I can find that immediately as well.
It will run slightly slower, but to me the tradeoff is worth it.

I assume writing in C is not going to be nearly as easy as writing in python? How do I even get started writing it in C? It doesn’t work the same way as python right? Although I suppose I barely know python now, maybe it is a good time to start writing it directly in C :smiley:

Well, you can use C to extend Python - http://docs.python.org/2/extending/extending.html
So if you have code that runs slowly, move it into a C/C++ extension. Don’t worry about it now,

Variables don’t exist across Python files (or modules). Pass the sensor, or the positive property, to the move_functions function (Main), or get it actually inside of the function with logic.getCurrentController():



#move_functions.py

def Main(cont):

     groundsensed = cont.sensors['g1'].positive

     print (groundsensed)

# movement.py

from bge import logic
import move_functions

cont = logic.getCurrentController()

move_functions.Main(cont)


It might be best to provide the controller as an argument, so that you can have more capabilities exposed (i.e. running a function on another object, rather than the one calling the function). As a side-note, I’ve found it best not to ‘from module import *’. It tended to give me inconsistency in my coding (i.e. using math.sin() in your main function, and just sin() everywhere else where I imported *).

Actually, provided that you set them, variables change across all python modules during runtime. So If you save over one it will be modified. Though, this is not always best practice for a number of reasons

Thanks guys, that should be enough to get me started!

I have a question.

I have several scripts, that each consists of several related modules. But only a part of all the modules work with constant values. Since it would be practical to have all constant values in one block I put them on top of every script, not in a separate module. But wouldn’t this cause needless processing while running the modules that don’t work with that constant values?

I thought to avoid this by making a class of all constant values in a separate script and importing this in the other scripts. But come to think of it, it’s still the same thing isn’t it? If you import a class, is it then processed at the start, or only when you make call to its arguments?

I guess this may not be easy to follow, so I give a little example to show you what I mean. And maybe someone has some suggestions for me, because I really want my coding to be optimal. Not only because I like that, but also because I want to code better.

FIRST OPTION:

### Translation.py ###

rot = 0.1
mov = 0.5

def turn(cont):

    cont.owner.applyRotation([0.0, 0.0, rot], True)

def move(cont):

    cont.owner.applyMovement([0.0, mov, 0.0], True)

def something(cont):

    pass

SECOND OPTION:

### Translation.py ###

from Data import par

def turn(cont):

    cont.owner.applyRotation([0.0, 0.0, par.rot], True)

def move(cont):

    cont.owner.applyMovement([0.0, par.mov, 0.0], True)

def something(cont):

    pass


### Data.py ###

class Parameters:

    rot = 0.1
    mov = 0.5

par = Parameters()

I think you are misunderstanding what a module is. (and what a class is)
A module: a text file containing some python functions.

When you run a module from the game engine bricks, only the function is run when it’s called. The rest of the module only runs initially.
This means that option 1 is perfectly valid.

What I would do in this case is have those variables in a dictionary:

parameters = {'rot':0.1, 'mov':0.5}  #Make a dictionary
print(parameters['rot'])  #Print an element
parameters['speed'] = 3   #Create a new element

A class is not a storage place for variables. It is an object. This makes option two not quite correct. It is one possible use, but it may cause some problems if you accidentally add some brackets.
Classes are like rocket ships. They are similar, but all different. Let’s have a look at this class:

class rocketShip:
    def runEngine(self):
        self.fuel -= self.engines
    engines = None
    wings = None
    fuel = 0

Now we make some rocket ships:

rocket1 = rocketShip()
rocket2 = rocketShip()

rocket1.engines = 2
rocket1.wings = 0
rocket1.fuel = 30

rocket2.engines = 1
rocket2.wings = 4
rocket2.fuel = 5

rocket1.runEngine()
rocket2.runEngine()

print(rocket1.fuel, rocket2.fuel)

You see how now we can have two different rockets. WIth your parameters class, if you accidentally add brackets, you may find that you no longer have consistent variables.

Also, just a note on code formatting, you should group functions with their titles:

def turn(cont):
    cont.owner.applyRotation(.....

def move(cont):
    cont.owner.....

It’s more readible, as you can see where a function starts and ends. PEP008 is a good reference for python code formatting.

Thanks very much, sdfgeoff. I appreciate your remarks. Another question: I prefer to have all parameters in a separate text block, so every module would import these and use some of these parameters. Is readability in this case worth the extra processing time? And how would I import these?

Also, I was thinking, since all modules are on the same GameObject, would it be advisable to import these parameters at startup and assign these to properties? Would this be less consuming when I run the modules in comparison with the first option of my previous post. So what I mean is this:

def setParameters(cont):
    own = cont.owner
    own['parameters'] = {rot:0.1, mov:0.5}

def turn(cont):
    own = cont.owner
    rot = own['parameters']['rot']
    own.applyRotation([0.0, 0.0, rot], True)
    
def move(cont):
    own = cont.owner
    mov = own['parameters']['mov']
    own.applyMovement([0.0, mov, 0.0], True)

def something():
    print('in this module none of the parameters are used')

Definitively - yes.

As you wrote these are configuration parameters. You expect the user to change or at least verify them. So readability is top-priority.

A common method to do that is to use .properties or .ini files. Python provides a view libraries to deal with them. Nevertheless they might be a bit overhead for your purpose.

So you can go with a simple Python file as Python is pretty straightforward.

Example.py:


params = {
 "players name": "Robinson"
,"age": 18
...
}

you can simply import this module into your processing module:


import example

...
playerName = example.params.get("players name","unnamed")
...

this is pretty simple to do but has one drawback:
you create a dependency to the file name example.py.

This itself is not a big deal and matches your requirement (one file for all parameters).

If you want to be more flexible, you can can set up the module name via property and dynamically import the the module. See this http://technogeek.org/python-module.html.

Thanks very much, Monster. Regarding my second question of my previous post, would it be even better to put the library in a property at startup and then for every module get the needed parameters through that property?

@ Monster:

No, not at all. I understood everything. Your description on script local variables made clear I was thinking correctly about their relationship with the controller. I wasn’t sure if it would make a great difference on the performance to read parameters stored in properties of the controllers owner, or to read them from another script. My worry was that maybe this would have some global effect on the controllers owner, making it to large or something. But now I’m sure that it would be the best place to store static values, after initialization. Not only this is good for the performance, but also it is the right gateway by which you would update changes to that static values, by reinitializing them (in Game).

Thanks again for another lesson on scripting. I’m getting the hang of it.