How to make code run faster when using a lot of lists?

I'm developing a platformer heavily inspired by Super Mario Bros 3. Progress has been smooth overall, but performance is a major issue. Even on my mid-high-end desktop, I'm only getting 7-15 frames per second, which is almost unplayable.

The project runs on a single forever loop that broadcasts all the code, and I'm at a roadblock about how to optimize the code any further. (Unless I resorted to using JavaScript).

The project constantly checks and changes several lists for tile and enemy data, which I suspect is causing the majority of the lag.

I've heard that converting sizable portions of code dealing with lists and complex operations to JavaScript can significantly improve performance. However, I lack experience with JavaScript and prefer not to delve into the source code.

If it were easy to learn the [ JavaScript function () {} ] block solely for just manipulating and reading lists, I'd be willing to tackle that. However, I find it extremely difficult to find online forums that tell you how to do this except for "just read the source code", but integrating the source code into my project and understanding how the JavaScript block aligns with it is unfamiliar territory for me.

How have you tackled performance optimization challenges in your projects, especially when dealing with extensive list manipulation? Any tips or strategies would be greatly appreciated!

(For the time being, I'm keeping my project private as I plan to release it only once it's completed.)

I've learned using tons of broadcasts can slow down your project while developing my tower defense game. You can also right-click some blocks like Map, Keep, and First to compile them, making them run much faster.

This also feels like it should go in Advanced Help with Snap! instead of Advanced Topics

I have an actual screenshot of my FPS counter going to -1 in minecraft

It'd be really hard to help you then

NEGATIVE FPS!? AND IN MINECRAFT!? So now Minecraft time can go BACKWARDS!?

It's not possible to get negative FPS in Minecraft. But I'm not sure if time can go backwards or not.

Let's get back on topic.

While that may be a common practice in some communities, here is not as common. If you share your project, we could more easily help speed your code up!

Don't do that! compiling your list blocks (from the right-click menu) can speed up your projects, too!

I general, if a bit of code can be written in Snap!, it should.

Also, have you tried using turbo mode?

I can immediately tell you that is what is causing the lag. In scratch, broadcasts are very performant, but that is not the case. Broadcasts are slow when creating games. Broadcasts are ok for sending events, such as when you start a level, end a level, jump, mainly just events that don't happen every single frame.

I absolutely understand that using broadcasts in a forever loop does allow for easier control over what happens each frame (which is why it's usually used), so if you want the same amount of control, you'll need to be a little more creative. I personally cannot think of doing anything else, besides just using forever loops in each sprite, and resulting in race conditions. This can actually can be a good excessive, figure out a way to synchronize loops in different scripts, using only a single broadcast at the start.

welcome to snap @sirders

The counter was apart of a mod, and it was Eaglercraft, so i'm not surprised. I have the screenshot on discord somewhere..

Can we please

Let @mr_owlssssnap2 show us the screenshot, then we can.

Thank you all for your advice. I've modified the looped broadcasts to run a single time and put forever loops on the receiving side. This has sped up my project to 20-30 frame per second.

I'm putting this here for anyone else with this problem:

You will have a new problem where if you have [ Stop this block ] or [ Stop this script ] blocks within some of the forever loops, it will end the loop forever. This wasn't an issue before as the loop would restart with each new frame from the broadcast. To solve this, I put the entire script except for the forever loop into a custom block, this instead stops just the custom block rather than the forever loop:

Before (stops loop if this = that):
script pic 1

After (doesn't stop loop if this = that):
script pic 2
script pic 3

Another thing to note, is that I've found that scripts inside custom blocks actually run faster. It all really comes down to the fact that snap doesn't need to constantly check if the script has changed.

After fixing all broadcasts and loops, I've encountered a problem with the objects in my game.

When there's no objects on screen, (like enemies and powerups), I'm getting around 20 fps. For every one that comes on screen, I lose around 2fps. For example, if there were 6 enemies on screen, I'd be getting 8 fps. (All enemies and powerups are all clones of the sprite 'Objects')

For them to interact with each other, I keep their data in a list called objectData.

Every frame I set object data to list (list, list, list):

script

This sets it up for what objects are on the screen per frame. The first list is the object's name, second is x pos, and third is y pos.

If the object is on the screen, it will add it's data like this:

script pic

Now this next script looks intimidating, but it's actually very simple. It checks if it 2 objects are touching each other. spriteW and spriteH are half of the width and height of the object, which are set in an earlier script. Then it goes through objectData to test if it's x +/- spriteW and y +/- spriteH are within the boundaries of another object's x +/- spriteW and y +/- spriteH, other than itself (which is why there's a [if not i = spawnNum])

Do you have any suggestions to make this run faster? If not, do you happen to know how I could do this with javascript? Thanks.

if ( touching = true)

should be replaced with

if( touching)

untitled script pic - 2024-04-26T015611.357 => untitled script pic - 2024-04-26T015620.340

Bounding box calculation can be simplified
abs( sprite.x - enemy.x) < 0.5 * ( sprite.width + enemy.width)
the same for y and height.

Thanks, the game runs a little faster with this better calculation. Still losing quite a few frames though.

This won't necessarily make it run faster, but it's good practice. I see that you are comparing a variable to true. You don't need to do this, because you can just drop the variable into the boolean slot.

untitled script pic (26)

What is the point of the (spawnNum) variable check? Since it's the length of the list, you don't need to check if (i) = (spawnNum), since it won't check for the last item (unless that's what you're trying to do, but in that case, just subtract 1 from the length in the loop end).

When it comes to adding an object to the list, I would personally keep each object data together, rather than in separate lists.

The first if block is only just to show what the initial value should be, if it's initialized somewhere else, you can put it there.

This has the advantage of being able to just grab the index of the object, then you have all the data you need.

Of course since that is completely changed, you need to change a bunch of other stuff (although it should just be the reverse of what you were doing before, so it's not too difficult).

Another optimization is to skip the y check if the x check is false.

I personally would also not use the index to get the width of the object, I would rather do this.

Which of course would mean changing some parts of the script.

Now, of course the best way to do all this is actually to use hyperblocks to handle both the x and y axis at the same time. Hyperblocks is basically just doing math on lists (if you don't know what it is, search it up on the forum. I have a feeling you should use it, because hyperblocks are super fast).

You can get the <any <>> block by dropping the <<> and <>> block (or any reporter / predicate) on the black arrows.

While I was editing the script for this, I realized I can just add another input to the <= block, and stick my right in the last input. It results in it having less work to do.

Actually, now that I've made it so that it only deals with the object data, no index, you can use the for each ((item)) in [list] block.

Just to be clear, all of this is untested, as I don't really have a way to test it.

  • You may want to calculate get enemy width of (i) once, and assign it to a variable.
  • I'm not sure if it's faster, but at least it makes for more readable code: why not use for each enemy instead of for i = 1 to ...? (you'd probably have to convert objectdata, using: columns of ....)
  • Why do you set touching enemy x (and y) to false in the beginning? and why do you calculate touching enemy yeven if touching enemy x has turned out to be false? I would use just one composed expression: if [insert the calculation of touching enemy x] or [insert the calculation of touching enemy y].

I noticed that @ego-lay_atman-bay provided similar advice just now. Well anyway, good luck and have fun!

That part makes sense, because it's looping over each object. If you didn't set them to false at the beginning of the loop, you'll get wrong collisions (if you couldn't tell, the script variables are created outside the loop, meaning, they will not reset after each iteration).