Beefy Boxes and Bandwidth Generously Provided by pair Networks
Keep It Simple, Stupid
 
PerlMonks  

OT: Tile maps

by BUU (Prior)
on Feb 14, 2003 at 04:35 UTC ( [id://235176]=perlmeditation: print w/replies, xml ) Need Help??

The back story for this node is that for the past while, I've been messing about with writing my very own 16bit rpg (think snes). I've gotten most of the image generation code working properly and so forth, so I can draw my purty pictures. I'm using a series of layers, with the first layer being the background made up of 64x64 terrain tiles, then layers of various objects, sprites, senery and so forth.

My problem is that I've run into an impasse as to how I can store a tile arrangement (a.k.a: "map"). I experimented with xml for a bit, but frankly its a huge pain in the ass, massively over kill for this app, and other reasons why I'm not using it. But I can't for the life of me think of another way I could store this data so It would be easily portable.

My current thinking runs along the lines of having a folder full of images, then just some how identifying each image in my map file, solely by some name, so I could keep the complexity of the map file down. But I'm really not sure even how I could implement that. So um, anyone have any suggestions?

Update: Most of my actual code for this is written in C++, utilizing openGL, so far. I have vauge plans to include a perl interpreter later, but I felt justified in asking here because I thought it was a very language non-specific question, dealing more with algorithms. The only reason I post this update is to help people not having to guess at how exactly I implemented it. (Of course, any C++ specific advice would be highly appreciated =] )

update (broquaint): marked off-topic

Replies are listed 'Best First'.
Re: (OT) Tile maps
by FoxtrotUniform (Prior) on Feb 14, 2003 at 05:32 UTC

    Here's a simple way to do it:

    Chunk your world into fairly big blocks, call 'em "areas". Each area has a tile palette of 256 (or however many) tiles. (This way, you get a simple form of compression: each area probably has fairly similar graphics, and you only have to keep a few of your tiles in memory at once.) Store nine areas in memory at once: the one your player's in, and the eight adjacent areas.

    On disk, store your areas the obvious way, as well as a map of which areas go where. When your player gets crosses the boundary of his current area, move him to the next one, shift which areas are which (bottom-left, centre, etc), and load the new areas when you have time.

    This makes it easy to add areas to the world, gives you seamless transitions, lets you do all kinds of crazy image-processing stuff on the areas later if you so desire, and it's pretty simple to implement.

    --
    F o x t r o t U n i f o r m
    Found a typo in this node? /msg me
    The hell with paco, vote for Erudil!

      That sounds like a very practical way to implement this. I have a few questions however:
      1. What about those fun little border cases, where the character is say, right in the middle of 4 different 'areas'?
      2. Are the areas actually stored as giant jpgs or what not, or just a reference saying 'these 3 thingies make up an area'?
      3. "as well as a map of which areas go where" How the bloody heck do I do this?!
          What about those fun little border cases, where the character is say, right in the middle of 4 different 'areas'?

        Well, you have access to the areas, so you can draw the intersections fairly easily. If you have something like:

        1111222222 1111222222 3333*44444 <- player is * 3333444444 3333444444

        it's just a matter of drawing the eight tiles for area 1, the ten tiles for area 2, and so on, with the right offsets. And since the areas are stored as arrays of tile indices (which, in OpenGL, are probably just texture names), rendering them is bloody easy (quads come to mind).

          Are the areas actually stored as giant jpgs or what not, or just a reference saying 'these 3 thingies make up an area'?

        I'd thought of the areas as n by n arrays of OpenGL texture names. Every time you have to draw them, you translate to the area's origin, then just iterate through the array: if a cell's visible, draw a quad with that texture.

          "as well as a map of which areas go where" How the bloody heck do I do this?!

        Well, you could use XML... ;-)

        Seriously, it's probably easiest to have some notion of a grid, with one particular area associated with each coordinate. (This lets you reuse stuff, too: sort of like the tile grid for areas writ large.) So, you might have a text file that looks like:

        0,0:plains.buu 0,1:plains.buu 0,2:rocks.buu 0,3:city_foobar.buu ...

        Probably best to manipulate it with some sort of purpose-built tool (Perl/Tk springs to mind) rather than by hand, but you could create, say, a 4x4 test world by hand pretty easily.

        You might find FlipCode and GameDev.net helpful, especially the fora.

        --
        F o x t r o t U n i f o r m
        Found a typo in this node? /msg me
        The hell with paco, vote for Erudil!

