“Why is the Blender Game Engine difficult to maintain?” This question is getting thrown around some more now (especially with talks of overhauling or replacing the BGE). I’d like to take some time to point out a few of the many issues I’ve stumbled across in the BGE. I will keep to mostly current issues, but I will occasionally talk about previous issues that have been fixed if I need to highlight a certain point. Also, be aware that this might get a bit technical.
Overview of BGE Code
In order to facilitate understanding some of the issues, it is first good to understand a little about how the game engine is organized. Inside Blender’s source code you’ll find the folder source/gameengine, which contains the following folders: BlenderRoutines, Converter, Expressions, GameLogic, GamePlayer, Ketsji, Network, Physics, SceneGraph, and VideoTexture. These loosely correspond to the various modules/libraries of the BGE. There are also various prefixes used as pseudo-namespaces. I will discuss these prefixes as I talk about the relevant folder. However, note how we’ve got files with prefixes in folders were they don’t really belong (a first hint that things are amiss).
Prefixes: BL, KX
BL files are files that are responsible for interacting with Blender code/data. The BlenderRoutines folder contains mostly code for the embedded player (the code that runs when you press P in the viewport).
Prefixes: BL, KX
This folder handles converting Blender data to BGE data. The BGE (for the most part) does not use any Blender data directly, however I have advocated for more direct use of Blender data in the past.
Here we have a lot of various data structures and utility classes (similar to Blender’s BLI code). The name Expressions comes from the various classes used to handle properties and how to make expressions with them (e.g., how to add two FloatValues together). Overall, I feel a lot of the code in this folder is the result of over-engineering. The main class for our Python wrapper (PyObjectPlus) also resides in this folder.
Initially this was meant to contain logic bricks. Any logic bricks that do not rely on other parts of the BGE are prefixed with SCA (sensor, controller, and actuator). Any logic bricks that require access to other parts of the engine (e.g., mesh data, etc.) are prefixed either KX or KX_SCA.
This folder contains two subfolders:
- common (prefix: GPC)
- ghost (prefix: GPG)
The common folder contains code that should be common to multiple game players. The ghost folder contains a game player based on ghost. It should be noted that the embedded player is not defined here, nor does it use any of the code in common. Instead, it duplicates code and resides in the earlier mentioned BlenderRoutines folder.
Prefixes: BL, KX, KX_SCA, KX_SG
Ketsji more or less contains the “core” of the BGE. However, this core is rather bloated and contains a lot of code that should probably be split into their own modules (e.g., navmeshes, audio, etc.). It can also be seen from the large list of prefixes that this folder has gotten a little out of hand.
This folder is for the BGE’s networking implementation. The only actual implementation is a loopback networking interface which is used for message sensors/actuators. In other words, another example of over-engineering: we shouldn’t have coded a networking interface until we actually had a need for one.
This folder contains three subfolders:
- Bullet (prefix: Ccd)
- common (prefix: PHY)
- Dummy (prefix: Dummy)
The common folder contains the interface for the physics subsystem. Dummy is just a empty implementation of the physics interface that does nothing. Bullet contains a physics implementation that makes use of the Bullet physics library.
Here resides the scenegraph, which handles tasks such as storing hierarchical transform data for nodes (game objects). Scenegraphs are also usually responsible for culling, but the BGE’s scenegraph does not. Instead, our KX_Scene class from Ketsji does. In addition, Bullet is also used for culling. So, this one task is scattered through out the codebase.
This contains the code for the VideoTexture Python module.
Examples of Problems
If the overview was not enough to make one already question the quality of the BGE codebase, lets look at some specific parts of the code. The following sections are examples of various issues in the BGE codebase (this list is not exhaustive).
Rendering Code has Broken Encapsulation
In general, our rasterizer is quite messy. Why not simply clean it up then? The problem we face is that we have rendering (i.g, OpenGL) code scattered all around the code base. For example here is a list of the earlier mentioned modules that contain OpenGL code:
- Rasterizer (good)
- BlenderRoutines (bad)
- GamePlayer (bad)
- Ketsji (bad)
- VideoTexture (bad)
The physics code also contained OpenGL code until recently. Why would a physics engine need to use OpenGL? You would probably guess for the “Physics Visualization” option, but that is being properly handled through the rasterizer interface. Instead, our physics code was using OpenGL in it’s view-frustum and occlusion culling tests. That’s right, our physics engine determines what is drawn, not our rasterizer or scenegraph. This should make a few people scratch their heads. The thing is, Bullet provides data structures for handling dynamic scenes and culling those dynamic scenes, and Bullet can do this a lot faster (logarithmic time complexity) than what we previously were using (linear time complexity). I can see why this optimization was done. However, the culling code is completely separate from the physics code. A GraphicsController was added to our physics interface to allow physics engines to do culling tests. Instead, we should have just created some code to allow the scenegraph to use Bullet to do culling (neatly encapsulated in some fashion).
Now, back to the rasterizer itself. Lets say I want to change how we use shaders (maybe to reduce the number of state changes). The most obvious spot to look for this code is in the rasterizer. However, the rasterizer does not know about shaders. For that mater it doesn’t know about materials or textures either, and, until recently, did not even know about lights. A fun note on lights: the fixed-function (i.e., Multitexture Material) lighting code used to be handled by players (e.g., the embedded player or stand-alone blenderplayer), which meant we had different fixed-function code for the different players.
Overall, the fact that our rendering code is all over the engine makes it very difficult to maintain. We cannot add a new rasterizer for things like OpenGL 3+ or OpenGL ES (mobile) until we get everything properly encapsulated behind our current rasterizer, which will take time.
Duplicate Code in VideoTexture
The VideoTexture module sits almost completely apart of the rest of the engine. At first glance this is a good thing (loosely coupled, few dependencies, etc.). However, taking a deeper look at the module shows that it achieves this lack of dependencies by copying large chunks of the render setup code from KX_KetsjiEngine (the core engine class). This creates a large chunk of duplicate code. Now, if a developer wants to fix/change something like how camera projections are handled (e.g., fix something related to orthographic cameras) they might find the code in KX_KetsjiEngine, make the change, and call it a day. However, they have just introduced a bug to the render-to-texture functionality offered by VideoTexture! Overall, I would like to see the VideoTexture features better integrated into the BGE and the VideoTexture module itself phased out. For example, we should just add “native” support for Blender’s movie texture type.
A further issue with the VideoTexture module is that it is only exposed through the Python API. The rest of the engine does not know it exists. This makes it difficult to use the VideoTexture module in other parts of the engine (e.g., for the earlier mentioned support for movie texture types). Ideally, the features in the VideoTexture module would be parts of the engine that are exposed to the Python API (like all of the other engine code).
Multiple Material Classes
In the BGE materials are handled via the following classes:
- RAS_IPolygonMaterial – This is an interface in the rasterizer for handling materials
- KX_BlenderMaterial – This is a concrete implementation of RAS_IPolygonMaterial that handles materials for the BGE (note that it’s in Ketsji!). This includes code for Multitexture and GLSL materials mixed together in the same file (breaking the single responsibility principle).
- BL_Material – Essentially a copy of Blender’s material data made during conversion. This is made since the BGE tries to avoid using Blender data. However, one of the fields in this class is the Blender material, which various parts of the engine access. This invalidates the need for having BL_Material in the first place! Really, BL_Material is not needed and is causing extra clutter and confusion.
- BL_Texture – Handles textures in Multitexture materials and custom shaders. GLSL mode uses Blender’s texture handling code (i.e., not code in the engine itself). Again, this isn’t in the rasterizer.
- BL_Shader – This is for custom shaders created via the Python API. It should be noted, that despite the BL prefix, this class does not (directly) touch Blender data or code. It does make use of BL_Material and BL_Texture, which ultimately do interact with Blender code, but BL_Shader doesn’t need to know where the data came from. In other words, BL_Shader does not care what Blender does, but it still has the BL prefix, which is confusing to new developers.
- BL_BlenderShader – Another shader class, but instead is used for Blender’s generated GLSL shaders. This means that custom-made user shaders have a completely different code path than the builtin shaders. Nice.
We used to also have a KX_PolygonMaterial for Singletexture mode. The general idea behind many of these classes is not bad, but, as mentioned, they have a lot of oddities.
Logic System Dependent on Logic Bricks
The game engine can not currently run without logic bricks. The entire logic system is dependent on logic brick code. For example, we cannot even run a Python script without getting logic bricks involved. Logic bricks are useful to have, but they should be properly encapsulated and organized. There are a few things that need to be done here:
- Logic bricks should be put into their own folder (GameLogic) and not be part of the core engine module.
- Logic bricks should be implemented as a logic system that is separate from the engine itself. This allows other forms of logic (e.g., Python and HIVE) to be integrated in a smoother manner. This could be achieved by having a list of logic systems that the game engine could iterate over and tell to update.
- Logic bricks should expose functionality, not contain it. A lot of this has been cleaned up, but we’ve had cases where features such as animations were coded directly into a logic brick. These types of features should be in the engine code, and then a logic brick can interface with that code. This allows other systems (e.g., Python, HIVE, other parts of the engine itself) to make use of these features. We’ve still got some features that only logic bricks have access to like 2D filters. We also have cases such as the Near and Radar bricks that create collision shapes in the physics engine to work. Where is this in the Python API?
Inconsistent Logic Bricks
While on the topic of logic bricks: logic bricks are rather inconsistent, which makes using or developing them awkward. For example, handling pulses is left up to the actuator (not some higher level system). This means that actuators can choose how to behave to positive or negative pulses. This sounds like a good idea until you realize two different patterns have emerged:
- Actuators that only perform tasks while receiving a positive pulse.
- Actuators that start performing tasks on a positive pulse and only stop when they receive a negative pulse.
This is just confusing to both new users (why do the actuators behave differently?) and developers (how should a new actuator behave?). What is worse is that this inconsistency cannot be fixed without breaking existing games.
I’ve just given a sampling of potential issues. Some other issues I haven’t gone in depth on are (among others):
- KX_GameObject starting to fall into the trap of the god object anti-pattern
- Physics being a mess in general (CcdPhysicsEnvironment.cpp contains a lot of classes, bad timestep, etc.)
- Logic bricks being tied to framerate
- The terrible inheritance structure used for deformers
I may write some articles on these problems in the future.
Patch Example: LoD
I would like to end this discussion with an example of how these types of issues affect actual development. For this example, we’ll take a look at the recently added LoD feature. Overall, this was a fairly simple patch (self contained, necessary changes were obvious, etc.). However, after being reviewed by three developers, we still had a few bugs creep in from it. One example was T39053. The bug was that using replace mesh via an actuator did not properly change the mesh material. The first sign of a design issue is that replace mesh is working different on the actuator than the Python/engine API. This shows that the actuator is doing more than simply exposing an interface. This bug took me way longer to fix than it should have. Between git bisects, head scratching and digging through code, I think I spent around 1~2 hours fixing the bug. The fix was committed as 6c9dd1. The LoD patch changed a little bit how mesh conversion worked, but this change was reviewed and was deemed to be fine. What we failed to notice is that the replace mesh actuator converts a mesh a second time instead of using the already converted mesh data. The actuator should never have done conversion like this (again, breaking the single responsibility principle).
I hope that this article gives people at least a small glimpse into the problems facing BGE developers and what makes the engine difficult to maintain.