Before I'll ask the question, I'll tell you something so this makes a little bit more sense.
In original Super Mario Bros game for the NES, levels had something called "blocks". Each "block" represented one level feature - be it first mushroom on 1-1 or end stairs after each level. Every block had unique id and actual level was just 1d array that showed what "blocks" in which order were making that specific level.
It would look something like this (in P8 terms):
level1 = {0,0,1,4,3,7} |
and blocks would be defined in map space. Each block having couple of tiles
It was done, obviously, to save cart space in order to make bigger game.
I plan to do something similar for my next game (after Heavy Duty Sprinkler will be done, which is my "learning" game), however I don't know how I would detect collisions.
Let's say each "block" is 8x16 (WxH, in tiles). Drawing those, no problem, just something like this:
map (8*blockid,0,0,8,16) |
But how do you get tile ID player is standing on with such design? Up to 4 blocks can be drawn on screen at once (one block is half of the screen in width to allow more variety and other two are drawn due to smooth scrolling), but I'm clueless about collision, except that I'd use one of sprite flags to indicate collision tile.
Convert world coordinates to an index into the block list and coordinates relative to the block using division and modulus operations. Then you can look up where the block is in the map and find the tile corresponding to the coordinates.
Can you write it again, but this time noob-friendly?
I've realized I have to convert screen to world coordinates, but the problem is that I don't know how.
You need to convert world to screen coordinates to draw the level, but to do collision detection, you need to convert world coordinates (i.e. position of a character) into map coordinates, a location within the map data.
World coordinates: logical position within an entire level.
Map coordinates: given in tiles, a location within the map data, suitable for using with the map, mget, mset commands.
Block coordinates: position given relative to a block, (0,0) is the upper left corner of the block.
Screen coordinates: position on the actual screen, you can use the camera() command to avoid dealing with these much.
Block list: the list of block ids that define the level.
Block id: a unique identifier for a block stored in the block list; you need some way to use this to determine the map coordinates of the corresponding block.
So assume the world coordinates are (x,y). x will run from 0 to the width of the level, y from 0 to 128 (for a SMB1-like level with no vertical extent). Determining the block these coordinates are in will only depend on x. If the width of a block in tiles is block_width, its width in pixels is 8block_width. Then the block-list index for this coordinate is flr(x/(8block_width)). Using this, you can look up the id for the block in the level. The coordinates relative to the block are (x % (8*block_width), y).
So now you know the id of the block and the block coordinates (in pixels). The block id gives you map coordinates for the upper left corner of the block (in tiles) using the same mechanism used to find the correct block to draw to the screen. Now you just need to convert block coordinates in pixels to block coordinates in tiles, which is done by dividing by the width and height of a tile (8x8 for pico-8). So the block coordinates of the original (x,y) are (flr(x % (8*block_width) / 8), flr(y / 8)). Add these to the map coordinates of the block (obtained from the block id) to get the map coordinates of the tile at the original world coordinates. Now you can look it up in the map and check its sprite flags and everything else you need to do for collision detection.
Also note that flr(x % (8*block_width) / 8) == flr(x/8) % block_width. These operations could be done using bitwise logical operations, but that is probably less efficient on pico-8.
Thanks, now I understand it slightly more! Also my question is about this: (8*block_width) / 8 - why are we multiplying by 8 just to divide it by 8 afterwards? wouldn't just block width be enough?
Parse that as (x % (8*block_width)) / 8. Do the modulo first, then divide by 8.
[Please log in to post a comment]