How I Made Upheaval For The LowRezJam
# 0
This here is my log for how I created Upheaval, a 64x64 FPS made in Godot 4 for the LOWREZJAM 2023. I don't know if this is against the rules, but the day before the jam, I put together some plans for the jam, including a day by day list of tasks. From the idea of the jam, I already had an idea of making on FPS in super low resolution, and ideas just came bursting out of me. I had ideas for weapons, enemies, and even some lore and story ideas. It just so happened that one of the themes announced was *Invasion*, which perfectly lined up with some story ideas I came up with. But I didn't start development until...
# 1
On day 1 I created most of the basic systems I'd use as a foundation for the rest of the game. I ended up with a player character that can move, jump, and shoot a pistol at an enemy that detects, follows and shoots back at you, until either you or it die. Let's go through how I got there.
First, I plopped together some cubes into a scene for a basic level. Nothing special, but enough to navigate around and get a feel for the mechanics. And, because this is LOWREZJAM - the resolution got cranked all the way down to 64x64. Also, I set the stretch mode to viewport and maximised the window. Look at them pixels.
On to actual mechanics. I started with a player character, a cheeky classic capsule collider with a camera on top. I used a combination of composition and inheritance, with reusable components for all of the player's functions. There's a component for motion, for rotation, gravity, player input, weapons, jumping, health, hit detection, state control, and for AI detection. As the names suggest, each of these components is responsible for a portion of the player's functionality, and they all communicate together to provide the whole of the character's behaviour. All player input is handled by the player input component, and the state machine comes in handy to decide when and how player input is handled by calling functions in this component, and to dictate the flow of information between the other components. For example, in the motion state, the motion component is called to apply motion. Without being called, the motion component doesn't do anything.
Going through the components, we first have the motion component. This takes in a direction, and applies speed to it, and sends it to the parent character body. It smoothly decelerates if the input is zero. There's also a gravity component which checks if the character is on the floor, and applies gravity if not. It also automatically changes the state to falling if it's not already. Related is the jump component, which just adds the jump velocity, and holds a jumping bool so the gravity component doesn't glitch out when jumping. The hitbox component does what it says on the tin - receives hits from projectiles and sends the signal over to a health component, which holds the hp and signals when it reaches zero. There's also a rotator component which - and you may have guessed this - is responsible for turning the character. The last component is a weapon component, which holds weapon scenes, and tells them when to fire, because they're dumb gits who don't know what they're doing. I really hope that was funny and not just offensive to gun nuts. Those guys at least might be entertained when I talk about the gun mechanics.
As mentioned before, the states dictate what components do when, and when and how input is handled. When in idle, the player can jump, move, or attack, and jumping or moving moves the state machine to those states; when moving, you can still attack, but not in the air. **I haven't yet decided whether to allow shooting in the air. Maybe that could be fun.**
Since this is a FPS, I then moved on to weapons.
I started by adding a pistol, and the beginnings of a weapon system. I created a base weapon class that defined most weapon behaviour for this and future weapons - damage, clip size, shot interval, etc - and a basic primitive cube based mesh. I'm using projectiles, not hitscan, so it also contains a reference to a bullet (another lil capsule), and the ability to spawn it at the weapon's muzzle. I did use a raycast (still not hitscan I swear) to help point the bullet towards the camera's center - if the raycast is colliding, the bullet will go to its collision point, and it not, towards a point 300 units off.
I then created a hurtbox for the bullet to use, gave it collision against walls, and then created a hitbox for the hurtbox to hit. A basic damage system came next, and after some testing and tweaking, the pistol was up and running.
Now, a basic enemy to put that hitbox on.
I created another basic capsule scene (sick enemy red this time), and copied most of the same components I used in my player scene. I did make the enemy slightly slower, though. But with the way I made the components, all the enemy needed was its own state machine, and the ability to detect the player. So I created a detection component, with an area3d and a raycast. If the player is in the area, it turns the raycast toward it, and targets the player if the raycast touches the detectable component. I should mention at this point that I'm using 3 different collision layers for all this: one layer for basic geometry collision so you don't run through walls, another for hitboxes and hurtboxes, and then one more for this whole player detection function. **I'll probably use yet another when or if I decide to set up pathfinding.**
Once targeted, the reference is sent to a target tracker component. This is simple enough, it just keeps a reference to the target and its location, distance and direction. This is mainly to clean up later code, so I don't have 50,000 declarations of target distance etc all over the place.
Now that state machine I mentioned. It's very straightforward, with all the other components, there's not much for it to do but tell them to do their thing. The enemy starts idle, and its state code is literally just: don't move anywhere, and if there's a target, move to the chase state. Chasing gets a bit more interesting: firstly, if there's *no* target, go back to idle and return. This is to abort all the following code which all requires a target, and would throw a fit if the target disappeared. Next, we tell the rotator component to face the enemy towards its target. Next, tell the motion component to move the enemy towards its target if out of follow range, else stay still. Lastly, while all this very exciting stuff is going on, it will fire its weapon if within fire range. Did I mention I gave it a weapon? Oh yeah. My code is just so good, I slapped it in there and said shoot, and it worked. Totally didn't take a load of search and replace functions and refactoring.
The last enemy state is falling which is just empty, since the gravity component handles that. But creating a state lets me later define custom behaviour for falling if I want. Nothing to see here.
And that about wraps up what I did on the first day. Tomorrow, it's on to weapons - creating more of them, giving them sound, and refining the weapon system.
# 2
The first thing I did on Day 2 is the first thing I should have done on the first - I created a git repo for version control. The link will be in the description. I'll also be using a new branch each day, so you can see how the project evolved and where I got each day. That is also how you're seeing so much footage from many stages of development.
Now, with that out of the way, the focus of Day 2 was to diversify and extend the weapon system. That means creating new weapons, deepening weapon mechanics, and adding in weapon changing mechanics. It also means one of the most fun parts of development, and my own specialty - sound design!
The blocky basic pistol I created is nice for a start, and helped me solidify the foundations of the gunplay, but sadly, it'll have to go. I already had a few ideas for weapons written down, so let's start with that, and get some basic modelling going. That's pretty much all creating newalls w weapons will take, considering all guns will pretty much work the same, but with different stats. However, one thing that won't be the same will be the heavy attacks. And also the sounds. And the animations. Okay, maybe creating new weapons is more than models and stats.
The first weapon idea on the list is Bone Pistol. Since I'm going with a sort of meaty, gory theme, it seemed appropriate. My vision for this weapon is something Giger-esque, but more meaty, that literally shoots fragments of bone. The the base pistol will work as a start, but like I mentioned, I had to do some modelling.
I threw together a mishmash of lengths of white meshes to represent bones, with chunks of pink for flesh, and thin pieces of blue and red for veins. I think the result looks suitably gross. I only had enough time on this day to produce meshes for the bone pistol and sinew rifle, but I think they serve as different enough weapons to demonstrate the new pieces of functionality that was added that day: a properly working ammo system, with clips and reloading. For ammo, there are three shared ammo types: light, medium and heavy. The bone pistol and sinew rifle both use light ammo, and I got that working in the game nicely. If I find the time on a later day, I may use it to add another weapon or two. Plus, there's always the weapons review day I have planned.
The weapon component holds the counts for the three ammo types, and the weapon itself counts down the total ammo and clip when it fires. Upon reloading, the clip resets to its max clip size.
I also added weapon switching, which was as easy as deleting the current weapon scene, and instancing the next one in the arsenal array held in the weapon component.
Lastly, I rounded it off with some procedural animation. Nothing too fancy, just tweens and lerps. In total, there's weapon switching, kickback and reloading. Weapon switching is just a basic peekaboo tween down then up. Kickback teleports the weapon backwards based on a kickback float variable, with a little random variation. There's also a slight bit of random x, and also slight random z rotation. These values are all set to lerp back to normal in process. The reloading animation is all timed tweens - it tweens itself up, tweens a roll to the right, waits, then tweens the rotation back and leaves the position to the lerp.
As mentioned, I didn't have much time to work on the game this day, so that also meant I wasn't able to design any sound. I did have some sounds lying around I was able to throw in, though.
All in all, Day 2 didn't add too much in terms of content, but the gunplay feels a lot better now, and maybe that's enough to call it a successful day.
# 3
The focus of the third day is diversifying and expanding enemies. As you may imagine, this includes modelling, sound designing, and implementing new enemies. I had only a few enemy ideas before starting Day 3, these being a zombie-like "grabber", berserk scientists, and chaos creations. These come from the basic story I laid down on Day 0 - which I promise I will get to when story day comes!
So, first things first - let's design a grabber. What even is a grabber? The idea was for it to be just a basic "grunt" type enemy, the first type you encounter in the game. It has no arms, and shambles slowly towards you, throwing tentacle whips. If it gets too close, it will pull you in with its tentacles and try to infect and/or eat you. These are humans that have been fully corrupted by chaos, and live a zombie-like existence, searching for uninfected humans. So what would that look like? My first thought is that I've basically ripped off the first enemy from Silent Hill 2. So that means I should come up with something new, right?
Anyway, here's the grabber I made.
I gave it a few animations. One for a tentacle shoot attack that thrusts out of its chest, and another for a close range vomit attack. A basic idle and walk animation, as well as death, too.
The attack AI needed some work, too, to be able to choose between attacks. So I worked a basic algorithm together into an attack decider component, to select between attacks. This relies on a custom resource, holding the attack range and animation or weapon, depending on type of attack. The attacks are then scored based on proximity to their attack range, and discarded if out of range, or if an associated weapon isn't yet able to fire.
Like the previous day, life got a bit hectic, and sadly, I didn't have much time beyond creating this enemy and fixing up AI. This is going to leave a lot of stuff to catch up to on the review days. Hopeully, I find more free time on those days. I actually forgot to record any footage for this day, or to create a new branch... but here's some footage I made later of me fighting a couple grabbers. Creepy guys, huh?
# 4
Day 4 is all about level design! My absolute nemesis. Well, time to try and cook up some interesting locations to play in. This game is going to be set in an underground research facility, so after having a look online at some images in similar locations, I'm going to go with... a standard room and corridor dungeon layout. Creative, I know. But at least I have a good idea of how to texture the place.
I jumped into blender and quickly extruded some walls, leaving standardised gaps in which to later insert doors. Separate materials were applied to the walls and floors, so I could reapply textures in godot. I went with concrete panels for the walls, and some tiles for the floors. I then removed the standard directional light, and turned off ambient lighting to plunge the place
into darkness.
Next, for some actual lighting, I put up a load of ceiling lights, made from cylinders with emission whacked up, and spotlights. I think this already adds immensely to the atmosphere of the game. Weirdly, even with the 64x64 resolution, this is beginning to look somewhat realistic. Besides the monsters.
As mentioned earlier, this will also need some doors to bring it together. I whipped this together with a few textured basic cubes, and an area to detect the player walking in front of it. This triggers a tween lifting the door up, and deletes the area. There are also locked doors, which... well, they won't open, as the name suggests. Another thing I put together is a screen with text on it. I made it so I can change the text of each screen, so I can leave messages around the place. I put one on the door too, so it can tell you if it's locked or not. It even glows different colours depending on whether it's locked! Neat, I think.
Continuing the trend, I didn't have time for much else other than that. But I did set up VoxelGI, which makes the atmosphere pop even more.
# 5
Day 5 was, in my plan, intended for working on the story, lore, and coming up with characters for the narrative. Since I had already come up with a lot of story ideas in the initial planning stage, I decided to instead use this day to catch up on things I wasn't able to on earlier days - like create more weapons, enemies, and spend longer designing the level, as well as fine tuning some mechanics. I still hadn't implemented secondary attacks for weapons, or the enemy, for one thing (though the grabber's secondary attack has been animated, it still doesn't actually do anything). I did take the liberty of editing the story a little, though. It was originally 7 chapters, with each chapter taking place in a different level. That idea has been scaled back. The story is still exactly the same, but it now takes place over only 3 chapters, and 3 levels. This seems a more reasonable scope, since it only took a couple of hours to get most of the first level done.
And, since today is story day, I'll let you in on what this game is all about. It's nothing groundbreaking, but I hope it's at least interesting. The player character starts the game by waking up in an underground facility that has clearly gone to ruin very recently. You quickly find yourself up against monsters, and arm yourself to fight through them. You then meet others, and discover this is a corporate research facility into things man should not be tampering with. Along the way, you find crazy new weapons and abilities, and suffer flashbacks that reveal your character maybe had something to do with those experiments. It all comes to a head when you face off against the boss, and discover dark secrets.
I know - it's all pretty basic and similar to both Doom and Half Life. But hey, it's a jam game. We're not tackling the human condition or geopolitics here. *Much*.
After this, I went back to my grabber character, and gave it some sound. It needed some ambient sounds, as well as attack sounds. I found some good zombie sounds here:
https://freesound.org/people/bradsimkisshill/sounds/554936/
and also a decent sound to use for the vomit attack here: https://freesound.org/people/InspectorJ/sounds/416076/
In my own library of sound effects, I also got a decent growl for the tentacle shoot attack.
I then added proper textures for the blood particles, and a particle effect and hitbox for the vomit attack. And, since seeing the enemies just wink away when they die isn't very satisfying, I actually created a death state, making use of the death animation I already created for the grabber. With that, the grabber enemy is effectively complete!
I then had the idea to create a whole new enemy, which led me to realise that all the other enemy ideas I had were humanoid, or at least bipedal - so I decided to create a creature. My final idea was some meaty, snakelike thing with arms, that slithers along the ground and spits at you. I call it the wriggler. I gave it the standard idle, walking, attacking and death animations, as well as some sound. The final result is pretty annoying, and that's just what we want!
# 6
Time for some music! Day 6 was set aside for composing music for the game, and, of course, that's just what I did. Got to put that music degree to use somehow, right? Unfortunately, like has happened before, I didn't get that much time. On the bright side, this was partly due to my uncle's dog coming to stay for a few days. Her name's Willow and she's adorable. Just look at those eyes!
Anyway, I did at least manage to get some music written, after struggling with some writer's block. I wanted something that was both atmospheric, but also high energy, and that's a combination that doesn't really make sense, so it was hard to get there. Here's a listen to the basic loop I created, at various levels. I thought I'd go with a sort of industrial metal, but also a bit of a horror vibe. I also had interactivity in mind, because I have an almost allergic aversion to writing static background music. And an obsessive need to always use my self-made Godot plugin, meta-player, the successor to my previous Godot mixing desk.
By the way, if you are a previous user of mixing desk and are wondering why it was never updated to Godot 4 - this is the updated version. The reason it wasn't uploaded to the same account as mixing desk is because Mixrosoft decided one day to ask me to confirm my Mixrosoft account, and my recovery email address is one from 2 decades ago that is inaccessible to me now. I've gone through the account recovery process dozens of times and have never managed to get it back. It's sad, because I look at the mixing desk GitHub now and again, and I see it still gaining stars, and people discussing issues, creating forks to port it to Godot 4, and I'm unable to access the account.
Anyway, rant over. Let's discuss implementing the music I wrote. I first imported my plugin, and... had to clean up some whitespace left over from importing two very helpful pull requests. After that was done, I set up the tree for the song, using a meta player for each node. Since I haven't done a tutorial on how to use it yet, I may as well talk about it now. I first exported each layer from reaper to a separate file, leaving plenty of space at the end of each for instruments to fade out. Back in godot, the first player node I added was a layer that would always play. For this, I chose the bass drop sound effect. I should also mention that my music is loaded into an autoloaded music manager singleton, so I can call to it from code easily, but also so music can play and transition between levels. The next few also always play, these being the electronic bass drum, and the strings.
The next four, however, have their volume controlled by a variable in the music manager called danger. This is all managed in the volume modulation tab in the meta player properties. In order, I enabled them, set their target to music manager, set the target param to danger, set param smooth to 0.7, gave them each a range_fade automation rule (I'll get back to that) and gave them each a high seek up speed, and a low seek down speed. To explain each - the target node and param define where to find the variable, firstly the node that contains it, and its name. Param_smooth controls how quickly to lerp the parameter, to handle quick changes and large jumps. Automation rules define exactly how to interpret the parameter; to either consider it a value on a low to high scale, or to calculate its absolute distance from a specific value. The former increases the volume as the value approaches a maximum limit, and the latter does so as it approaches a point, either from above or below. Lastly, seek up and down speed control how quickly the player turns itself up and down.
Now I've covered how it works, let's examine how I used it here. For the four tracks I mentioned earlier that use volume modulation, they each use identical settings, save for the automation rules. They each respond to danger at the same speed, but to different ranges. The screech comes in almost immediately, when there is even a little danger. Its minimum is 0.0, and maximum is 0.3. This means it is silent when danger is at 0.0, but full volume at 0.3. Since even one enemy raises danger to 1.0, this means the screech is at full volume before danger even reaches 1.0. The next few layers have increasing ranges, with the synth gtr having a minimum of 1.0, and max of 2.5. Bass has 2.0 to 2.8, and finally, the drums cover 3.0 to 3.6.
This is all driven by a couple of lines of code in the enemy script. When the enemy acquires a target, the music manager's danger variable is raised by its exported danger_level value. When it dies, it is reduced by the same value. Taken together, this whole setup means my music raises in intensity as more enemies target and chase the player.
I should also mention another big reason I didn't do more this day. Fellow programmers will relate to this problem hard. I spent altogether too long on a bug I was experiencing with my music. The music was taking far too long to fade in, and fading out far too quickly. I spent hours figuring it out. I noticed that the fade in always seemed to line up with a repeat in the music, so I was debugging my player trying to figure out if the fading code somehow got tangled up in the playback or bar/bar detection code. No dice. I wondered if the parameter smoothing was the culprit. Still nada. In the end, it was one of the rookiest of rookie errors. A single character. A greater than symbol, where there should have been a less than symbol. The crocodile failed me again. Long story short, the calculation of whether or not the target volume was above the current volume, and thus the decision of whether to use the up or down seek speed, was reversed. And so, it used the up speed instead of the down speed, and vice versa. Hours lost, because of this simple slip up.
Moral of the story - *THE CROCODILE EATS THE BIGGER NUMBER!!*
Anyway, once that was fixed, I was quite happy with how the music responded to gameplay. Here's a taste.
# 7
Day 7, the end of the first week. The plan for today was to work on the level sound design and ambience. Because this is my game and my plan and I wrote this whole thing, I'm deciding that means I'm gonna finally add footsteps, fix the gun sounds, and rework some enemy sounds. Plus, of course, some ambient sound for the level. I'm thinking it also needs some props to break up the monotony and aid in finding your way around. One thing that is definitely needed, is weapon pickups. It doesn't make any sense to start the game with all the weapons already equipped.
Firstly, then, let's fix the gun sounds. It's passable for now, but the pistol and rifle both use the same single audio file, and it isn't even a gunshot. It's a punch impact sound. It weirdly works, but it's gonna have to go. So, I found an appropriate pistol sound, and a rifle sound, and tweaked them up to fit in the game. I also tweaked the reverb settings in the buses, as well as tidied them up. The buses are: room (any sound that exists within the world) and music - self explanatory. I then had a burst of inspiration when I got annoyed at being able to hear a monster outside the room, and rolled up a basic occlusion system. It's made out of Area3Ds that map out the rooms and corridors of the level. I call these sound zones. Each sound also has a little Area3D attached - a zone scanner - and it checks which "sound zone" it is currently inside. The player also has a zone scanner. Any sound which is not in the same zone as the player gets sent to the OutsideRoom bus, which has a low pass and compressor enabled, and sends to the normal room bus.
Long story short, sounds that are outside the room the player is standing in will sound muffled.
I also replaced the wriggler's spitting sound with an actual sound of spitting. The hissing sound that was being used for its spit attack is now used for its death animation.
Another glaring omission in the sound of the game was footsteps. I quickly added a footstep system that just checks if moving, counts delta and plays footstep sounds at intervals if moving. Some nice concrete footstep sounds, and voila - it feels like you're actually moving.
Something my cousin pointed out was the lack of reload sounds. I have to admit, this was a bit of a blind spot because I couldn't think of what reloading a meat gun sounded like. I eventually settled on some peeling sounds. Your imagination makes it work. While I was at it, I couldn't resist adding a little visual flavour to the whole ammo/reload thing. I added a piece of "bone" to each gun that shrinks when you shoot, and regrows on reloading. I spent... maybe too much time on this. As well as adding an animation of the bone coming off and coming back during the reload. I really mean too much time. I even added a particle effect of pieces of bone flying off when you shoot. Really, far too long. Was it worth it?
Maybe.
---
# Week 2
Before I get into week 2, I want to reflect on the progress I made this first week. On day 1, I laid the foundations for all the game's mechanics - a player that can move, shoot, take damage and die, two enemies that can do the same, of their own accord, two weapons with all their mechanics, modeled and sound designed, and a level for it to take place in. A good start, I think!
# 8
Day 8 - the first day of week 2. Its focus was reviewing weapons, which basically means taking another look at the weapons to tweak them, and add new ones. Day 8 was severely busy for me, though, and all I managed to make time for was creating a third weapon, and reworking enemies' attack logic so that they don't attack unless they can see the player. I also added a visual component to the ammo counter, so there's no need for huge text on screen telling you how many bullets you have.
The new weapon I created I call the meat launcher. It's a shotgun type weapon, that fires chunks of meat. Tasty. It does the most damage, but is slower to fire and to reload. Classic shotgun stuff. I didn't bother adding a proximity effect where it does more damage up close, because that never seemed right to me.
Each weapon now has a visual indicator of ammo - the bone pistol and sinew rifle both have bones sticking out of them, that shrink as the clip gets drained. It vanishes when empty. For the new meat launcher, it's a skewer type bone with meat on it, and it's the meat that shrinks. To go along with this, animation was added to show the current ammo chunk being pulled off, and a new one added.
Lastly, I refactored the detection system so that the area acquires the detectable, and the raycast continuously looks at it, to ascertain if the player is currently visible or not. This was so that enemies can always check if the player is visible, which is used to decide whether to attack. No more attacking through walls, which had been... a bit of a problem.
A small but significant chunk of progress. That's about all I had time for that day.
# 9
Day 9, and another day of review. This time, I'm reviewing enemies - enemy designs, enemy sounds, enemy mechanics. At present, I have 2 enemy types, the grabber and the wrigger. The grabber is a large, sort of slow mid to short range monster that deals heavy damage. The wriggler is a small, fast ranged enemy that deals less damage but fires quicker. Maybe it's time for a third.
I'd been toying with the idea of a flying enemy for a while, so I thought I'd give it a shot. I tried picturing a few interesting flying creatures I could.... meatify... but kept coming up with batlike ideas and didn't like any of them. Eventually, I asked chatgpt, and it came up with the idea of twisted machines. So, I came up with a drone enemy! It's just a flying ball that makes an annoying sound, with a gun attached, that shoots at you. It's... quite annoying. Perfect!
# 10
All I managed to do on day 10 was change the material of the drone and add spinning blades. I watched Barbie with my girlfriend, though, and that was fun. The drone now looks shiny, and the buzzing sound now actually makes sense, since it is flying with... well, blades.
# 11
Day 11 I created a system of pickups. The game severely needed some reason to explore, as well as a way to actually *find* new weapons instead of having them all to begin with, so I added pickups for weapons, ammo, health, and *keys*, crucially, to open locked doors. These all extend a basic pickup scene, which is just an area that emits a signal when the player collides. It also holds a custom resource, of one of four types - can you guess which ones? Yes, weapons, ammo, health and key. Each one has its own custom data, such as amount of ammo, weapon scene, how much health to restore, and the associated key for key types.
Each pickup has a sound associated with it, and I also added sounds for approaching a locked door, and unlocking a door with a key. I managed this by adding a `key_name` export variable to the door scene, and giving locked doors unique key names. Then, I created a key pickup, which holds its own key name variable, and adds this to the key array in the player's keyring component. The interaction is similar with the other pickups - weapon pickups add the weapon scene to the player's arsenal array in the weapon component. Health pickups just increase hp in the health component by the amount specified, and same with ammo.
# 12
Day 12 was spent creating a checkpointing and save/load system, and fixing a reload glitch, as well as adding a progress bar showing the current health of the player.
The save and load system saves:
- Player weapons
- Amount of ammo
- Player health
- Opened doors
- Picked up items, including keys
- Visited checkpoints
The system uses a custom savedata resource, which is fed by the various systems involved, upon passing through a checkpoint scene. This scene is basically a repurposed door. It plays a sound when you pass through, then contacts the player, and the root of the door, checkpoint and pickup scenes. From these it extracts weapon, health and ammo info from the player, and from the others, which of these scenes have already been interacted with - the roots update an internal list when an item is picked up, door opened, etc - and saves this data to a resource. When the game is loaded, this resource is loaded, and pickups that were picked up are deleted, doors that had been opened are instantly opened, and checkpoints which have already been passed are disabled.
# 13
Day 13. The day before last. The number of bad luck, but in my case, the reverse was true. This turned out to be the day upon which I found the most time to work.
Pressure is *on*. You may have noticed that, the past few days, I haven't mentioned my plan at all. Well, as they say, everyone has a plan til they get punched in the mouth. I was punched in the mouth by a lack of free time to develop, so my initial ideas had to keep getting scaled back until all that was left was just finishing the damn level I made. Story is practically nonexsistent at this point. The priority has shifted to getting the level to a stage where it is playable start to finish as a coherent and compelling piece of gameplay. Anything beyond that I have decided to complete as extra, after the jam, in a "full" release of the game.
On that front, I largely succeeded in laying out the level. I placed enemies in rooms and hallways in ambush formations, and laid a few traps. Using what I've managed to learn from an unhealthy amount of GDC videos and other assorted game dev stuff (shout out to Game Maker's Toolkit and Architect of Games for having great content about game design in general, with honourable mentions to Jacob Geller and Cinema Cartography for tangentially related content), I managed to forge a path through the level using locked doors and pickups to dissuade and guide the player in a fairly linear path, that loops back on itself a couple times.
I introduce this mechanic right at the start with an unlocked door to a room that contains a single monster and a single key pickup. This easily teaches the player that these rooms can contain either danger, rewards or both. The door the key unlocks is close by, and contains health and ammo. This overly positive reinforcement may just overshadow the knowledge that the rooms can contain danger.
One particularly mean thing I did layer in the level as a red herring was place a locked door on a small room, with a key nearby. Inside the room is nothing but two drone enemies. It's a nasty surprise, and keeps you on your toes. Besides these two examples, keys are placed increasingly far from their doors to encourage exploration, leading the player to encounter more and more monsters.
I also expanded and refined the saving and loading system to include enemies. It's soulslike and punishing for enemies to respawn as you do, but I decided it's just far too hard that way, and makes the amount of ammo needed quite unpredictable and possibly unbalanced if you keep dying. I refactored the system, and added logic to disable, rather than delete, the scenes that are unneeded on reloading. Pickups, for example, are hidden and flagged as disabled if the loading system sees that the player has already picked them up.
Enemies have their AI and gameplay components removed, and play their death animations through. To avoid seeing the awkward mas death on reload, I placed the checkpoints away from enemies. This also, obviously, makes it so you don't get absolutely wrecked when respawning in near live enemies. Another problem this raised, though, was the fact that enemies make sounds when dying. This would cause an infernal racket. Creepy, but too weird to include. I brute forced a solution by making the loading system tween the SFX audio bus from silence to full volume over the course of a second. If you're not paying attention, you don't even notice the second of silence at the start of the game. It helps that music is playing. I should also mention that I added a couple of atmosphere enhancers - the lights and screens flicker, and the screens also error out with random characters. Door screens, too, flash up with the name of the associated key upon approach. Also, I created a sign to show area names, and added a few of them around the level to help guide the player. I also placed more screens with some vague messages to set the vibe.
So, I have a level covered in enemies, pickups, and a path suggested by keys and locked doors. Where does this path lead? From the start of the level, to each corner of the map, then back to the start, of course! The first locked door you'll encounter will ask for a white key -
\-- oh, did I not mention I added a message system? Yeah, it's just a label on screen with a function that changes its text then fades it out. You know what these things do. It tells you when you pickup an item, and what key a locked door needs. It also alerts you when you pass a checkpoint and when you unlock a door. --
this white key is found, oddly enough, very close to the door. In a straight line, anyway. You actually have to travel the entire map to get it. The room the white key is in is locked, needs a red key, and is filled with monsters. The room with the red key is - you guessed it, also locked, filled with monsters, and needs a green key. Thankfully, the green key is located close-ish to its door.
As you can see, the journey goes from green door, to red door, to white door. Inside the white door is a typical bunch of monsters - after the obligatory stack of ammo nearby - and an elevator that serves as the end of the level. If I have time, I may actually create a second level tomorrow. It may be more wise to just polish the current level, though. Either way - here's a look at how the level works on day 13.
# 14
The final day is here! I'd been toying with the idea of trying to quickly create two more levels, and implement narrative on this day. But that seems like too much to try and cram in all at once. Instead, I'm going to rely on what I know best. So I'll spend the day writing more music for the level. The idea is to create a full "song", and divide it into sections. I'll continue using the volume modulation tactic for the danger - though perhaps i'll tweak the values - to bring in the full arrangement as danger rises. The first section will, of course, play at the start of the game. Passing through a checkpoint will advance the section to the next, on and on til the end of the level. The one exception is the music cue that starts after collecting the white key - this section of music will increase in intensity as the player approaches the final fight.
I leveraged the transition feature of meta player to allow the music to change as the level progresses. Each checkpoint will trigger the next piece, and reloading upon death or restarting the game will also load the appropriate music piece. I also recorded win and death segments for the end of level and game over, respectively. I had to make some minor edits to the level - such as adding a door to restrict the level to a linear flow. This ensures the player will follow the intended path, and the music will trigger in the right order.
---
# Conclusion
That about sums up the development of UPHEAVAL for the LOWREZJAM. A hell of a lot of my original ideas just weren't able to fit into the game, due in part to both my lack of free time during development, and the potentially unrealistic scope I had set for myself. Plus, I messed up the whole one-branch-a-day idea by missing a few days, and pushing and branching too late... Nut, on the bright side there, at least I preserved that first day, and there is still a clear progression through the branches, for whoever is interested in looking at the source code.
It's possible I may have been able to implement the narrative, create more levels, more enemies and add the friendly NPCs and dialogue I had planned, if I'd had more time.
But the beauty of it is - there is still the future! I've achieved a lot these two weeks, and have a very sturdy foundation on which to build. And I do intend to build.
Over the next few weeks, I'll be working to make UPHEAVAL what I intended it to be.
Leave a comment
Log in with itch.io to leave a comment.