Category Archives: Devlogs

Randomization

Welp, it’s been fun, but Dark Souls III comes out this week, which means Gunmetal Arcadia Zero is paused indefinitely. (Not really but yeah but not really but yeah.)

The last few weeks’ or months’ worth of blogs have been iterative progress updates, and I’ve been itching to do an in-depth, technical, tutorial-style blog for a little while now. So today I’ll be talking about random number generation in Gunmetal Arcadia Zero. I’ve written about some of these concepts in the past, but I don’t think I’ve ever done a complete top-to-bottom review of the entire scope of this facet of the game, and certainly not since making some important changes for the vertical slice build.

Motivation

Let’s start with the obvious. Some things in this game should be random, enemy behavior and loot drops being the prime examples. But how should they be random, and how random should they be?

(As an aside, I should note that I’ll be using “random” to mean “pseudorandom” throughout this blog. I’ll leave the substitution to the reader’s mind.)

I made the decision at some point that enemy behaviors, while “random” in the sense that two instances of the same enemy may choose different actions than each other, should be consistent across each play session. That is to say, when you see a particular slime blob, it will behave exactly as it has in previous sessions, all else being equal. Enemies who react to cues from the player or their environment may diverge from normal patterns based on this input, but they will still behave consistently and reproducibly. This decision was based partly on how enemies tended to behave in classic games and partly on the assumption that it would make the game more palatable for speedrunning.

Loot drops, on the other hand, wouldn’t be very interesting if they exhibited this same consistent, reproducible behavior. These need to be more random, such that the player can’t predict what they will receive for killing an enemy or destroying an environmental object.

In both cases, however, the ability to completely save and restore the state of random number generators is important. Consider the case of an attract loop recorded from a previous game session. If loot drops changed each time the attract loop were played, the results could be disastrous. Imagine if a torch that had originally dropped  a new type of subweapon now dropped a coin, throwing the rest of the session out of whack when the player attempted to use that subweapon and failed.

So, with these use cases in mind, we want to be able to generate numbers in two ways: deterministically (consistently and reproducibly) and non-deterministically (unpredictably), and we also need to be able to serialize RNG state in both cases.

Implementation

I chose to use Mersenne twisters for random number generation in Gunmetal Arcadia Zero. I won’t go too much into the basics of how Mersenne twisters work, as that topic has been covered frequently and in greater depth than I would be capable here, but the important things to understand are:

  • A Mersenne twister must be seeded with an input number in order to produce valid results.
  • Seeding the twister with the same value will always guarantee the same results.
  • The internal state of a Mersenne twister can be saved and loaded such that a position in a “stream” of random numbers may be maintained.
  • A typical implementation of a Mersenne twister regenerates its internal representation every 624 times a random number is extracted.

The first three points are true of most random number generators; the last has some ramifications on serialization of RNG state in Gunmetal Arcadia Zero specifically. The simplest way to save and load a Mersenne twister is to copy its entire internal state to and from a file on disk. In fact, this is how my implementation worked for a while, and for an application with a single RNG (or only a few), this would be acceptable. The problem I ran into is that I create one or two Mersenne twisters for each entity in the game that deals in random numbers, including dynamically generated things like slimes that spawn from eggs. When each of these wrote its own state to file, I wound up with an unmanageable amount of file bloat on disk which would have made it difficult to port saved games from machine to machine, whether manually or via a cloud save system.

In the future, a system that aggregates all active RNGs and publishes all their internal states to a single file might be a good solution, but what I’ve chosen to do in the meantime is to serialize a minimal amount of data to the normal saved game file which can be used to restore the Mersenne twister to its previous state at the expense of some extra cycles.

As noted previously, a Mersenne twister must regenerate its internal state every 624 extractions. So if we keep track of (1) its seed, (2) the number of times we’ve regenerated the set, and (3) the current index (0-623), we can restore its previous state from scratch. In theory, doing this regeneration step this is more expensive than saving and loading the entire set of 624 values, but in practice, I rarely extract more than a handful of values from any given twister, meaning we only have to redo the initial seeding in most cases.

Generation

Now that we’ve covered the technical side of things, let’s look at the really interesting stuff, which is how we produce the seeds that are going to ultimately be responsible for the uniqueness of the random number streams.

As I’ve said, one of my goals is to allow multiple instances of the same enemy type to behave differently. Each enemy gets its own random number generator, so in order to get different results out of each, we need to seed them differently.

My solution was to lean on an existing piece of data: entity GUIDs. My editor automatically assigns a 31-bit GUID to any entity placed in the map. (The reason this is 31-bit and not 32-bit is that the high bit is reserved for GUIDs created by the game itself, with half of that space being further reserved specifically for dynamically-generated entities.)

Beyond these reserved values, my GUIDs don’t currently have any constraints, considerations, or intrinsic meaning. But they must be unique, of course, and there are any number of methods I could use to generate unique values. At the moment, I’m using the millisecond value of the system timer truncated to 31 bits, which gives me a period of twenty-four days before values start reappearing. The likelihood of a collision is therefore trivially larger than zero.

So if every entity has a unique GUID (yes, I realize that’s redundant), then I can use that GUID to seed that entity’s Mersenne twister, and each one will produce a unique random number stream. So that’s one problem solved. But now there’s the problem of things like loot drops that don’t want a completely deterministic random number stream.

Before I get into my solution for that problem — it’s going to be a long one — let’s start by observing that the system clock has historically been considered a satisfactory way to seed a random number generator to produce unpredictable results, as exemplified by the prevalence of “srand(time(NULL))” in C/C++. This operates on the assumption that the system’s clock will never be in the same state as it was on a previous run, so the results should be unlike any seen before.

Time-based random seeding

When I first encountered the need for unpredictably random loot drops, my first idea was to use the time() function to seed these entities’ random number generators. However, this quickly proved a flawed solution, as time() returns the system time in seconds, meaning that each and every entity spawned within the same second would get the same seed, and would in turn produce the same random number stream. This briefly created a bug where all the torches in a given room would drop the same item as each other.

My fix for this issue was to hash the result of time() with the entity’s GUID, producing a value that would be unique at any given time and unique per entity. (For the curious, my “hashing function” was to simply XOR these two numbers together. It seems good enough.)

