Organizing files in large, multi-user projects

Alright guys it’s time for that topic again. Here’s some previous threads for reference:

But it’s all wrong. There… doesn’t seem to be a magic bullet. Unless you don’t mind using a strict naming convention and/or cramming everything in the same folder. Or not using module mode, which can dramatically hurt performance.

Let me go ahead and shut down some of the better answers.

Ony my BGMC project i had a “Models” folder, where the player model blend was in. There were also some other blends in there.
I don’t know what you mean by saying that you can’t move an object after it’s linked. For me it works. What Blender version are you using?

I’d recommend creating folders for each type of your assets. On all my projects i have these folders:

-Textures
-NPCs
-Player Objects
-Props
-Audio

On each of these folders i also have a “scripts” folder, which contains all the scripts for each of the assets. Organization is more about the way you find more useful to find all your assets.

That breaks due to one of the lesser-known BGE quirks: Linking a file (correction: but not libloading) will add that folder to the python path, potentially causing conflicts with identically-named scripts in different folders.

I simply use LibLoad on all files in a models folder, and then use empties as spawn points.
Levels are in a different folder, and loaded more specifically.

This makes it really hard to visualize level design, but otherwise it’s pretty much the same as what I first noted - cramming all assets in the same folder.

What Yo Frankie does ( and me too ) is that you have your assets in a blend file. Then you create groups of these objects, and link the groups in the level file.
You can then move and duplicate the group instances in the level file.
See that for more information : http://www.yofrankie.org/tutorial-library-linking/

Same issue with naming conflicts. At least using module mode. Perhaps that’s the answer… link all text files to your blend and never use module mode. But… performance!

Is there something I’m missing? Does anyone have insight on how we could patch this without a major overhaul?

EDIT
Taking some inspiration from BPR’s maddening banter, I suppose a person could have one script that performs all loading, then throw on a text property containing the python import path so people aren’t fighting over a central database file. Kinda weird though…

Need fresh eyes on this. Pls help

WE HAVE SOLVED THE ANCIENT RIDDLE

After some intense brain work with WKK in discord-

/ships/xwing.blend
/ships/xwing.py

Call the module controller in xwing.blend with:

ships.xwing.main

If you linked the asset, be sure to clean up the python path to avoid conflicts down the road.

import bge
import sys

sys.path.remove(bge.logic.expandPath('//ships'))

Then link to some random empty from the root directory so it gets added to the python path, enabling you to link/libload/play directly. Thanks to a post from sdfgeoff for the inspiration on that one.

Perhaps there’s a better method, but this is totally the best thing I’ve seen so far.

One disadvantage of that method is that it “ties” your whole project together. If you want to rename a folder, you have to rename a dozen paths in your python scripts.
Other than that, I like the sound of that method.

After many years of fumbling around, I discovered that making a game is actually making two separate things:

  • Making an “core” to play your game on
  • Making characters and levels and environments to populate your game.

Why is this important? Because it means that you never need an xwing.py. The Xwing is “simply” a “Spaceship” object with specific parameters. THe spaceship behaviour is defined as part of the game core. The parameters can be read from a json file in the same directory of the Xwing. Something like:


core/ships: (the only thing needed in the python path)
  - fighters.py  (probably python packages actually)
  - cruisers.py
  - battleships.py

ships:
  - xwing.blend
  - xwing.json {"type": "fighter", "speed":15, "manoeuvrability":5, "energymax":150}
  - deathstar.blend
  - deathstar.json {"type":"battleship", "drive":"warp"}

There is never any custom behaviour in a character, environment or level. Or if there is, it is “configured engine behaviour” eg: using sandboxed arithmetic functions as parameter values.
Essentially you teach BGE how a spaceships should fly, and then you can create a thousand spaceships, all with different graphical models, engine models and aerodynamic models.

However, this requires a good understanding of the game when writing the “core” and is highly susceptible to feature bloat ravaging your file formats.

