For the past few days I have been working on a tile engine (for like an RPG or something). It runs pretty smooth if there's less than 100 tiles, but any more and it starts to lag.
Basically, whenever the block to place a tile is pressed, it adds a list to an initial list, (tiles)
, which is basically [x pos, y pos, dir, costume]
, and goes through each to see if it's in stage bounds. If it is, it stamps it. This optimizes it sorta, but not enough to make a huge difference with large amounts of tiles.
https://snap.berkeley.edu/project?username=mr_owlssssnap2&projectname=terere
I think the best thing to do is using a grid (a list of lists), since with a grid, you can grab the items that are visible directly, you don't need to check every single tile in the level. If you want to be able to have multiple tiles in the same location, just make the grid position a list.
please help me with that
I thinkg like (list (list [] @delInput @addInput) (list [] @delInput @addInput) @delInput @addInput
(?)
but anyway,
404 Error
This project does not exist or is private
It's public? Unless snap lied to me
you're entirely bottlenecked on the amount of blocks you're running. the one and only way to improve performance is to have less blocks in your loops and run your loops less often.
if you can avoid it, don't use any custom blocks. if you absolutely need a procedure of some kind, use a ring created at the start of the project and stored in a variable (putting the ring in the loop recreates the ring every time)
i assume you're always going to be using the same tile size, so i just hardcoded the stage bounds. i also don't loop over the list twice to check for what's onscreen and what's not.
this is still going to struggle real bad when more tiles get involved. i would highly suggest you don't store tile positions in your list, just store the tiles in order instead. that way you would be able to do some math beforehand to figure out which part of the list to loop over, skipping every tile not on screen.
i made a version without tile positions in the list:
this only ever loops over the amount of tiles that can possibly be on screen, no more, no less.
the tiles aren't centered on the screen but you can just move the camera to account for that.
tiles are stored in the order bottom to top, left to right. world height is how many tiles tall the world is.
I've actually found that custom blocks tend to run faster than scripts outside custom blocks, so I'd beg to differ on this statement. This behavior actually also makes sense, because outside custom blocks, snap is constantly checking whether the script has changed, whereas in custom blocks, you have to apply the changes before your edits can run.
Why in the world would you use this mess? Custom blocks are a whole lot easier to work with than calling a variable and having to fiddle with inputs.
test
I looked at the code, and I have a bit of a question, why would using a flat list be easier than storing it as a grid? A grid is super easy to just get the tile from the x and y, just do (item (y) of (item (x) of (tiles)))
. And plus, I'd assume @mr_owlssssnap2 would want to create a tile editor, which may also include changing the world size on the fly. Adding a whole row is as easy as add (reshape [] to (width) @delInput @addInput) to (tiles)
, and adding a column is as easy as just looping through each row and adding an item to the end. Using a flat list requires you to add items in the middle of the list, shifting the indexes, making you do even more math, just to increase the world size. We're using a programming language that supports lists of lists, not scratch, so why resort to using the awful mess that is grids as a flat list. Also, if you say using a list of lists would hurt performance, well, it would still be a lot faster than looping over every tile twice (as what is currently in the original project).
every time snap goes to run a custom block, it does 3 very slow things:
- look through every available custom block and parse the blockspec (mostly unnecessary)
- iterate through every single block inside the custom block to add "tags" (no clue what this is)
- look through every available custom block and parse the blockspec again (definitely unnecessary for multiple reasons)
all the benchmarking i've done shows that projects with custom blocks end up usually taking 10% of the total time just on these actions. yes, the time it takes to run a custom block depends on the length of the name of every other custom block in the project.
custom blocks are still faster in some cases because the blocks inside custom blocks don't glow.
any place you would use a custom block, a variable with a ring is significantly faster.
i think for optimal speed, you would need to just have run (global variable)
under every hat block.
as far as i can tell snap never stores any useful information that would allow running blocks faster under any circumstance.
i'm just used to working with flat lists from the systems programming i do. in that scenario, it's pretty much always faster to do math on the index on a single list for cache locality, and sidesteps many difficult allocation/lifetime issues.
a list of lists in snap means absolutely nothing for performance, the bottleneck is always just the number of blocks.
i considered using a list of lists but didn't bother because i'd have to make the world generation code more complex. couldn't be bothered.
You might want to explore the "Tiles" library. Here's a little exploration I made a while ago that uses some of the rendering approaches discussed by @ego-lay_atman-bay, who is generally pretty spot-on. When it comes to "performance" the solutions are often not the seemingly "elegant" ones - such as going through each item in a data set to check whether it satisfies a condition ("inside stage bounds") - but doing less and precomputing and caching the rest (e.g. only enumerate the items in a table that you already know to need rerendering). Geometry and Hyperblocks can help you with that much more than fairy tales and superstitions about the alleged inner workings of custom blocks or multi-dimensioned data structures.
Perhaps you could stamp many of the tiles, but scaled down to fit more on screen, then take a costume of the pen trails, scale it back up, and just make it move that around. And when an unrendered part comes on stage, it redoes it, but shifts it over? Might work, but it might not be what you're looking for.
Or maybe a smaller optimization, but an optimization nonetheless would be to make an algorithm that finds which items of the list would fit on screen without needing to check every single one individually. After all, it is a grid, so that should be very possible. Since you're using a 2d list, expanding that to a 3d list, or a grid of data, should make it much easier.
the rendering for this project also runs significantly faster with a basic loop and no custom blocks or hyperblocks (about 3x faster on my machine, from 6fps to 20fps):
https://snap.berkeley.edu/project?username=sarpnt&projectname=Zoom%20fast
like i said first and foremost, speed is almost entirely about the number of blocks ran. simple loops mean less blocks.
if you press space, it'll use a better method for this particular project, taking the pen trails and only drawing the extra row. i didn't suggest it for the tarare tile engine project because that doesn't have very many tiles on the screen and the camera has unlimited speed, so it would probably end up being slower to grab the pen trails than to just redraw everything.
Okay wow there was a lot of replies here, too much for me to respond to on my phone lol
Before I actually realized how to use lists of lists months ago I just did what sarpnt is wanting me to do, which was noticeably slower because it has to iterate three to five times more items than necessary (case in point: my unpublished image encoder)
I do remember experimenting with this when it first came out in Snap 8(?) and was thinking about experimenting with it some more
I'd like each tile to be more than four pixels in size, please.
If you're getting less than fifteen frames on the first tileset consistently, I'm concerned for your computer. My Chromebook runs it pretty well other than the obvious lag. The school computers run the first tileset almost as well as the second, which mind you has around 10x less tiles (20 something to two hundred something)
Also, I am going to use custom blocks in this project, it is literally the basis on what BYOB was founded upon. The performance issues that may be caused is Snap's fault due to their way of handling custom blocks
i'm not sure what you mean by first tileset. that reply is referring to the project jens sent. the second project i sent (here) is a modification of your project that stamps all the tiles and runs perfectly.
you can use custom blocks, my point is that they're slow and if you're trying to work on performance, keep them out of loops (or move the loops inside them).
If you look at the code, there's a script that creates another room with 20 tiles instead of 200