That worked well enough for a while, but when I finally got around to implementing attract loops, it fell apart once again. Because the time-based seed would be produced at the moment an entity were first spawned, the results would vary depending on when the attract loop were played back. This necessitated moving away from time() to an alternative solution which would provide the same element of randomness but in a deterministic fashion.

My game session now keeps track of something I call a “fixed time seed.” This is a value that can be used in place of time() but which follows some additional rules to better serve my needs.

The fixed time seed is generated at the start of each session. At this point, it is actually just the result of time(), cached off once when starting a new game. In this manner, I can have the benefits of time-based randomness, but I can also save and restore this value in order to predictably generate the same values.

Where this gets a little tricky is in determining when — and how — to reroll this fixed time seed. Consider the case in which the player kills some enemies, breaks some torches, sees a few random loot drops, and then dies and replays the same section again. Given the same fixed time seed, these RNGs would produce the same values, and the same loot would drop on this life. That’s not what I want; loot drops should be re-randomized on each life, and the easiest way to do this is to update the fixed time seed once again to the value of time() at the start of a new life.

Attract loops foil this once again, however. I didn’t want to rule out the possibility of an attract loop that contained a death and respawn, and as before, requesting a new time() value during an attract loop would lead to bad results, so in this case, I needed a way to deterministically update the fixed time seed such that it would give me different values on the next life, but so that they would also be the same different values every time.

My solution to this was to simply increment the fixed time seed by one at the start of a new life.

That was the extent of my RNG solution for a few months, but just recently, I began to notice how strange it felt to load a saved game repeatedly and see the same loot drops every time. This was consistent with the rules I had previously established and with my initial goals for wanting saved games to be a completely accurate snapshot of the game in progress, but it didn’t feel right as a player. So as of last week, the fixed time seed now gets reset to the value of time() whenever a saved game is loaded.

Conclusion

Numbers are hard, yo.


Upcoming tasks for the week of April 11, 2016:

  • Monday: Dialog and vendor inventory pass on the entire game
  • Tuesday: Cutscene work, intro and ending
  • Wednesday: Cutscene work, intro and ending
  • Thursday: Finalize all menus and options
  • Friday: Record Ep. 34, write blog, addl. work as time allows
  • Saturday: Audio pass on the entire game
  • Sunday: Play and take notes

I had had cutscene work scheduled for last weekend, but after revisiting my schedule in light of wrapping the soundtrack and key art earlier than expected, I realized I could space my remaining tasks out a bit so I don’t have to crunch so much. So, that.

Grab Bag 8

In case you missed it, I wrote up an impromptu Sunday blog with comments enabled to gather feedback on checkpoints and how to handle player state across death and game over. If you have any thoughts on those issues (especially if you’ve played the vertical slice build), please leave a comment!


Last week saw me move from creating tilesets to decorating and populating levels with environment art, enemies, and NPCs. I finished the Basil, Cilantro, and Tarragon levels (levels 3, 6, and 5, respectively), and as Cardamom (level 2) was already completed back in January for the vertical slice build, that leaves only Fennel (1) and Sage (4).

fullmap
Spoilers!

It will still be another week or two before I can really get a sense of the scope of the finished game (just in time to ship it, ha!), but I’m at least getting a better sense of the variety of levels than I could when everything was grayboxed and I just had to imagine how each environment would look.


I don’t usually blog about bugs because they’re not fun to write about and I suspect they aren’t fun to read about, but I ran into one this week that was worth mentioning. The bug manifested as “an NPC walks off screen but a second copy remains and can be interacted with as usual.” The catch was, this only happened when progressing through the game normally; launching directly into the map through the editor worked correctly.

jaywalk

That immediately raised some suspicions of what the level transition code was doing, and, skipping over some boring technical stuff, in short, when reaching the end of a level and transitioning to the next one, the new level would be loaded once, immediately discarded, and then loaded again. And if it weren’t for this odd edge case, I’d have never noticed! The only reason the NPC was appearing twice was because of a side effect related to suppressing entity spawning until the room is fully visible in order to appear more NES-like.

When entering a room, I queue up all the entities in the room for spawning, but I wait to actually spawn them until we’ve finished scrolling or fading in (with the exception of things like teleport exit markers that necessairly must exist earlier). Once the scroll or fade has finished, I spin through the list and spawn the queued entities.

So what was happening here was, I would load the level once, queue up all the things that needed to spawn, including this NPC, then immediately discard the level but not the queued entities. Then I’d load the same level again, queue up the same entities again, and spawn both queued copies once the fade-in had finished.

My first thought once I realized this was, “I guess I should throw out any entities queued for spawning when the level is destroyed.” And in all likelihood, that would’ve fixed the bug, but of course, it overlooked the larger issue of why the level was being loaded twice in the first place. That turned out to be more boring technical stuff. The game has an opportunity to load the incoming level in response to displaying the title card for that level, and in many cases, that is the correct behavior (as when loading a saved game or dying and restarting). However, in some cases, such as starting a new game or transitioning to the next level, other code is better equipped to handle the level load. In this case, I was already loading the next level as soon as possible in response to the previous level fading out, but I had forgotten to opt out of letting the title card also attempt to load it. So, both were happening.


A few weeks back, I showed some work I had done on the key art or cover art for Gunmetal Arcadia Zero. I’ve made a little more progress on that this week.

I hadn’t been happy with attempts to color the image digitally, but it wasn’t feeling good enough as a black and white outline, so I decided to give it a try with paint. In the worst case scenario, I figured, I could digitally remove the color and be left with a more interesting grayscale texture.

painted

I printed out an image of the inked art I had done previously and, lacking actual watercolors, went over it with acrylics thinned with water. I used an Aquash brush which felt pretty good and matched the look and feel of the brush pen I had used previously.

Since I was using water on a sheet of plain printer paper, the sheet got a little warped and wavey, but after scanning it, I just cropped out everything outside the existing outline, desaturated the colors a bit, and overlaid it with the original art.

painted_overlaid

Next, I needed a background. I had the idea to do a sort of mottled wash of colors that would vaguely imply a landscape. I built up a few layers of faux watercolors, using greens and teals for the earth and purles and reds for the sky. I then worked outward from the corners with more opaque black paint to vignette the whole scene.