Advantages of this system:

  • No namespace conflicts
  • All similar objects behave consistantly as they share the same underlying model
  • Can allow custom ship addons and mods without it affecting the core physics/feeling/balance of the game (assuming well designed interfaces)
  • People don’t need to understand python to create levels/players etc.
  • Separates behaviour from data - something which OOP is terrible at understanding.

Disadvantages:

  • Configuration ability is limited by the config file interface
  • Requires more complete abstractions
  • Easy to accidentally make god classes

This is how it’s been done in things like flightgear (uses xml for config) and many other games. In practice, it has worked fairly well for me.

In my vision xwing.py stores the configuration while inheriting from a generic ship class. Basically the same result except minor differences + the name conflict problem.

But I didn’t think about renaming folders… Updating scripts is a non-issue (only the one path-cleaning script) but all those python controllers in nested blend files? That’s even worse.

Overall in this specific case (star wars) json configuration should work great. All ships generally inherit the same behaviour (I think).

But… is there a nice way to use that method without breaking the group-linking workflow?

(double post woo!)
Let me use a more tangible example, this thing:

The current structure is rather horrible. All re-usable assets are dumped in one folder, all scenes dumped in another. Plus a bunch of other stupid design decisions. But TLDR it’s a generic LEGO game. All characters have similar functionality but each vehicle works quite differently.

What I’m feelin is a hybrid of posts #2 and #3. In this project group linking is very, very important for level design. Also designers (who are not necessarily programmers) should be able to create simple puzzles with logic bricks.

So here’s what I’m thinking right now. Figure on using the blend library addon modded to support multiple folders.

/core/buildable.py
/core/character.py
/core/trigger.py
/core/vehicle.py
/core/__init__.py

/characters/chief.blend
/characters/chief.py
/characters/elite.blend
/characters/elite.py

/triggers/button.blend
/triggers/button.py
/triggers/switch.blend
/triggers/switch.py

/vehicles/scorpion.blend
/vehicles/scorpion.py
/vehicles/warthog.blend
/vehicles/warthog.py

/levels/sandbox.blend

The vehicles seem to benefit from having their own script, since a generic “wheeled vehicle” class would still only be used once. The parent class only defines common logic like input and enter/exit. warthog.py isn’t put in core because:

  1. It’s needed to initialize the object (group linking workflow)
  2. It’s only used by warthog.blend and looks pretty
  3. If a modder was to add their own vehicle with unique mechanics, I wouldn’t want them polluting /core/.

Characters on the other hand could certainly use json config, but I’ve opted to use scripts anyway for consistency + simplicity.

These linkable objects would only have a single sensor + python controller that runs once. They won’t be using pulse due to another unexpected quirk that I won’t detail here.

Feedback? (theorycrafting is half the fun I swear)

edit: only the root folder will be in the python path, so no conflicts unless someone calls their script “core.py”

theorycrafting is half the fun I swear

Yup, and then you try it out and three months later you hate yourself for the abomination you made.

I would still make a wheeled-vehicle setup. What happens when someone wants to add a mongoose? Other than that, I agree with [most of] what you say. +1 for the not running scripts always. There are multiple reasons why you probably don’t want to.

Hmm. If you’re using UPBGE, you can use the components system to make the vehicles customizable without requiring a py file or a JSON file…

Oh, another useful hint:
Make it so that levels start the entire game. This means you don’t get caught up navigating the menu several thousands times. This can be done quite easily if you libload the “core” of the game, but link the levels. This approach also means that starting a level is easy - simply use the “start game” actuator. Sounds backwards, but in practice it works really really well.

THAT is so accurate lmao! It seems each time the abomination gets a bit nicer… but it’s still an abomination nonetheless. This game has already seen one major rewrite. Can’t wait to start on the 2nd.

I would still make a wheeled-vehicle setup. What happens when someone wants to add a mongoose? Other than that, I agree with [most of] what you say. +1 for the not running scripts always. There are multiple reasons why you probably don’t want to.

