Sexy Afternoon v0.2.0 Portmortem


Can you really do a postmortem with versions? I think it's more so for completely completed projects. Oh well, I'm doing it anyways.

Hopefully by this point you've gotten to play the new version. If not, take a sec to check it out, since that will help your understanding of what we're talking about here.

The major new feature in this update was Dialogue, at least as far as time spent coding goes. One of the main things I've spent my free time working on for a few years now is different codifications for interactive storytelling and BOY have I had some bad ideas along the way. Before I spent any real time thinking about data structures, I created a short interactive story using an array of simple objects (sort of a proto-dialogue node). Very quickly I realized that this was a nightmare in terms of management, because each node simple referred to the index of the node it was supposed to connect to. Move/add/remove anything to/from the array and now you had a LOT of broken links, a lot of refactoring code if you have any state you're working against. It was bad.

Then I learned of Dialogue Trees.

On Dialogue Trees

The main conceit of a Dialogue Tree is that it is a collection of story nodes (or leaves) that connect to one another by branches, sometimes directly and sometime under certain conditions (button presses, certain state criteria). Above is a screenshot of Diagrams a SUPER useful tool when laying out Dialogue Trees. It allows me to easily see and adjust the flow of dialogue in a way that a text document has never rivaled. As you can see, each node is represented by a rectangle, connecting to other nodes with arrows. Some of the arrows have dialogue over them, representing a player dialogue option. 

With v0.2.0 of Sexy Afternoon I created a DialogueNode class that stores some helpful data:

  • Who is speaking
  • What they're saying
  • Who/What should be displayed
  • Any potential player responses
  • What nodes this node connects to and under what conditions it leads to each connecting node

For this release, this is good enough. By that I mean that I have a released product that works and wasn't hampered by a lack of functionality or too much complexity. Will this be the shape of the class forever? Probably not. Will this even, necessarily be "good enough" for the next release? I'd honestly be surprised if it was.

(Don't Fear) The Refactor

This is a good point to discuss a pertinent lesson I learned rather explicitly through this project. It's sort of a programming truism that "the more general a piece of code is the more useful (and therefore better) it is". If I have a general purpose AI that can learn to play a game naturally and it can defeat a more specific Chess AI, it's the better code since it can do everything the Chess AI does and many other things too.

It can be tempting to take this idea into your own code base, especially when you're starting a new project. If you've coded for more than 15 minutes, you've dealt with bad code (and it was probably yours). I have often fallen prey to this high-minded conceit that if you just architect your code properly at the outset, you won't have to deal with bad code, but increasingly I don't think this is the case.

This mindset has a few costs to it. Firstly, creating a general purpose application at the head of the trail is just guesswork and costs you hours "solving" unknowns. Maybe you'll add a crafting system down the road and what if your crafting system involves different sized grids for crafting things? Okay, but what if it doesn't? There are some questions that can be answered at the beginning, but a lot of the challenges will only emerge as you start to get to work. There are best-practices in place like "DRY" and keeping your code loosely tethered that are best-practices because they allow you to encounter unexpected challenges in the wild and absorb them without too much fuss. Encountering troubles is not the sign of a bad programmer, the inflexibility to cope with those challenges is though. There is a fragility to trying to solve all problems you MIGHT encounter from the start, you waste a lot of time (especially in Game Dev) not having a product that you can use and react to and there is a sort of snapping that can occur when you've spent precious hours of development making the most general purpose code, only to encounter something unforeseen.

Secondly, a perfection mindset tends to cause a lack of refactoring. When I first started coding I saw refactoring as a sign that there was a problem with the code and, therefore, that there was a problem with me. Sorry if I'm the first to tell you this, but there is a problem with your code, but, there isn't something wrong with you. We're all human. We all err. It's why refactoring is essential. On top of that, there are a lot of issues that arise with your code that have nothing to do with you. My day job is writing business software and ALL OF THE TIME I have to update code because some service we're using is changing or an API as updated or some module I was using won't be updating to accommodate other changes in the environment. 

This is perhaps the biggest reason that refactoring is ESSENTIAL. Your code endeavors come baked with two massive limitation, fundamental to the human experience:

  1.  A limited understanding of the existing state of your environment
  2. A further narrowing understanding of the future of your environment

You do the best that you can to problem shape and understand as much of this environment as you can, but things will change. Customer needs evolve, businesses fail, services deprecate. A code base's (and the development team's) ability to adapt is usually one of its greatest strengths or most apparent weaknesses. The original conceit of the Agile Development Manifesto was the idea that you take a step toward a goal, reassess the environment and how close to/far from the goal you are and then refactor. Pretty simple, just difficult to hold to in practice.

All of this is to say, I wasted a good number of hours prepping for things that are in my roadmap, but were not pertinent to this release. I fully modeled out Character State Code as well as a Memory System that allowed characters to know what they'd done recently. This isn't an issue on its own, except that I wasted hours trying to build out my Dialogue Tree code to accommodate these other modules, despite knowing they weren't going to be used for this release. I was trying to build the roof on a house with no walls (while I was simultaneously trying to build the walls, mind you). As soon as I dropped any concern for that future from my code, I was able to solve the one problem at hand as thoroughly as possible, leaving room for, but not pre-solving, the future of the code.  It's like Linus Torvalds said on his experience at TED:

I've actually felt slightly uncomfortable at TED for the last two days because there's a lot of vision going on, right? And I am not a visionary. I do not have a five-year plan. I'm an engineer. And I think it's really – I mean – I'm perfectly happy with all the people who are walking around and just staring at the clouds and looking at the stars and saying, "I want to go there."But I'm looking at the ground, and I want to fix the pothole that's right in front of me before I fall in. This is the kind of person I am.

There is no shame in solving each issue as it comes your way. You're traversing rough terrain. It's worth checking on the horizon to keep your heading, but you could get seriously hurt if you're not frequently checking the turf right in front of you to make sure you know where you are and what your next step is.

This release took about 4 times as long as it should have because of this and my plan in the future is to keep building things out bit-by-bit and not blame myself when my code inevitably needs refactoring. I probably shouldn't be waiting for major issues in the code in order to refactor anyways. Battling with bad code is less punishment for poor architecture than it is the inevitable cost for not refactoring.

Eat your veggies, kids!

Leave a comment

Log in with itch.io to leave a comment.