wash1

I got that scanned, but it wasn’t really clicking. On a whim, I inverted the image, and suddenly it made more sense. Dark teals for the sky appeared stormy and ominous. Dark reddish purples for the earth appeared blood-soaked and barren.

wash2

The colors in both the character and the background were a little too exaggerated, so after scanning and compositing, I brought everything closer together towards sepia tones and also added a digital vignette over everything, even the logo.

layout

As before, I can’t say for sure whether this is the final shipping key art, but I’m pretty happy with it, and, you know, there’s not too much time left to make any more changes.


Upcoming tasks for the week of April 4, 2016:

  • Monday: Decorate “Fennel” with environment art
  • Tuesday: Populate “Fennel” with actors
  • Wednesday: Decorate “Sage” with environment art
  • Thursday: Populate “Sage” with actors
  • Friday: Record Ep. 33, write blog, addl. work as time allows
  • Saturday: Tweak intro cutscene and begin work on ending
  • Sunday: Ending cutscene

This upcoming week’s schedule is noteworthy in that Thursday’s task concludes core development of Gunmetal Arcadia Zero. There’s still some ancillary work to be done beyond that, including tuning and balancing the entire experience and adding a few features like transitioning from the final level to the ending (and figuring out what to do with the saved game after the game is finished), but there shouldn’t be any new core gameplay content coming online after that point.

What are we down to now? Three weeks? That sounds about right.

Ad Hoc

I have another regularly scheduled blog for tomorrow, but in light of some work I’ve been doing today related to saved games, I wanted to bring up an issue that’s been at the back of my mind for the last couple of months.

I’ve enabled comments for this post, and anyone is welcome to contribute thoughts, but I’m especially interested to hear from those who have played the vertical slice build. (And if you haven’t played the vertical slice build yet, you can find that here.)

To establish some context, here’s a series of tweets I made earlier today:

The issue is this: when you die and continue from the last checkpoint, what state should your character be in? I’ll explain first how this works in the current build and why it works that way, then I’ll present a few alternatives I’ve considered along with pros and cons for each. None of these is objectively the correct decision, but I’m curious to hear opinions on these or any other options.

The way things work today is, when you first enter a level, a checkpoint is activated, and a snapshot is made of the current game state. If you die, you return to this exact state minus one life. If you run out of lives, you return to this same state with the default number of lives.

After killing a boss or a miniboss, a midlevel checkpoint is activated. Another snapshot of the game is recorded separate from the one at the start of the level. If you die after hitting this checkpoint, you return to that state, but when your lives are depleted, you go back to the start of the level.

The pros of this approach are:

  • This is how things already work. This implementation is believed to be stable and bug-free. (And yes, with only a few weeks of development left, every alternative will necessarily have the inverse as a con.)
  • This is roughly analogous to the way many classic games have worked, including Castlevania and Mega Man.
  • There’s less risk of winding up in a state in which the game is saved in a very difficult scenario with little or no resources.

And here are the cons:

  • Actions taken during the course of the previous life are lost when starting the next life.
  • Checkpoints are infrequent, possibly requiring playing through the same segments repeatedly.

That first one is the big problem here. It’s really the only reason I’m considering making any changes to this system so late in development. It’s frustrating to play through a leg of the game, maybe buy some upgrades and find a subweapon you really like, only to die and have to do it all over again. It’s doubly frustrating when this happens in the first level and you have to replay introductory narrative/tutorial stuff, even as minimal as those elements are.

There are two options I’m considering to improve this experience. They are not mutually exclusive; I could do both, or either, or neither.

The first option is to increase the frequency of checkpoints. Rather than checkpointing only at the start of a level and after any boss fight, I could also checkpoint before boss fights, or on any room transition, or any time equipment is changed, or any number of other criteria.

The second option is to carry over changes to the player character’s equipment on death (and possibly on game over, although I haven’t yet thought through the ramifications of that one). In this way, any new gear you’ve found, any money you’ve collected or spent, any upgrades you’ve equipped would carry over to the next life. You’d still start from an earlier location, but you could forgo the frustration of having to retread all the same ground in pursuit of the same gear you had found previously.

In combination, these options provide us with four scenarios:

  • Infrequent checkpoints, no carry-over. (This is how things currently work.)
  • Frequent checkpoints, no carry-over.
  • Infrequent checkpoints, carry-over.
  • Frequent checkpoints, carry-over.

Let’s look at the pros and cons of each of these.

Frequent checkpoints, pros:

  • Less potentially frustrating retreading of the same ground.

Frequent checkpoints, cons:

  • Risks making the game too easy by virtue of being able to exploit checkpoints by ignoring damage from enemies and getting a cheap health refill by dying immediately after reaching the next checkpoint.

Carry-over, pros:

  • Less frustrating loss of previous acquisitions.
  • Feels more congruous with what other games do.

Carry-over, cons:

  • Risks making the game too hard by virtue of saving in a state with no resources (e.g., after expending all ammo and bombs fighting a boss and then dying to it).

So, that’s what I’ve been thinking about. My default behavior is to be super resistant to any change that could destabilize the build this late in development, so left to my own devices, I’m most likely to leave things the way they are. But this also feels like it has the potential to be a big source of frustration and shelf moments in this game, which is why I’m opening it up to discussion.

Comments are now closed, but you can send me feedback on Twitter (@PirateHearts) or by email (jpittman at gmail dot com).

Shifting Sands

I forgot to account for Easter Sunday when I scheduled my tasks, so I’ve lost a day. It’s not a huge concern as I was ahead of schedule in other regards (notably the soundtrack, character portraits, and key art), and I’d prefer to start the process of decorating and populating each level on a weekday anyway. This is going to be the last big content creation sprint on Gunmetal Arcadia Zero and it demands my complete attention. This is the phase of development that will define the experience. All the systems and features I’ve spent the last eighteen months building won’t mean a thing if this content doesn’t put it to the best possible use.

I’ve been using this expression “additional work as time allows” in my posted schedules recently, and I think it’s worth talking about what that means. I’ve reached that point where the work that I’m actually doing on any given day is only partially informed by the daily task list I’ve been maintaining. That’s not to say the scheduled work isn’t getting finished too, at least in some fashion, but a majority of my time is being spent putting out fires and addressing longstanding bugs and polish issues that I absolutely have to tackle before shipping.

