1 big script or Several small scripts

I have seen a similar thread posted somewhere before but for some reason i cant find it. Heres my situation:

I have an enemy. Here are the things he does:

  1. Idles
  2. becomes alert
  3. dies

My question is…should I make 3 different python controllers for each individual script? Or should I “mash” the 3 scripts together and have the new script run on just one python controller? Which way is more optimized?

Depends on your Python ability.
I’d create methods to call from main() that control all of that, in one script.
If you don’t understand what I just said, use three different scripts.

Don’t take that as being rude. I wasn’t trying to be. :stuck_out_tongue:

ok. That leads me to another related question. Is there a specific reason for putting my method definitions and IF statements into a main()? I say this because right now as I have it, I just have my statements and methods “out in the open” that arent enclosed in a main function and it works fine. What are the benifits of enclosing my script in a main()? Is a reason for doing so because it prevents all of my peripheral scripts from running all the time, which saves computational power?

It’s cleaner to do it with a main(), and sometimes the Global code will run alongside (or even after) the methods. It’s just for ordering purposes, and to add an air of neatness.

One script ,Class and multiple functions(“def”). No need to make so many scripts ,it is also easier to maintain and edit variables inside one single script …rather than spreading them all over .

ok. so in order to get my whole enemy module to work then, all i would need to do is stick one if statement outside of the whole thing to call the main() right?

Check out this script


from bge import render, logic 
from mathutils import Vector # for easy editing of Vectors


distance = 1.5 # the distance of the ray


def castRay(cont):
    
    object = cont.owner
    
    # Create the lists
    objectProps = object.getPropertyNames()
    polyProperties =[]
    polyValues=[]
    polyIndex = []
    #polyRayCastId = [] | I didn't actually use this list
    
    # initiate x to 0
    x = 0 
    
    # Check the objects properties
    for prop in objectProps:
        
        # Look for Properties that have "FaceProperty" in them
        if "FaceProperty" in prop:
            polyValues.append(x)
            polyProperties.append(prop)
            # increment x
            x = x + 1
            #polyRayCastId.append(x) 
        
        # Look for Properties that have "FaceIndex" in them 
        if 'FaceIndex' in prop:
            polyIndex.append(prop)
    
    # if "polyData" is present in the object then this wont run
    if not 'polyData' in object:
        mesh = object.meshes[0]
        CalcMeshData(mesh) # I just moved these calculations to a function so it was easier to read the code
        
    transform = object.worldTransform
    polyData = object['polyData']
    hostID = object['hostID']
    
    # this loops through the indexes we collected earlier   
    for item in polyValues:
        # Check to see if the property needs to send data
        if object[polyProperties[item]] != "Empty" and object[polyProperties[item]] != None:
            
            # loops through the mesh data
            for data in polyData:
                id = int(data[0][-2:])
                if 'FaceIndex' in data:
                    
                    String = str(data)
                    String2 = int(String.split("_")[1])
                    p 
                
                #if the current FaceIndex is equal to the mesh Id then it casts a ray
                if object[polyIndex[item]] == id:
                    start = Vector(transform * data[1])
                    end = Vector(transform * (data[1] + data[2]))
                    render.drawLine(start, end, [255, 0, 0])
                    hitOb, _, _, hitPoly = object.rayCast(end, start, 0, '', 0, 0, 1)
                    
                    # if the ray hits another object,
                    # it gets the face id and gives it the data
                    if hitPoly:
                        hitMat = hitPoly.material_name
                        
                        if 'Sensor_' in hitMat:
                            hitID = int(hitMat.split("_")[1])                             
                            
                            hitOb['hostID'] = hitID
                            
                            if hitID < 10:
                                hitOb["FaceProperty_0%s" % (hitID)] = object[polyProperties[item]]
                            else:
                                hitOb["FaceProperty_%s" % (hitID)] = object[polyProperties[item]]
                            
                            object[polyProperties[item]] = "Empty"
                            object['hostID'] = 0
                            object.state = 1


# I just made this a function so I could read the code better                                 
def CalcMeshData(mesh):
    
    polyData = []
    
    # loops through all the polygons in the mesh
    for polyId in range(mesh.numPolygons):
        poly = mesh.getPolygon(polyId)
        polyMat = poly.material_name
        
        # is the polygons material name cantains "Sensor_"
        if 'Sensor_' in polyMat:
            matID = poly.material_id
            numVerts = poly.getNumVertex()
            coords = Vector((0, 0, 0))
            normalList = []
            totalNormal = Vector([0,0,0])
            
            # Loops through all the vertices in the polygon
            for vertIndex in range(numVerts):
                vert = mesh.getVertex(matID, vertIndex)
                coords += vert.XYZ
                normalList.append(vert.normal)
            
            # adds all the vertex normals together
            for n in normalList:
                totalNormal += n                              
            
            # averages out the normal and applies the distance
            polyNormal = (totalNormal / len(normalList)) * distance
            # averages out the position
            polyPos = coords / numVerts
            
            # adds the data to the list
            polyData.append([polyMat, polyPos, polyNormal])
    
    # assigns the list to a property on the object       
    logic.getCurrentController().owner['polyData'] = polyData

