So my adventures in Mappy continue. Since I did not see any useful guides online really detailing the process of making a game in this way, I figured I would make a post kind of detailing how you do that. For those who are interested, Mappy is a map-creation utility that allows you to make maps in a way that is similar to Zelda Classic that you can import into Allegro 5 games. I want to say "almost as easy," but it's really not.
For starters, of course, you have to have set up C++ code in Allegro to do something with the map file -- get input, move things around, whatever. Luckily, doing that just takes a few weeks' of learning. I recommend getting Visual Studio 2010 (plays well with Allegro 5), which is free from Microsoft, and the Allegro 5 binaries. Once you have that, I'd recommend following along with this EXCELLENT tutorial series: https://www.youtube....277DEF3A4AEE4A0
If anyone wants to do this, and has questions about strange things not working, I encountered a ton of non-obvious rules and issues as I mucked around through the process. Happy to try to help and make a thread that better explains the process.
Okay, so assuming you've got a beginners understanding of C++, now you download Mappy AND the .png support package AND the header and C files (.h and .c) that will give your code access to the Mappy commands. The thing none of the tutorials tell you about Mappy is how you need to set up your tiles. Unlike ZClassic, where you set up "combos" by going to a specific combo and then loading it with a specific 16x16 tile, Mappy will grab all of your tiles at once from a single .png file. And, at least initially, how it chooses to do this was a mystery. You present it with, say, an organized 16x16 tilesheet and it loads up however it damn well pleases, putting things out of order and generally making a mess.
What I found worked well was to create your tilesheet according to very specific rules so you know exactly how it will load into Mappy. I used Aseprite and set up each of my tiles on "frames" that are 5x5 tiles large. (My tiles are each 32/32 but they could be any size; you specify this in Mappy). Then, leave the very first tile in the top left corner of the first frame blank, because Mappy reserves that tile for the "zero" or "blank" tile. Next, I made sure that the color "black" in my Aseprite palette is not QUITE true black, because Mappy is going to assume pure black (RGB color 255,255,255) is transparent as a default. Finally, I exported my tiles as a (1) vertical strip and (2) .png file type out of Aseprite. The result is a .png file that will load up and look well-organized in Mappy as long as you make sure you're viewing the tiles in a tile browser window of 5 tiles wide. (Unlike ZClassic, you can change the size of the window holding the "combos" in Mappy.)
So now that you've got the tiles organized, you need to set up their properties. You know how ZClassic gives you lots of options about combos? Some are solid, some are water, some are damage, etc? Well, you can do the same thing with Mappy, but instead of getting pre-made options you just get user fields for each tile (BlockStructures, to be precise). In theory you get four "collision bits" (tl, tr, bl, br) which don't do anything out of the box but are designed to allow your engine to check for solidity. I chose to use "tl" as collision for solid tiles, and "tr" for collision with platforms you can jump up into, but not fall through. What you use them for is up to you; Mappy.c gives you functions your Allegro program can call to examine the variables you've set in a given tile and then you do whatever you want with that information.
For backgrounds, Mappy does support multiple layers and parallax scrolling, but I don't understand it yet. What does seem to work well at the moment is just telling your code to draw a background picture to the screen before telling Mappy to draw. Remember how the black was different from transparent? This ensures that background only shows where you left the blocks transparent. Looks nice.
Unlike ZClassic, you can also use Mappy to place enemies, objects, items, etc on the screen as simple tiles as well. That's the stage I'm at in my engine. In addition to the four "collision bits," that are either true or false, you also get these User fields (user1, 2 3, and 4) that supposedly can hold whatever you want. Well, the first two are long ints and the second are chars or something, whatever. The point is you can use the first one to indicate what kind of tile it is (I'm using "1" for "enemy placement") and the second one for what kind of thing to spawn (so like "1" in user2 would be the simplest enemy on the map). I'm having trouble getting my code to read the user1 and user2 variables at the moment, but I'll update once I get this working.
More to come!
Alright, so the Mappy spawning function is now working. Here's what I did.
I have an Object in my game called the GameObjectManager. Each frame, I have that object scan the tiles visible on the screen. If it finds one of the "spawn tiles," that I've set up in Mappy, it then reads the user1 data to decide what to do with it and user2 to decide what to create. Then, it overwrites the tile with a blank one so that it only fires once. So, it's sort of like the enemy flags in ZClassic.
On top of that, the GameObjectManager is stashing things in vectors. A "vector" in C++ is a fun little data structure that is basically an array that grows or shrinks depending on how much you need to shove into it. So it's ideal for things that can spawn in variable amounts. Each time the GameObjectManager is told to spawn a projectile, an effect, an enemy or (soon to come) items or doodads, it will create the appropriate object and then stick it into the appropriate vector. Then, each frame, it goes through it's vectors and updates each element.
One thing that stymied me for awhile was the algorithm for checking for the spawn tiles. When I'm checking what tile is at a particular X / Y location on a Mappy map, it will always be measuring from the top left corner, regardless of where the player has moved along to. So, you need to start checking your X and Y not at 0,0, but at xOff, yOff (being the offsets from 0,0 where the top of the visible screen starts.) Whoops!
Coming next: collision detection between objects! Each vector cares about collisions with only some other kinds of objects. Enemys care about collisions with player projectiles. Players care about collisions with enemies, enemy projectiles, doodads, or items. And so on.
Edited by C-Dawg, 29 September 2015 - 11:35 AM.