A real taste of object oriented Design (Battlebeyz 4)
WIP! Last updated Oct 26 2025 while I am still updating my site
Well, things are moving quite well along with my spinning top game project. The physics came out quite nicely, now that all the units, time, and constants are scaled correctly. And this basic implementation only took a hundred hours of debugging! Woo, go figure.
Now that brings me to today’s topic of everyone’s favorite: OOP. I’m now at the stage where I am starting to need to reevaluate my design choices when developing new features. Here’s the scenario.
This all started from me wanting to add names to beyblades and display them on a menu before the beyblade launch. Should be simple enough, I created a GameControl struct to pass in variables anywhere with the GLFW window, and I could just add “name” as a field in Beyblade. However, I realized that I had no way to actually extract all of the Beyblade data into the UI. PhysicsWorld would be a good candidate, but given that I planned for it to only involve physical data (and therefore only store rigid bodies), it would not have a field at that level. Hmm. I could add “name” at the rigid body level, but that would couple the physics elements with non-physics elements, and at least that part was handled well. I could also create a different struct, such as ObjectWorld, that would hold all the object data. But since the objects contain their rigidBodies, there was obviously duplicated data.
There’s a fine balance between the amount and complexity of changes caused by an adjusting in structure, and the long-term benefits it would have in organization. I’m lucky to both know my code extremely well and have a project that is constantly shifting and not an extremely complex legacy. In this case, I evaluated that it would be best to bite the small amount of extra space and tweaking involved in changing physicsWorld to hold objects instead of just their rigidBodies.
Ok, let’s do that. But waaait. Most of the fields in Beyblade are for the mesh, and name doesn’t seem to fit in anywhere as a more general variable. If I’m abstracting/coupling the logic to beybladeBody and stadiumBody in the first place, why am I not doing the same for the mesh? I almost forgot, but…it’s inheriting from gameObject for mesh related functionality? That doesn’t make sense. Why is it called gameObject and not meshObject???
This is a phenomenon I dub the cascading stupid syndrome (CSS).
It’s not just normal to spot another flaw or inconsistency after inspecting an existing one - in fact, it’s ubiquitous. The uninformed approach I took to managing game objects previously was now back to bite, once again. I guess I might as well get into that one too.
My previous design had Beyblade and Stadium inheriting from gameObject, whose functionality should be more optly named meshObject. meshObject was really just a container for vertexData, tangents, colors, and similar data, with the actual important functions being virtual and overridden in the respective inheriting classes.
I have since watched enough tech content and read enough articles to know that inheritance fricking sucks. What was I thinking? Of course it would make sense to couple everything rendering in aggregation.
But there were slight differences between functions like render and initializeMesh in beyblade and stadium. For instance, beyblade had an additional materialsColor map to allow for more detailed color rendering.
Once again, I had to evaluate the best course of action. I would definitely want to highlight performance for my app, but realistically very little would be lost if I implemented these functions in the renderMap. Doing so would require additional variables that I might not need for all cases, but it was good.
I guess the lesson here is that OOD sucks, but the second lesson here is that making a large, significant project is one of the best ways for you to feel the effects of your architectural decisions. For instance, I think it’s quite poetic how I have transitioned from global states in main to boolean flags in GameManager to a stack of enumerated Gamestate types in GameEngine. Trying to take your implementation and running against the wall of feasible complexity teaches you more about why your ideas don’t work more than any lecture ever could.