I like using modules instead of running the whole script at once (even though this still technically does, more on that later). This way, I can split different actions into different methods, and keep them all organized on a per-script basis. You don’t even need a main() method, this just cherry-picks particular methods to run.
You run scripts modularly by choosing “module” from the dropdown in the Python controller, and manually typing in the module name. (The script name needs to end in .py for this to work.) For instance, I have a “player” script, which holds all of the functions and methods pertaining to controlling the player object. If I want to run the “walking()” method, I’d type “player.walking” into the module field. Now, that controller will only run that one section of that script.
(I think there are some tech-side downsides to this approach, but I’m not knowledgable enough about them to go into detail.)
Also, when writing systems that rely on player input, it’s REALLY important to think about dynamic modularity (IE “allowing the player to change keybinds”). I fixed this by using an external file that I load into any script that relies on player input, and reading that for the player’s stored keybinds.
Okay, let me try to put this all into an example format, in case I’m not making sense. I want my player to walk. But later, I’ll want my player to do other things as well, such as jump, take damage, use a weapon, etc. So I make a new script file and call it “player.py”. I write it like this:
## This is all global definition stuff that I'd want to use in every module.
key = bge.logic.keyboard
mouse = bge.logic.mouse
sce = bge.logic.getCurrentScene()
obl = sce.objects
## This is where I load that external keybinds file, which I named "controls.json".
filePath = bge.logic.expandPath("//files/")
ctrl = json.load(open(filePath+"controls.json", "r"))
## I use "cont" as the call tag here because the script's controller is always sent to the method as a variable. "cont" is the current controller.
own = cont.owner
sen = cont.sensors
act = cont.actuators
## And of course, these are the particular keybinds. This is a script for a first-person game, so I'll be setting my controls up to strafe left and right.
key_fw = key.events[getattr(bge.events, ctrl['fw'])] == 2
key_bk = key.events[getattr(bge.events, ctrl['bk'])] == 2
key_lf = key.events[getattr(bge.events, ctrl['lf'])] == 2
key_rt = key.events[getattr(bge.events, ctrl['rt'])] == 2
## Everything from here to the end is just stuff to make the walking work.
act_walk = act['walk']
var_speed = 0.12
var_x = 0.0
var_y = 0.0
# Get inputs
if key_fw or key_bk:
var_y = var_speed
var_y = -var_speed
if key_lf or key_rt:
var_x = -var_speed
var_x = var_speed
# Start walking
## Oh yeah, I have a character motion actuator attached to this script. The object's physics type is "character" so the "character motion" type will work here. I named that actuator "walk", by the way.
act_walk.dLoc = [var_x, var_y, 0]
I plug everything in that’s necessary, such as the “always” sensor and the “active” Boolean property set to true, and it should work. If I want to add anything new to this script, I can just jot it into the method here. If I want to add a new function to the player, such as shooting or entering a vehicle, I’d just add another method, like this:
I’m sure other people have much better systems than I do. I kind of… really suck at making games, despite having been using Blender for like 7 and a half years.