I'm still not entirely convinced there needs to be a plugin, or even runtime features for this. There are some improvements we could make though, but I think they are just general optimisations. Several of the comments seem to be unaware of what the runtime already does. Here are some of the considerations from my runtime-engineering point of view:
You can use existing objects, e.g. Tiled Backgrounds, to identify "zones" within a large layout. I'm not sure a new plugin needs to exist for this.
The (relatively recent) collision cells optimisation effectively removes the size of the layout from collision performance. Since objects only check against other nearby objects, without even considering others, you can have a virtually unlimited sized layout and collision performance remains relatively constant. There will be more collision work to do if you have say moving objects or behaviors making collision checks over the entire layout at once, but it should be straightforward to use events to stop objects, skip collision events and disable behaviors for the entire layout apart from the active zone. Collision performance should not be a problem at all if you do this.
Tilemaps are even better, since they are uniquely able to exploit the collision cells for rendering. In other words, it doesn't even consider most of the tiles in the tilemap, even if it's the size of an extremely large layout, except the tiles near the viewport. So you should be able to have a gigantic tilemap with an incredible number of individual tiles, and it will hum along just fine with no performance issues at all.
Objects which don't animate (such as Tiled Backgrounds or Sprites with just one animation with one frame) are totally inert in the engine. If you have no events for them and don't collision check them, they take almost zero CPU time (with one tiny caveat - see below). You can also limit your own events to only deal with instances in the currently active area, ensuring you don't process anything unnecessary.
Dynamically loading/unloading portions of a layout based on room objects.
The question is what exactly are you hoping to load or unload here? There's talk of loading and unloading images by sections. This is only useful if you strictly keep certain types of objects to certain areas. The impression I have gotten from screenshots is that very large layouts still tend to use the same look all the way through, suggesting that you are using the same set of images all the way through, which suggests that there are rarely sets of images unique to just one particular area of the layout. So it doesn't seem that there's much to save here. Perhaps very large areas will use different "world" styles, such as "desert" world turning in to "snow" world, but surely these are large enough sections to conveniently break off in to separate layouts anyway? Has anyone actually really run in to image memory use problems here?
Then even if we implemented this, then it will jank the game as it loads new images (e.g. a 500ms pause while it uploads the next set of textures). This ruins the smooth flow between sections, so I'd still recommend using the existing layout-by-layout loading system where an entire layout is either loaded in its entirety or not at all.
2) Destroy all objects outside layout zone (x1,y1,x2,y2)
As I covered above, in many cases even destroying all the objects outside the current active area will not affect performance. It won't improve collision performance nor will it save you running events on extra instances, if you've designed your events well. Also since there's a surprisingly significant amount of work in the engine when creating and destroying objects, creating and destroying large numbers of objects at once could cause jank as the next section is created. I would prefer to allow the entire layout to exist at once, but make sure the engine can handle that with good performance (assuming the game is efficiently designed as well).
1) Re-Load layout zone (x1,y1,x2,y2)
This is as far as I can tell so far the only unique and possibly helpful suggestion. As I understand it you simply want to take an area, and revert it to its initial state as it appears in the layout view, correct? Surely you'd want to limit this to certain types of object (e.g. not the player)? It seems a relatively straightforward suggestion, but there are still difficult edge cases. Suppose there is a ball inside that area, and during gameplay it rolls away to outside that area, and then some event creates a new ball inside the original zone. Then you re-load the layout zone. There are a few options:
- destroy both balls and recreate a new one
- destroy the new ball and move the original one back
- destroy the original ball and move the new one back
- leave both balls and create a third new one in the initial location
I'm not sure what option is best, or if there are legimitate reasons to use more than one of these cases. I'd prefer it if it was either the first or last, since they are the simplest cases (possibly destroy everything, then create the initial setup again). This could be added though if it's helpful, although as I mentioned before creating batches of objects can jank.
A bit of a sidetrack: I tried creating an extremely large layout (64,000, 48,000) and populating it with an extremely large number of objects (~64,000).
This was an interesting project to profile, thanks for sharing
1. The resultant runtime.js file, even after minification, is huge (over 6mb).
3. The game takes a long time to start...having to generate all those objects no doubt.
2. The resultant game slows down badly ... I notice that draw calls take up a lot of the cpu
This exposes the one performance issue with extremely large layouts using lots of Sprites (you have tens of thousands!). The draw call overhead is expensive because collision cells don't apply to rendering objects: every tick it has to check if it needs to draw every instance in the layout, and you have 64,000 objects, so it has to check all of them every tick! The drawing algorithm currently looks a bit like this:
For each layer:
For each instance on this layer: (you have 64000 sprites here)
- skip if invisible
- skip if width or height is 0
- check if bounding box changed (if so, update it)
- skip if its bounding box is entirely outside the viewport
- draw it
So currently for 64000 sprites it does:
- check if visible: yes
- check if width or height is 0: no
- check if bounding box has changed: no (the objects never move)
- check if it's bounding box is entirely outside the viewport: here most of the 64000 are discarded. Ultimately only those visible on-screen get draw calls made for them.
These steps are very simple and fast, and normally don't use any significant CPU time at all. But since you've shoved 64000 sprites in to a single layer, each and every step becomes expensive. So you can optimise it: make it skip sooner!
Notice the very first check is "skip if invisible". I made an event where if you press a keyboard key, it sets all the sprites invisible except those which are on-screen. This correctly renders what is on-screen, and dropped the CPU usage by half (from ~40 to ~20). So simply by setting all the off-screen sprites to invisible you can save a lot of CPU time. Obviously you don't want to do that every tick since that would be even more work, but if you can once-off set all objects invisible except those in the active area when moving between areas, that measurably helps. It ends up skipping the width, height, bounding box and viewport checks for all those instances.
Even when you set off the explosions the main increase in CPU is just creating/destroying lots of objects, and more draw calls. That can be optimised more, but it seems an unusual case. Conspicuously absent from the profile was collision checking - that seems to be working great!
I still find it hard to see why you'd need tens of thousands of sprites in a layout. Remember tilemaps can also draw more efficiently since they can use collision cells when rendering, and if most of your layout design is done with a tilemap, why would you need tens of thousands of sprites? So I'd be curious if anyone can send me more realistic examples, based on actual games or what games at least might realistically do, so I can profile it and figure out if anything's slow in the engine.
tl;dr - I'm not convinced memory use is a significant issue, and creating and destroying large numbers of objects is actually a lot of work in itself and difficult to make smooth. So if possible I want to aim for a solution where the entire layout exists, and the runtime/events can efficiently handle that, possibly by deactivating but not destroying objects. It's possible to optimise draw calls for large numbers of non-tilemap objects, but I'm not clear that real-world games really need that.
I think I will write up some of the above advice as a blog post on how to best deal with very large layouts - I think it will be useful beyond those who find it on page 4 of this thread