Melody Muncher Post-Mortem

Posted by (twitter: @ddrkirbyisq)
September 12th, 2015 11:15 pm

Hi there!  DDRKirby(ISQ) here with my =10th= LD entry (wow!), Melody Muncher!

2

Link to play and rate: http://ludumdare.com/compo/ludum-dare-33/?action=preview&uid=7285

This one was a blast to make, and I ended up working for 2 weeks to make the Post Compo version (out now!), adding a new mechanic, animated backgrounds for each level, more songs, 3 separate difficulties, and more!

When the theme was announced this time as “You Are the Monster” I was actually quite disappointed, just due to the fact that it was so similar to “You Are the Villain”!  I mean come on guys, really?  But now that I think of it, You Are the Villain was EIGHT LDs ago, so I guess I can’t fault people too much for it.  I wonder if anyone decided to redo the same game concept that they did for LD25?  It would be an interesting challenge, just to see how far your game jam skills have come over the past years…

Anyways, despite my initial dislike for the theme, my idea and game came together really smoothly this time; I can’t even remember running into any hiccups at all!  As always, let’s go over what went well and what didn’t go well.

 

What went well:

Avoiding other commitments during LD weekend
Almost every other LD I’ve done, I’ve had =something= else to attend to over the course of the jam, usually on Friday night.  Usually I tell myself that it’s fine and that I can just try to brainstorm in my head while that happens, but to be honest, that never quite works out and it’s basically like I start off behind by 4 hours already.  This time I decided that I really was just going to dedicate the whole weekend to LD and besides some driving here and there and dealing with meals (gotta eat!), I was just heads down working the whole time, which was GREAT!  An exhausting weekend, for sure, but I don’t think there’s any way around that.  For Sunday, after breakfast and running a quick errand in the morning, I pretty much worked straight through the entire day until the deadline…I stopped twice for the bathroom, once for refilling water, and once for a massive yawn/stretch…that’s it.  Hahaha, you other LDers will know what I’m talking about…Sunday is usually that day when it’s like “omg I have 5 hours left and I still have to add in 2 more enemy types, also my game has no menu, title, or tutorial and I haven’t tested the difficulty at all AHHHHHH”.

3

Game concept and execution
As mentioned earlier, everything came together really smoothly for Melody Muncher, without a hitch, really!  I spent Friday night doing my usual brainstorming routine, and considered plenty of other possibilities, but the idea of having the piranha plant munching guys as a rhythm game occurred to me pretty early on, and it clearly had the most potential while also playing to my strengths, so I went for it.  Huge success!

Level data layout
One of the reasons I was able to cram in six (!) full songs to the 48hr version of Melody Muncher was because I made sure the levels were really easy to program in.  Unfortunately this was NOT the case for my previous game of this genre, Ripple Runner.

Here’s what a level definition looks like in Ripple Runner:

// Section 1
addPlatform(startTime + -10.0 / bps, startTime + 36.0 / bps, -40, timingWindow, level);
addPlatform(startTime + -10.0 / bps, startTime + 36.0 / bps, 40, timingWindow, level);
addPlatform(startTime + 37.0 / bps, startTime + 44.0 / bps, -40, timingWindow, level);
addPlatform(startTime + 37.0 / bps, startTime + 44.0 / bps, 40, timingWindow, level);
addPlatform(startTime + 45.0 / bps, startTime + 52.0 / bps, -40, timingWindow, level);
addPlatform(startTime + 45.0 / bps, startTime + 52.0 / bps, 40, timingWindow, level);
addPlatform(startTime + 53.0 / bps, startTime + 60.0 / bps, -40, timingWindow, level);
addPlatform(startTime + 53.0 / bps, startTime + 60.0 / bps, 40, timingWindow, level);
addPlatform(startTime + 61.0 / bps, startTime + 62.0 / bps, -45, timingWindow, level);
addPlatform(startTime + 61.0 / bps, startTime + 62.0 / bps, 45, timingWindow, level);
addPlatform(startTime + 63.0 / bps, startTime + 80.0 / bps, -40, timingWindow, level, true);
addPlatform(startTime + 63.0 / bps, startTime + 80.0 / bps, 40, timingWindow, level, true);
GameWorld.world().add(new Checkpoint(startTime + 64.0 / bps, -40));
...
// Section 2
 addPlatform(startTime + 80.0 / bps, startTime + 100.0 / bps, -40, timingWindow, level);
 addPlatform(startTime + 80.0 / bps, startTime + 108.0 / bps, 40, timingWindow, level);
 addPlatform(startTime + 108.0 / bps, startTime + 116.0 / bps, -40, timingWindow, level);
 addPlatform(startTime + 116.0 / bps, startTime + 120.0 / bps, 40, timingWindow, level);
 addPlatform(startTime + 120.0 / bps, startTime + 124.0 / bps, -40, timingWindow, level);
 addPlatform(startTime + 124.0 / bps, startTime + 126.0 / bps, 40, timingWindow, level);
 addPlatform(startTime + 126.0 / bps, startTime + 128.0 / bps, -40, timingWindow, level, true);
