Category: architecture

  • some crazy game symphony

    I have recently been accused of writing things that no one understands.

    “I don’t even know what half those words mean.” A friend remarked. “All that stuff you post about your game.”

    The effort of trying to bring a glimpse of a guy trying to do some simple game design to the masses is as difficult as the coding, it seems.

    The problem is that coding and technical stuff is jargony. It is tough to describe complex things, after all, without falling into the easy rhythm of just using the complex words.

    And, it turns out, if that isn’t a metaphor to describe the challenges I encountered in my code this week, I don’t know what is.

    Falling back on the easy, I mean.

    It wasn’t so much that I shortcutted or made bad decisions, per se, but rather that in my effort to do something complex I described it to the compiler—I wrote code—in a way that, um, confused it a little bit.

    My code got just complex enough that I ran into one of those juicy jargony words that begrudge programmers all over the world: race conditions.

    Let’s back up a bit. What the hell is coding anyways?

    You probably know or have heard that computers speak binary. Zeros and ones. Right? And that is true. Virtually every computer program every built for every consumer product relies on binary computation.  (I could go on about quantum computers or analog computers or whatever, but let’s keep this simple, shall we?) Digital computers speak binary.

    I’d love to be able to spit out a string of zeros and ones long enough to fill a library and not make any mistakes and do so well enough that it results in the computer making a game called Pleck’s Mart that you all want to eventually play.  But I will never be able to do that. 

    I can, on the other hand, write down all those complex ideas in a text editor in a language called Rust (which I have been learning quite well) and then ask this little bit of software on my computer to turn it into that string of zeros and ones so that it becomes said game.

    That’s coding. Writing out complex instructions for a compiler to turn your plan into binary numbers.

    And knowing this, you should also know that this can fail in two basic ways: Either you can write code so bad and wrong that the compiler tells you it kinda sucks and you should fix this or fix that and go back and try again because it doesn’t understand—or, you can write code that makes enough sense to get translated but results in something unintended happening when the computer starts mathing and computing all those resulting zeros and ones. We call the first bad code and the second, usually, a bug.

    The first fail, bad code, happens to me a hundred times per day, and I go back and fix stuff. I mistyped a variable or forgot to make something mutable (flexable and changable in the system) or missed a semi-colon at the end of a statement or—the list goes on.

    The second fail happens alot too, but is occasionally more subtle in as much as you don’t notice that the results are wonky until you really do. Easy to spot bugs are incorrect coordinates on sprites or calling a valid but incorrect variable or switching the signs on a math calculation.  The hard ones are things like the thing that bit me in the butt this week: race conditions.

    To understand this you have to also understand something that is probably pretty basic but important to my point: computers are fast. Freaking fast, these days in particular, but they have always been way faster than we realize.

    You can look at this little video of my game that I recorded yesterday of things on the screen and realize that there are about five hundred individual images on the screen, each a file that needs to be loaded into memory, and simultaneously the computer is reading information from the screen, the mouse, the keyboard, my gampad controller, playing music, loading up individual click sounds for the little interactions, checking the math to see if anything I do causes any two of those little image files in the pretend space of the screen to crash into each other, and about a hundred other things I can’t even keep track of—and it is doing all of that on repeat over a hundred times per second.

    Per second!

    And I, the invisible conductor of this crazy digital symphony, need to to have staged all of that down to the millisecond.

    And when things are simple, nothing really deeply goes wrong. At least not wrong enough to matter.

    But then things get complex and—well, let’s go back to that symphony analogy: I play in an orchestra so believe me when I say it is easy for things to get out of sync. The timpani is pounding out a steady beat but then the clarinet comes in half a second late and the violas sitting next to the clarinets try to pace them, but the violins are watching the conductor and timing themselves to the baton, and then the trumpets who can’t hear anyone but themselves way at the back decide to wing it and jump in too. The result might be music, but it more likely will just become noise.

    One thing got ahead of the next thing and the thing that was supposed to be following in a planned order happened out of sync and—that’s kinda a race condition.

    In coding, as I understand it, a race condition is when the code is not exactly wrong, but you as the coder have not put in the right set of rules to stop things from getting out of sync. 

    On a small program you might not even notice.

    On a growing and complex program? Chaos ensues. If things don’t crash outright, then you suddenly have eighteen soundtracks playing because they are not properly being removed and replaced, or all the objects in your room spawn a mere half a second before the rest of the room making it look like ten cases of tomatoes just blipped and appeared in empty space for a moment, or else your player flashes brightly because the lighting effects took effect a beat too late to maintain the illusion of that shadow you had meticulously coded. But then, mostly, yeah, mostly it just crashes. Or bogs down to a crawl as eighty-seven thousand copies of your player sprite spawn into the program and the computer (as fast as it is) just throws in the towel because even it isn’t that fast.  And then it crashes anyways.

    These things are not laziness in coding, but they are me making assumptions about that long string of zeros and ones and assuming that the computer can make sense of them as much as I think it can without tripping over its digital shoelaces.

    They are adding a dozen more musicians to the orchestra and just hoping they will all keep perfect time.

    Thankfully Bevy, the game engine I am using, has mitigations for this. I have spent a few hours restructing my code to give it a little bit more of a compartmentalized order and some timing conditions. That, for people who thing I am being to jargony, just means that I can specifically tell the computer to wait for one thing to happen before starting something that is supposed to happen after it, and I can also tell something else to count to ten and hold its breath before doing something else. Like cues and an enforced script, mostly. Sheet music and each musician with their eyes glued to the conductor and noise cancelling headphones so that they can only hear themselves. No more winging it and hoping for the best.

    All of these challenges are themselves spawning out of something good, tho: progress. Real progress. The game is moving forward. As much as my code is occasionally tripping over itself, that can be managed and fixed, and the result still is that the game does something this week that it didn’t do last week. It’s progressing. 

    In this phase I have dug into objects: stuff on shelves, mostly. I built the core functionality that allows the little guy (who up until now was just walking around exploring) to order objects from the shop, carry them around in an inventory and put them onto shelves, pick them up again, and put them somewhere else. 

    And it is weirdly satisfying to organize cases of vegetables in the store or the warehouse. So long as the game isn’t racing and crashing that is.

  • my great big spreadsheet ledger user interface adventure

    I’ve been thinking a lot this week about Dwarf Fortress. 

    Have you played Dwarf Fortress?

    Have you even heard of Dwarf Fortress? It’s a bit of a #iykyk kind of game, but one that some gamers have #k about for almost as long as the game has been in active development, which is well over 20 years.

    Two guys. One dream. One insanely complex game engine that fools you into thinking it is simplistic because its graphic design is, bluntly, outwardly visually rudimentary and (until recently) completely composed of colourful ASCII text characters.

    I’ve been thinking about this game, Dwarf Fortress, because as I develop my own little game project I’ve come to realize that many games, at their core, are essentially just pretty user interfaces for databases of varying complexity. I mean, I knew this. It’s always been my core understanding that to be a good games designer that at among many things you need a solid awareness of database design in your toolkit.

    My game is a database at its core. 

    I’ve mentioned this in a past post, but in creating my game I’m essentially designing a fancy UI for a really complex accounting ledger spreadsheet. THAT doesn’t sound nearly as much fun as a Science Fictional Supermarket, so you can see why I don’t lean into the former description in my marketing copy. That said, I am friends with more than a few accountants who might be into that, so—

    But I digress.

    Dwarf Fortress has been in active development for more than twenty years, because as the (perhaps apocryphal) story goes, the developers can’t seem to stop adding more rows and columns and macros to that game-as-spreadsheet metaphor.  The game is insanely complex. It is like the Matrix of database queries but with the cascading text representing dwarves and sheep and weapons and logs and, heck, I can’t even begin to name a fraction of a fraction of the elements here. I sure there is complexity layers in the game that are games in and of themselves that barely a handful of players have even discovered. It’s complex. Have I mentioned it is complex? 

    This complexity is on purpose, of course. (I assume.)

    I know many gamers who are (you know who you are) into wildly complex games with rules spanning many dozens or hundreds of pages, and in some cases, rule systems. DnD is essentially a giant rule system, that has depth and a bit of complexity but tries to keep it simple enough for our human brains to keep up. But Dwarf Fortress seems to embrace the complexity in the other direction and has both depth and complexity. Thousands of variables tangled in around themselves.

    Pleck’s Mart has from nearly the beginning had a database. But until yesterday that database was almost entirely about saving states and progressively loading game data.  Yesterday, tho, I added objects. And objects, see, are more than just a picture of something that appears in the game. Objects are the start of data complexity in the game. Objects are containers with variable states and value and properties that affect how they ultimately matter in the game. Objects are rows in that spreadsheet whose values react to conditions of the game, the passage of time, and the interaction by the player.

    It’s that start of my own game complexity that begins on one end of the spectrum with merely a virtual caselot of imaginary potatoes and ends all the way at the other scale with Dwarf Fortress.

    I’ve been thinking a lot about Dwarf Fortress this week not because Pleck’s Mart will ever be that complex but because it will always now, going forward, be some fraction as complex as Dwarf Fortress. 

    You might even call that a Dwarf Fortress Complexity Quotient, like as in Pleck’s Mart DFCQ equals about 0.01 as of right now. I’m striving for maybe about a 0.2, subjectively speaking of course.

    I have a long way to go, but the journey into a wildly entertaining spreadsheet ledger user interface adventure has begun. But let’s keep calling a game, ok.

  • building an in-game menu system from scratch (in Bevy)

    I’ve veered lightly away from building my virtual grocery store this week in favour of an equally important aspect of this game project: the menu system.

    Yikes! Yeah, I know, me too. I’d rather be painting sprites and building an interesting world, but oh that user interface is going to be important.

    As with almost everything I’ve approached this particular problem—that is, how to build a menu over top of a game world—incrementally.  And as I’ve been lightly glazing over the coding progress in most posts on this blog, I wanted to do something a little different and deep dive into how I approached this particular problem, if only because it took me two solid days to work through and get it functioning in the barest-of-bare minimums (that is, as of another three hour coding session this morning.)

    So, here goes.

    You may have picked up on the vibe that story is very important to what I’m building here. I am a novelist in my other crazy project life, so having a coherent story with lore is key to this whole deal.  To that effect one of the earlier things that I built (a week or so back in phase 0.2.3) was a kind of simple story engine that runs through the whole game.

    Stories in Pleck’s Mart are comprised of a few simple database tables: One keeps track of all the words, and one keeps track of all the statuses. Just to complicate things, a story can be comprised of multiple “beats” or pages. This is for both flow and because my font choice at the moment really only allows for about 250 characters on the screen at any given time, so that’s like 20 words. Any substantial story needs to graced with a bit more expositional text than a tweet, you know? In other words, pagination.

    #[derive(Component, Debug)]
    pub struct Storybeat {
        pub storybeat_id: i32,
        pub storybeat_code: String,
        pub storybeat_name: String,
        pub storybeat_nextcode: String,
        pub storybeat_complete: i32,
        pub storybeat_built: i32,
        pub storybeat_replayable: i32,
    }

    (There was a deliberate choice not to use bools for some of these status flags as I’ve got some ideas about non-binary status states and using the system for books, notes, etc, but more on that in a future post.)

    The story engine works pretty well, if I’m being honest. I’m happy with it so far and I haven’t run into any big flaws short of it could use a nicer UI design.

    All the chapters indexes are loaded into the status database on a new game initiation, and if a story has an unread status then a collision trigger coded into the game map can fling it’s unique code into the story queue which is constantly checking for active stories in the queue and bobs-yer-uncle it reads it all (text from the data file) into the database, spawns a story UI up on the screen and you can either NEXT your way through each beat or EXIT out and move along to just play the game. Meanwhile things churn in the background to change the status to read so that particular part of story never spawns again—

    —which leads to a little sad face from Brad as I’m building this game.

    See, unless you play the game over you can’t (couldn’t) go back and read the story chapters that you already triggered.  They were complete=1 and would never spawn again on any given save file. Thus, one of my first “this needs to be in the menu” features was the addition of a story history so a player could revisit and review and reread—and thinking about how to accomplish this was where I was sitting two-plus days ago, staring blankly into a computer screen full of five thousand lines of game code that barely even had a menu system, let alone something useful for a story history navigator.

    I started to address this problem by tucking in and refactoring the whole menu scheme that I’d started to build last phase. (It was okay, but it had some limitations.)

    First, I divided the in-game menu into three distinct, heirarchecal panels: there is now a TOP menu which is a kind of icon-based ribbon of menu categories. Next there is the LEFT menu which is a scrollable list of sub-categories. And finally there is the RIGHT menu which is for now a kind of panel that displays the results of whatever you selected in the left menu.

    To break that down with an example, when the STORY icon is selected in the TOP menu, then a scrollable list of STORY CHAPTERS appears in the LEFT menu which subsequently results in the selected story chapter’s TEXT BODY to display in the RIGHT panel.

    Like so:

    If all this sounds fairly straightforward, well, it is—but it took me a couple days of debugging and coding effort to not only get to that architecture straight in my head and in place, but also to make it actually work.

    how does it work? 

    For starters it’s all based on a collection of menu state components that are tracked and manipulated by player input.  This is the root and core of how this all functions. 

    That is to say, when a player moves into the GAME MENU App() state by pressing the M key while in-game, the App() moves out of the GAME state and into the overall GAME MENU state. (And vice versa on closing.)

    This action spawns all three menu panels:

    TOP menu has a GameMenuState of 0;

    LEFT menu has a ScrollMenuState of 0 with a bundled Length value;

    RIGHT menu is a blank canvas;

    A player now looking at the menu on the screen can either move Left or Right which cycles the GameMenuState from 0 to 1 to 2 to 3 and back to 0.

    #[derive(Bundle)]
    pub struct GameMenuBundle {
    	pub menu: Menu,
    	pub gamemenustate: GameMenuState,
    }

    Or, a player can move up and down which cycles the left menus’ ScrollMenuState from 0 to 1 all the way up to a variable length value stored in that state.  (Because of the variable length this also does some fancy math in there to adjust the scroll_y value so that the active item is within the view area of the left menu’s draw area. Math, math, always lots of math. And my daughter writing her Math final in her last year of high school in just two days from me penning these words wonders when she’ll ever need math again! Bah!

    #[derive(Bundle)]
    pub struct LeftMenuBundle {
    	pub menu: Menu,
    	pub scrollmenustate: ScrollMenuState,
    	pub scrolldelta: ScrollDelta,
    }

    If all this seems pretty basic, it is. The tricks all come when we need to the GAME MENU systems in the App() to react to all these variables.

    For starters the contents of the LeftMenu are uniform in design but simultaneously conditional on the GameMenuState. That is to say, it’s always a scrolling list with an icon and a piece of text, but what icons and what text depends on the number in that GameMenuState.

    For example, right now my STORY menu is the fourth icon on the top menu ribbon. This means that the App() listens for a GameMenuState of 3. If that happens to become true a lot of things then are conditionally allowed to happen:

    • the fourth icon sprite in the TOP menu “remaps” to the “highlighted” version of itself on a texture map so that it appears in colour instead of in greyscale. In other words, it highlights itself and de-highlights all the others.
    • a query extracts all the “complete” story codes (things the player has seen) from the database as a vector of data and iterates through them to build out an n=rows number of icon and story title pairs inside the LEFT menu canvas. In the case of a player who has just started the game and played into the first room, this means the function will spawn two item pairs in the list: (1) an icon an a piece of  text with the title of chapter 1 (the introductory story text) and (2) an icon an a title of chapter 2 (the story that activates when the player first enters the store).  Later, as more story beats are unlocked this list will grow longer.
    • the LeftMenuStatus resets to 0. (Top item in the scrollable list.)
    • the first item in the list of things in the left menu goes into it’s highlighted state in the same fashion as the top menu does when it is highlighted (ie. the sprite shifts its texture map and de-highlights everything else)
    • the right panel function checks for valid content based on a hierarchical check though GameMenuStatus of 3 and a LeftMenuStatus of 0, which in this case populates the canvas with the first story block’s text.
    • the right panel (still checking through this conditional mode) waits for a keypress and if the player clicks the NEXT key it does some more mathy-texty-database stuff to pull the next beat from the story chapter and then replaces the content of the right panel if that all happens.

    Of course, there are some extra conditionals and “something happened” checks tucked in there somewhere to balance things out and prevent these conditionals just going into processor eating loops of infinity, but those are details that don’t materially impact the overall deal here.

    The net result of all this is that if, while playing the game, the player presses the M key a menu appears.

    If the player then Arrow Key Left/Right’s over to the story menu, a list of her story history items will appear in the left panel. 

    And as they player Arrow Key Up/Down’s through that list of story history, the first beat of each story will appear in the right panel.

    And if more than one beat exists for a story, the player can press the N key to cycle endlessly through the various beats that exist in that chapter:

    let new_text: &str = this_story_text.as_str();
    								for mut this_text in rightcontentpanel.iter_mut() {									           
    	this_text.0.clear();
    	this_text.0.push_str(&new_text.clone());															}

    Voila. A menu with story history.

    Check.

    side note: If enough people are interested in such a thing, I could pull this menu code out of my main project, clean it up for general use, and release it as a Git repository.

    Comment if that’s of interest.

    looking back

    My biggest hangups after all this got working as I expected were making sure that the canvas contents were responsive to the conditional changes: mutable text fields, scrollable nodes, etc, etc — which you would think in retrospect should be pretty obvious but designing this all in raw code with overlapping coordinate systems across three spawned objects just seemed to be keeping track of things and naming stuff in meaningful ways.

    And having built it this way, I think—right now, I think—all this work lays the foundations for most every other menu in the game:

    • The items in any the left menu can be dynamically defined by the contents of a database or hand coded to be very specific items.
    • The contents of the right panel can be adapted to display just about anything, text, images, or inputs.
    • And, not that I have much to fill them at the moment but I left room for up to eight menu categories, which is very probably overkill.

    Now, back to building that virtual grocery store, huh?