Log In  

Hi there,

I'm new to pico-8 and was playing around with a system to generate levels for a top-down adventure game on the fly. My setup is as follows:

  • in the map editor, I have screen-sized (16x16 tiles) compatible level blocks
  • when I call my generate_map() function, it makes a table with a predefined number of entries, each containing the coordinates to row/columns in the map
  • in my draw function, I then loop through this table and draw the map
--generate the map
function map_setup(map_size)
    total_area=map_size
    level={}
    for area=1,total_area do
        level[area]={flr(rnd(7))*16, flr(rnd(3))*16}
    end
end

--draw the map
function draw_map()
    t=1
    for i=0,sqrt(total_area)-1 do
        for j=0,sqrt(total_area)-1 do
            map(level[t][1],level[t][2],i*128,j*128,16,16)  
         t+=1
        end
    end
end

This works for drawing the map nicely, however I am having a lot of trouble getting the collision detection with the map tiles to work. After digging around for hours, it appears my problem is that when I check player.y and player.x and feed these into mget (after dividing by 8 to convert to row/column coordinate), that mget returns whatever is at that coordinate in the map editor, not in my actually drawn map.

Is there a clever way of checking what is actually on my screen, or do I need to rethink this entire setup? I was trying to transition smoothly from one screen to the other (i.e. the world is continuous), so getting hold of an offset is not super straightforward, I think, but I might be missing something super obvious!

Any help would be much appreciated!

P#103889 2021-12-31 06:00 ( Edited 2021-12-31 06:04)

Ok, I think I managed to figure it out by myself eventually ;) for future reference, what I ended up doing is set up an offset function:

--calculate map coordinates from screen coordinates
function calculate_offset(x,y,level)    
    section=flr(x/128)*4+flr(y/128)+1
    offset_x=level[section][1]*8
    offset_y=level[section][2]*8
    return offset_x, offset_y
end

and then check if movement into the tile is possible like so:

--check if can move to tile
--note:converts map coordinates
--to tile coordinates
function can_move(x,y,sprite)
        off_x,off_y = calculate_offset(x,y,level)
        tile_x=flr((x%128+off_x)/8)
        tile_y=flr((y%128+off_y)/8)
        return not is_tile(unpassable,tile_x,tile_y)        
end

If there is a more elegant way to do this, would be great to hear it, otherwise, keep an eye out for the finished game (hopefully soon!).

P#103891 2021-12-31 07:12

Hello @alpha_helix

Going from these functions and your comments (which is an incomplete picture, so my comments could be mistaken - I've had to make presumptions). Anyway:

Yes, in general -if you have a level table that refers to the map - you need to identify what part of the map your player (or other character) is on via the level table, then calculate where this puts them on the map area so you can get information about the tile. And if you're tracking pixel-like coordinates for your player, because the map is in 8-pixel segments, when comparing them a divide or multiply by 8 of one or the other is necessary.

Your code does all this. (I'm taking your comment that you've figured it out, and my understanding of the partial code, to mean it does indeed do this, but I haven't in fact tested it.)

There can be other (more elegant?) ways to do this, but it tends to depend on how you're tracking where your player is. So, you could track separately which screen/section of the level the player is on, and the x,y position on that screen (in the range of 0..127). At point of potential collision, you can then (a little more directly) use the separately tracked segment coordinates, and the player x/8 and player y/8 when you interrogate the map.

There are other consequences to this too, of course, such as needing to wrap the player's x and y movement to %128 and update which screen/section the player is on when such a wrap occurs. And possibly needing to calculate the player's draw x and y if you're moving the camera around (which I presume you are).

If you consider such a major change worth a look (it might not be), make a backup first of course. (Edit: Sometimes, trying out a different approach is better saved for a different program, rather than trying to rework the current project.)

It's possible a more elegant way exists without such a change.


A couple of notes which aren't directly in answer to your question:

Looking at calculate_offset and can_move, it looks like in calculate_offset you might be multiplying by 8 unnecessarily as you then divide by 8 in can_move. I haven't tried it, but does this give the same result:

--calculate map coordinates from screen coordinates
function calculate_offset(x,y,level)    
    section=flr(x/128)*4+flr(y/128)+1
    offset_x=level[section][1] --*8 removed
    offset_y=level[section][2]
    return offset_x, offset_y
end

--check if can move to tile
--note:converts map coordinates
--to tile coordinates
function can_move(x,y,sprite)
        off_x,off_y = calculate_offset(x,y,level)
        tile_x=flr(((x%128)/8+off_x)) --/8 shifted
        tile_y=flr(((y%128)/8+off_y))
        return not is_tile(unpassable,tile_x,tile_y)        
end

In draw_map it looks like you're looping over your entire level. It's possible that you only need to draw a block of screens that is 3 screens wide and 3 screens high, with the screen your player is on at the centre of this block (or even only parts of the surrounding screens). But this (or how easy this is to change) may be dependent on how you have coded the rest of the program.


As I said at the start - this is based on the partial code posted, and without testing it, so any of this could be erroneous.

P#103905 2021-12-31 14:19 ( Edited 2021-12-31 15:30)

[Please log in to post a comment]