...
...
...

And so on and so forth.  And that’s only the =first half= of the EASIEST level.  Yuck!  Unfortunately I was super duper hacky while coding up Ripple Runner so the way that I constructed the levels was actually just by placing each platform individually.  This involved tons of hacks, especially trying to deal with assymmetrical timing windows which varied according to which kind of obstacle you were using (spikes, rippling, jumping), and a bug that prevented me from creating single platforms that were too long….etc etc.

Thankfully I didn’t repeat the same mistake this time.  Here’s what a level looks like in Melody Muncher:

result.SfxName = "sfx/level3";
 result.BeatDivision = 2;
 result.BeatPixelLength = 80;
 result.Left = 
 "........ ........ ........ ........" +
"1.....1. 1....... 1....... 1...1..." + "1....... 1....... 1.....2. 1...1..." +
 "1.1..... 1...1.1. 1....... ........" + "1...1.1. 1.1..... 1....... ........" +
 "1.1..... 1...1.1. 1.1.2.1. ........" + "1...1.1. 1.1..... ........ 1.1.2.1." +
 "1....... 1....... 1.....1. 1...1..." + "1.....2. 1....... 1....... 1...1..." +
"........";
 result.Right =
 "........ ........ ........ ........" +
"1....... 1....... 1.....1. 1...1..." + "1.....2. 1....... 1....... 1...1..." +
 "1...1.1. 1.1..... 1....... ........" + "1.1..... 1...1.1. 1....... ........" +
 "1...1.1. 1.1..... ........ 1.1.2.1." + "1.1..... 1...1.1. 1.1.2.1. ........" +
 "1.....1. 1....... 1....... 1...1..." + "1....... 1....... 1.....2. 1...1..." +
"........";

Much better!  Here I’m using “1” to indicate a green enemy, “2” for a red enemy, and “3” for blue enemies (which don’t appear in this particular level).  I ended up using “[” and “]” to denote the yellow centipede enemies in the post-compo version.  The periods just indicate points where there are no enemies, and spaces get automatically ignored.  It’s still perhaps not 100% ideal as I still had to build up separate strings for the left and right sides of the screen, but overall inputting notes for songs went pretty quickly.

Coding it the Right Way
So, again, for Ripple Runner I used a bunch of stupid hacky coding, and as a result, even though your X position in Ripple Runner is locked to the position of the song (good!), your jump height and y position is not (bad!)–it’s actually just normal platformer gravity.  This led to some pretty clumsy manual hacking about to get the gravity to be correct for each level (it needs to be adjusted for each BPM setting!), and in general just led to sad times within the code.  The end result still ended up just fine, but…

When it came to program Melody Muncher, I had learned my lesson, so I made sure to do everything right.  The position of all of the enemies is dictated solely by the position of the music, and there are NO collision boxes or movement physics or anything!  Each enemy knows what beat it should be hit on, and the enemies on either side are kept in an Array, sorted by order of arrival.  When you press left or right, we run through the first part of the Array looking for enemies whose beat is within the defined timing window–no collisions or any other nonsense needed!  Very clean, very sensible, and the code was much better and simpler as a result.

4

Post-Compo Version
This might not technically count as something that “went well for LD”, but this was the most fun I’ve ever had working on a Post-Compo version of a game I’ve made.  Probably because of the above two factors, and also because I knew I had something with a lot of potential.  Making new songs and mechanics was a blast, and even though it took a lot of work to add all of the new features in the Post-Compo version, I’m super happy with how it turned out, and I believe this is my most polished game ever as a result.

 

What didn’t go so well:

Input Delay and Lag Calibration
Okay…so this was mostly a stupid mistake.  So one of the mechanics in the game is that after a few levels, enemies can come at you simultaneously from both sides, so you have to press Left and Right at the same time to do a split munch.  Simple enough, right?

Well, on the coding side, I implemented this by having a separate animation — one where Ms. Melody has two heads that are each attacking.  (As it turns out, while working on the Post-Compo version I had to redo this and just implement each head separately to allow for the yellow long centipede enemies to work)  I then also decided that because people probably weren’t always going to hit left and right at exactly the same time, what I would do is this:

When you first hit left or right, the plant transitions into a “getting ready to attack” state (with a different animation frame) and waits for a frame or two, during which you have the opportunity to input the other direction.  Once the frame or two is up, the attack actually happens.  So this was good because even if you hit left on frame #1 and right on frame #2, you still get the split munch on both sides.