GunPreq 2016-03-24 15-44-52-476

Some examples of these sorts of issues include:

  • Crumbling tiles now respawn after a length of time or on room re-entry.
  • Vendors can never buy back items for a higher price than they sell the same item for, to prevent infinite money exploits.
  • Healers can charge variable rates for healing based on the same criteria as vendors use to determine their prices.
  • Collecting a new subweapon causes the old one to drop so it may be collected again.
  • Collecting the same subweapon adds a small amount of ammo.
  • Starling’s character portrait has been redrawn because I wasn’t happy with the old one.
  • Minibosses now have more consistent behavior in how they react to damage and death.

I’m slowly starting to get the shopping loop in place, and I’ll be showing a little bit more of that in Wednesday’s video, but it’s feeling like that’s going to be a balancing act all the way to launch. It’s a weird thing where I feel like maybe I’ve invested a little too heavily in RPG-like mechanics for a game that’s not deep enough to warrant them. I won’t really know for sure until I have the entire game built out and can play through it start to finish and understand its scope fully.

I’ve reached the stage of development where I start questioning and doubting everything I do, and now you can too! :D

With only a few weeks of development left on Gunmetal Arcadia Zero, it’s very late in the game to be doing editor tasks, but as I’ve been spending more time using the editor recently and working with entity markup in particular, it’s become worthwhile to improve my ability to search all markup.

markupsearch2

This new editor dialog allows me to search any markup in the game that may exist in entity templates or instantiated entities, and to search by tag, attribute name, or attribute value. This has already proven to be extremely useful in tracking down old and deprecated data, and I can imagine it will become even more valuable in the future as the scope of my content grows for the roguelike Gunmetal Arcadia.

Upcoming tasks for the week of March 28, 2016:

  • Monday: Decorate “Basil” with environment art
  • Tuesday: Populate “Basil” with actors
  • Wednesday: Decorate “Cilantro” with environment art
  • Thursday: Populate “Cilantro” with actors
  • Friday: Record Ep. 32, write blog, addl. work as time allows
  • Saturday: Decorate “Tarragon” with environment art
  • Sunday: Populate “Tarragon” with actors

Five weeks to go. Actually closer to four, probably. Let’s just say four. Four weeks to go.

GDC Recap

As I mentioned in the previous blog post, I spent last week out in San Francisco for GDC and therefore don’t have a development update this week, and I won’t have a new video on Wednesday. But I did want to write up some thoughts about this year’s event and post the development schedule for the next few days.

gdc16_badge

This was my third visit to GDC, my second as both a full-time developer and an indie developer, and my first time speaking. So there’s a life goal Achievement Unlocked. I gave a talk first thing Monday morning as part of the Math for Game Programmers tutorial. It was called “Building a Better Jump,” and it was the final form of a blog post that I drafted years and years ago on my old personal blog.

My slides should be up on essentialmath.com shortly, and I’ve already temporarily uploaded them (along with annotations of a rough transcript of my actual talk from memory) here:
PowerPoint Slides
PDF (no annotations)

gdc16_poster

This was by far the largest and most visible talk I’ve ever given (and it will be even more so in another month or two when it hits the GDC Vault), and I feel like it went pretty well. I thought I’d be super  nervous, hands shaking, voice cracking and all that, and I really wasn’t. Part of this might have been avoiding caffeine before I spoke, part of it might’ve been the Xanax I took when I woke up, but I also wonder if spending the last few months talking to a camera weekly has helped in that regard. It’s not the same thing as a real audience, but it’s definitely helped me improve the way I think as I speak in order to predict what I’m going to say next and account for any mistakes or omissions in real time. Also I rehearsed the heck out of this thing, so there’s that.

For the last few years, GDC speakers have received a deck of playing cards featuring the top speakers from the previous year. So that’s my new life goal, I guess: make the GDC speaker deck. Send in those evals if you were there!


This was the first year I had a GDC pass that wasn’t just an expo pass and would get me into talks, so naturally I spent most of the week doing just that. The highlights of my week were the Indie Soapbox, #1ReasonToBe, and the GDC Microtalks. Each of these consisted of a number of short talks from a diverse panel of speakers, and each was a great experience in its own right. I don’t know whether these will be released as free content when this year’s talks hit the GDC Vault, but when and if they do become available, I’d encourage everyone to check them out.


I’ve seen a lot of game developers talking about impostor syndrome recently. But I don’t usually experience myself, at least not the way it’s described. Tommy Refenes gave a short talk about impostor syndrome during this year’s Indie Soapbox which opened with a similar sentiment. And by the end of the week, I could commiserate. I’m confident in my abilities as a moderately isolated indie developer. I can make cool things that I believe in. But selling them is another matter, one in which I’m actively growing but still far from proficient, and meeting people in person at an event like GDC, especially people I follow and have maybe spoken to on Twitter…well, that’s where I hit the wall that is impostor syndrome, I guess. I had an experience multiple times this week (and probably a few times last year as well, though I might’ve suppressed the memory), where I met someone, exchanged words briefly, and only much later realized that I had been talking to someone I knew through the internet and held in some esteem. That’s…pretty embarrassing.


I’m becoming increasingly conscious of the fact that the roguelike Gunmetal Arcadia, historically the definitive eponymous Gunmetal Arcadia, the game I’ve been developing and trumpeting for a year and a half now, it might need a subtitle. That’s weird.

My intent was that Gunmetal Arcadia Zero would be a prequel that would ship ahead of the flagship game and establish the game mechanics and world, and it’s still in a good place to do that. But I’m finding that it’s sounding more and more ambiguous to my own ears which game I’m referring to when I just say “Gunmetal Arcadia.” I can’t count how many times I’ve prefaced it with “the roguelike” as I just did in the preceding paragraph. And that represents a failure in branding.

I don’t yet know what this subtitle would be. It would need to still communicate this aspect of being the definitive experience so as to not be perceived as a sequel to Zero specifically, which is a smaller game (and importantly, a cheaper game). I’ll sleep on that one for a while. It’s entirely possible by the time that game launches, it’ll be a non-issue, but it’s something that’s been on my mind recently, and I felt like it was worth talking about.