Hmm yes… For now I’ll make the assumption that we’re sticking to Halo 1 assets. A nice bonus with this project is that the scope is already well-defined, so it doesn’t need too much future-proofing. Of course I’ll regret this later when someone comes along that wants to expand into different games. Probably won’t happen though.

Hmm. If you’re using UPBGE, you can use the components system to make the vehicles customizable without requiring a py file or a JSON file…

I’d really like to use UPBGE. Performance freakin rocked. But I had to revert due to broken IK. And now I’m all paranoid about the component feature auto-running malicious code on open. A step in the right direction for sure though.

Oh, another useful hint:
Make it so that levels start the entire game. This means you don’t get caught up navigating the menu several thousands times. This can be done quite easily if you libload the “core” of the game, but link the levels. This approach also means that starting a level is easy - simply use the “start game” actuator. Sounds backwards, but in practice it works really really well.

Oh definitely! Got that going on right now, though I’m libloading a bit less than I should…

Thanks for the feedback!

EDIT
Put in action. Here’s the resulting boilerplate for each character:

from core import Character

class Chief(Character):
    config = Character.config.copy()
    config['hp'] = 4
    config['shield'] = 'spartan'
    config['parts'] = 'chief-parts'

def register(cont):
    owner = cont.owner
    if not 'component' in owner:
        owner['component'] = Chief(owner)

If not for the group workflow, json would have looked like:

{
"hp": 4,
"shield": "spartan",
"parts": "chief-parts",
"object": "chief"
}

I don’t really understand the “core” system ?

Do you mean that unless we play some kind of “main.blend” the other blend files would uniquely be assets ?

Either way, when developing bgez, I went around the idea that we could write a whole package that would get loaded (I was actually importing user logic as a Python module/package) and were we would define classes. The classes would then be fuzzy linked to game objects in game.

For instance, you could have a blend file storing a character model / armature, and anywhere else a script being loaded through the user logic package that would describe the behaviour. Right now I realize that there are some problems with the current implementation, but in retrospective it would be perfect if we could define a behaviour and bind it to a symbol (say “ships.battleship”) and then in the blend file storing the actual asset have a game property pointing to this symbol (like bgez.model = “ships.battleship”).

Other mecanisms I played around with were to link by name: problem is when you libload multiple assets, you can end up with duplicates, and no real way of accessing the difference between the two… I mean just having a field specifying from which blend got libloaded the gameobject would solve the issue. Bad luck: its not this way AFAIK (wonder how hard it would be to do, I could give it a try far later myself).

Anyway, I really believe there is something to do using this fuzzy linking. Because it also means that we could easily replace logic for X or Y reason. Just replace the binded class to a symbol by another.

Then the project architecture can be as anyone would want it: Assets may point to the symbol that defines their logic, and the logic just registers itself to symbols.

At game launch I was even considering parcouring every inactive object to then prepare the logic classes so that we could directly spawn objects doing something like “LogicClass.spawn()”. Because even if the object is inactive we can still access the game properties ! Problem : if there is a gameobject name duplicate then almost no way to know who comes from where, but that can be bypassed using specific game properties to identify stuff (adding the author as a game prop maybe).

Fuzzy logic/asset linking ?

So the core thing is a python package containing most of the game logic. Or at least the re-usable stuff. It might make more sense to put those scripts alongside their respective assets where applicable. Brain just hit a wall so I may circle back to this later. (EDIT: Brain restored. Core should have base classes and other things that aren’t tied to specific assets. And it probably doesn’t need to have all of the base classes. I shall modify my setup.)

Taking in your ideas: Doing the property thing would eliminate a lot of my per-object boilerplate. It actually sounds quite attractive. The only hangup afaik is getting add-object actuators to work properly, which would be a requirement for this specific project. Seems like I have three options:

  1. Spawn proxy objects which call a script (fast but more effort, sorta defeats the purpose)
  2. Check the scene for new objects every frame (slow but less effort)
  3. Hack the source so stuff happens when calling that actuator (would rather not)

