Endless Attack REMIX Development Journal #1
02 Mar 2022
So! It appears it's time for me to start yet another series of development journal posts, this time for my next-but-before-ExaStar game, Endless Attack REMIX! Much like ExaStar's first development journal, I'll start with a lengthy amount of history behind the game, then spend some time talking about what I have worked on thus far. Let's go!
What is Endless Attack? Why REMIX? I'll explain more through the walls of text to come, but for context: Endless Attack is an unreleased¹ "endless" arcade shooter game I made a long while back. It's endless in the same sense that, while you could keep playing a "non-endless" game after you finish it, the post-game is just going to become a whole lot of the same in the end (again, saving a full explanation for later). Endless Attack REMIX is a sequel to that game, with several more years of programming, spriting, and game design experience added on. And more development time than 16 hours. Did I forget to mention I'm targeting MS-DOS as the primary platform it'll run on? Because yes, making a DOS sequel to a Windows game made sense to me.
The history of: Endless Attack
It's Summer 2014, and I'm in a 2-week-long game development "boot camp". I put "boot camp" in quotes because it was really more of a short course at a community college that I'd get driven to each weekday (so weekends were off). The class was about 2 hours long each session, for a total of 10 sessions. The first session was pretty much introductory, basic "get to know each other"-type stuff and how to use the engine we'd be using (Multimedia Fusion 2). By the second session we were essentially set off to make whatever we wanted, until the final session, where we'd play each other's games and score/rank them. That gave us a whopping 16 hours to make a game, individually (like I did) or as a team! And to make things even more difficult, I had never used MMF before, so I had to get acclimated with its scene/room/screen (I forgot what they called it) and visual programming systems within that time. Luckily, I came up with a gameplay idea and was ready to go from the start: make an endless (or at least as close to that as I could get) top-down shooter, where the player can get upgrades and health refills using the score earned from defeating enemies in order to progress further. Simple, effective, and gave me something to work towards as I got used to the engine. I proceeded to give the game the most creative title ever: Endless Attack.
And so, the two weeks went by. Most of what I can remember during that time was a lot of people learning what emulators are for the first time, people competing in Super Bomberman instead of working, and me accidentally creating the greatest bug in my game development career, wherein the player could rack up a cataclysm of bullets and shoot them all off at once. The teacher was shocked. I never ended up figuring out how to re-implement it as an upgrade. It's definitely going to be added into REMIX.
The final day comes around, and we all start playing each other's games. One thing to note here is that MMF2 came with a lot of pre-made assets that never really looked the greatest, and since no one in the class really had any prior pixel art or programming experience… everyone used the pre-made assets. Everyone except me. I really hate to put myself on a pedestal, but Endless Attack stood out from the rest with its custom art, gameplay, and (though a bit fatiguing) actually tolerable controls. The scoring went as follows: You'd play each person's (or team's) game. You'd then give the game a point in the one category it excelled at, or just not give it one. Unfortunately, I don't remember what the scoring categories were. Regardless, given how unique my game was compared to the rest, I was eager to see which category it would win; or if it would win one at all. Unsurprisingly, it did: the "wild-card" category. Whether that meant it excelled at multiple things or that it was just an "other" category, I'll never really know. But people definitely liked Endless Attack, and given I had never really gotten feedback for my games before that point, it was good to see!
Title screen screenshot.
Gameplay screenshot. Note the sideways Spikeys; I don't know why I rotated them like that, considering every other enemy looks correct from the side.
During the last few days, I actually copied over in-development versions of the game onto a flash drive so I could show it to my cousin, who was also taking a course at the time. What I didn't copy over, and I'm still kicking myself for this, was the game's actual project file. By forgetting to do this, I had pretty much sealed the game's fate: there were no more additions that I could have made, given more development time. As a result, Endless Attack ended up being very far from "endless"; after about 35 or so waves, or around half an hour, the last wave would just loop over and over again. The game was, whether I felt it was or not, a finished product. The loss of the project file did, however, spur the development of…
Moving forward a few months, I decided it was time to recreate Endless Attack in Stencyl, my engine of choice. There was no other option; with no way to add onto/polish the original game, I would just have to start from scratch. Obviously, I didn't just want to recreate the game; instead, I decided to call it a sequel since the original game was "complete". So, I got to work planning and creating a prototype for Endless Attack X. Without the original project file, I had to create new wave data, which sounds easy, but it was pretty tedious trying to get it to match the original game. Beyond replicating the content of the original, there were new things I wanted to add aside from more enemies: bosses and a larger shop were the two big ones. In order to help the player gradually get further in the game and create some sort of gameplay loop, I planned a "prepoint" system. With it, the player would earn points during normal gameplay to use before starting their next run, making some shop items immediately accessible. I also toyed around with a Co-op mode by simply adding in a second player object and changing the controls for it. It worked pretty well, even if the player sprites were (confusingly) the exact same!
The sideways Spikeys were fixed!
Funnily enough, I did most of the graphics for the game during school, in the computer lab, where they had Photoshop installed. Keep in mind I was still using Paintbrush on OS X at home for graphics work, which was really just Windows XP-level MSPaint at the end of the day; so my desire to have anti-aliased, "vector" sprites for the game meant I had to resort to the school computers for that.
After working on the game for a bit, development- like a lot of my other projects- suddenly just… stopped. Lack of motivation is the theme here, I suppose. Although the basic gameplay was there, I hadn't gotten to the same number of waves that were implemented in the original game. The shop wasn't even implemented, either. I didn't completely give up hope, though.
In 2016, I decided to try again with EAX. I started from a clean slate, implemented about the same amount of content (even adding in a main menu, unlike the prior attempt), and then stopped after 4 builds. The co-op feature didn't even get re-implemented either, and didn't make a return for future attempts. Good things never last, it seems. There are a couple of noteworthy things about this version of the game, though; for example, the "cooldown periods", which were a new addition to the gameplay. These let the player take a break (crucial for a game like EAX), and also provided a better way to access the shop. Another interesting addition was game saving, which I never originally planned for. I guess the original idea was that you'd play until you simply didn't feel like going any further, making the game seem truly endless, but eventually I realized that was a bad idea and added a save system anyway. I also redid all of the graphics for this version, as I had switched to Windows since the last version of the game, and learned of the mythical Paint.NET. The aforementioned main menu also looked pretty good for a prototype build:
Take note of how this looks, it comes up again later.
Gameplay looked mostly the same:
Later on in the Fall, I tried yet again to create EAX, this time in GameMaker Studio instead of Stencyl. The only reason I did this was due to performance concerns I had in Stencyl when dealing with large amounts of on-screen objects, which occurred very frequently in EAX. GMS did, in fact, handle large amounts of objects very well, but unfortunately this time I only implemented the first 4 waves before giving up. I did try a new control scheme with this version after playing some twin-stick shooters, though. In Endless Attack and prior versions of X, the move and aim buttons were the same: the arrow keys. This meant that, in order to shoot (space) at an enemy, the player would need to move in order to aim towards it. This resulted in a lot of running-directly-into-enemies,-not-actually-shooting-them,-then-having-to-run-back-into-them-again, or as I like to call it, "panic shooting". It was annoying. The GMS version of EAX was much better; movement was still down to the arrow keys, but aiming was moved to WASD. This wasn't perfect, though; you still had to use space to shoot, and having your thumb repeatedly pressing it just wasn't comfortable.
The third attempt at making EAX. Yes, that's motion blur.
By this point I had already gone through 3 attempts, none of which came close to the original game. I tried (and failed) again in late 2018, going back to Stencyl and creating a version that used pixel art instead of anti-aliased, high-res graphics. I only got as far as implementing basic enemy logic in this version, but it was notable for introducing a new player character that wasn't a top-down view of a stick figure.
Development notes from the fourth attempt. Looks like I was pretty geared up to work on this, but development only lasted 2 days. (Also, why'd I use a hashmap for wave data…?)
Four attempts, then. Surely I'd have given up by this point. Except… I wasn't quite done. And to explain how I got to attempt #5, we need to look at my history with the library I'm using to develop REMIX.
In early 2019, I was playing around with my childhood PC, which I had recently gotten running again thanks to a PSU swap. I had the idea to try and experiment with 2000s-era game development, as after all, that's when the PC was from. I don't quite remember how I knew about it beforehand, but I remembered a library for game development called Allegro. I decided to try out version 4 of the library, which was in its prime by the 2000s. I only made a small "hello world" program before deciding I'd had my fun, and quickly went back to the comfort of modern gamedev.
Later on in the year, I decided to try and create my own game engine… which, if you've already read through the programming page of my website, you'll know how that ended up. Before I decided to write the engine under OpenFL using Haxe, I attempted to write it under Allegro 5 with C++. This… didn't last long, as I only had a few weeks of experience with C++ and wasn't very used to how it handled OOP.
By the time it was early 2021 and I had installed DOS on that childhood PC from earlier, Allegro quickly jumped to the forefront of my mind. Thanks to messing with it twice now, I knew that Allegro (specifically Allegro 4) had support for DOS. What I didn't know up until then was that it was originally made for running under DOS², unlike my previous assumption that it was some half-baked port. So, with nothing better to do, I decided I would try and write a quick demo with Allegro 4 in C. Figuring out how to get the tools up and running was simple; someone had already put together an Allegro + DJGPP bundle, making compilation extremely easy. After spending a bit of time reading up on Allegro documentation, namely how to go about safely initializing the library, as well as the API references for bitmap drawing, I had a small demo up and running!
Hard to tell since it's an image, but those are bouncing around!
I was having a lot of fun. I really wanted to keep going. This was unfortunate, since I had already started a lot of throwaway projects around this time, such as a game made in HyperCard, a minesweeper clone, and a couple of TI calculator apps, so I was a bit wary of starting anything new. Making something bigger than a small demo- in a language I was only slightly familiar with- seemed like a recipe for disaster. Regardless, I decided to shelve any extraneous projects I had going on at the time, leaving just ExaStar (which was, and still is, barely alive), VixenKit, any small 1-or-2 day projects that could cross my mind, and this new DOS game. But… what would this new game be?
It's all coming together
Somehow, everything fell into place, and I had my answer almost immediately: Endless Attack X would be developed as a DOS game using Allegro 4, written with C, and under a new title: Endless Attack REMIX. The new title sort of alludes to my feelings towards the relationship between the original game and its "sequel"; REMIX is, technically, what the original should have been, given more development time and experience. It's not exactly a sequel in that regard, more of a "half-sequel". The "X", to me at least, just made the game sound more like a true sequel, thus not really fitting. But REMIX is still Endless Attack X at the end of the day, as you'll see; in fact, a lot of REMIX's source code still refers to the game as "EAX" in places where a short acronym is needed (because EAR just sounds weird).
Deciding requirements, or "how low can I go"
One thing every game developer or studio needs to consider when first making a game is what platform (or platforms) the game will be distributed for, and if the game will even be a good fit for it (…or them). I had to think long and hard about which DOS systems I wanted to support, as "DOS" in general is a pretty broad platform. You could almost call each CPU family a platform within DOS. Anything like a Pentium III or newer was a safe bet, considering I have a PIII system ready to test the game on in case I need to, and they're just plain overkill for any DOS game. But systems with a PIII typically run Windows 98, 2000, or XP (or a *nix!), and of those three (four?), only 98 has DOS support. The PIII seemed just a bit too new for me to be targeting, so I decided that I would at least want the game playable on a Pentium II.
But that steep of a requirement for a DOS game still bugged me. Surely the game could run on something older, right? I proceeded to do some tests using a modified version of the small demo I made prior, with double-buffering added in. Since I didn't have any hardware slower than a PIII on hand (still don't, anyone wanna donate me a 486 or Pentium system?), I had to use DOSBox, which is where I usually test the game. DOSBox offers the ability to limit the number of clock cycles, and there's even a list with approximate cycle counts to run it at the speed of various CPUs, so I started messing with the number of bitmaps to draw at different cycle counts.
Beginning with 100 bitmaps at the speed of an average PII, I slowly decreased the cycle count. The demo, surprisingly, continued to run at 60fps when clocked at average Pentium speeds. I started going lower to see at what point the demo would drop down to 30fps. That point was, shockingly, halfway between a 66MHz 486DX and a 100MHz Pentium. This is when I finally decided to look into something I saw in the Allegro API: RLE sprites. According to the API, these would draw much faster than bitmaps do.
…But I clearly wasn't prepared for how fast. After learning about the tradeoffs (you can't scale, rotate, or draw on an RLE sprite, fine by me) and familiarizing myself with the functions, I tweaked the demo once more to convert the bitmap to an RLE sprite before drawing. I ran the demo again, and started to decrease the cycle count. It ran at 60fps at 486 speeds! It continued to run at 60fps up until somewhere between a 66MHz 486DX and a 33MHz 386, so it was clear that a game like REMIX could run comfortably on most 486 systems. I say "most" since both Allegro and the game itself require an FPU, however the game can be built with floating point emulation if needed. This does incur a performance penalty, though given how many sprites even a slower 486+FPU could handle, the game should probably still run at a solid 30fps.
I have to take these results with a huge grain of salt, though. At the end of the day, DOSBox is an emulator, and an emulator is… well, an emulator. Not real hardware. DOSBox doesn't emulate slower memory speeds, so the demo was able to use 2017's memory speeds while running at 1991's CPU speeds. Not the best scenario for testing, but until I can run the game and/or demo on a real 486, I'll have to work with the estimated speed. The demo tests also didn't take into account the runtime needed for game procedures, but I'm hoping that's not very significant compared to sprite rendering.
As for hardware outside of the CPU, I decided to go with the lowest common denominator for video and audio support. The game will run on any standard VGA-compatible display adapter, given it provides mode 13h. That's a graphics mode with a resolution of 320x200 and support for 256 colors, if you weren't aware. This is actually the second most primitive video mode that Allegro supports (text mode being the first), so hardware support should be pretty high. Likewise with audio; I'm writing the music with AdLib and Sound Blaster cards in mind, which many DOS sound cards strove to be compatible with.
I'm not too certain on RAM requirements just yet. It seems like sound effects will use the most memory, so I'll need to limit how many I have loaded in at any given time. I have ideas in mind for determining which spritesheets should be loaded and unloaded during gameplay (the menus are pretty easy to manage), so memory usage from sprites shouldn't be too high (though I do use some massive sprites at times…). Memory usage from music isn't a concern, since MIDIs only take up ~30KB at a time.
Now, DOS isn't a particularly accessible game platform nowadays. Selling REMIX as a DOS-only game would limit the game to people who already know how to use DOSBox, or those with a PC running DOS (we exist! in small numbers!). Here's where Allegro really shines; I don't need to limit the game to DOS. In fact, here's where it could run, provided I take the time to test for any platform-specific quirks:
- DOS, which is the main development target
- Windows (95 and later, still haven't tested anything newer than XP!)
- macOS (10.1 and later, up until and including 10.14; 10.15 and later need to run the game in a customized DOSBox wrapper, unfortunately)
- Linux (tested recently)
- BeOS/Haiku and QNX (maybe there's a market…?)
So most PCs should be able to run the game, and the hope is that I can distribute versions of the game to run on modern systems alongside the original DOS version.
Development thus far
The very beginning of development on REMIX consisted of further experiments using the demo I made as a base. First I started with getting a single bitmap from a spritesheet.
That was simple enough, so I turned my attention towards music (MIDI) playback. Also easy. Then I got into more advanced work; things such as double-buffering and redirecting the game to a (placeholder at the time) setup screen when given the right argument.
I took a break for a few months, but when I returned I realized the single source file got a bit too unruly, so I broke the source up and created a state machine for the game's room/scene system. This immediately made development feel a bit more "at home"; each state has an enter and update function, much like how I'm used to Stencyl's scripts containing essentially the same functions. Different, however, is the distinction between "updating" and "drawing"; Stencyl has a special function per script where primitive drawing should take place (things such as lines, squares, etc). This is due to the fact that Stencyl has separate update and render timings; while the game updates much more than 60 times per second, drawing/rendering only needs to happen on each frame, so the two run at different rates. In my "engine", both updating and rendering take place at the same time. This is partly because having more updates per second than the current framerate is just a bit too much for a PC running DOS, and partly because it makes things simple for me. Luckily, because updating and rendering happen at the same time, I can make an "on draw" function per state and have the update function call that.
With the state machine out of the way, development got even faster on the front end: I made the intro screen for my development studio, the title screen, and then the main menu.
Then I moved back to more structural additions, such as defining gameobjects and linked lists, in order to add the player and bullet shooting. Instead of using space to shoot like in the GMS version of EAX, shooting became automatic when holding any of the aim keys. It was an immediate improvement. I also took the time to add in the setup screen.
Afterwards, I took a few more months off, before coming back to rework the build system (more on that later) and write this post. And that's pretty much where I'm at now!
Immediate & future plans
As for things I'm working on right now:
- Dynamic arrays. Far from the hardest thing to implement in C, but I'm writing one to support…
- Spritebanks, to hold sprites loaded in from a sprite sheet in a more easily-addressable format
- A better sprite loading system; I've taken care to optimize the game as much as I can early on, and one of those optimizations is, as I mentioned, the use of RLE sprites. However, it seems their speed benefit is either negligible or detrimental on other platforms, so I'll need a way to switch between using RLE sprites and bitmaps at compile-time before it becomes too daunting to add.
- Dynamic framerate switching; I have an idea for how this will work, but I fear it might not work as well as I want it to… we'll see!
And more general plans for the future:
- Obviously, getting the core gameplay in: enemies, waves, shop, etc.
- A co-op mode- yes, my little experiment from the game's first incarnation is a key part of the roadmap!
- An interactive tutorial and practice mode
- Adding in other unlockable modes after that
- When the game is complete, a physical release?
The ideal goal is to get the game finished, or in a near-finished state, by the time VCFMW 17 rolls around (at least I hope it happens this year… please get vaxxed). This is so I can set up a playtesting table to get some feedback and generally get the word out about the game to other DOS gamers. But given how often I lose focus, that definitely won't happen in time. So the minimum goal is that I get the first 30 waves implemented, including co-op support, by that time. It's a daunting task and I still have a lot of doubt that I'll make it (thanks to uni taking up much of my time), but we'll see!
Ooh, the fun part! Here I get to explain how I'm making a DOS game in 2022 (technically 2021 as well). As mentioned earlier, I typically test the game in DOSBox, only rarely copying the game to real hardware. At first, I would compile the game in DOSBox as well; but this process was slow, as GCC had to run with the overhead of emulation. It made complete recompiles of the game take around a full minute, and the game was only starting development at that point! So a month or two ago I looked into cross-compilation; luckily, a cross-compiler specifically for DJGPP on DOS exists, and after a couple hours of tinkering and writing a new makefile, compilation could then be done outside of DOSBox. Full recompiles now take just a few seconds at most!
I program the game inside of Visual Studio Code; I've found it integrates very nicely with DOS development. As long as I have the C extension set up to use the cross-compiler, and have the include path pointing towards Allegro's headers, I get proper syntax highlighting and IntelliSense while writing. I can even build the game from VSCode and have it launch the game in DOSBox for quick testing.
Making graphics for the game couldn't be any easier; I'm able to use my favorite sprite editor, Aseprite, to create BMP files compatible with the game. You might be thinking, "any image editor can export a BMP, so what do you mean by 'compatible'?" Allegro only expects a 256-color BMP, and having more or less colors in one will crash the game. Aseprite comes with a really good palette system, so I'm able to save a BMP with a 256-color palette- and, get this- it comes with the default VGA palette. It's a pretty versatile palette, and while I could have my own palette for the game if I really wanted to, I just find the default one easy to work with. So making graphics for the game is pretty much the same process as any other game I would make; I just need to switch palettes beforehand.
Music is a tricky area that I haven't completely figured out yet. Thus far I've only made a couple of tracks for the game, and getting them to loop properly + have the correct instruments was a bit of a chore. It doesn't help that I made them using Logic Pro, which I don't have ready access to since switching to Linux a few months ago (rebooting my MacBook into macOS just for Logic is a bit annoying…). So, I have to relearn the process of getting program changes (in non-MIDI terms, let's call those "instrument mappings") and loop points correct in a different DAW, like LMMS. While we're on the topic of music, I asked my girlfriend for a bit of help on this front. She started writing music while I was taking a long break from doing so, and as a result, she's now a bit more skilled than I am. The only problem is, our songwriting styles are completely different! While I favor strong melodies with not many effects added on top, she tends to write faster songs with shorter, repeating melodies, better drumlines, darker tones, and lots of VSTs. Since I want the game to have a more arcadey soundtrack, we need to somehow find a way to blend our styles together. She also has to avoid reliance on VSTs for a bit, since the soundtrack needs to be pure General MIDI!
Sound effects are just heavily-crushed WAVs in order to save on memory usage. Nothing special there.
REMIX won't be a hard game to make. The concepts I made for it are almost 8 years old (ooof…) by this point, so I know exactly what I want to make here. The only "real" hard part is getting used to C, but I've definitely already learned a lot and gotten used to non-object-oriented programming. I suppose the main hurdle for me, then, isn't the game itself; rather, staying focused on it and trying not to get distracted by side-projects. Lest it ends up like ExaStar³; I wouldn't really want that to happen again.
I hope to come back with another journal a month or two from now, and if that doesn't happen… then hey, bug me on Twitter, why don't you?
1: I'd love to release it, and had plans years ago to do so, but unfortunately 2014 me had the brilliant idea of putting my old website link (deadname included) on the title screen. In 2019 I tried reverse engineering the game (again, don't have the project file) to try and edit it out. I got as far as extracting the title screen's background and converting it to and from a PNG, but my efforts to stuff it back into the executable were fruitless. There are supposedly some ways to decompile MMF2 games, but all I could find were either incomplete tools, FNAF-specific tools (yes, same engine), or tools for MMF2.5. Maybe some day I'll try again…
2: Allegro wasn't originally a DOS library. It actually started out on the Atari ST! But not much was done for that platform compared to DOS, so I like to say it was originally a DOS library just to keep things simple.
3: I'm still in the process of getting Dev Journal 8 out for that. I started writing it all the way back in 2019, but ended up leaving it around 3-quarters of the way done. Then I had to "soft-restart" development twice. So now I'm left trying to figure out how to reformat the whole thing with those restarts (plus any new developments from the past ~3 years) in mind.