On July 4th, 2019, I created the initial commit for a game demo that was designed to be a learning experience for me. The main intention was to force me to practice adding juice and polish to my games. Additionally, it was supposed to force me to develop my pixel art skills.
Considering those objectives, it was a wild success.
In fact it was so successful to me that I grew excited to flesh out this idea I had into a full, mature game. So great was this success that I decided to stop the development of this little game, at least for now.
A Great Success
I took a different strategy to the development of this demo than I have from my other games. I started the game with the explicit goal of practicing polish. I wanted to create a demo with little to no gameplay, but with the kind of polish you’d expect to see from a complete (or nearly-complete) game. Previously I had this thought in my mind that polishing a game was something to be strictly reserved as one of the last steps of development. I had myself convinced that polishing a game before its features were in place and more-or-less set in stone was a waste of my scarce time.
That idea was totally wrong. About as wrong as an idea can be. I was astonished at the amount of fun the polish and juice added to the game. The effectiveness of those seemingly superficial elements doesn’t make logical sense. The positive feelings evoked from interacting with a game that not only is fun to play but is fun to play with cannot be understated. I was having fun with my game before there even was an objective, and that’s because killing enemies was just a fun interaction. It didn’t need an objective to keep me interested. It was a level of engagement I had never felt from a game I made before.
The success of this demo was showing me that I had been doing game development wrong for years.
An Unfortunate Realization
So excited was I in the tremendous personal success that I was experiencing, that the game ballooned to a point now where I can’t possibly realize the potential that I created for it in a reasonable time frame.
I had this grand vision of a bullet-hell RPG with quests, NPCs, lots of enemies, and a story to tie them all together. I was all for that, and excited to work and get it done. As development continued I found that creating the art, the sounds, and writing the code all myself was extremely time consuming. Creating and animating a single character took me several nights of work in most instances. I wasn’t using randomization as a crutch as I had in some of my previous games. I was creating the areas, quests, and items all by hand.
I found that it was not feasible. I could spend the next 1-2 years of my free time crafting this game that I was excited about. I’m not even certain that time frame is realistic. I spent 6 months creating the demo that you see.
So I considered cutting back my ideas for the game and decided that this would not be satisfactory to me. Most of the time, I am more than comfortable cutting features from my games. More time for me! But I feel like the elements I’ve described are essential to making this game work in the way I had envisioned. I could cut the NPC count, but then the world would feel empty. I could only create a few areas, but then the game would be short. I could only create a few enemies, but then the variety would be lacking. I feel like there’s a baseline minimum amount of work that needs to be done to make an RPG come together, and that baseline happens to be outside the amount of time I am willing to spend to meet it.
That’s why I am comfortable tabling the idea for now. Here’s hoping I’ll have the time and resources to pick it up again in the future.
Despite the fact that I cannot dedicate my time to creating a full game from this experiment I did learn quite a bit.
Juice! As I mentioned before I had no idea the impact that juice can add to your game. If you’ve taken the time to play my game you’ll notice that nearly everything is animated. The UI will bounce in and out. Loot items bob up and down begging you to pick them up. The particles are chunky and vibrant. And of course the camera shakes and damage numbers fly when you hit an enemy. If I were to take away all of those things I’d still have the same game at a functional level, but it would feel dead. It wouldn’t feel snappy. It wouldn’t feel good to play. If I had to guess I’d say that UI animation and camera shake are probably the things that will add the most life to your game. If you have the capacity add animations for anything that the player can interact with. Don’t have static idle sprites for NPCs, which is a mistake I have made before.
An area that is still lacking for me is sound design. It’s not a skill I’ve spent any time developing and I know that nailing sound design can add that extra juice that may push my games over the top. It’s definitely something I need to learn.
This was my first real attempt at creating art for my game. Sometimes I spent hours on something as simple as a static asset. I have to say that my art did get better. The simple act of repetition, scrapping and restarting sprites, and persistence resulted in a very measurable increase in my competency. Over time I found that I was getting quicker at choosing the right colors, creating the right shapes, and making animations lively and fun to observe. I am by no means a good artist. I wouldn’t even say at this point that I am a competent artist. But I am much better than I was before beginning the project. I plan to continue developing my art skills in the same way for my future projects.
The concept of a behavior tree was vaguely familiar to me before this project. It came up once or twice in a game AI class I took in college. Beyond that, I never engaged with it or really understood it. I decided that my finite state machine way of doing things wasn’t going to give me the kind of intelligence that I wanted and I rediscovered behavior trees while trying to find alternative AI strategies. I decided to make an editor-first implementation, with my behavior tree being structured as a tree of Godot’s nodes. Each node ended up being a distinct BT Node (composite, leaf, decorator, etc). I then utilized signals to notify the BT of state changes. It worked very well, and I was able to leverage the Godot editor to construct an arbitrary behavior tree. That was a massive time-saver. It’s worth noting that I am unaware of the potential performance impacts of the extra nodes. It worked amazingly well from a strictly functional perspective. If you’re interested to learn more I highly recommend this post about behavior trees.
I developed my own quest designer application which essentially used Godot’s graph nodes to construct a directed graph of different node types. Each node was one of a few different types. There were “start” nodes, “stage” nodes, and “event” nodes which could be chained together to create a functional quest. A simple iteration over the connections allowed me to save the connections in a JSON format and load them into the game as pure data. Then it was only a matter of creating the right classes to process quest data and coordinate quest completion and game event triggers. The main point of learning with this quest designer was getting a handle on Godot’s GraphNodes. I think this knowledge will be handy in the future for things like similar quest systems, dialogue systems, or even an alternative behavior tree implementation.
My dialogue system was on the chopping block. I was ready to scrap it and start over. My initial strategy was similar to my approach to the behavior tree implementation, which was to make it fully configurable via a series of Godot’s Nodes. Functionally it worked fine. However, it became too confusing to work with and modify. I routinely found myself not sure what dialogue appears when, what dialogue triggers a quest, and what dialogue wouldn’t be shown until a certain condition is met. One of the challenges I found was I had a set of 2 requirements: I needed a series of generic dialogue options that would be available at all times and then I had quest-specific dialogue items which would be made dynamically available depending on the overall state of the quests that were in progress or completed. I think my mistake was trying to satisfy these two distinct requirements with a single solution. Additionally, I don’t think the use of nodes to configure dialogue data was an appropriate way to meet the requirements. Had I gone forward with reworking the dialogue system I probably would have investigated an approach similar to my quest design system. A visual-script-esque way of structuring the data probably would have been much easier to work with and modify.
The game succeeded in its original purpose. I learned so much from it and I was able to develop new skills that I can carry forward. Along with that I learned a hard lesson on scope.
My next game will be limited in scope but full of juice. Thanks for reading!