Upcoming tasks for the week of March 21, 2016:

  • Monday: Create “Tarragon” tileset
  • Tuesday: Create “Fennel” tileset
  • Wednesday: Create “Sage” tileset
  • Thursday: Create primary weapons
  • Friday: Record Ep. 31, write blog, addl. work as time allows
  • Saturday: Create secondary weapons
  • Sunday: Decorate “Basil” with environment art

As I’ve said, there’s not going to be a new episode of Let’s Make Gunmetal Arcadia this week since the pre-recorded one went out while I was away at GDC, but I’ll be picking that up again next week.

I have about six weeks left to finish this game. Let’s do this.

Get Rich Quick

Bit of a short blog post this week, as I’m out of town for GDC, but I do have a sort of rambling stream of consciousness video ready to go for Wednesday. The tl;dr (tl;dw?) for that one is, “Everything’s crunchy, everything’s coming online all at once, sleep never, make game.”

Hey, that’s not a bad name for a GDC talk next year.

I’ve been doing a little more work on the shopping/economy loop. As I mentioned a week or two ago, I increased the money cap from 99 to 9999 to provide finer granularity in the value of money and items, and now I’m following up on that by creating multiple denominations of currencies. The standard gold coin I’ve been using for like a year now, that represents 1 Unit of Money. (Sidebar: I should probably figure out what the Arcadian unit of money is, huh?) Additional coins in a variety of colors now exist which values currently ranging up to 100. I’ll be doing more tweaking and tuning of these values as the game coalesces in the coming weeks and I have a better sense of its scope, but this is already feeling like it provides a more interesting, compelling sense of treasure hunting, and importantly, it means I can drop lots of coins without worrying about throwing off the economy.

GunPreq 2016-03-11 19-39-31-825

Choosing colors for these coins was kind of a fun challenge. They had to be visually distinct, which meant using the entire color spectrum, and I also wanted to find an intuitive color ranking system. I wound up using sort of a hybrid of Zelda‘s rupees and the World of Warcraft rarity colors:

Gold < Silver < Green < Blue < Red < Purple < Orange

I’m a little concerned that orange and gold are too similar, and it would be unfortunate if you missed out on the thrill of seeing a rare coin drop because it looks too much like a common coin. The NTSC color shift in the CRT simulation does help separate these a little more clearly, though. I’m also certain there’s going to be some ambiguities for players with some forms of color blindness, but since there’s no real decisions to be made with regard to the color (i.e., it’s a coin, it has only positive benefits and will despawn if you don’t grab it quickly), I’m not too worried about it.

GunPreq 2016-03-11 21-59-15-466

Gems are another form of currency. These do not immediately increase your money count, but instead go in the inventory and can be sold to vendors. Different vendors will pay different rates for different gems, so depending on your needs, you may be better off saving them until you can find a better deal. Gems are currently ranked:

Green < Blue < Red < White/Clear

Treasure chests now contain coins and gems exclusively, and I’ve been doing some work to make it easier for different loot droppers (enemies, sconces, chests, etc.) to pick and choose what types of things they drop. This addresses a problem I was feeling in the vertical slice build where treasure chests felt only marginally more valuable than the average torch or sconce.

In the future, I may also add an alternative loot dropper akin to treasure chests but that only drops hearts, as that’s beginning to feel like a much-needed resource. (I’ve also been considering healer NPCs, who would serve the same function.)

sellall

Finally, I was getting irritated with having to sell off stacks of gems one at a time, so I added a “Sell All” option. This is a little bit of a compromise between no support for selling multiple items and a full-fledged spinner for choosing a number to sell, but given my time constraints and how much of an unknown it would be to add a spinner, this felt like the most practical option. I’m also operating under the assumption that selling an entire stack is going to be the most common action. I can imagine some scenarios in which selling only part of a stack would be desirable, as in the case of consumables which serve some other purpose besides being sold for money, and maybe I’ll broach that in the roguelike Gunmetal Arcadia further on down the road.

Programmer Art

Last October, I sketched and vectorized some character art with the idea that it could maybe serve as key art or cover art for Gunmetal Arcadia Zero. But I wasn’t totally happy with the finished product; it was a little too flat and cartoony and not in keeping with the tone of the game. So last week, I drew another sketch.

1 - initial

I liked the direction, but there were a number of big problems, notably the awkward twist between the torso and waist that I was having trouble resolving. I did two more versions of this pose but still couldn’t work these out.

23

I scanned all three versions and mirrored them to get a more balanced view. I’ve found in the past that my sketches tend to lean to one side, and regularly mirroring the image while iterating helps me account for this lean. I printed out light outlines of each, and went over them again, making changes and trying variations where I could. I brought the raised knee out a little bit more, which meant figuring out how to foreshorten the leg a bit, and I tried to straighten out the lines of both arms and the sword.

4 - redo of 15 - redo of 26 - redo of 3

Each of these had bits and pieces that I liked, but no one was standing out as the single obvious best version, so after scanning them again, I composited elements of each into a single image, shifting and scaling limbs until things looked a little more proportional. I printed out this composite and went over it again in pencil. I was still having some trouble with the tilt of the wrist, and I made some notes here regarding how I hoped to address that.

7 - combined with notes

One more iteration of scanning, editing, printing, and tracing, bringing over the noted changes to the wrist and refining all the lines further.

8 - after notes

At this point, I was satisfied with the shape and ready to ink it. I had been unhappy with previous attempts to ink or vectorize drawings because of the loss of expression incurred by reducing pencil sketches to fixed-width lines, so to mitigate this, I picked up a brush pen (the fantastic Pentel pocket brush pen) and gave that a try. I did three separate versions of this, each with a few idiosyncrasies.

9 - ink 110 - ink 211 - ink 3

I intended to composite these after scanning as I’d done with the earlier pencil sketches, but I liked the third attempt well enough that with a few additional changes to shading, it was ready for use.

12 - final ink 3

So there’s my character art. I haven’t decided yet how I’m going to use this. I tried coloring it digitally but wasn’t really happy with the results. I’ve been debating trying to do it manually in watercolor, but I haven’t picked up the art supplies yet.

13 - colored

While thinking about how to lay out the logo and character for the cover, I put this together. I liked the stark contrast, but it was still a little too empty.

