My second successful LD! (Rate my game. Comment and I will rate yours.)

Feedback so far for my LD48 compo entry "Ore Hoarder" has been very positive, and I am hoping to get more eyes on it.

https://ldjam.com/events/ludum-dare/48/ore-hoarder

Check it out, if you are interested. If you reply to this post or comment on my game, I will be sure to review your game as well.

Screen1.png

Screen3.png

Ore Hoarder was less ambitious than my LD46 entry, but I think it was still pretty ambitious for a 48-hour game, and I am happy with what I accomplished in that time. There's a bunch of polish that I wish I had time for, but you know how these game jams go.

In the end, I think the game has a fun and addictive core game loop that creates some tense moments as you race back to the surface while running out of fuel. I've been wondering if the stakes are just a bit too high with the game forcing you to completely restart on death, but those stakes are also the cause of such tense moments, so I don't think I can make the game much more forgiving without losing one of the biggest things that make it exciting. And in its current state the game can be short enough that restarting isn't a huge deal anyway.

The art turned out less garbage than it usually does for me. A lot of the ideas I had for "animating" the bot just worked perfectly the first attempt, so I lucked out.

  • The drills are just static wedge-shaped sprites that I add a random 1-2 pixel shake to as you are drilling, and the effect gets the job done.
  • Same with the tread. It's a static sprite that I shake more the faster you are moving.
  • The flame, again, uses a single static sprite. I render 2 of them on top of each other: one red for the outer part of the flame, and a smaller yellow one for the inner part of the flame. I randomly scale both of them in the x and y direction every frame, and the result is a convincing flickering flame effect.
  • Moving the drills in and out is done on a curve, and the effect is pretty satisfying. I use the same curve for moving the flame in and out.

A lot of people have commented that they actually liked the art style here, so I guess these things worked.

Challenges

The biggest challenge for me this LD was the "sliding window" tile system I created: basically, the only tiles that actually exist in the game world at any given moment are the ones within view of the camera. I had to do this because each tile is a physical object in the game's physics world, and having too many of them active at one time will grind the framerate to a halt.

It took me 3 attempts to get something I was satisfied with.

The First Attempt

My first idea was one that would be the quickest to implement, but was definitely going to be the slowest: I would store all of the existing tiles in one list, and every time we change rows/columns I would check to see which tiles exist in the list that are no longer needed and delete them, and then look at all of the tiles that need to exist but don't, and create them. This results in loops that grow exponentially with the number of tiles added, and I had to iterate through everything at least twice. I didn't think the impact would be nearly as bad as it was. The result was that on my beefy gaming PC I was only averaging 45-60 FPS, down from thousands. If I didn't fix it the majority of my players would be experiencing completely unplayable framerates.

The Second Attempt

The first attempt was a disaster, but it wasn't unexpected. I knew I was taking a risk by implementing my worst idea first, just because it would be extremely quick to code. But it was really quick to code and I didn't lose all that much time in the attempt.

For my second attempt, efficiency was my main focus: when we shift the view, we should check the fewest number of tiles possible, and we should only check them once. For example, if we shift our view left, we create the tiles in the column to the left of our view, and we delete the tiles in our right-most column. It sounded easy enough, but there was a fatal flaw in my plan: it was currently around 9:00 AM EST after working on the game through the night for around 7-8 hours straight (plus another 3-4 hours of designing the game in my head while playing X4 after the theme was announced), and it had been over 24 hours since the last time I had slept.

In my sleep-deprived state I'm sure I made some questionable choices about how I implemented it, and the end result was something that was overly complicated to read and debug (but man was it fast; I was getting 2-3k FPS, compared to 45 FPS with the first implementation). I thought it was working until I noticed some quirks with the shifting, and occasional straggler tiles not being properly deleted as a result of a race condition when moving diagonally too quickly. I proceeded to slam my head against my keyboard for about 2 hours straight trying to debug it, and all the while my focus and abilities were continuously degrading over time due to lack of sleep. Eventually I decided there was no way I was going to find the bug in my current state and resigned to taking a short nap, hoping I would at least wake up with some ideas about how to find the issue.

