About this blog
Development of Soulwielder, a 2D action platformer
Entries in this blog
This is part 2 of the postmortem for the Week of Awesome game I made called Soulwielder.
This is about the development of the game; the first part was about the design of the game.
There were several things that daunted the creation of Soulwielder, notably the difficulty I had with making art, the level-creating tool I made, and the physics/collisions system I made.
I started out trying to make the player with a higher resolution than I ended up with, and about 3-4 heads tall. He didn't look awful in the still version, but oh boy, when I started trying to animate him...
I technically had never even dealt with animation, especially not 2D animation. I don't know why I was so confident I could make 5 sprites with animations considering this. The walk animation looked about twice as bad as you'd expect someone's first walk animation to look.
After about five different attempts at making his walk look good, I went to bed; before I fell asleep, I thought I ought to take look at the art of Cave Story for help on the animation.
This is where I got most of my inspiration for how the art of the sprites ended up. I popped the game open and looked closely at the player and his walk animation.
The player was far from 4 heads tall in Cave Story, and his legs were actually so hard to see moving that I had to beat the first cave to get to an area where there was less rock and dirt poking up getting in the way of his feet. The most noticeable part of the animation was his bobbing up and down, not the legs at all. It was simple, but readable, and it worked very well for that style of art. When I adamantly played through Cave Story twice in a row a while ago, I didn't question how my character walked on legs that seemingly start 90% of the way down from his head, or look intently for his feet to see if the motions they were making looked like those you would actually take to walk. I just saw him bob up and down and thought he was walking.
I also noticed that a similar method I planned on using for Soulwielder was being used in CaveStory (I think, don't quote me on that): sprites were made in a small size, then scaled up in a pixel-perfect way to make them bigger and more readable, but maintain their sharp, pixelated, blocky style.
If you double the size of the sprites (I think this is what is done in Cave Story), then every pixel is made of a square of 4 pixels of the same color, but you don't get to use those extra pixels; you stick to the constraints of the pixels you have at the lowest size, never changing the scaled-up version of the file.
For me, the size was 16x16, scaled up 2x to 32x32. I liked the player best this way.
Ultimately, he ended up less than 2 heads tall (10 pixels of head and helmet, 6 pixels of body and legs).
His walk animation was a single frame, where he lowers down a bit and his one-pixel-tall, two-pixels-wide feet go out at his sides and become two pixels tall and one wide.
"This looks stupid," I believe I remember thinking before seeing it play. "He doesn't look like he's walking at all, it just looks like he's spreading his legs out and shrinking".
But I put it in Unity and gave it animation, and it read well and it looked nice and stylized. I liked it a lot after seeing it play.
Here he is, sized up to 8x for easier viewing:
Now, I'm not saying the art of Soulwielder is objectively great...I suppose I'm just saying it is relatively great compared to my first walk animations...
Once I got the player's running, looking-up, looking-down-while-midair, etc. animations in-game and working properly with the movement and aiming controls, I felt pretty glad with how he turned out.
Ultimately, I ended up making the humans and the 'goblin' in a similar way, with similar walk animations.
Everything else is also made in some format similar to 16x16 and then sized up twice, from the flowers to the grass-and-dirt tileset and the bricks used to make the humans' houses.
The buildings don't look great; I would've liked to have made a roof tile tile to add at the top of them, but time didn't really permit it and I knew I'd probably need a good bit of time to get it right. So I just made the buildings out of one seamless square tile found from SpiderDave on OpenGameArt, somewhere within the depths of the many tiles he posted here.
Unfortunately, background art didn't make it in, either; I just threw a vertical gradient into the background because time was running out and at least the gradient looked a little better than using a single, flat background color with the camera setting.
Here's a little screenshot where almost all of the environmental assets can be seen, as well as some of the goblins and the player:
The Environment Editing Tool
Now we enter the programming-related issues I faced.
I thought I'd be making a platformer with low-res graphics for the Week of Awesome, because I found nice-looking resources for the environment on OpenGameArt.org, and I wanted to make a game like that. So I figured I could prepare for it well by creating a little Unity script that could place down sprites to make the game's levels.
It was one day before the competition that I started on this.
I wanted it to let you select a sprite in the Project view and place it in the Scene view by clicking. I didn't want it to just throw down a bunch of sprites and organize them as children of the GameObject with the script, though.
I wanted it to be able to 'bake' the sprites into 1024x1024 textures and arrange them on a grid covering the whole map. This would be more efficient, I figured; when I inevitably got carried away with the placing of flowers later, I wouldn't have 90 different sprites in one area, each with their own GameObject, when I could have one with all of those sprites built into it.
I'd dealt with this sort of code in a game I was making before the compo, called Realm Crawler, where I had dungeon generation that made the dungeons by drawing tiling textures onto a grid of textures, so that each e.g. 8x8 or 16x16 tile of the dungeon didn't have to be a separate sprite, which would be a massive number of GameObjects and sprites.
It involved turning a world position of a pixel into its local position in a texture, based on where that texture was.
The creation of this tool took longer than I'd anticipated, of course; I didn't manage to whip it up in one day, although I got a lot of planning and thought through the most difficult parts of the code in that day.
So I was making this tool during the competition, which was unfortunate and all. It took a few days to get it right, I think (it's a bit of a fuzz of confusion and mild panic), but I didn't devote that time entirely to it; I also planned and designed during it.
The tool was a bit hackneyed and annoying to use, but I'd expected as much.
Rather than trying to make it as an editor script, I decided to make it a normal script, which means it only operates in-game. You put down a GameObject, give it the environment-editing tool script, and then go into Play mode.
You put down your sprites by selecting them in the Project view and clicking to place them. For now, they're just instantiated GameObjects parented to the GameObject that has the script. Before you exit play mode, you copy that GameObject. You then delete the old GameObject and paste the one you copied, to retain the changes you made in Play mode. If you don't remember to copy before you quit play mode, you can kiss goodbye the environment you just made.
I did it this way because it's easier to make a script work in play mode than it is in editor mode; that's just something I've always noticed about extending Unity's editor. It has a lot of gotchas that I've witnessed before, but that I wasn't confident I could avoid. It would just be too much trouble to make a proper editor script in a few days.
To 'bake' the environment, you just pressed a button in the inspector after exiting play mode and pasting the environment. This button was added by a simple inspector script for the environment-editing tool script.
A new GameObject is created in the scene with the textures as children of it, all of which you can set the sorting layers for.
If you wanted to put the baked environment in another scene, you'd have to copy-paste the non-baked version over, then bake it there; because the textures I made for the grid were sort of anomalous little mystery textures generated by code, they only survived in the scene they were made in.
The miscellaneous issues regarding the baking of maps with this tool lasted a few days into the competition, if I remember correctly, then I got it working pretty solidly.
Physics and Collisions
The most panic-inducing difficulty I faced is here: physics and collisions.
There isn't really a way to use Unity's collision system right out of the box to make objects hit things or bounce off of them, but still retain reliable, highly-specific control over how your entities move. You either use physics with rigidbodies so collisions actually stop objects, which entails apply forces to the object, or you use colliders with kinematic rigidbodies and define your own system for how things stop when they hit objects, or bounce off of other objects or whatever you want. Kinematic rigidbody colliders don't collide with static colliders; they only collide with rigidbody colliders.
This means you can't just make an object that isn't subjected to Unity's gravity and physics, but have it actually hit static colliders and bounce back off them, or get affected by them at all.
My solution was to have scripts that handled the movement of objects and their collisions by using Unity's various, very-useful Physics2D methods. Everything that wanted to move the object would give this script its movement every frame, and then this script, in its LateUpdate function (to be sure all other scripts had given the movement they desired), it would apply that movement and clear the movement vector.
I was planning on making a special one for bats, but what with the startling lack of bats in the game, you can probably see that script didn't end up being made.
The only one I made was a GroundedMovement script, for movement of entities subject to gravity.
It would determine if you were midair or grounded by box-casting down from the entity, and setting you to grounded if there was ground there, or midair if there was no ground there.
When you became newly grounded or newly midair, it would call a delegate that other scripts could add functions to, to detect when grounded happened. The player used the landing one to reduce the sideways velocity by half so you wouldn't slide so much after you landed.
It used the Physics2D.BoxCast method to check for walls and ground.
I separated the walls and ground into two different colliders and had the midair BoxCast only detect walls. That was not a very good idea.
The reason I thought this was a good idea was because the box cast was given a direction of movementThisFrame.normalized and a distance of movementThisFrame.magnitude, so I couldn't figure out how better to distinguish between ground and walls. I thought of making it detect if what you hit was ground (and thus you should be grounded) by checking if your movementThisFrame.y was negative (meaning you're falling down), but that wouldn't work if your side hit a wall while falling; you'd become grounded instead of bouncing off.
I thought the only way around this was to just commit to using a completely mad setup for colliders where you use one collider for the ground, or anything you could stand on, and a separate one for the walls, or the things you can't stand on and bounce off of instead.
The midair movement would box cast along your movementThisFrame.direction with movementThisFrame.magnitude, as usual, and only detect wall-type colliders. When you hit a wall collider, you had your X velocity altered to bounce you back based on your 'bounciness' multiplier.
A separate box cast would check beneath you for ground, and make you become grounded if there was ground down there. If I was smart, I probably would've made this only happen if you had negative Y velocity, so you didn't just suddenly become grounded while shooting upwards if you happened to be above a ground collider during that, but I probably didn't do that...
The problem with this was not only that I had to set up the scenes with plenty more colliders, but also how carefully you had to set them up. If the player falls on a wall-type collider, they get stuck on it, as they aren't losing their negative Y velocity, so they keep falling down onto it. There was a way to wiggle out of this with midair movement and dashing, but of course, we didn't want that very disruptive glitch in our game.
If I put the wall collider too high up, the player would land on it instead of the ground (and we already discussed how fun that was). If it was too low, so that the top of it was under the top of the ground collider, bad things could happen.
You would get caught on the top of the wall if you approached it from the side, and then you'd usually just end up getting slowly scooted over until you were inside the platform, eventually taking you off the wall collider until you were falling forever down with no ground to catch you, essentially ending up in the Twilight Zone. I could've avoided this by using a single wall collider the whole width of the cliff/platform, but for some reason I thought using one on each wall was better - and that would have just been making things less bad, not making things function properly.
Another very significant problem was how it reacted when you didn't bounce hard off of walls, but rather, moved into them with low sideways velocity. For example, if I stood at the foot of a wall, jumped, then started moving sideways midair, then I'd get stuck on the wall; the X direction added to the box cast would point the box towards the wall, causing it to hit, and then you'd be placed at the centroid instead of going the full Y distance you had, so you'd get stuck or manage to gradually go more and more up, letting you scale walls just by pressing yourself against them. The enemies would do this; they'd vault past you if you jump over them, then they'd fall off the platform, then just scoot back on up and come after you again.
It was August 15th, very early in the morning (I go to bed at about 7:00 AM and get up after noon the same day, so this was late into my day cycle) when this issue was finally resolved, near the time I went to bed. I thought I was utterly done for, and that my game would have to be a single-platform game where you fought the demons on a flat village with no jumps.
I finally found the solution near 5:00 AM or so.
Rather than box casting along 'movementThisFrame.direction' by 'movementThisFrame.magnitude' distance, I just had to box cast once for the X, and once for the Y. The X box cast would go straight left or right, the Y box cast would go straight up or down, and their distance would be movementThisFrame.x or y.
I'd cast them both against a single, holy layer, called "Environment" or "Solid", the only layer I used for the environmental colliders.
The sideways box cast would cause the X velocity to be 'bounced' if it hit something, otherwise it would move your X by movementThisFrame.x normally.
The vertical box cast would cause you to be grounded if it hit something and the movementThisFrame.y was negative. There was on risk of hitting the side of a collider while falling like this, because the box cast would go directly down, not along the movementThisFrame.direction.
It would cause you to bump your head if your movementThisFrame.y was positive. This was the same as hitting your side against something, but your Y velocity got bounced by it.
This worked wonderfully. It felt quite good to be able to put a collider down and have it act as a ceiling, ground, and walls all at once, without meticulously adjusting two different collider's edges only so things could work sometimes.
This fix was probably the only reason the project ended up coming through; if I hadn't found a solution to this, I probably would've ended up making flat levels so the walls wouldn't be a problem, or if I didn't make such a compromise, I probably would have not submitted the game in time.
I could also set the bounciness to 0 so the player only loses their velocity upon hitting a wall, rather than having it redirected backwards. I believe Cave Story does something like this; you can jump into a wall, rub up against it as your jump carries you higher, get over the top, and then keep going to stand on the top of it. This is a bit more comfortable, but I'm not sure if I like it for Soulwielder yet. The bouncing ended up in the final build, although I may change it later after more playtesting.
Anyway, that's about all I could say about the woes I faced during the development of Soulwielder. Seeing the length of this post, maybe I shouldn't have written "about all I could say"...
It was definitely not a calm, sunny ride, the physics being the major cause of panic and self-directed mutterings of "I'm screwed", but the game ended up OK, I think: the art looks arguably decent and there aren't any major glitches to the general gameplay. I personally like the gameplay, although it's far too easy (as discussed in the first part of the postmortem), and I'm proud of how much work I dumped into this project in only a single week.
I will likely be working on the game more in the future, including updating the environment tool to be less of a hassle to use so I can more easily make levels. I want it to be a little longer and have more enemies and levels.
If you're interested in how that goes, you can follow this journal.
Soulwielder made it to the deadline, despite some of my serious doubts during the hectic week of developing it.
If you want to download it, the zip is attached to this post.
This is the first time I've ever written a postmortem, and I'm not really sure how they're supposed to go, but I'm going to approach this by talking about the game first - how it ended up, how I think it could've been better, etc. - and then, in a second post later, about the development process and the hurdles that took up most of my time or caused me the most heartache.
Ultimately, these posts are probably just going to be what I should've been writing during the competition, but didn't really have the time to.
This first part is primarily about design, and the second part will be about the making of the art and programming the game.
The state of the game
A lot of features either didn't make it in or got changed to make them easier to develop.
Summary of Gameplay
If you don't know, the game is a 2D platformer with A and D or left and right moving you, W and S or up and down aiming your gun (down aiming only possible midair, otherwise you'd be able to shoot the ground beneath you uselessly), Z or J to jump, X or K to fire, and C or L to pay 3 souls to fire a piercing, double-damage soul blast.
You can also dash with space, which was a big tool when it came to getting around quickly.
Each level has some houses that spawn humans, who are helpless and run around with their arms out and their open mouth probably catching a lot of bugs, and demons running around killing humans on touch.
When touched by a demon, you take some damage and temporarily become invulnerable.
Every Human leaves behind a soul when it dies; you can pick these up to spend them on soul blasts. They also heal you by 1 health out of your 100 maximum health every 2 seconds, per soul.
If you don't pick up a soul within 18 seconds (I believe that's what it ended up being), it spits out 2 or 3 demons and disappears.
Souls are white initially, and they get redder and redder until they turn into demons. This gives the player some indication of when demons will pop out of a soul, so they know if it's a risk to charge in to try and pick it up before it turns.
The souls turning to demons and the usefulness of souls for the player is how 'death is useful' in this game.
The goal is to kill all the demons, which advances you to the next level.
There were supposed to be 4 demons: a fast wolf, a slow ogre, a warlock that fired projectiles, and a flying bat.
I adapted the wolf into a goblin of some sort after struggling to make a good-looking wolf sprite. I made a bat sprite that flapped its wings, but didn't have time to script the flying units. The ogre and warlock got cut entirely (although technically the bat did, too, it just has a sprite laying around unused...so really, that little scrub just made the build bigger, I reckon).
To sum it up, we ended up with 1 enemy instead of 4.
Souls and Corpses
Rather than the corpses of humans being left behind when they die, I made them die like everything else: with a 'splat' generated by feeding their sprite's pixels into a particle system and pushing them away from the center.
You could probably guess, this was because I failed artistically at making a corpse sprite that read well instead of looking odd or confusing. I also avoided making a death animation out of this, although I suppose it wouldn't look too far out of place to have them just instantly turn into corpses upon death in a game where everything has a 2-frame walk animation...
The souls of humans turn into demons eventually, not the corpses (since there aren't corpses). Since there's only one demon, the goblin, that's the only thing a soul spits out; it was supposed to choose a type of demon to generate, and generate a number of them that's balanced based on how strong the enemy is. Instead, it randomly generates 2 or 3 goblins, then spits one out every .15 seconds, giving them some Y velocity to push them up a bit.
Rather than pressing a key to heal by expending some souls, I made them regenerate your life over time; 1 health per 2 seconds per soul, with a maximum of 12 souls in your bar at any time.
Thinking back on it, this was probably a mistake; this makes the health you get from souls unlimited if you choose to camp out for a while and wait for healing, and as I touch on below, the game is too easy once you've learned the ropes.
Movement, Navigation, and Dashing
The movement is still quite capable of making you go very quickly, as was intended. Your dash refreshes whenever you hit the ground, and the dash cooldown is very low if you do it while grounded. This means you can dash, jump, dash again before hitting the ground, and then your dash will be refreshed, so you can do it again.
On top of this, you carry over about half of your sideways momentum when you hit the ground, and you can jump again immediately after hitting the ground. While midair, your sideways momentum doesn't decrease like it does when grounded. This means you can just dash, jump quickly, dash before landing, dash quickly after landing, and jump again before your much of your momentum slides out.
This is a rather extravagant amount of speed, but it's not necessarily useful to do more than a few jumps like this, as the levels aren't particularly long and are broken by the need to make controlled, high jumps onto platforms.
There's a jump each level that's designed to force you to properly use the dash to get the highest upward momentum, which is done by jumping, then dashing up immediately after the jump begins. This is instructed as the way to get the highest height possible during the tutorial, so hopefully people aren't thwarted long by the tallest platforms.
I considered reworking the dash system in some way that made this not the case: it seems kind of not right that jumping and quickly dashing up takes you literally 200 pixels higher (about 160% higher than dashing at the last second, right before you start falling). This is how it ended up, though; getting that extra velocity on top of your jumping velocity makes the gravity take longer to reduce it to 0 and make you start falling, so you move up faster for a longer period of time.
I didn't really have time to work on this more. I needed to design levels, and I couldn't design them for the existing system, then change the system and have to redo the levels because you can't jump X high or far anymore, etc.
The combat ended up disappointingly easy and somewhat drab.
Avoiding enemies is as easy as lightly jumping, dashing over them and to their other side, then turning and shooting them, and repeating when they get near again.
I would've liked more challenging combat, because now it just seems like letting too many enemies come to be by neglecting to steal the souls before they turn to demons is simply a hassle, not an added challenge.
It just means dashing above their heads and shooting them for a longer period of time than you would've if you'd played it a bit better.
The only real threat of losing isn't actually dying and being told you've lost, it's of losing extra time clearing out the clump of 30 goblins that spawned while you were away because you accidentally left a soul in a field of humans.
Once you learn how to control your character properly and realize that you can't really die if you just dash around stealing souls and letting the regeneration mend the blows you take, the game becomes quite easy, and the main reason to keep playing, I found, is to enjoy the particle effects rendered from dying demons and humans, from absorbing souls, and from firing soul blasts through a hunk of demons (or humans if you have an excess of souls...whatever floats your boat).
I can clear the game in a little under 2 minutes just by quickly scooting around and wiping out the demons; you don't even really have to concern yourself with your hitpoints.
But anyway, enough bashing my own game: here's what I think could have been done to make the combat better.
I could have made enemies jump periodically, so they'd sometimes get you if you jumped over them.
The addition of warlocks, bats, and ogres would've made things better, I think.
The warlocks' projectiles would have to be avoided if you tried jumping and dashing over goblins, and the bats would pose a similar issue for you here as well.
Also, getting rid of the warlocks with goblins running around them would require some more strategy, as the goblins would constantly be getting in the way of your projectiles.
Had the ogres been implemented, they would've been too tall to jump over with a light jump and a dash, so it'd at least make it take longer to scale them and get to the other side of a group of enemies.
Ultimately, I think the game ended up pretty glitch-free, which I'm happy to say, especially considering the state it was in near the last few days.
The only glitches I'm aware of right now are:
Starting over after seeing the victory screen doesn't get rid of your souls like it's supposed to. This doesn't affect much, since the first level is so easy and there's a spawn of humans not far off anyway, and any souls you stockpile in that first level will still disappear when the next level loads in.
The "screen fading out and back in" effect sometimes looks messy, letting you see the previous level for a moment before the new one loads in (the whole point was to mask the current level disappearing and the new one appearing, so this feature sort of utterly fails sometimes).
I went into this a bit too cocky, I think. I thought I could do too much in a small time frame.
I enjoyed myself, but the going was rough; I pretty much barely scraped by despite putting in 5-8 hours a day on it (above 9 hours on the last day).
I think this was a good and helpful experience, despite the fact that my game might not be scored highly.
I do like the idea of the game and the notion of charging around stealing souls before they turn to demons, and I feel the combat would be fun and have its own sort of flair if I got extra units in to add more variation. I just didn't have the time or foresight to execute it properly within a week.
This is the first post about progress besides the design and planning post; I'd hoped to post more, but I've been putting a lot of time into making the game, so the posting has sort of slipped through the cracks.
The art is coming together.
I decided to use the DB32 color palette for all of the art after finding out about it from the environmental art made by SpiderDave on OpenGameArt.org.
I'd like to show videos of the animations, but my computer isn't really capable of animating even 400x400 GIFs at a decent framerate, so unfortunately, there'll just be stills.
It took me some effort to get the player's sprite how I wanted it; I tried too high-res a version at first before settling on a 16x16 version with a simple 2-frame walk animation. He's compact, but I like the style and it's easier to manage.
Here he is in 3 frames, first normal (forward-facing), then looking up, then looking down while midair:
The player and most other entities are created at a low resolution, then have their size doubled by a simple editor window I made in Unity a while ago (none of the programs I have seem capable of scaling in a pixel-perfect way).
This makes it so every pixel in the original 16x16 image is a 4x4 square of pixels in the sized-up version.
The humans are so far my favorite art asset. I made them in 16x16 with a similar style and animated them in a similar way.
I wanted them to look hopelessly frightened and panic-stricken.
The environment has yet to be strapped together, but it's going to be made out of some simple dirt tiles adapted from a single tile found within one of the sets of dirt, grass, and snow tiles found here on OpenGameArt.org from SpiderDave.
They've yet to be thrown into Unity and sized up, but I've put them together, complete with corners and tiles that make perpendicular tiles make a neat corner.
A single brick tile from here, also from SpiderDave, will be used to make the houses of the humans.
The souls are in as well, with some nice particle system magic to make them look neat.
Here's a still of one; please imagine it's animating nicely.
As far as the gameplay goes, the player can run around, shoot, jump, dash, collect souls, and fire soul blasts.
The humans run around frantically and die to demons and player projectiles, yielding souls that bob up and down. They splat with a particle system that copies the pixels in their sprite, generates them, and throws them away from the center, which I came out victorious from a tangle with Unity's particle system to make.
The demon sprites still need to be made, and I might have to exclude a demon (likely the warlock) to make them all in time.
Time's running out and things are tight, but hopefully we'll have at least a few levels and the demons implemented in this last day.
For the first day, I've been designing the game I'll be making and planning out its technical side and the required assets (which I used www.mindmup.com for, a free mind map creator).
The theme is "death is useful".
I don't want to make one of those puzzle platformers where you have to use your dead body to hold down switches or let you step on it to avoid spikes. I want to make something that incorporates the theme into the gameplay a bit less heavy-handedly; something that people unrelated to the Week of Awesome might look at and not even notice the theme was there. I feel like themes are supposed to be sort of subtle like that.
So, I've decided to make a game called Soulwielder, a simple and short 2D platformer with a low-res, pixelated art style.
The game will play a bit like Cave Story: you use the left and right arrow keys to move, the up arrow key to aim up, the down arrow key to aim down (if you're midair), Z to jump, X to shoot, and space to dash. If you're not holding up or down, you aim forward, which is directly left or right based on your character's facing.
Alternatively, you can use WASD to move and aim, J to jump, and K to shoot.
Grounded dashing is a quick burst of momentum in the direction you're facing, with a low cooldown.
Midair dashing is more powerful, but has a higher cooldown. It travels in the direction you're holding with the WASD keys, so you could aim it diagonally up and right by holding W and D, or even aim it down with S.
The enemies are a host of 4 demons scattered throughout the land: quick wolves, flying bats, and tall, slow ogres, which all hurt you when they touch you; also, immobile warlocks which throw little red orb projectiles at you from their staff.
If the demons kill you, you restart the level from the beginning with full health.
Humans and their Souls
Several times throughout the game, you'll come across little villages of humans being attacked by demons.
Humans will keep popping out of the doorways of buildings in the village. They are helpless, and run around frantically until you shoot them or a demon touches them, at which point they die and leave their body on the ground.
If you step on a human body, you absorb its soul.
You can press C to pay 2 souls to fire a more powerful shot that explodes in a wide radius when it touches a demon, or pay 3 souls to regenerate about 15% of your health.
If you don't collect a human's soul within a certain amount of time (probably 1-2 minutes), the body disappears and turns into 2 wolves, 4 bats, 1 ogre, or 1 warlock, chosen randomly; the number of demons is different because they vary in strength (bats are quite weak).
You have to make sure you don't let too many dead humans turn into demons by collecting their souls yourself, which lets you spend their souls for healing and more powerful bullets.
Eventually, the humans stop coming out of the buildings.
To clarify, the incorporation of the theme is through the usefulness that you as well as the demons find in the death of the humans.
I may also incorporate a mechanic where dying demons explode into little shockwaves that push the player away from where the demon was, so that you could avoid falling on demons for an extra second or so by killing them and so you get pushed away from oncoming demons by killing the ones at the front of the line.
I'll be using environmental art from SpiderDave on OpenGameArt.org, such as this for the ground tiles.
I'll likely make buildings out of some of the brick-like tiles he's made, and I may create some tiles for the roofs of buildings.
The enemies I plan on making myself, and giving simple animations.
Those are the plans for now; let's hope all goes well and nothing has to change too much during the week.