14 - white

I colored the background orange to help the character and title pop a little bit more and added some subtle gradient-modulated swirls to break up the monotony.

15 - orange

So that’s where I am right now. It doesn’t necessarily look like game cover art, but I like the way the color pops in contrast to its surroundings when I do the Steam new releases test. (This is just a photoshopped image, so don’t read anything into it.)

steam_test

I might do a little more work on this over the next few weeks, maybe see how it looks with a proper environmental background or some monsters in the foreground. We’ll see where it goes and whether I have time to do any more work on it.

Upcoming tasks for the week of March 7, 2016:

  • Monday: Create “Mint” midboss
  • Tuesday: Create “Rosemary” midboss
  • Wednesday: Create “Basil” tileset
  • Thursday: Create “Cilantro” tileset
  • Friday: Record Ep. 30, write blog, addl. work as time allows

I haven’t scheduled any tasks for this weekend, as I’ll be preparing for a flight to San Francisco for GDC, which is only one week away. I will be bringing a build of the game with me, partially in case I have an opportunity to show it off to anyone, but also so I can play through my grayboxed levels a few more times and get a better sense of the complete scope of this game, because it’s still not really clear in my head, and I still have yet to decide which two levels of my eight candidates to cull.


Last-minute update: I did a few character portraits over the weekend, so I’m sneaking those into this blog post too since I’m already talking about illustrations. These will appear in the game next to dialog boxes when talking to these Apparently Important Story Characters.

Gannet Grackle Jay Starling Thrush Wren

Wren is obviously the coolest kid in Arcadia.

wren_portrait

Grab Bag 7

I spent most of last week working on a new shopping interface, but I’m saving that one for Wednesday’s video because it works better in that format. Instead, this week I’ll be looking at a handful of ongoing changes to the game in the context of a shifting schedule. That’s right, it’s Grab Bag Time!


Let’s start with the GIF-able stuff.

GunPreq 2016-02-27 04-00-22-264

I posted this one on Twitter a couple days ago. I had been toying with the concept of homing projectiles for a miniboss in a slightly different context (detailed below), and I thought it would be interesting to see how they worked in a more typical case of aimed shots fired at the player. The big takeaway was that it was frustrating to not be able to destroy them, so I added that option. They can still be a little overwhelming when there’s more than one or two of them on screen, so this is something I’ll have to use sparingly, but it’s a nice unexpected addition to my toolbox.

GunPreq 2016-02-28 00-11-16-239 panoptic

This is the boss of “Basil,” which is the…fooouuurth level, maybe? Maybe the third. Almost certainly one of the wilderness levels, in any case. The concept for this boss was, “How do I make an immobile turret challenging?” My solution was to give it aimed shots, place it out of reach of many attacks, and throw in some additional enemy spawners as a distraction.

GunPreq 2016-02-28 00-08-41-175 gourd grail

This is the miniboss of the same level. It’s a pair of enemies, tentatively titled “Gourd” and “Grail.” Gourd is large and moves quickly but has no ranged attack and is fairly harmless alone, while Grail flies around and fires projectiles at its companion. These projectiles harm Vireo on touch, but they fully heal Gourd if they reach him, so Grail must be dealt with first.

As with most of my minibosses, I’m imagining these guys will make additional appearances later in the game, perhaps in scenarios that alter the dynamic with different environmental hazards and such.

GunPreq 2016-02-28 00-22-26-768 event lock

Hey, remember this room from the vertical slice build? It previously contained another “Parity” who would drop a chest when he died. That wasn’t really what I wanted, but it wasn’t until just a day or two ago that I finally added support for a feature I was intending to showcase here: lock-in rooms.

This is an idea I’ve blatantly lifted from the likes of Wonder Boy in Monster Land. Every door in the background presents an opportunity for risk and reward. Some of these might be item shops, some might be optional platforming challenges to reach some desirable loot, and some of them will lock you in and make you fight monsters. There will be a reward for clearing these rooms proportional to the challenge, but when you’re running low on health or lives, I want there to be some tension in deciding whether it’s worth the risk to see what’s behind a door.

That dynamic is one that I’m especially excited to see take shape in the roguelike Gunmetal Arcadia, once levels are randomized and the contents of a door can’t be learned and known in advance. But for at least a first playthrough, hopefully it will be an interesting and exciting element of Gunmetal Arcadia Zero as well.


newhud

Keen-eyed readers may have spotted some small changes to the HUD this week. I’ve updated the art for subweapons and removed the square brackets around this icon, which in turn cleared up space to expand the money counter to four digits. So let’s talk about that.

When I started laying out the HUD and choosing resource caps, I was looking at Binding of Isaac primarily. Isaac caps money at 99, but that cap is rarely reached. A standard item costs 15 coins by default, and in many runs, that price is high enough to give pause before making a purchase. Coins are few and far between, and it can be tempting to spend them on consumables and recovery items.

That balance works well for Isaac, but unfortunately, it necessarily precludes something that I love about Castlevania games and want to integrate into Gunmetal Arcadia, and that is the notion that everything drops items all the time. In Castlevania, virtually every torch or sconce can be destroyed, and most of these drop money. In Symphony of the Night and its derivatives, these return every time a room is revisited (along with enemies), guaranteeing an endless supply of things to attack. Part of the reason SotN works as well as it does is that it’s fun to simply move through the world, jumping and hitting things, and this feature provides an incentive to do so. And yet it’s not really practical to grind for money by repeatedly reentering rooms and breaking the same torches over and over, because on repeat visits, they will only drop small amounts of ammo or coins worth a single gold. (For comparison, a single potion costs 800 gold.) But the value isn’t what really matters in this case, it’s the experience of hitting something, getting the feedback of seeing it be destroyed and drop an item, and then collecting that item. That’s where the fun is.

All of that is to say that I was having trouble reconciling the desire to drop loot all over the place because I know that’s it’s fun to collect with a relatively low money cap. My solution is to effectively decrease the minimum amount of money I can drop at once. Since we’re dealing with integers and each pickup was already worth only one unit, that means increasing the cap, in this case by two orders of magnitude. In doing so, I decrease the relative value of a single coin and can justify dropping them as frequently as I would like without worrying about unbalancing the game.

