Log In  

I'm making a maze game...I know how to use a tile map but I wanted to have a bigger pool of possible screen (128x128) layouts than the map offers. I'm randomly choosing screen layouts and putting them together.

My solution to this was to create 8x8 sprites that then get translated into a table of wall objects. I then loop over that table to draw the maze walls. Nothing too fancy here...checking pixel color, etc.

And this works great up until a point...that point being once player objects and AI objects starting moving around and need to check for wall collisions, etc. It doesn't take long for things to start slowing down once there are 100+ wall objects to loop through per actor.

So given I don't want to use maps outright yet maps are more efficient processing-wise...is there a way to create a map on the fly? Using coordinates that are derived from the sprite.

There doesn't seem to be a vanilla way to do this (which doesn't surprise me), and I hate looking for hacks to get it done, but thought it was an interesting enough question to ask. It might just be a case where I want Pico-8 to do something it's not really suited to do (at least in the manner I'm doing it and I just need to accept Pico-8 limits).

P#41826 2017-06-20 22:21 ( Edited 2017-06-21 19:01)

Hey toast!

NEVER accept the limits of built-in functions! What you're trying to do can definitely be done. It isn't a hack -- it's programming.

One quick way is to load your map data as a 2d array and then make collision checks that way:

Cart #33533 | 2016-12-15 | Code ▽ | Embed ▽ | License: CC4-BY-NC-SA

This way you aren't looping through a list of objects, you're just doing 1 check: map[y][x]

To load in your data, the code would be something like:

--assuming you want to store things as they relate in your sprite area
mapdata={}
function load_map()
for y=0,31 do --assuming it's just 1 sheet; extend as needed. y=0,127 would cover all 4 sprite sheets
mapdata[y]={}
for x=0,127 do
mapedata[y][x]=sget(x,y)
end
end
end

And then your lookups will be similar to this:

function is_blocked(x,y) --takes a tile coordinate x/y
--you can divide by your tilesize here if you want to check using real coordinates
if mapdata[y][x] == 11 then --add one of these per each 'solid' color
return true
else
return false
end
end

P#41828 2017-06-20 23:10 ( Edited 2017-06-21 03:13)

I'm basically doing everything you said except indexing my table with x/y values...which is so simple that's probably why I didn't think about it. Dammit.

Since my map is grid based but the actors don't move on a grid, I can just convert the pixel x/y into the grid position and do checks that way.

I guess my question about creating a map on the fly was more so asking if you can create an actual map that then lets me use the mget methods to check for collisions...but maybe a better table lookup is just as effective and efficient.

P#41837 2017-06-21 10:18 ( Edited 2017-06-21 14:18)

There's a neat game by impbox here called Tempest that had used a large procedurally generated map. The map was made when the cart booted up, and then saved inside the map data portion of the cart. In older versions of pico-8, you could apparently change the map data at run time and it wouldn't overwrite the pages it shared with sprite tile data. Eventually that oversight got 'fixed', which made it so when the map was created in Tempest, it wrote that random data over part of the tile set. This ended up making the game unplayable.

To fix this, I just edited the map creation routines to write to a large 2d table. And then I made new versions the map function (mget, mset, etc) that worked the same as the built-in functions, but referenced that array.

Here's that fix:

Cart #33663 | 2016-12-19 | Code ▽ | Embed ▽ | No License
6

As you can see, I named the new functions "_mget", "_mset", etc., and then updated all references in the cart to use them instead of the built-ins.

However, you should be able to just name the functions the same as the built-in functions (ex: "mget") and if you have them defined at the top of the cart.. then all calls to those functions will use your modified version instead of the built-in. Meaning the rest of your code can stay the same.

Hope that helps :)

P#41840 2017-06-21 11:34 ( Edited 2017-06-21 15:36)

Okay...another probably dumb question...

I refactored my stuff to use an x/y indexed table, which is working on much better overall. I'm checking the "next" tile each time to see if it's a wall and blocking the player.

This works great except for diagonals. In cases where there are two walls diagonal to each other, the player can go right between them because the "next" tile is the empty one. The player is not moving on a grid, so they can move freely.

I started to think about collision boxes and so on but then that's just going back to the way I was doing it, looping over a set and checking...which is what I'm trying to limit this time around.

I suspect it's another simple solution that I'm just not thinking of.

P#41843 2017-06-21 14:11 ( Edited 2017-06-21 18:12)

What I would do is check collision for x, then move, and then check for y collision separately at the new location, and move as is allowed.

This will keep them from going through a diagonal hole, but also allow them to do things like skirt a while and continue moving in the non-blocked direction.

Hey, I couldn't get the formatting to look right using text, so here's an image instead. The AI is moving north-west each frame.
GREEN means it passed the collision check for that axes, RED means it failed:

Oh, I wanted to say too... This isn't a dumb question at all. The above trick is simple and effective but not necessarily intuitive. It's just something that you'd have to experiment with to find.

P#41844 2017-06-21 14:40 ( Edited 2017-06-21 18:55)

I gotcha, thanks. I like that...I'll see what I can do...

In the meantime, what I tried is giving each actor a hitbox (which they'll need anyway) and then doing a "next tile" lookup for each corner of the hitbox...if all the tiles are open then it continues, otherwise it blocks it.

Since I know the hitbox will always be bigger than diagonal gap between walls, it seems to be holding it up. And it avoids any looping and just does direct table lookups across the board. It's all just basic math too so I figure that's easier for the machine to crunch a ton.

I haven't made any games with moving players around a map like this, so thanks for all the thoughts and feedback. Hopefully once I get stuff like this under my cap, I'll be able to add a new genre to my capabilities :)

P#41845 2017-06-21 15:01 ( Edited 2017-06-21 19:03)

[Please log in to post a comment]

Follow Lexaloffle:          
Generated 2024-03-19 03:29:48 | 0.011s | Q:23