Obviously, this post is going to include spoilers for the game, so (if you haven't already) you might want to go to the game page here and have a play (it should only take between 5 and 10 minutes to complete). If you have the time, feel free to rate the game as well (I would appreciate any and all feedback you have to give)! And, if you have even more time, I'd recommend you check out some of the other entries of the game jam, as there's a lot of talented developers taking part!
With that aside, I'll continue to the topic at hand...
Note: If, at any point, you want to go to the GM48 page to download the game, just click on any of the images in this article!
Stranded - A development diary and post-mortem
With the theme being revealed at 1AM on Saturday for my time zone, I decided to get to sleep before it was announced. For previous GM48s, I had stayed up to find out what the theme was, and then had a really hard time sleeping as my brain whirred with ideas. I wanted to get some decent sleep this time, so I could concentrate when it came to crunch time.
I woke up on Saturday morning, and checked the theme straight away: Space. That's good. Open. Lots of possibilities. I took a shower while I brainstormed. I wanted to make sure I had a pretty firm idea of what I wanted to do before I started. With such a short time-frame, realising part-way through that you want to change your idea significantly, is a big deal.
I had my cereal in front of my computer, and wrote down the following notes:
I wanted to go for a VVVVVV meets Super Metroid kind of feel. I love the platformer genre, and have worked in it before, so felt comfortable doing it now. I always make games that I would want to play, and this was no exception. I liked the idea of being stranded on a planet, with no resources, and having to make your way through these strange caves, gaining abilities as you go, until you finally manage to find a way home.
Now I knew what I was aiming for, I copy-and-pasted the summary and emailed it to my friend Phillip Schlosser, who had done a great job working on the audio for my previous GM48 entry Elements.
The first coding to do was to mockup the basic platforming engine. I used the inbuilt Box2D physics engine, and made my Player and Ground objects. For the Ground objects, I wrote a bit of autotiling code to draw appropriate border tiles, as I knew this would be a big time save in the future. For the Player, I made sure to include gamepad controller support - because I love playing platformers with a gamepad.
The next thing I took straight from VVVVVV, and added a camera that follows the player one block at a time. I'm not completely sure why I like this mechanic so much in VVVVVV, but I do, and I felt it would work well here.
At this point, I realised a few important things:
1. The art was shit.
2. I was shit at art.
3. I needed to find someone who was not shit at art.
I considered doing the art myself, but I had a feeling I was going to be pushed for time as it was, and didn't want to add my inefficient spriting skills into the mix. I've never worked with an artist before, but I knew, if I wanted to get the whole idea completed in time, I'd need to find one. I posted a thread in /r/gamemaker, and prayed that someone would be interested.
It was 11:30AM at this point, and I was wary to start implementing more mechanics without completely knowing what I was aiming for, so I did the only logical thing:
I went to the pub.
I took my notepad and a pen to the pub down the road, and ordered a beef pie. I was there for quite a while (I decided I wanted a brownie as well...), and I used to time to write out my ideas so that I had a concrete plan to aim for. I wrote down a few specifics for certain things (How do the teleporters work? What about the enemies? What happens if you kill an enemy, leave the screen, and return? How does the player progress through the game? Exactly what sprites and tiles do I need to request from my artist, if I find one?).
I got back home, enthused, and found I had a reply from Phillip. He was happy to help with the audio, but he was working on Sunday, so could only help so much. I said that was fine, and sent him a list of the sounds that I knew I would need, trying not to ask for anything superfluous to make sure I got everything that I needed.
I knew that the next thing I needed was a minimap mechanic, which I hadn't done before. I wanted to almost replicate the minimap from VVVVVV, so I knew what I was aiming for. I used a ds_grid to store where the terrain was, and drew small squares appropriately. I also made sure to only draw the sections that the player had visited (which was recorded by MapMarker objects that were flagged when the player entered their screen). Finally, I drew a little yellow square for where the player was. Having never done any minimap work before, I was quite happy with how it turned out.
The next thing I needed were the teleporters. I wanted the game length to be somewhere between 5 and 10 minutes, as I knew there would be a lot of games for people to play, so they might not be able to spend long on any game, and I wanted them to be able to play mine all the way through. This meant that I couldn't make the game super hard, or introduce unnecessary travel time. This was the point of the teleporters.
As I was coding away on the teleporters, Aaron Armstrong got in touch with me on Reddit, and said that he was interested in helping me with the art for my entry. I had a look at some of the portfolio stuff he sent me, and knew he'd be able to do what I needed. We started sending messages on Skype, and I wrote up a list of the sprites and tiles that I knew I'd need. I asked if there was enough time to get it all done in, and he reckoned there was, so everything was looking good!
One important decision we had to make was the pixel scale of the sprites. The view size in the game was 640 by 360, and we needed to decide whether there would be 640 x 360 pixels for that whole screen, or whether we would make it more pixellated (for instance, having each "pixel" actually being 2 pixels by 2 pixels, so we would only need 320 by 180 pixels per screen) or not. We chose not to, because we knew it would look much better at the highest resolution possible. In comparison, my GM48 entry Elements was 640x360, but used 4x4 pixels per pixel, so Stranded was going to have sixteen times as many pixels as that! But Aaron was confident, and that made me confident.
I got back to work on the teleporters, which were giving me a bit of trouble. I wanted the player to be shown the minimap when they used a teleporter, and for them to select which section they wanted to teleport to. If that section had a teleporter, they would be transported (unless it was the same section they were teleporting from). I'm not exactly sure why, but I was having trouble with the whole thing. The minimap code wasn't massively clean, and I don't think that was helping. I got it all working in the end, but it took almost three hours of tweaking to get it there (it was now 4PM!). Already, the code wasn't as clean as I'd like, but I didn't have the time to tidy it all up without sacrificing future features, so I left it.
Now I had a world that could be explored, and locations that could be easily teleported between. The next step was to implement the progression. I wanted certain locations to only be available once you had certain abilities. Some of the map would be blocked off by a weak wall that could only be shot down once you fixed your laser cannon, and some of the map would be unreachable until you fixed your jetboots. This is a classic feature of Metroidvania-style games, where you unlock new areas naturally as you get more powerful.
I started with the jetboots. I imagined them working similar to the jetpack from Spelunky (of which I have clocked close to 150 hours playing... oops). With the jetboots using the same control as jumping, I had to do a few checks to ensure the control was being pressed once the player was already in the air etc, but all-in-all, the jetboots didn't take me too long to do.
While I was doing the jetboots, Aaron had got back to me with the first two pieces of work: the tiles for the terrain, and the barren planet background:
After replacing the placeholder art with these, I had this:
It looked great! I loved it. I was very happy knowing that I could code away with the art being taken care of much better than I ever could.
Getting back to the code: I had the jetboots mechanic done, so it was the laser cannon next. Before I started on that, I wanted to add something to shoot: enemies! I have been working my ass off on enemeis in my main project for quite a while now, so I was well in the zone for doing this kind of work. It wasn't long before I had mocked up the most basic enemy I could: a yellow square that moved left and right.
I added collisions with the player, and gave the player health to lose when they collided with an enemy. I knocked the player away when he was hit, using physics_apply_impulse, gave him an immune timer (a window of safety where he couldn't be hit again), and made him flash by toggling an inversion shader based on how long he had been immune for. The last effect was to implement a ragdoll version of the player when he ran out of health. The alternative would have been to have Aaron draw a specific death animation for the player, but I knew how to easily do some ragdoll using the Box2D physics engine, so I went ahead with it to save Aaron the time. The result was this:
It was 7PM by now, and Phillip had just emailed me back with all of the assets that I had asked for (it amazes me how quick he can work). I listened to the track he had composed on loop for a while, and it really got me into a dark, desolate, space groove - it was perfect for the mood and atmosphere I was aiming for. I coded the music and sound effects into the game thus far, and was very happy with how it was feeling.
I took a break at this point for dinner, and even let myself watch a whole episode of Daredevil as a reward (which I later felt guilty about, as I wanted all the time I could get to code!).
Now I had enemies, I needed to be able to shoot them. This wasn't too hard to implement. The enemies behaved very similar to the player when they got hit, being knocked back, temporarily immune, losing health, and flashing. The laser cannon functioned on a "charge" resource that would regenerate over time. I added a "has_gun" boolean to the player object, because I knew that they would unlock this ability mid-story, and so the simplest way to enable the gun would be to toggle a boolean (I had done the corresponding implementation of a "has_jetboots" variable).
Things were looking pretty good. The basic game engine was coming into place. I wanted to add a few more platforming challenges, so I started with phase blocks that were simply Ground objects that changed into objects without a physics fixture and back again on a timer. A very simple mechanic but could add challenge and variety to a few areas.
The next thing was to add spikes - a staple of a lot of platformers because it makes some jumps more focused on precision. My spikes inherited from my Enemy parent object, but had a bit of code to make them immune to taking damage (so they would hurt the player, but the player couldn't hurt them).
I also made the teleporters act as checkpoints. If you entered a screen with a teleporter, a boolean on that teleporter was set to true, and set to false on every other teleporter. Then, when you died, you would teleport to whichever teleporter had the boolean set as true.
Earlier on, I had made the decision to have one massive room for the level, rather than one room per screen, as having one massive room would make it easier to design the level in the room editor, and would also make coding the minimap easier. One problem that this did lead to though, is the fact that I wanted all enemies to reset when re-entering a screen. If each screen had been a room, this would have been easy (in fact, no work would need to be done). However, with all the screens joined together into one big room, I had to manually manage this.
GameMaker objects automatically store their xstart and ystart positions as the x and y co-ordinates they were created at. To reset an enemy, I had to move them back to these positions. I also had to implement hp_start and dir_start variables, so that their health and direction could also be reset upon entering a screen. What's more, if an enemy had died, I wanted it to respawn when the player entered that screen again. To do this, I added an instance_create to the Enemy destroy event, where they would create an invisible EnemyRespawner object, that stored the x, y, hp, direction, and object_index variables of that enemy. Then, these EnemyRespawner objects would wait until the player had left the screen, and then recreate the enemy so that it would be there when the player returned.
I knew that unlocking the gun and the jetboots would be the player's key to accessing new areas of the planet, by shooting down weak walls or flying over tall rock formations. "Tall rock formations" were as simple to implement as stacking Ground objects on top of one another, but "Weak walls" would require a new object. It was very simple to implement, inheriting from the Ground object and then being destroyed when colliding with the player's laser.
It was approaching midnight now, and, while I had the energy and motivation to code well into the early Sunday morning, I decided to be sensible and get some rest. I would have to go to sleep at some point, so I might as well do it when everyone else was sleeping and when it was dark. My mind was whirring, though. I had made good progress, and for anything else, it would have been more than enough, but there was so much more that I needed to do. I didn't even have the level designed yet, or any of the cutscenes and dialog. The power crystals that enabled you to fix your laser cannon and jetboots were nowhere to be seen, and there was a lot more art that needed to be implemented, as well as new enemies with new abilities. It took me a long time to get to sleep.
The first thing on my Sunday agenda was to design the level. The room was 6400 pixels by 3600 pixels, a total of 100 screens. I had made it big on purpose, because I wanted to give an adventuring/exploring vibe to the gameplay. Certain screens would need to be challenging, with the challenge varied based on the locations of spikes, enemies, and platforms. It wasn't long into the level design that I realised I should plan out my enemies first. I wrote down a few ideas for enemies (shoots projectiles, jumps at the player, moves up and down or left and right, floats towards the player), and made placeholder objects for these so that I could position them in the level, rather than guessing where they would be.
The planet was split into three sections:
1. Can access without laser cannon or jetboots
2. Can access after fixing laser cannon
3. Can access after fixing jetboots
Each would contain a power crystal that would allow the player to progress to the next zone by fixing the relevant equipment (or, in the case of the last crystal, let the player complete the game by fixing the ship). I wanted the game to have a difficulty curve, so I tried to have the first zone have the easiest screens, and the last zone have the hardest. I also didn't want to have too many enemies in the first zone, before the player even had a working laser cannon to fight back with.
Once I had the planet designed, I did several playthroughs of the level to ensure everything felt right in terms of difficulty, progression, and atmosphere, making tweaks and adjustments as I went. It was close to 1PM when I had the "close-to-final" draft of the level, and it looked like this:
It had taken me all morning, but it was a crucial part of the development process. You can have all the best mechanics in the world, but it might not matter if your levels are shit.
Because I had all my objects within one big room, I needed to think about performance issues. As I understand it, unless you compile with the YYC, every GameMaker object will automatically be constantly checking for lots of event triggers and collisions and what have you, even if they have no relevance to the execution of that object. Correct me if I'm wrong, of course, but there is *some* reason why GameMaker objects are inherently slower when not compiled with the YYC.
Luckily, there's a solution to this, which is deactivating objects. When an object is deactivated, it basically acts as if it isn't there, and won't bother executing any of its code until it's reactivated.
I built my own computer, and it's more than capable of running most things on their highest settings, so it's hard for me to judge how well something will run on a low-end budget laptop, but I wanted to make sure that the game would be playable regardless of what it was being run on. To this end, I created a Deactivator object that followed the player from screen to screen. It would deactivate every instance in the room, and then reactivate those that were within the player's current screen, as well as any neighbouring screens (I possibly didn't need to have neighbouring screen instances active, but I knew it wouldn't make much difference to performance, and I would rather be safe than sorry).
While testing the level, I noticed that the jetboots felt quite clumsy, particularly in the last section, where there were small platforms to fly between. I tried a few tweaks to the jetboots power / consumption / recharge, but none of them felt like what I was aiming for. Then I realised that what I really wanted was to replace it with a classic "double-jump" mechanic. This would provide much more precision for the player to navigate with, rather than flying through the air with hardly any control.
You can also see, in the above GIF, that I had started coding the extra enemies at this point. I had a lot of the default enemy down (hurting player, taking damage, dying), so each enemy only needed very simple code to let it move in a certain way or shoot projectiles.
I was very dedicated to the idea of setting the right atmosphere and mood, and I knew one good way to do this would be to introduce dialog, so the player can get into the head of the stranded astronaut. It would also be a good medium for giving hints about what direction to go (as I wanted to make sure people didn't get lost or confused - especially if they only had 5 or 10 minutes to spend playing the game).
If you've ever tried to implement a dialogue engine, you'll know that there are several things that can be awkward to code (specifically typewriter-style text reveal and cutting off text at the correct string_width). I decided to use JuJu's Dialogue Engine that handles all of this and more. I hadn't used it before, so it took me a bit of time delving into the code to get the exact textbox styles I wanted. The result, however, looked pretty great!
The actual text is stored in a .csv file, that can be edited with Excel.
I would definitely recommend it to anyone who wants to add this kind of functionality to their own project!
It was about 4PM when Aaron sent me over the player sprites. They had taken a while, due to the high resolution, but they had definitely been worth it. I finally replaced the crappy programmer art I was currently using for the player, and got this:
As you can see, there was no running animation yet, but everything else was looking pretty sweet.
I had been aiming to be finished by 10PM (as I would be going into work on the Monday), which gave me 6 more hours to finish. I was still missing power crystals existing and giving you the ability to fix your equipment, code for several enemies, start and end screens, and a lot of artwork. 6 hours? I was definitely panicking.
No use in wasting too much time thinking about it, I just had to get my head down and code.
Power crystals were next, and were quite easy to implement. I needed the object that would be collected by the player (destroy on collision, and toggle a boolean on the player object), and then the cutscene that would fix the player's laser cannon / jetboots (JuJu's text engine lets you execute scripts once you reach certain blocks of text, so I used this to execute a script that set "has_gun" or "has_jetboots" to true - very simple stuff). I knew that we wouldn't have spare time for fancy animations on the art side of things, so I did a bit of code to make the crystals float up and down according to a sine function.
Talking about art animations, I now had to update the ragdoll to correspond to the new, more detailed player sprite. The old one was composed of only 4 limbs, but the new one had 8! Once I had the positions of all the joints sorted, it wasn't too much hassle, as I was reusing the code from the previous ragdoll.
The ragdoll's not perfect, but, for a quick solution, I think it's quite good.
You can also see that the UI now only showed health, rather than also showing laser charge and jetboot fuel. I was having a bit of a crisis with these elements, because I didn't know what the best way to display them was. Just having coloured bars looked terrible, and would really detract from the beautiful artwork that Aaron was making. I could alternatively get Aaron to also design some kind of interface panel that would show the stats in a prettier way, but I didn't want to add anything more to Aaron's already full plate. The jetboot fuel didn't need to be shown anymore, due to being changed for a double-jump mechanic. I decided to do a similar thing with the laser cannon, and made it reload on a timer, rather than consuming a regenerating charge resource. I replaced the healthbar with health globes, and everything looked tidier. I figured a 16x16 health globe would be a very feasible thing for Aaron to fit in to his schedule, to ensure that everything still had a consistent art theme.
On the topic of cutting art corners, I decided to do something about the weak walls too. Initially, I had asked Aaron to draw a "crumbling" animation for when they were hit, but I knew I could achieve a cheap imitation of this by creating a bunch of physics objects which, while not optimal or massively pretty, would be quick, and we needed to save all the time we could at this point.
I got another batch of art sent through by Aaron, and this included the ship and the player running animation, which were beautiful.
It was 10PM by now, which was when I had hoped to be finished by! The official deadline was 1AM, but there wasn't much hope of me staying up until then and still waking up in time for work! I was panicking hard. The game wasn't finished! Let alone properly tested!
The solution? Cut more corners. I told Aaron to make the teleporter sprite by bastardising the ship sprite and rearranging it. Instead of animations for the enemy sprites, I dynamically adjusted their image_xscale and image_yscale so that they looked like they were wobbling or writhing. A poor excuse for proper pixel animation, but it gave them much more life than a static sprite, so it would be more than sufficient.
There was one thing that I didn't want to cut corners on, though, and that was the background cave walls. From playing the game, I hadn't liked how, when you fall down the first hole and into the cave, the background is still just the planet horizon. I wanted to add a layer of cave walls inbetween the foreground and background. I hand painted these tiles into the room, and, while it took a while, I think was worth it (it's probably not even something players are likely to consciously acknowledge, but I knew it would look wrong if they weren't there). You can see what it looked like in this final GIF that I ended up using for the game logo on the GM48 page:
At this point, I threw together some very simple Start and End rooms, so that the game wouldn't start as soon as someone opened the .exe, and would also give them a hint that they can play in fullscreen and with gamepads if they wanted to.
The end was in sight. No more major changes needed to be made, but I wanted to spend some time testing it so that I could fix any bugs that were leftover. I sent a copy of the .exe to Aaron as well, so that he could spend some time testing it too. Nothing major came up (I had been doing my best to fix bugs as they occured during development, so that they wouldn't grow into horrendous problems). I decided it was ready to submit, so I submitted it.
That was a good moment. I had literally been working my ass off for the entire weekend. Only taking semi-breaks for food, and just coding away all the time. I hadn't been sure it would be finished in time, but it was, and it felt really fucking good.
I didn't bother writing a description or uploading a logo or banner, as these were things I could do the next day - I just went to sleep.