A second axis of balancing currency is how I scale things from the start of the game to the end. Going back to the example of Isaac, that game doesn’t scale prices at all over the course of a single run. Items cost 15 coins at the start of the game, and they cost 15 coins at the end of the game. The value of a single coin never changes.

At the other end of the spectrum is…just about every RPG ever made. Prices are only one facet of the ubiquitous rise in numbers throughout RPGs; health bars get longer, armor ratings get higher, and the numbers just keep going up. This is, I suspect, most often a gating mechanism to ensure the player has spent enough time fighting monsters and leveling up before attempting to progress to the next leg of the game, but it also creates an interesting mechanical aesthetic in which, in the best case, the player constantly feels like they’re getting stronger and stronger, even while remaining relatively balanced to everything. (In the worst case, the player never feels like they’re quite strong enough, no matter how big those numbers get. That can be a tough line to walk, and it’s one of many reasons I haven’t pursued experience points and leveling in Gunmetal Arcadia.)

I can say with certainty the balance in Gunmetal Arcadia Zero is going to be closer to Isaac, but I’m not sure it’ll be entirely flat like that. It’s possible I’ll still have enough wiggle room to capture the feeling of escalation that accompanies seeing numbers get bigger. We’ll see.


As always, I’ve been plugging away at new ideas for tunes whenever I have the opportunity. Here’s some of my more recent snippets. None of these are finished, and most of just a single repeating measure, but they could grow into finished pieces by the time at least one of these games ships.


That’s it for this week! Let’s take a look at what’s coming up…

Upcoming tasks for the week of February 29, 2016:

  • Monday: Create “Cilantro” boss
  • Tuesday: Create “Cilantro” midboss
  • Wednesday: Create “Mint” boss
  • Thursday: Create “Mint” midboss
  • Friday: Record Ep. 29, write blog, addl. work as time allows
  • Saturday: Create “Fennel” boss
  • Sunday: Create “Fennel” midboss

Yep, it’s gonna be boss city around here for the next week or so. You may also notice I’ve started scheduling tasks for the weekend. I’m in full-on crunch mode and probably going to be working every evening and weekend until launch. I was already on a pretty aggressive schedule for a June release but I have an opportunity to launch a little bit sooner, so I’ve shaved down my task list where I can, and I’ll be putting in extra hours to get as much content as possible into the shipping game.

GDC is only two weeks away! As I mentioned before, I probably won’t have a new blog or video that week (or possibly the next, depending on when scheduled content goes up), but it should be a nice change of pace in between weeks of crunch.

One last note for my Patreon supporters! Please keep an eye out for a very short survey I’ll be posting soon. I’m in the process of finalizing the credits for Gunmetal Arcadia Zero, and I need your responses! If you’ve contributed to my Patreon in the past but are not currently contributing, please get in touch with me by Twitter (@PirateHearts) or email ([email protected]) so I can make sure I get your response as well!

Watermarks

I realized as I was preparing this blog post that I totally forgot to continue my newly proposed habit of posting upcoming tasks for the week last time. For reference, it would’ve been three days of enemies, one of NPCs, and one day for recording and blogging.

So I’ve been doing a lot of work on enemies recently. Most of this has been spriting, and I’ll be talking more in Wednesday’s video about some of the new sprites I’ve drawn and changes I’ve made to older sprites to bring them more in line with where this game’s art style has wound up. But I’ve also been taking a high-level look at my entire rogues gallery and trying to identify any missing pieces that I can address before it’s too late. I still have a week or two scheduled for bosses and midbosses later this month and into February, but I’m quickly approaching the cutoff for new enemy types to make it into Gunmetal Arcadia Zero.

For some time now, I’ve been wanting to implement a burrowing enemy. I sort of knew intuitively that this thing would need to be able to switch between two different states (burrowed and surfaced) and have different functionality in each state, but until this week, I hadn’t really gone through that whole process yet. I guess the next closest thing would be the biped enemy, which has different behaviors depending on whether or not it’s aware of the player, but even that one didn’t change its physical appearance and collision bounds in response to this event.

GunPreq 2016-02-15 15-22-32-825

I already had most of the pieces necessary to make this work, including the option to alter collision bounds depending on the current animation, but the two things I couldn’t do were alter the entity’s idle animation and hurt animation (respectively, the one that plays by default when no other animation is playing and the one that plays when the entity takes damage). Since this state is fundamentally different enough that it wouldn’t make sense to fall back to a generic idle or hurt anim (as the player can when taking damage while crouched despite that change in posture and bounds), I finally had cause to add support for these changes.

Next, I turned my attention to wildlife. Wild animals (and wild elves, who are probably worth discussing in a later blog for their narrative importance) are docile until provoked, at which point they will attack the player just like any other enemy. You can choose to engage them or not, but they may also appear in situations in which they are likely to get caught in the crossfire between the player and Unmade monsters, potentially introducing another hazard there.

GunPreq 2016-02-17 12-47-29-346

As I mentioned in a recent tweet, I gave wild elves the ability to use the same throwing knives the player can use, with impressively deadly results. This will probably require some tuning, but I’m happy that I was able to reuse content in this way and have it mostly just work.

GunPreq 2016-02-17 15-07-11-907

As of the time of writing, my enemy count (not including bosses) is up to something like 22 or 23. My goal was to have at least 20, my reference points being Castlevania (14), Battle of Olympus (22), Faxanadu (30), and Zelda II (35). I’m planning to have a number of variants of each of these as well, with different palettes, names, projectiles (where applicable), health values, behavior timing, and any other sort of balance tuning I might want to tweak. For a six-level game, that feels pretty jam-packed, and it’s always possible some of these concepts might slip a bit and get pushed back to the roguelike Gunmetal Arcadia, but this should also be the sort of content that’s super fast to produce, so I’m optimistic.

Oh! Here’s a cool thing that almost slipped through the cracks between when I wrote last week’s blog and when it went live. That Saturday, I finally took a stab at a feature I’ve been wanting for as long as these games have existed: boss intros a la Ocarina of Time. These serve two purposes. First, they clearly delineate boss fights as a major event. (By contrast, midboss fights, though functionally similar in terms of locking the player in until the boss is dead, will not utilize these intros.) Second, they offer the chance for just a hint of additional narrative by virtue of describing the boss not only by its name (which will be unique, unlike normal enemies and possibly midbosses), but also by a “class” or “archetype,” as in the of the Cardamom vertical slice boss “Absolved,” whose class is “Lucid Facade.”