Taking two steps back: How could one improve on a “standard” workflow, that which is commonly understood by average Blender/BGE users. At this point the main problem seems to be name conflicts from libloaded files. In Godot that is achieved by specifying the full path to the asset. In BGE we have to enforce a naming convention. The one true fix lies in re-factoring parts of the engine, but that’s beyond my intended scope.

So… it sounds like this game is gonna get a big fancy naming convention for libloaded assets. Everything else is totally clean.

ok, so what about components that are the base core mechanics that you attach to the vehicles after libload?

https://pythonapi.upbge.org/bge.types.KX_PythonComponent.html

this way you can have universal component models in the core, and when libloading grab values to set the base values?

libLoad -> attach componets and set listeners [players] to control bind,

on spawn enemy do the same and bind him to a ai controller?

The Core can be more than just code. The core is everything that is common to every level in the entire game. It is everything that needs to be present all the time. In many cases this means it doesn’t contain assets, but sometimes it does. In one project I worked on I was simulating a robot. All of the robot’s manipulators were part of the core because they were all available in all the levels all the time. And there was exactly one robot with a well defined set of manipulators. Along with the behaviour of those tools, things like scoring, loading, GUI’s and so on are also part of the core. The core should also include an API to allow levels to query it/pass information to it. This can include things like bringing up on-screen messages, changing the player character, changing who’s turn it is etc.

Anyway, I really believe there is something to do using this fuzzy linking. Because it also means that we could easily replace logic for X or Y reason. Just replace the binded class to a symbol by another.

Hmm. Sounds nice to me in theory, but I can’t quite see how you things that switchable. Assuming sane interface sizes and no god-objects, then code is frequently quite tightly bound to assets, particularly at the lower levels. Sure, you could replace the behavioural “brain” of the system, but you still need something to abstract the control from the motion of the objects.

when you libload multiple assets, you can end up with duplicates, and no real way of accessing the difference between the two

At this point the main problem seems to be name conflicts from libloaded files

Ahh, only if you don’t do some book-keeping while loading. In some of the larger systems I developed, I implemented a class for loading blends, and when you called the loading function, it kept track of what objects were added. This means that you could look up objects by the blend file they were stored in. It solved a lot of problems, and wasn’t particularly challenging to make. The trivial implementation only works if you do sequential loading at game start - which does give you a nice loading screen!


Side note:
Classes and objects aren’t the end-goal of programming. Classes have strong coupling between behaviour and data. This is great for stateful systems as it allows you to keep the size of a stateful system small and well defined, but if you do everything OOP you frequently end up with a huge number of interfaces. These are fun to play with and design, but frequently mean a lot of boilerplate and not actually getting anything done. Other programming styles also work very well, in some cases better than OOP. The reason OOP is so prevalent is that it is challenging to integrate OOP (self contained state) with non OOP (external state) code.
Fun story: I once did rather poorly in an embedded systems course because I didn’t use OOP to implement a real-time-filtering system. There was no state as it was just a filtering pipeline. Data comes in, and goes through a series of filters before ending up in an envelope detect and displaying a bunch of numbers on-screen. It took me a fraction of the amount of code to do it functional based, but I got marked down because … I couldn’t draw a UML diagram of it. I’m sure someone is going to say “but you could implement the filters as classes” and sure, you could. But why? Each takes input data, does some operations and generates output data. Sounds like a function to me.

I do not think that you can solve the naming conflict issue without defining your own naming conventions for your parts of the project. I guess you need to invest quite some thoughts how to organize your dependencies.

  • When each component provides it’s own dependencies you get use-as-is components. But you might easily get duplicates and version conflicts.

  • When you place everything in a “shared” area, you might loose track to what depends on what, but dependency and naming conflicts are easier to see and to resolve.

