Stick with the Design
A clear design for Platform hack was laid out at the beginning: a mash-up of Metroid (platform jumping, exploratory powerups) with dynamic world creation of Roguelikes. This was followed through from the start. It helped to not have to change genres or styles halfway through, and the core gameplay feels very similar to what I had in mind.Prototype, Prototype, Prototype
Breaking the challenges of the game into smaller problems, and then getting those working in isolated environments was key. The main issues - grappling hook, double/wall jump, traps, melee / ranged combat, level design - were all handled in a "sandbox" project. This way I didn't have to worry about breaking the game to try out a new feature, I could hardcode physics constants or whatever until the feature worked how I wanted it. *then* port it over to the game project.Don't Reinvent the Wheel
Platform Hack borrowed from a lot of great sources, both for code and art. The samples on App Hub were essential, especially the Platformer starter kit, which included the core code for tile-based levels, basic physics (jumping, gravity), sprite-based animation, and collision detection. Other samples were useful as well - Game State Management, Pixel-Based collision with rotation. For the sprites, I ended up using sample spritesheets and having my artist base all the animations (run, jump, attack, die) on those models.Limit Features...For Now
The original plan for platform hack had dozens of procedurally generated levels, 4 huge boss fights, an overarching story, and a leveling system that let the player specialize in different types of powers. Those plans are still on the table for the final release, but about a month out from the deadline, I realized I wasn't going to hit all those marks, and set my sights for a much smaller goal - a stable game with polished art and gameplay. In the end, I was able to deliver by the deadline.Things that Caused Problems
Collision Detection
Pixel based collision detection can make you want to tear your eyes out. When it doesn't work, it’s very hard to figure out why (you can't step into and iterate through the 10000 item color array to see a certain pixel isn't set to alpha), especially if you are using matrix math for rotation and translation. One thing I ended up doing was creating a TestPixel object, which would basically flip on if it was "collided" with. This let me see a path of where swords, bullets and enemies passed through. Unfortunately, this was very slow (1 fps) and didn't always work. At the end of the day, I removed most of the per-pixel collision, and only used it where absolutely necessary.Procedural Level Generation
I created a project that would use the Angband algorithms to generate a map, and throw in various enemies, traps and platforms. However, I still had to manually go through the text files and make sure there were no inescapable dead ends or unreachable keys. The tough part of procedural level generation is making sure the "maze" can be beaten, once you start adding in locked doors. The early levels also needed additional manual intervention, to give the whole thing a bit more flow and gradually increase the learning curve. As soon as the player uncovers a new trick (jumping boots, grappling hook), you want to give them the opportunity to use it, which you have to manually design. this is why I'm thinking, even in the final game, the procedural levels will only end up being 50%. Stages where you uncover a new item, or fight a scripted boss fight, will need to be hand-crafted.Loading Assets on Xbox
The scripted boss fight was fun to put together, but not the most elegant solution. The first build I ended up with dozens of 1500x1500 images that not only had to be loaded into Texture2D objects, but also read into Color arrays for pixel-based collision. Needless to say, this caused some OutOfMemory exceptions when attempted on the XBox. The solution was reduce the size of the image files (to 800 x 600), and then build a manifest loader that would reuse certain textures for various animation frames (the idle frame is used in almost every animation), and also only generate the Color[] on frames that needed collision.Animation State Machines
This is something I'm still dealing with. Animation is one big state machine - trying to map both the player input + the world controls into a believable animation for the player to be in. For instance, if the player is swinging his sword and then jumps, do you continue to play the first ground swing animation? or switch over mid-swing to the jump-swing animation? What if you can't transition halfway between animations and have to start at the beginning? This "transition" issue was a headache to get right. In the end, I decided that animations must be played from start to finish, and you can't start a new action while you are in the middle of one (aka start shooting while rolling, roll while swinging, etc). Of course, the button press will usually still be registered once the animation is over and you'd start the new action immediately. The more complex your animation state machine, the harder this problem becomes.Lessons Learned
No comments:
Post a Comment