Making a 3D Game: How to Not Start a Project (Part 1)
“Do a personal project over the summer.”
“It’s fun.”
So here I am, as my freshman year is wrapping up, without too much planned for the summer. I had signed up for some courses and was continuing with a nonprofit internship and research, along with seriously studying LeetCode, but I didn’t have any full-time internships (largely because I didn’t apply the previous year due to my lack of any experience). This was the perfect opportunity to create a great personal project, and I even had a novel, passionate idea in mind. How could this possibly go wrong?
Mid-July. I’m dead inside. This stupid thing won’t work. I just need to fix this one issue. Why. Why… Ughhh. Has I ever????
Let’s rewind a bit.
My great project idea came from my previous exposure to Beyblades—y’know, those spinning tops that hit each other. After playing Hasbro’s terrible mobile Beyblade app a couple of times, I had the same thought as many others have had before: I want a game with realistic physics, not this basic cookie clicker-esque way of just defeating the other bey’s stamina. It might surprise you that one does not exist despite the success of the franchise!
Ideas immediately started pouring in. I could model them like this! And calculate the movement and collisions like this! And even support importing a custom model to provide accurate stats! I knew making a game and learning everything that comes with it would be quite a long and tough undertaking, but I was confident it wouldn’t be too difficult with ChatGPT in hand. Oh, how naive I was.
I first started by trying to use existing game engines like Unreal, as building with existing tools would of course be a lot easier. I knew I had custom logic that needed code beyond the drag-and-drop interface, so I decided to go down the coding path after getting familiarized with the engine UI. Multiple projects and a 10-hour beginner tutorial later, I had a spinning Beyblade model, but I was still stuck. No matter how much I tried or asked for help, I could not get the C++ development build to work! This was a huge blow to my interest, as I felt I had wasted so much time trying to set up something that didn’t work. You can tell from this edit history when my annoyance gave in to despair: link to forum post.
After a week of being dejected, I finally had the interest to build the game from scratch. Yeah—yeah! It would be like going from a website template builder to an actual tech stack! I could learn the very fundamentals of game development, which would make me a better programmer.
(Looking back, I think Unreal’s C++ failure was just a bug from that engine version that didn’t get patched for months… dang.)
First was the process of just… rendering things from scratch. Choosing C++ as a language was an easy decision for performance and familiarity, but I had a whole world of libraries to potentially use. Obviously, I couldn’t use something as basic as SFML from school, as I needed powerful, low-level 3D graphics, so I looked into the standard OpenGL with GLFW and GLM. Everyone jokes about the difficulty of drawing a triangle on screen in graphics programming, but seriously: even with an LLM, it still took an insane amount of time to figure out all the imports and dependencies and code to render a tetrahedron to the screen.
My worst experiences, however, came from integrating physics.
Even if I didn’t use a game engine, I would still use things like a physics library for handling collision detection and physics calculations. So I tried everything from Nvidia’s PhysX to Bullet Physics, and you know what? The thing I hate the most in the universe is integrating C++ libraries. F&@#. There’s no standard build process, and you can’t just download the library, no no no. You have to build it from these unmaintained descriptions, migrate the correct files to your project, and then figure out how to link everything in CMake. Maybe if you’re an experienced developer growing up with these systems, it is not too bad, but as a learning student, it was extremely painful to try to figure things out, especially compared to the ease of Python or Javascript that do have a standardized manager. None of the physics worked, so once again, I turned to the tried and true method of just writing my own code from scratch.
I think you should start to see the pattern here.
There’s no way I would have the paper to explain everything that went wrong in my development, so I would generally describe it as just trying to bite off more than I could chew in a large-scale, low-level project without any external help. GPT is excellent when you have small bits of boilerplate code with a specified usage, but if you don’t completely understand any part of the workings of a large project, it becomes extremely tough to debug what goes wrong. In one case, I spent days trying to figure out why my Beyblade was not rendering correctly. So many print statements. So many breakpoints. The actual bug? I was defining locally scoped variables in the rendering function rather than referring to the class variable, which thus evaded bug detection for quite some time.
Along the way, I also found that there’s just so much in game development to learn. I think most people would never begin to imagine the complexities of homogeneous coordinates that handle camera movement, VAOs, VBOs, and EBOs to model objects, and lighting calculations in shaders—and that’s only the basic rendering stuff! It’s certainly a struggle, one that requires a good balance of taking the time to learn and ensuring that it is applicable to your use case.
By the end of July, I had a somewhat working 3D space with a movable camera and some shading; decent, but certainly not reflective of all the hours I put in. I learned a lot through this project, but I do have some regrets about the time I spent going down unproductive paths. In hindsight, planning quick initial implementations that could be refined later would have saved me a lot of time. Additionally, knowing when to step back and simply take a break when progress stalls is crucial.
In hindsight, a saving grace would’ve been a knowledgeable programmer that could advise on all the decisions I make, not letting my immaturity lead down expensive rabbit holes of hell. It was a learning experience, but I was so grateful once I finally had the foresight to reach for this saving grace. Development quickly sped up soon after, but that’s a story for another time (i.e., the next blog post… or two. Or five.).