see the

def castRay(cont):

?

this is a callable function,


I prefer to have one module running on an object. The advantage is that you have less logic bricks which makes the logic more portable. If you wanted to make a different mesh have the same behavior as your current object, you just add 1 Always sensor and 1 Python controller and you are done. If you have them separate then you have to duplicate the logic bricks in the new object.

ok well i combined all of my modules together into a main(). However, my framerate seems to have droped since the change…is executing one long python script the reason why my game is now running slower?

It is not likely that is the cause of your frame rate problems. Combining the logic should be just adding some ‘if’ statements which will not cause a slowdown in your system.

You should turn on the profiler and see where the time is being spent in your frame.

  • Have you added any of the following sensors?
    • mouse over
    • radar
    • near
    • collision

Can you post your code?

I’m no real programmer so I’m not quite there yet to tell you what the best approach should be. I’ll give my impression though. Hope someone will correct me if I’m wrong. I think the way to organize things depends on weighing functionalities. By spreading the code over multiple scripts you get more transparency and enables you to recycle, which I found to be useful, especially when dealing with a lot of data. For example, by importing modules from a script with general functions I don’t have to have the same code multiple times. Also with libraries I found it useful to have them separately. In that case I don’t mind if it’s costing a little bit in performance (which I doubt will be much).

But for the rest you want the code to be as efficient as possible. And I think it’s more efficient to have just one script for one type of game object if you need access to all modules from a main function. But also avoid running a script more than necessary, because most of the time processing through Python will be more expensive than processing through logic bricks. For example, better to check a property with a Property Sensor to activate a script instead of checking a Property by running a script all the time. And better switch to other states when scripts have had their purpose. For example, when an enemy died and his body only serves as a decoration to the scene. So accessing all modules from a main function can be effective, but in some cases it’s better to access the modules of the same script through different controllers, making use of states.

“when an enemy died and his body only serves as a decoration to the scene”…lolol. But yea, I believe raco has point. But here is my latest situation:
I found out that the reason for my framerate drop was that I had a keyboard sensor with true pulse enabled which significantly lowered my framerate depending on how many of my enemies I spawned in the scene. I know this discussion has veered away from the name of this thread, but I have a new question. I believe Kastoria made a reference that certain sensors slow down your framerate more than others. Specificly, “mouse over”, “radar”, “near”, and “collision” sensors.
My question is is it good practice, in the situation of spawning multiple carbon copy enemies, to have as few of these sensors as possible running with so many instances of the enemy running at the same time? Especially when these sensors have true pulse enabled?
p.s. I will soon combine my now optimized scripts together into 1 working module and tell you guys if my problem has been resolved.

Yes, it is good practice to have as few sensors on your objects as you can. From a logic brick standpoint, it is easy to put a mouse-over sensor on every object that you want to detect a mouse hover on. However, lots of those sensors will take a lot of logic time so you are better off using a single, mouse-over-any sensor and having all the objects check that one. I’m not sure if it is possible to do that trick with just logic bricks.

Sometimes you cannot avoid it. If you need to test if an enemy had a collision with something in the environment, then there is no way to get around having a collision sensor on each guy.

the trick is having the sensors off when the enemy is far away, or move 1 sensor from enemy to enemy, and use it to set there properties (last know position of player etc)

like a ray sensor across a path, and if the player passes it, activate the enemy logic, and de-activate if the player crosses another ray, or door or?

basically, break up the scene :smiley:

have most of it not running until the player arives…

if you use script mode with one single block of text , you have to have one single controller that trigger the script:
you can have many sensor but only one controller python ,
using more controller and one single script is possible using statement of sensor as fake entry point but the script is read more times in the same frame and do a work useless in the best case

if you want use more controller and one big script you have to use modules

I suggest to avoid “main” as much as you can.

A) “main” is used as entry point when stating a pure Python application. As the BGE is no Python application there is no such entry point. You see it as you have to call main() from own code. In a real Python application you never do that.

B) “main” is a very generic name. It does not provide any information (except the information mentioned in A) which is irrelevant in the BGE). Imagine this as somebody is calling you “human”. This is right but not helpful. Better choose a name with a meaning: e.g. moveForward().

C) “main” needs to be called after defining this function. This forces the reader to scroll to the end of the code just to see how it begins. This is like reading a novel starting with the last page. So it is not really a good idea.

My recommendation:
Script mode - choose a name of the file that describes what the complete code does
Module mode - choose a name of the file that categorizes the callable methods of the module - choose names of the function that describe what the functions do.

Back to the real question:

One big module or a few modules:
This depends…

If the code belongs together it makes sense to place them into the same container (module/package)
If the code has no or weak relationship it makes sense to place them in separate containers.

As usually there is a large gray area in this topic. You need to decide by yourself.

Regarding sensors:
You can use the internal state system.
Only sensors connected to active states are evaluated. (This means the other sensors are not evaluated and do not eat processing time)
This is a good way to deactivate heavy sensors.

thanks for the detailed resoponses guys. So, I guess when it comes down to dividing a module or not, its just a matter of situational preference.