Sunday, January 22, 2012

Levels

This is kind of a fun one to talk about. The level system is the subject of the single largest and most complicated overhaul I had to make in the course of this project! It was a tedious and terrifying process, but the results are awesome. I'll start with the old system.

In our original level system, the Background object mentioned in a previous post held a Bitmap. This Bitmap was essentially the entire level. Here's what one looks like
As a fun aside, here's the image Marty would give me to place the enemies and such.

The player and enemies would actually refer to this bitmap in memory, looking up pixel positions to see if they were black. This is how collision was managed, and it wasn't a very good way for a few reasons. First off, we were limited to black platforms/walls/etc and nothing else that was a part of the background layer could be black. However, it lead to a MUCH more significant issue. Memory management.

Alright, this is going to require a hardcore digression so skip this paragraph if you aren't interested in how memory is managed. Image files on a computer can be saved in several formats: GIF, JPEG, BMP, etc. However, this file extension only refers to how the image is compressed to store on a hard drive. While actually working with the picture (viewing it or manipulating it, for example), it is expanded and stored in RAM, which I'll refer to as memory here. The images size in memory has nothing to do with what format it's saved as; in fact, it's pretty simple to figure out it's size. Multiply the number of pixels by the bit depth (typically 32) and divide it by 8 bits in a byte to get the number of bytes. One megabyte is a million bytes (sort of), so the rather small level depicted above is 1600*1966 pixels, or about 12.5 MB. On Android, these images are stored in an area of memory called the native heap which is as small as 24MB on some devices and has no problem crashing your app if you try to go past that. Since I'm also loading player sprites, enemies, and all sorts of other fun images, this level crashed phones.

So I made a new system, that didn't suck. In the new system, I used tiles. Each tile is loaded only once, and a level file is loaded in that is used to create a massive data structure describing what kind of tile goes where. We only needed to load around 20 or 30 different 32x32 pixel tiles into memory, and we could create massive levels! This was stress tested with a 200x200 tile level, which ran perfectly but was absolutely miserable to navigate (thanks Alex Mateik..) In this fancy new system, levels are made like this.


Marty loads in a tile set, creates a level, and sends me the tile set and .tmx file produced. The level is parsed out using a nice little chunk of code called TMXLoader (Thanks David Iserovich) and I create a bitmap of what the player can currently see. The bitmap is constantly recreated, and all of our memory issues are solved!

Collision is also a bit easier in this system. Rather than checking pixels, I can look up what tile the player is entering and decide if it is a collision tile or not. To determine what tile number each type of tile is, I open up the .tmx file in a text editor and I am greeted by something like this.
Slightly eye-melting, I admit. However, squint your eyes and you should see a level amidst these numbers! 0 is a transparent tile (or lack of tile), 1 is a solid block, 2 is a platform, and all of the other numbers are pieces of the curved cliff edges. With this, I can decide what tiles should cause the player to react in what ways, and keep the player from randomly falling through floors! Well..sometimes.

1 comment:

  1. 'm flattered that you've used my code :)

    I haven't updated the project in a while, as it has most of what I need for now, but feel free to ping me if you have any issues.

    David

    ReplyDelete