GunPreq 2016-02-13 18-23-56-115

One of these days, I’ll probably write up some notes about how and why I’m naming enemies this way; the short version is that I love the names of the angels in Bayonetta and wanted to establish a slightly different take on a similar concept.

Upcoming tasks for the week of February 22, 2016:

  • Monday: NPC/vendor sprites and behavior
  • Tuesday: NPC/vendor sprites and behavior
  • Wednesday: Create “Basil” boss
  • Thursday: Create “Basil” midboss
  • Friday: Record Ep. 28, write blog, addl. work as time allows

Somehow, GDC is only three weeks away! I’m pretty happy with where my talk is; rehearsals are consistently hitting under the 25-minute mark and I’ve smoothed out the transitions between slides, so I’m feeling pretty good about that. Depending on what my schedule looks like and whether I have any additional material to discuss, I may take that week (or the following) off from blog and video updates. In the past, I’ve made an effort to have content scheduled even when I was away from work for some time, but since I’m so far into content production at this point, I may not have anything extra that fits that bill. We’ll see.

Nonlinear

In my continuing efforts to confuse the linear nature of this blog, today I’m documenting the process of fixing a bug that I’ll be showing off in Wednesday’s upcoming video, recorded last Friday. So it goes.

The bug looked like this.

GunPreq 2016-02-12 17-12-37-296

I had seen this bug a handful of times over the last few months, dating back to October when I initially implemented shallow water. I first noticed it when bombs began falling through the world, and I was later able to reproduce it with the player character. But it popped up so rarely (largely due to the absence of water in most of my test maps) that I kept forgetting or choosing to ignore it.

I did finally find a consistent repro, as seen in the GIF above, and that has allowed me to fix the bug. I’ll get to the fix in a bit, but first let’s take a look at what’s happening here.

I fabricated this repro case based on an observation that this tended to occur when the player hit the corner of a solid tile adjacent to water while falling. This didn’t immediately offer any indication of what might be causing the bug, but it did help me find a more consistent repro. As it turned out, the exact situation necessary to reproduce this issue was slightly framerate dependent and involved the player character moving far enough in a single tick to collide against both the surface of the water and the adjacent wall, as shown below.

collision

Once I’d had this realization, I found it even easier to reproduce the bug, and I could begin tracking down exactly why the player fell out of the world when this occurred. To fully explain why this was, I need to start by explaining how some parts of my collision system work.

As I discussed in one of my earliest posts on this blog (and these later ones) the collidable surfaces of the environment are dynamically generated (and then cached and retrieved for better performance) in response to sweep attempts made through the world. I chose to implement this by representing the world’s collision as a single primitive that may be represented by arbitrary geometry depending on the nature of the thing colliding against it. This is unusual in comparison to most other things in the game, which are typically represented by a single box primitive that may be decomposed into its four sides for sweep tests.

When a sweep test collides against something solid (a “blocker” in my engine’s vocabulary), the sweep path is interrupted and possibly redirected based on how the sweeper wants to react. A typical reaction is to slide along the blocking surface until another collision occurs.

When a sweep test collides against something non-solid, things work a little differently. We cache off the result of the collision such that we can react to it, but we do not interrupt the sweep and instead continue testing against everything else in our path.

The world’s dynamic collision primitive is once again highly unusual in that it may consist of both solid (blocking) and non-solid surfaces. Floors and walls that the player can stand on or collide against are solid. Water is non-solid. This dual nature turned out to be a fundamental part of the problem.

If, in a single tick, the player character’s sweep was to pass through both the surface of the water and the adjacent wall, here’s how it would go: First, we would touch the surface of the water. We would react to this appropriately, altering our physical constants to slow our movement. The surface of the water would be flagged as non-solid, so we would continue our traversal. In fact (and this may be an error in and of itself, and certainly something to investigate in any case), we would effectively repeat the last step of the sweep, beginning to end, rather than continuing from the last known point of intersection. We’d test against every primitive in our range, including the world itself, but here’s where the bug was: We’d see that we had a previous collision result against the world, and that that result had been non-solid, and we’d assume therefore that the entire primitive — that is to say, the entire world — were also non-blocking and that it would be redundant to test it further. We’d opt out of any more tests against it (tests which would otherwise have collided against the adjacent wall), and the sweep would continue unhindered. The player character would pass through the wall unobstructed, and we’d never get a collision result saying we had exited the body of water, so the physical constants would remain altered.

If that seems like a complicated mess to digest in written form, I can assure you the code itself does little to elucidate the matter. My collision code has never been pretty, and though I’ve made some attempts to reduce its size by breaking common patterns out into their own functions, game by game it’s grown into a monster stretching across multiple files and projects. My concern as I moved from understanding the bug to fixing it was that I might inadvertantly break other cases without realizing.

In order to minimize the risk of introducing side effects, I tried to alter the program flow only in this one particular case. I added hooks to the core collision system to allow this case to be trapped and handled by other code elsewhere. When checking whether a primitive is redundant by virtue of having previously generated a non-solid collision result, I now allow the primitive itself the chance to forgo this early out if it believes itself to be important. In this way, the world’s dynamic collision primitive can be tested again even when a previous collision occurred against a water surface in the same tick. The other side of this coin, however, is that the primitive must then ensure that it actually does not produce redundant results, as the sweep test could enter an infinite loop if it did. Any time a collision result is generated, it’s compared against previous results if and only if the previous result were one that could have prompted this retest. If a match is found, the redundant result is discarded and the sweep continues as normal.

All said, it took a few hours to find and fix this bug, which isn’t too bad considering its nature. Along the way, I also discovered another bug that I initially thought to be related (and probably was in some fashion, although its fix was simpler). This turned out to be a silly mistake in offsetting collision surfaces relative to tile boundaries, such that the player was reacting to exiting a body of water that they had not previously entered when standing directly over it and jumping.

GunPreq 2016-02-12 16-09-25-968

Fixed that one too. Game quality plus plus.