The problem is that by doing this, I essentially delayed every input (as well as the resulting “munch” sound) by something like 17 or 33ms.  Now, that may not seem like much, but in a rhythm game where your actions need to be really tightly synced, you can really notice, and people did.  Add that to the fact that my default lag calibration for Flash builds was slightly off (I had to shift it by maybe ~50ms compared to native builds) and people definitely felt that their inputs were delayed.  Now, part of this was that I simply didn’t have enough time to program in a robust and user-friendly lag calibration setting (it’s much better in the post-compo version!), but most of this was just my own fault for adding additional input delay unnecessarily.

The good news is that the post-compo version fixes this entirely, and if you compare the two the post-compo version should feel MUCH better.

Missing was too punishing
In the original 48hr version of the game, if you try to attack when no enemy is on the corresponding side, Ms. Melody does this ugly faceplant animation which leaves you stunned for a half beat or so.  This was designed intentionally as a means of punishing you for trying to attack when there was no enemy, as well as to eliminate the cheesy strategy of just trying to munch on both sides on every single eighth-note beat.  If you didn’t have the recovery animation, you’d just be able to do that and get a perfect score, which was obviously no good.  I had been trying to think of various ways to solve that issue, and after trying it, this seemed like a clean solution, as well as making it very unrewarding to miss notes, which is what I wanted — it should feel good when you hit enemies, and bad when you miss enemies.

Well, the problem is that players don’t like feeling bad.  One of the complaints that I got was that the recovery time for missing was too long and it led to people feeling like the game was “unfair” (ugh, loaded term).  Now, if you’re used to rhythm games, you probably didn’t mind this as much, but if you’re not a rhythm gamer, what happens is that you miss one enemy, then because of that your input for the second enemy doesn’t register (since you’re still in recovery), which throws you off and then you end up missing again ……, in the end that’s a situation that just doesn’t really feel good.  So lesson learned — reward your players for succeeding, but don’t punish them for failing.

The solution in the post-compo version was to eliminate the recovery delay and just add in a proper scoring system that adds points to your score based on your current chain, a la Guitar Hero.  Now I no longer need the recovery delay because if you try to use the strategy where you attack both sides on every eighth note, you’ll keep on breaking your chain over and over again, leading to a poor score.  Problem solved!  But unfortunately, I just didn’t have time to get the more fancy scoring system and everything done in the 48hr version.

No Shovel Knight
Okay, so this wasn’t really that big of a deal, but when I was drawing up the graphic for the red enemies I knew I wanted it to be something big, beefy, and blocky, so I went with an armored knight.  Since it was a knight, I decided to give it a sword.  I even referenced some Shovel Knight images as I was drawing it up….but for SOME reason I missed the golden opportunity to just have my Red Knights carry shovels and be “shovel knights”.  Which would have made perfect sense (they’re trying to dig up Ms. Melody), AND would have been a great callout to a great game.  Biggest missed opportunity everrrrrr =(

48 Hours!!!!
Alright, I guess this isn’t really something that “went wrong”, but it still amazes me every time how quickly the 48 hours goes by, despite you wanting to cram in more and more features.  “If only I had 1 extra hour!!!”  This was apparent this time around as well; the submitted version of my 48hr entry was missing some key components that I really really wanted to get in, but I just. did. not. have. enough. time.

Specifically, better lag calibration was one item high-up on the wishlist that I didn’t end up getting to squeeze in until later.  It wouldn’t have taken long, either!

And, changing backgrounds was another real big item.  I had a hue shifting effect that I used in Ripple Runner which worked fabulously, and I really wanted to do the same thing in Melody Muncher, because without it the backgrounds feel very static, especially when the music is very energetic and has these big builds and climaxes.  Of course, it turns out that because I’m now working in Haxe and Haxepunk, I can’t just use punk.fx (flashpunk) to do an easy hue shift; in fact I still don’t know of any good way to do hue shifting in Haxe/Haxepunk without digging into low-level RGB code yourself. =(  The silver lining on that cloud is that because I wasn’t able to do easy hue shifting, I ended up making much more intricate and involved animated background effects for the post-compo version, so it all works out. :)

1

 

And that wraps up another post-mortem!  Results will be in very soon–good luck to everybody and remember, the real prizes are your games, not your ratings!  Be sure to take your scores with some salt; sometimes you get scores that don’t quite make that much sense.

Thanks for taking the time to read about Melody Muncher!  Hope you enjoyed it as much as I did! :)

Tags: , ,


3 Responses to “Melody Muncher Post-Mortem”

  1. GhostBomb says:

    I’d like to see a video of you beating the expert songs with 5 stars just to confirm that it’s possible.

Leave a Reply

You must be logged in to post a comment.

[cache: storing page]