Re: Tile maps
by shotgunefx (Parson) on Feb 14, 2003 at 04:45 UTC
    I would assign a number to each tile image (01021.jpg) Then just use an A0A to store it. If the maps are big might be able to use a PDL to gain some efficency or even just store each map row as a packed string. The later would certainly take less memory then an AOA of numbers. You could do the map storing, munging in XS as well and gain much speed and lower your mem consumption. Hope this helps.

    -Lee

    "To be civilized is to deny one's nature."
      To avoid running out of memory, current PDL has PDL::IO::FastRaw for easy memory-mapping of disk files as ndarrays.
Re: OT: Tile maps
by l2kashe (Deacon) on Feb 14, 2003 at 15:29 UTC
    The posts in this thread should get you rolling just fine. My question is have you defined how "far" the character can see? Will it be single or multiplayer? How often will you be reloading a "map".

    1) One you know how far a given character can "see", then you can use the suggestions from above to define the area. Personally I would define tile set types, (i.e desert, plain, castle, dungeon, etc..) as well as (pulling from Blizzard's StarCraft map editors vocabulary) doodads (trees, broken pillars, staircases/ramps, etc...). Then assign particular doodads to type set types. Ie Stairs_up-4.jpg belongs in desert, whereas Stairs_up-1.jpg belongs in the castle set. Then the char enters a castle scene. If you have say something along the lines of a bitmap describing what the area should look like, you can load the tileset, assign random tiles for the floors, walls, (could be multiple tiles for floor design, wall design, etc to display difference in terrain, think Final Fantasy 2 for SNES), sprinkle doodads across the non door/stairs/special bitmap offsets, all with simple method calls, and a tile set definition.

    2) If you are gonna do multiplayer, then I'm not sure what types of optimizations are gonna be needed in terms of location/surroundings updating you are gonna have to do. (Im presently working on a text based MUD, which is along the same lines, but I dont have to deal with graphics, so if I come across some decent insights there I can msg you with the thoughts)

    3) If you are reloading a map fairly regularly you might want to actually build once, via whatever method, and then store that do mem/disk, so that it gets paged in and out at once. As opposed to rebuilding every reload. I don't know what kind of constraints you are placing on yourself in terms of memory usage, wether you are implementing this on a PC/workstation type platform, or a emulator/specialized hardware platform. So with that in mind, I don't know what type of optimizations you can perform to minimize performance hits.

    If you have defined a 64x64 grid, and use the tileset idea from above, and lets say the character can actually see 60x60, then you have a 4 tile buffer in any given direction. If the char were to move north, then the southern tiles simply move outside of your buffer reagion, and you randomly generate a new row of tiles across the northen buffer zone. This way you are only ever "paying" to create one row of tiles, as opposed to updating the entire "map" every move of the character. (I think this is filtering up from reading about how the original doom and wolfenstein games were thought up, and how to limit performance hits while creating a 3D world. While you aren't necessarily dealing with 3D, the benefits of implementing this way can be fairly substantial). If you prefer that they "see" 64x64, and can determine how long it takes to generate a line of tiles, you can alter your "offscreen" buffer to suit.

    I hope this is at least somewhat helpful, and happy hacking :)

    /* And the Creator, against his better judgement, wrote man.c */
      If you have say something along the lines of a bitmap describing what the area should look like

      This is really my main problem. I don't have anything along the lines of a bitmap or anything else describing how my over all map looks. And I really have no clue as to how I could/should develop one.

      At the moment I'm kind of thinking about just defining tiles at the beginning of the map in a header, then just having a list, like:
      ##header## name: foo.map author: baz size: 250x300 tileset: forest 0=ground1.jpg 1=ground2.jpg 2=trees1.jpg 3=trees2.jpg //etc etc ##endheader## 0 0 0 0 1 1 1 0 0 0 1 2 2 3 3 2 3 3 //etc
      So first I would read the header (uh, some how), go through all of the 'terrain tile definitions' and generate an array with array[0]=grass1.jpg and so forth. That way i wouldn't have to do hash look ups on every tile. Then from there maybe I could apply further optimizations, such as chunking them into areas and having rows already generated off screen and so forth.
        I was thinking more along the lines of a hash:
        my %map = ( name => 'foo.map', author => 'baz', width => 250, height => 300, tileset => 'forest', tiles => [ 'dirt1.jpg', # half dirt, half grass 'mud1.jpg', # half dirt, half mud 'grass1.jpg', # grass with a few shrubs 'trees1.jpg', # sparsely populated with trees and many + shrubs 'trees2.jpg', # moderatly populated with trees, less s +hrubs 'trees3.jpg', # heavily populated with trees, sparse s +hrubs 'stream1.jpg', # stream running north/south (variation +1) 'stream2.jpg', # stream running north/south (variation +2) 'stream3.jpg', # stream running northeast/southwest 'stream4.jpg', # stream starts in northeast but winds a +way to the west ], layout => [ [ 1 6 1 0 2 2 3 ], [ 1 7 1 0 2 3 3 ], [ 1 8 1 0 2 3 4 ], [ 6 1 0 2 3 4 5 ], [ 7 1 0 2 3 4 5 ], [ 9 1 0 2 3 4 5 ], [ 1 0 2 3 4 5 5 ], [ 0 2 3 4 5 5 5 ], ], );
        I would go about describing in English how it is you should be able to describe your map. For example, is there going to be a hard-coded set of N areas each with a set 16x16 tiles? Or, are you going to be more dynamic (allowing a player to build a city, thus changing the tile)? Obviously, the solution is different in each case. Once you've determined the parameters for what your map-description needs to be able to do, then the solution should be relatively obvious.

        Almost always, design is driven by requirements concerns. More than that, it's often discovered as a logical conclusion from the requirements. Sometimes, the only logical conclusion from the requirements. If you're casting about for a good design, make sure your requirements are solid first. If they're not (and they usually won't be), fixing them will help solve your design problems.

        ------
        We are the carpenters and bricklayers of the Information Age.

        Don't go borrowing trouble. For programmers, this means Worry only about what you need to implement.

        Again talking about your area of "view", and tilesets. Lets say you define a 100x100 area as being forest. Heres what I am thinking.

        Now what you can do with this is start testing if present_pos <= some_val_from_next_area then start mapping that area before they actually encounter it. Also with this approach you can generate "random" dungeons of differing sizes to explore, and they should all have a slightly different look and feel, depending on how many tiles you add to each tileset. Also you can gen an area once, print out the map to a file, then tweak to add doors, etc via a simple editor which you can create to work with this data structure. Then save again, in edited form. That way if you have say a main castle, which is pivitol in your quest, all that needs to be done, is to map the def to tiles, and print, as opposed to generating the area then mapping then printing. {l2kashe}.o O ( maybe even do the mapping via the editor to avoid the mapping cost completly at run time?)

        Hope this helps and that the readmore tag works like I hope it does :P
Re: OT: Tile maps
by Anonymous Monk on Feb 16, 2003 at 05:24 UTC

    Hi, I'd like to take this opportunity to note that game programming kicks ass. It is exponentially more challenging and fun than any systems administration, text parsing, and other tedious programming. Where else can you combine advanced physics, graphics, and artificial intelligence in one project?

    You won't find too many resources on this site for it though, I recommend you check out gamedev and the hundreds of other game development sites out there. A couple searches in google turn up tonnes of results. Make sure to learn OpenGL first, stay away from that directx crud. Programming for open systems and releasing the games under open source licenses is a great way to gain input. Also make sure to check out existing open source games, you can learn a lot from them.

    Best of luck, and remember to have fun :-)

Log In?
Username:
Password:

What's my password?
Create A New User
Domain Nodelet?
Node Status?
node history
Node Type: perlmeditation [id://235176]
Approved by shotgunefx
help
Chatterbox?
and the web crawler heard nothing...

How do I use this?Last hourOther CB clients
Other Users?
Others rifling through the Monastery: (2)
As of 2024-04-25 06:46 GMT
Sections?
Information?
Find Nodes?
Leftovers?
    Voting Booth?

    No recent polls found