Last month I made a game!
The original title was "Head-to-Head Fruit Popper" but I just shortened it down to "Fruit Popper" for simplicity.
The full game source code, all assets, as well as pre-built executables are available via:
This was a game I created as part of the GDR 4x4x4 Challenge. The GDR forum is an old forum that I started lurking around way back in the early 2000's, but it existed before then. It seems to go through purges/resets every so often due to unfortunate web hosting incidents or site owner hand-offs, and the latest reset happened this year. I decided that this time around instead of just lurking as I usually do, I would try to be a more active participant, and coincidentally, around that time, the June 2021 4x4x4 Challenge started up.
The challenge rules:
GO HERE. Generate 4 emoji until something makes sense. The goal is to get weird so don't just sit there and reroll until you can make Mario.
Make a micro-game using the 4 emoji as your theme.
All targets are allowed. All libraries/engines are allowed. Code re-use is encouraged. Get out there and get shit done.
Challenge ends in one month.
Simple enough. Given you can re-use all kinds of existing code of your own, one month feels surprisingly long at first glance. Heh. That's kind of the trap here I think, but I'll come back to that in a bit.
Anyway, after a few rolls of four emojis, I ended up with:
Upon seeing this, the tomato and thumb-tack right next to each other immediately made me think of popping a balloon. Then I noticed that the grapes too, are another fruit. I am very quick you see. The two heads took me a bit longer to think about (ok, maybe I am not so quick after all) but eventually figured they could just represent "two player mode" or "one-on-one game matches" or "head-to-head play" whether the other person is a second player or the computer.
Thus arriving at "Head-to-Head Fruit Popper."
The basic game idea was that you and another player (or the computer) would be dropped into an arena (that would be limited in size to just a single screen) which would be some sort of magical garden where only two types of fruit are growing, tomatoes and grapes. Armed with your trusty magically-attuned thumb-tack you need to pop as much of your fruit of choice within the time limit.
- Popping fruit of your preference (what your thumb-tack is "attuned" to) awards you score.
- Popping fruit of the opposite type does not award you score.
- Whenever a fruit is popped it sprays a small area with fruit juice. If the fruit matches your fruit type, this does not affect you. If, however, you get sprayed with fruit juice of the opposite type, you get a debuff that slows your movement and attack speed.
- Players can stab each other with their thumb-tacks. They cannot kill each other, but getting stabbed also applies a debuff which causes a movement and attack speed reduction, as well as also pushing them backwards some distance.
- New fruit plants will be spawning randomly as time goes on. These plants need a short bit of time to grow into actual fruit before they can be popped for score.
- Stabbing a fruit plant with your thumb-tack does not do anything except destroy the plant.
- Fruit plants as well as actual fruit will block the players' paths, so they may want/need to stab plants or fruit of the opposite type to clear a path forward, e.g. to get to a bunch of already grown fruit of their chosen type.
- When fruit grows from a plant, it has a small chance to become a golden fruit.
- When a golden fruit is popped, all other fruit of the same type currently on screen also get popped, and the player will be awarded score for them all.
I had some additional ideas for things to include (like some random "enemy" of some sort that would occasionally pop in from off screen and start snatching up fruit away from both players) but did not have time to include unfortunately. Also, unfortunately, support for a one-player mode (with computer AI for the second player) did not make it in. This was really unfortunate to me, but I kept pushing it off and ran out of time. I'll come back to this as well a bit later.
I decided right off the bat that I wanted to develop this game for DOS and also restricting myself to using only tools that I could run on my DOS PC. Because of course I would do something like this.
This is of course, my 486 PC, using Turbo Pascal 7 on MS-DOS 6.22. I was also going to challenge myself to draw all of the artwork myself! This means pixeling it all using Deluxe Paint II. The idea of doing all the artwork myself was actually the most anxiety-inducing part of this project by a long shot. I haven't really tried to seriously draw anything beyond quick doodles in the margins of a notebook since ... probably, since the last time I took an art class in high school ... so, tenth grade in the fall of 2000. I also seem to recall trying to do some pixel art for a QBasic project around that same time and not being terribly happy with the result. Heh.
The good news here, is I had been off-and-on developing a games development library for Turbo Pascal since 2018 (I don't think I've really written very much about it here yet) called "GDlib" (for "Games Development library"). This is basically just a Turbo Pascal equivalent-ish of my libDGL project that I'd also been working on for Watcom C. So, since code-reuse was encouraged for this challenge, I would be able to get started quite easily from the coding side of things since GDlib is pretty full-featured already (notably absent is support for audio and game controller input devices e.g. Gravis Gamepad or similar devices).
Anyway! With art being the biggest personal challenge, that was where I began. It also helped to further cement some aspects of the game in my head. Spent a bit of time scribbling on paper first.
Some accomplished artists out there may think this is silly, but even drawing just this bit of "simple" stuff here on paper like this was extremely intimidating to me, heh. Which is funny even for me to think about, because back in elementary school in the 90's, I used to draw a lot. But for whatever reason, I stopped drawing, and I don't really know why. I guess I ended up meeting a lot more accomplished artists and that made me feel very intimated and even embarrassed by my "simple" drawings? Not really sure to be honest.
Anyway, this all seemed good enough to me so I then took a stab at turning it into pixels with Deluxe Paint II. I kinda wish I had taken more "in-progress" shots along the way as there was a fair bit of experimentation that is not shown here.
The grass tiles I cannot really take credit for. I based them off some random "how to draw a grass tile" pixel art tutorial I found via Google search. It may be obvious to some, but the fence tiles are inspired by The Legend of Zelda: A Link to the Past's fence tile (though they are still fairly different, if I do say so myself). The tree tile is also largely inspired by the tree tiles from the SNES version of Dragon Quest: I & II. But the sprites on the right-hand side were all just drawn myself without any direct inspiration from anything else. They are all fairly simple though, but that is good! Simple meant I could finish them after all! The fruit splashing animation was particularly difficult and I dunno how happy I am with it at the end of the day, but I guess it looks decent enough when you see the animation play. Some of the character animations look a little funny in places. Particularly the arms. Oh well, heh.
With most of the "hard part" now out of the way (and the month about one-third over), I could then of course proceed to the code! Got a basic tile engine up and running soon enough.
Starting off a programming project is, for me, almost always the hardest part of any project. I am not sure how many other programmers out there share this feeling. I suspect not many. It seems like most programmers enjoy this part the most. For me, it is "hard" because I find myself (especially now that I am older) running over various design and architecture ideas in my head, over and over, second-guessing my ideas and not wanting to write a single line of code until I get it "right." Which is of course silly, because as most programmers eventually realize, you ain't gonna get it right the first time. You start off, and you revise and refactor as you go. But I still always get this bit of "decision paralysis" at the start.
But once I get going I often find it gets much easier. Once you have a basic skeleton up it is easier to envision things in your head and how the additional pieces needed will fit into that.
From here, adding basic game mechanics got easier, and I was able to add a bunch relatively quickly.
I didn't do too much fancy stuff in the code, really. The game works in VGA Mode 13h. This is definitely the easiest VGA mode to work with. For 386/486 hardware, using one of the "un-chained" modes like Mode X or Mode Y would probably have been best from a performance perspective (definitely the case for 386-class hardware!).
Many Mode 13h games work by drawing to an offscreen framebuffer kept in "normal" memory (that is, not in VGA memory,
because with Mode 13h, there is no free VGA memory left for you to use in that way) and then you essentially just
memcpy the offscreen buffer to the VGA framebuffer at
$A000:0000 each frame. With a 32-bit VLB or PCI bus system,
this copy operation is still expensive, but you can definitely achieve a solid 70 frames-per-second speed even with
full screen scrolling while also executing complex game logic. With the VGA running off a 16-bit (or even worse, 8-bit)
ISA bus achieving 70 FPS with this method is extremely challenging because that
memcpy is eating up a significant
chunk of your time budget for each frame.
So, of course, if you are stuck with Mode 13h, you need to look at other ways to work around this. One way would be
to forget the offscreen framebuffer and just draw directly to the VGA framebuffer at
$A000:0000 and just be very
careful to do this only during V-blank to avoid any visible re-draw artifacts. Tricky, because drawing directly to
the VGA memory means you are limited to the speed of the bus and also the speed of the graphics card itself (which
would vary from card to card). But it may be perfectly doable if you don't need to draw much per frame.
I decided to opt for just trying to draw less per frame, but still keep the offscreen framebuffer. I actually did not do this very well at all. I meant to come back to this and improve it but ran out of time. The way I wanted to reduce re-draw was to have each "entity" (player, fruit, particles, etc) be a type of "managed sprite" that would track its background and when it moves or animates, it redraws the original background underneath before drawing the sprite itself overtop. That way I would never have to re-draw the entire screen each frame in the offscreen buffer.
What I actually ended up doing is using an extra offscreen buffer to draw the map into once (only when it is marked as
"dirty", which is actually only at the start of each match because the map data doesn't change during game play), and
memcpy'ing it onto the offscreen buffer at the very start of each frame. Because this
memcpy is between two
buffers in main memory, it is decently fast (definitely faster than a
memcpy from main memory to VGA memory), and it
also doubles as a "free" way to clear the entire screen. But it means an extra 64k
memcpy each frame which chops off
a couple milliseconds of the frame time budget. At 70 FPS, the total per-frame time budget is 14.28ms. The final
offscreen buffer copy to the VGA framebuffer takes up a lot of this (depends on the bus speed as well as the speed
of the memory on the particular graphics card).
This idea of "managed sprites" is something I want to add to GDlib in the near future, just to make it a lot easier for myself for the next challenge I participate in.
For entity management, I just did the simplest thing I could. Arrays of structs. Nothing like the entity-component systems of today. I don't even need that complexity for a simple project like this. Nothing much else to say here really other than that I did a very bad thing initially and used frame counts as a means of timing things initially. I knew this was very bad at first, but it was the simplest way to get started. My GDlib did not have high-frequency timer interrupt support yet, and the default BIOS timer frequency is 18.2hz, which gives you a resolution of about 55ms, which is about 4 frames long at 70 FPS. Can't really use that for timing, so I would need to use a custom interrupt. In the last week of the challenge, I did come back to this and add that support for high-frequency timer interrupts, and then added a 1ms resolution timer to Fruit Popper, and then promptly updated all the various time counters and other things like that in the game's logic to use milliseconds to count time. Much nicer!
This was also supposed to lead into updating the game logic to be frame-rate independent. Because I knew from some very simple performance testing where I quickly built a shoddy 386 system on my dining room table that performance on a 386 machine was slow due to the lack of 32-bit Vesa Local or PCI bus.
This photo shows the basic engine runing about 24-25 FPS. However, it is important to note that it is also running here in a "worst case scenario" with 50 fruit/plant entities on screen. Most of the time, the active entity count would be far less. With zero other entities other than the two players, engine performance on this 386 system approached 50 FPS (sorry, I forgot the exact number now, but I do remember also testing that specific scenario too).
It is also worth noting that at some point I tried swapping in a 16-bit VGA ISA graphics card into my 486 PC, replacing
the 32-bit VLB graphics card it normally has, just to see how that affected performance. I used the very same ISA VGA
card that I previously used with the 386 shown above. It was interesting to see that the performance ended up fairly
similar to the 386 at that point! It was a bit faster of course, as the extra CPU muscle does help, but it was not
significantly faster. The 16-bit ISA bus really takes its toll. That
memcpy to the VGA framebuffer is absolutely the
main bottleneck without a VLB or PCI bus! Thus underscoring the need for fancier rendering tricks than what I was doing
here in Fruit Popper.
Thankfully, Fruit Popper was already using fixed-point math everywhere that floating-point math would've been used otherwise (entity positions and velocity used fixed-point), so at least that wasn't dragging performance anywhere! This 386 system did not have a 80387 unit installed.
At some point in the last week of the challenge, I did update most of the game logic and rendering to work in a frame-rate-independent manner, but ran into some issues due to confusing myself with using units of speed that had different frames of reference (at least, that is what I believe the issues I was encountering were caused by) and it was taking too long for me to figure out (again, with only a week left and still a ton more stuff to do) so I backed out the change. The final challenge submission is unfortunately frame-rate dependent and will play best if played at 70 FPS. Ugh.
Honestly, though, the fact that I ran out of time for things I wanted to also include was my own fault. I had plenty of time. But I procrastinated a ton. This hurts a bit to think about as the lack of single-player mode using a computer-controlled second player hurts this game a lot. I couldn't even play this game myself "properly" because I do not have a second person around with whom to play with! Ouch!
Another thing that hurt from the "lack" of time, was that the way in which I originally wanted to have players choose their fruit was to do a little mini-game thing where would be dropped into a little mini-room before the actual match started where they had to rush to stab their fruit of choice before the other player. They would start off beside each other at one end of the room and need to run to two pieces of fruit at the opposite end of the room. So, they could stab each other or push the other out of the way to slow the other player down, etc. I thought it would be a fun little pre-competition before the main competition. But because I left ALL of the game menu stuff until the very last day I had no time to do this, and had to instead settle for a coin toss. Boring!
The lack of audio also hurts! I wanted to at least get sound effects in. I was planning to use an old QBasic tool by Angelo Mottola (who back in the late 90's created the popular QBasic game development library "DirectQB", amongst other notable projects of his) called "SoundLab" that was basically a pre-cursor to the now popular Bfxr and sfxr tools that let you randomly generate sound effects. The code to actually play these sound effects created with the SoundLab tool on a Sound Blaster compatible card is pretty simple and would've been easy to integrate .... if I had not slacked so much! Argh! Oh well.
Finally, I really wanted to include game controller support. This would've been for controllers like the Gravis Gamepad but also for any other controller of the time that was compatible with a standard (at the time) DA-15 game port. My GDlib library also does not currently have support for this, so I would've needed to add this support from scratch. It's not terribly difficult or anything, but again, I slacked a lot, and ran out of time.
Over the course of the month, I had to make alterations and additions to the tiles and sprites needed for the game. The final tile and sprite set looked like this:
You can see in this final revision of the tile sheet that I split up the left-hand-side tiles a bit. The upper region with the grass and dirt tiles only represented the "no collision" tile group, while the bottom region with the tree and fence tiles represented the "collision" tile group. This way of doing collision checking is very simple and very effective, especially with a small tileset like this.
Since I am using a palette/indexed colour graphics mode of course, I did briefly at the start consider doing sprite rendering for the tomato and grape coloured/styled sprites using some fancy sprite blit that added a colour offset so I didn't have to duplicate sprites like this for each colour. However, since I was not hurting for memory at all with this project, I decided against it and just went with the duplicate colour-swapped sprites like you see here. Sprite blits are already slower due to their nature of needing to check for transparent pixels. I was already thinking about performance here and was not totally happy with performance on 16/8-bit VGA graphics hardware, so further slowing things down with palette index tricks like this didn't really appeal to me.
Finally, I needed menu title graphics! Luckily, about a year ago when I originally had the idea to try to get myself
into doing my own game artwork, I bought a DOS-compatible Wacom ArtPad tablet. This was pretty cool! It came with a
driver disk (which still worked!) that included a DOS and Windows driver. The DOS driver worked as a full replacement
for the mouse driver (you have to unload any existing
MOUSE.COM driver or similar before loading the Wacom driver),
and because of this, the tablet can be used with any mouse-compatible software in DOS.
So, on the very last day of the challenge, I drew up the simplest menu title graphics I possibly could using this tablet, and then fine-tuned the pixel outlines and coloured it. I don't think it turned out too horribly, but it is kinda silly looking, I admit, heh.
All the game menu stuff did take longer than I thought, and as a result, I did end up rushing a fair bit (another thing I wanted to include that I had no time for, was a control customization screen) and the menu code is fairly sloppy I think. Overall though, despite leaving all of the menu stuff until the very last day, I still think that all came together decently enough! The final submission entry for Fruit Popper does at least feel like a mostly complete game to me.
Also, as you can see from the above, I did have a level selection screen. This was something added also on the very
last day of the challenge. It presents a list of any
.MAP files present. I very hastily created three maps by
manually editing bytes in a file. Because I was rushed for time and this process of manually editing bytes in the map
file was very tedious, I don't really think any of the maps are that great. Oh well. That'll teach me for slacking!
I remember thinking at the very start of this challenge when I was writing down notes for the design of the game and the features that I wanted to include, that my design was perhaps, "too simple." In hindsight, I don't think that was the case at all. I think it was probably about the right size. If I had slacked less throughout the month, I think I could have ended up with something more polished very easily, and that had probably the single most important lacking feature included: single-player mode against the computer.
I've never been fond of the shorter "game jam" type challenges, with things like 48-hour time limits or similar. This ends up being basically a hackathon type of thing where you're coding straight for a long period of time. I've seen for myself that I cannot really do this. I tried my hand at the 2013 Global Game Jam and ended up stressing myself out very quickly and bailed within the first 24 hours (much to my partner's disappointment, I might add ... not something I am very proud of).
However, a month long challenge I like. It's enough time that I can take a few days to ponder things and not feel very stressed. The key is, however, to properly manage your project scope. A month can feel like a long time at the start, but it really isn't! Even with the ability for code re-use, etc.
Regardless, it was fun, and I'm looking forward to the next one. I will probably also do a DOS game next time, which means I will need to spend some time upgrading my GDlib library with extra things!