I woke up 3 hours later feeling pretty well rested, all things considered, and spent maybe 20-30 minutes trying to track down the issue before deciding to just burn the whole thing down and start from scratch. So I deleted all of my tile-creation code, and moved on to attempt #3.

The Final Attempt

My last idea would sacrifice some speed in exchange for code that was much more readable and easier to debug. I organized the data into layers which held a row of tiles, and whenever we shifted I would figure out which layers need to have tiles in the world, and then those layers would delete any tiles that don't need to be there and add any ones that are missing. I could have used some of my ideas from the previous attempt to limit the number of tiles that the layers are looking at when we shift, but that would mean added complexity that might not even be necessary as long as the simple implementation is fast enough to get the job done.

And it was. Sure, I was no longer getting the blazing fast 2-3k FPS that came with my second implementation, but I was settling around 800-900 FPS which was more than enough.

And so the hardest part of my project was finally finished and working. I'd estimate that hurdle ate up around 5-7 of my precious few hours, which is about 5x longer than I expected it to take. Though that's a very rough estimate, as my memory of the whole thing is a bit blurry now.

Other Challenges

I dedicated about an hour to attempting to get particles working, but for some reason either I couldn't get them to spawn or they weren't visible where they were being spawned. After the tile system I was wary about falling into another time-sucking black hole, so I decided to cut my losses and leave out the particle effects in favor of focusing on more important things, like gameplay features. It's a real shame, because the game would look much, much nicer with particle effects.

I spent a long time tweaking the death effect. On death I break the robot into chunks and attempt to send them all shooting off in different directions, but there was a lot of physics engine weirdness going on and I am not really happy with the result. And without a particle effect explosion the whole thing is much less exciting than it should be.

The black checkerboard background is just a room background that is automatically tiled, but the brown checkerboard background is actually made of tiles that are created wherever there is a gap. At first I was just going to leave the black background there showing you where the holes are, but once I decided I was adding heat damage I really needed that background to turn red as the temperature rises. I'm glad I took the time to implement it because it looks way better than it did before, but I got held up by a stupid bug that resulted in duplicate tiles being created and the framerate dropping significantly. I didn't realize it at the time, but I actually solved the issue with the duplicate background tiles without actually solving the layer creation bug that caused it. I covered up a symptom without treating the cause. Whoops.

What's missing?

There are a lot of areas of the game where I wish I had spent more time.

Sound. I spent an hour during the early morning of the final day using Bfxr to randomly generate sound effect after sound effect, saving any that I thought might come in handy. But the few sounds that made it into the game were only added in the final hour before the end of the compo, because I realized there were some things that just needed audio feedback. For example: selling your cargo, or taking fall damage. These things could easily be missed if the player isn't staring at the UI when they happen, which would cause confusion.

The UI. It's pretty self-evident, but I really just didn't get to prettying up the UI. What made it into the final game is just a slightly-improved version of what I was using for debugging purposes during development.

The title screen. If the title screen looks like absolute garbage, it's because I added it in the final 15 minutes of the compo. It's a real shame, because the title screen is the first thing the player sees. In my head I imagined a screen with an endlessly scrolling ground, and an AI controlled bot that would continuously be digging down as the camera descends, attempting to always stay in view. I liked this idea so much that I have already implemented it in a version of the game that won't be made public until the results of the compo are announced because I don't want to risk influencing the rating. It's neat. I hope some of you will come back later to check it out.

Conclusion

All right, well that was way too many words, and I'm sure nobody is even going to make it this far. Despite the many things I wish were improved before the end of the deadline, I am happy with the result. Judging from the feedback so far, I am cautiously optimistic that this game will outperform my entry from LD46. Fingers crossed.