Nevertheless your components should be designed to avoid naming conflicts to other components. That can be done by qualifying names. E.g. placing python modules in packages (“myComponent.myComponentCode.py”), adding prefixes to python scripts (“myComponent_myComponentScript”), adding prefixes to group and object names (“MyComponent:Group”, MyComponent:Cube") even other things will benefit from such naming convention (e.g. action names, texture names).

Obviously when you create two “Car” components you need to think about what the difference is and how to express that with two different names e.g. “Racecar”, “Truck”. These names need to be used inside the components to avoid “Car:Group” in both groups)

So much feedback! Awesome

That is VERY ideal. However - for this specific example (generic lego game) it’s not an option due to other issues with UPBGE. But that sort of component setup will be great on a complex project where behaviours are frequently mixed.

Hm ok. I assume that means watching for changes in KX_Scene.objectsInactive between loads, then indexing the reference (not name) by some unique value in a dict, probably involving the file path. A bit hacky but totally do-able. Or is there a nicer way?

As for the purpose of the core folder: For me the “main goal” is to have something that’s optimal for level designers and casual observers, with the secondary goal being modularity. Hence why assets are split into their own topical folders. Logic could really go either way, so I guess it’s a matter of preference. Personally I prefer keeping logic next to its associated asset.

Kinda with you here. Partially because I feel like the default API should be used as-intended as much as possible, rather than abstracting common tasks like addObject. Projects are much easier to learn / come back to when everything is using “standard” bge conventions. Of course in many cases it’s more practical to create an abtraction.

For my specific example (generic lego game with multiple contributors that disappear for months at a time) - I would try really hard to stay with common bge conventions. The folder.file.asset naming scheme sounds pretty good. Moving/renaming could be an issue on larger projects, but that’s what bpy is for.

Next: Linkable prefabs that also spawn dynamically.
In the lego example it’s collectable studs that are placed by hand, but also spawned as rewards for defeating enemies.

Do you:

  1. LibLoad anyway, which wastes memory by creating duplicates
  2. Ensure the prefab is instantiated at least once, adding boilerplate to each level
  3. Something else?

Possible answers:
-Create a single “master” group for all such assets and throw that on a non-visible layer.
-Parse the scene and libload anything that’s missing, complicating the project with some kind of object database.
-Don’t link these. Use an ugly proxy object instead. (WYSIWYG is strongly preferred)

Thoughts?
Btw I’ve fired up a document with some more cohesive notes. Comments welcome.

That was asking for troubles lol

But its like how in Java anonymous functions are created: its a Runnable implementation with just a Run method. Or “OOP gone wrong” (joking). Still avoiding Java today (more like didn’t find a project where Java would be interesting, not searching for one either :p)

You are right. Functionnal programming has been around for quite some time (like, almost before procedural with lambda calculus among other things). But today and in the context of game design, its more of a nuanced color:
GameObjects are stateful. They are a pack of data, OOP is required at least here. GameLogic as in progress in the campaign etc are rather stateful too. Now behaviour could be expressed as functionnal, where you can process GameObjects, filter them and whatnot.

But in the case of default behaviour OOP can be mixed to functionnal programming, using an object as a handler which you can predefine some behaviours and pass them using inheritance. It is about getting the right mix (everything is object at the end of the day using Python…)

Now as with everything there is often some ways to do it right, and a lot of ways to make it wrong. So as long as your system makes sense and is abstract enough (good function flow / OOP core maybe) then its all good.

Well, was thinking about parsing the inactive object list, but I was looking for things to avoid it… Glad to know you got it to work, it means that it still has potential despite the whacky feel to it lol.
(Also doing sequential async libload is the way to go ! run each libload in async mode, but start the loading one after another, not all at once. You may have done it this way already tho right ?)

Well, there are certain things to respect when trying to swap logic, but the first thing is that the systems I implemented are not run by an object’s controller. They are run by a “main” empty controller (having a general onLogicTick callback list would have avoided this god empty object but thats the way it is…). Assets are assets and the core of the game is tasked with managing the logic for the different game objects / entities / whatever fits your design.

Then because logic execution is centralized and disconnected from the actual assets in their blend file, there is no naming conflict anymore. Just symbols like “LogicClass is linked to <entity.logic>”. And then you can either define inside the asset in a gameprop the symbol that would represent the logic it will use (“bgez.model=<entity.logic>”), or you could try to apply some logic scheme to any game object you would want to spawn: this indeed requires you to ensure to some extend that the objects you want to pilot are compatible with the calls made by the logic.

Because the whole user logic (python scripts and classes) are loaded at game launch, after the initialisation the core knows every component and just have to query the objects based on the startup procedures defined by the users and apply the models following who registered to which symbol.

bgez implementation details: The symbols I am currently using are the qualified class/method names (<module.submodule.file.class>). I am using metaclassing to auto register derived classes by the users (thank you Python OOP and meta mecanisms). Could use decorators too (functionnal or class decorators). But using more arbitrary symbols as mentionned before could allow to replace logic easily based on the level being run, but with the same assets. Maybe just define a base class “BaseClassForEnemies” and then implement different child classes that do things differently. Just bind the desired class at level start and the logic gets applied.

When it comes to interfaces, it could be a composition of methods to execute actions on certain events, or more finely tuned user methods for his logic. I don’t have a perfect idea there. (I like the handler idea where we could use an instance and register it to different events ? Just registering its methods ?)

To be fair I was looking at Inversion of Control designs and this symbol story would be the simpliest concept to extract from it without going full nuts in IoC and keeping things fun, in my opinion.

This whole “behaviours (classes/functions) get loaded at launch and then can be hotswapped” avoid most conflicts other than “we got the choice between multiple implementation for symbols”, where a config file could point to the behaviour to use in the context. Or a dropped asset (could call it a module) could be auto loaded in the same fashion as the rest of the project and know where are its own scripts and which classes it has defined (no conflicts).

I haven’t fully parsed the second part of WKnights’s post yet, but here are my initial thoughts on the simple things. The rest will have to wait until I have a little more time.

Create a single “master” group for all such assets and throw that on a non-visible layer.

Is what I chose in the past, and it worked just fine. As a bonus, it can also ensure that the python path is correct for when you start the game. (Assuming one of the blends that sources one of the objects in the master group is where the python path should be). You can also use this to link in additional scenes (eg HUD’s) simply by ensuring that there is a scene actuator referencing it on an object within the linked-in group. It doesn’t have to be connected to anything, but if one exists, then linking that one group can bring in everything​.

Well, was thinking about parsing the inactive object list, but I was looking for things to avoid it… Glad to know you got it to work, it means that it still has potential despite the whacky feel to it lol.
(Also doing sequential async libload is the way to go ! run each libload in async mode, but start the loading one after another, not all at once. You may have done it this way already tho right ?)

If you already have a class to handle adding/freeing libloads (which you probably do if you’re using async’s), then adding this is probably around ten lines of code. I chuck a game property in all the objects before the load starts (“LIBLOADED”), and then when it ends, look for ones that don’t have that game property. I check both the active objects and the inactive ones, and raise an exception if there are active objects. This helps catch errors at loading time rather than when they fail to addObject later.

I actually don’t bother with async loading, well, ever. It’s a somewhat weak implementation and isn’t very async. Sure, loading the blend is asyc, but merging it into the scene is not. And unfortunately, guess where shader compilation falls. You got it: the merging process. This means that an async libload running on integrated cards blocks almost as much as a normal libload. All it adds is a whole bunch more boilerplate (and at one point it’s stability was questionable, though I think it’s been fine for the past few blender versions).

Now as with everything there is often some ways to do it right, and a lot of ways to make it wrong. So as long as your system makes sense and is abstract enough (good function flow / OOP core maybe) then its all good.

Truth.

having a general onLogicTick callback list would have avoided this god empty object but thats the way it is…

The magic of the GPL license means that you can take it from my companies UPBGE repo, where I asked Benoit to add it.