[C2 Performance] Best Way To Handle Large Game Worlds

Discussion and feedback on Construct 2

Post » Sun Sep 25, 2016 8:56 pm

Introduction + Minor Request
Hello there, before fully going into this, I would like to advise everyone that doesn't know anything about this, not to post on this topic!
I don't mean this in an offensive way, I just want to avoid posts like "I want to know this too!", which tend to make topics like this unnecessarily big.

Reason + Short Explanation
This topic is meant for opinions on how to handle large "game worlds" (also knows as "game maps") and their impact on performance.
For this purpose I've made a poorly drawn example of a typical game world.
Game World Example (with sections named as "layers")
Image

Game World System #1 (Layer Based Loading)
This system would be based on the current players position on the world.
The layers would have world borders, which would detect on what part of the world the player currently is
and unload* all other layers that are not next to the current layer.
*with "unload" I mean setting the layer invisible/opacity 0%.

Here are some scenarios that should make it clearer:
Player is on Layer#1 = Load Layer #2 + Unload Layer #3
Player is on Layer #3 = Load Layer #2 + Unload Layer#1

Game World System #2 (Layout Based Loading)
This system would separate each layer into its own layout.
That means when the player has reached the edge of one layout, it will have to change to the next layout.
Using this system also means that the game has to load a whole new layout and pass all the data
from the previous layout over to the newly loaded one (e.g. last enemy positions, player state and many more).

Here are some scenarios again:
Player is on the right edge of the layout named Layer #2 = Go to the separated layout named Layer #3
Player is on the left edge of the layout named Layer #2 = Go to the separated layout named Layer#1

Game World System #3 (Full One-Time Layout Loading)
This system would be the easiest to setup and basically have every layer loaded as a whole, inside one single layout.
This also means that the layout will have a giant size of 100.000 or more.
You could take this system as a lazy version of System #1 but without changing any visibility/opacity and only one layer.

Conclusion
Please note that my game is a multiplayer game, if that matters in any kind of way.
The game also uses HD sprites which could drastically increase layout loading times.

Which system would be the best performance wise and why?
Please share your opinion or suggest better systems below.
ImageImageImage
B
68
S
24
G
78
Posts: 693
Reputation: 45,301

Post » Sun Sep 25, 2016 9:49 pm

We have a method to help with bottlenecks on large layouts with many static objects.
Creating, and destroying objects based on a player's position is only useful for endless levels.
The size of a layout should be determined by the number of objects you can safely have on the layout at any given time.
Image ImageImage
B
172
S
50
G
182
Posts: 8,439
Reputation: 115,097

Post » Sun Sep 25, 2016 10:10 pm

TheRealDannyyy wrote:The layers would have world borders, which would detect on what part of the world the player currently is
and unload* all other layers that are not next to the current layer.
*with "unload" I mean setting the layer invisible/opacity 0%.


This is kind of pointless cause everything that is not visible on screen is not rendered, so setting a layer to invisible for objects that are not rendered have no point at all.

TheRealDannyyy wrote:Using this system also means that the game has to load a whole new layout and pass all the data
from the previous layout over to the newly loaded one (e.g. last enemy positions, player state and many more).


For this one i think you are over complicating things a bit. All you have to do is store important stuff in arrays, dictionaries or whatever - which are global objects.
You can also apply a lot of own rules what to do with that data, for example. If npc is on same layer as player -> do some stuff else do something else.
I did something similar using about over 10 arrays and dictionaries and to store data for npc, inventory, items, quests for main map (900x900 tiles) + 80 minimaps.
Everytime when player moved between main map -> mini map -> main map every important data from current map was saved, then layout changes and on start of new layout all important data was loaded again. And that was a game for mobiles, each change of level took no longer than 5 seconds on Samsung Galaxy S3. So on desktop you can easily push it limits a lot more.

TheRealDannyyy wrote:Here are some scenarios again:
Player is on the right edge of the layout named Layer #2 = Go to the separated layout named Layer #3
Player is on the left edge of the layout named Layer #2 = Go to the separated layout named Layer#1


That's rather super simple thing to do
ImageImageImageImage
B
158
S
67
G
43
Posts: 2,603
Reputation: 36,003

Post » Mon Sep 26, 2016 1:18 am

My game can only use solution 3, as it is a big game world with an emergent "ecology". Solution 1 is doable in theory, but the amount of layers is too high to use it, as many objects use their own layers also to trigger events and so on.

There are some "persistent" objects which are destroyed / recreated based on camera position (happens on a timer, i carefully tested the trade-offs).

All in all, if you know how to use timers and don't exceed with webgl effects, the main issue is number of objects, as we all know...
B
66
S
22
G
4
Posts: 360
Reputation: 6,584

Post » Mon Sep 26, 2016 3:02 am

#2 is the safest method but you lose your "world reference" - you have no idea where each layout is connected so you'll have to draw out your map beforehand.

Sounds like #1 and #3 are just dynamically loading portions of the layout based on zones. This is what I do in the Metroidvania Game Kit and other projects. This works fairly well and performance is nearly identical to having each zone a separate layout. Managing all the objects can be troublesome later on but I'll take that over managing 100+ layouts with independent layers and properties any day. Only drawback is like you said - the layout sizes will be pretty big. Loading times could blow up on larger maps or games with large graphics. Also you'll probably want to destroy objects and use "recreate initial objects" for unloading/loading instead of just setting their opacity to 0 or whatever like you mentioned.
Image
B
243
S
30
G
13
Posts: 1,787
Reputation: 18,770

Post » Mon Sep 26, 2016 6:54 am

newt wrote:We have a method to help with bottlenecks on large layouts with many static objects.

I believe you mean "render cells" right? In any case I guess I will use that on my static background layers, I have 3 layers in total with different parallaxes for the illusion of dept. I hope that parallaxing doesn't turn the essentially static layers into a dynamic one or does it?


shinkan wrote:This is kind of pointless cause everything that is not visible on screen is not rendered, so setting a layer to invisible for objects that are not rendered have no point at all.

What exactly does this mean though?
For my understanding this would mean that C2 games load content like the old Super Mario games did?


shinkan wrote:For this one i think you are over complicating things a bit. All you have to do is store important stuff in arrays, dictionaries or whatever - which are global objects

There are a ton of instances and things that I have to consider though, besides the fact that I also have to sync it up on the connected peer.
I think for me this would be a pain to do, I'm not really good with "predictive" eventing that contains content from a lot of different instances.


Danwood wrote:My game can only use solution 3, as it is a big game world with an emergent "ecology".
There are some "persistent" objects which are destroyed / recreated based on camera position (happens on a timer, i carefully tested the trade-offs).
All in all, if you know how to use timers and don't exceed with webgl effects, the main issue is number of objects, as we all know...
Tokinsom wrote:#2 is the safest method but you lose your "world reference" - you have no idea where each layout is connected so you'll have to draw out your map beforehand.

Sounds like #1 and #3 are just dynamically loading portions of the layout based on zones. This is what I do in the Metroidvania Game Kit and other projects. This works fairly well and performance is nearly identical to having each zone a separate layout. Managing all the objects can be troublesome later on but I'll take that over managing 100+ layouts with independent layers and properties any day. Only drawback is like you said - the layout sizes will be pretty big. Loading times could blow up on larger maps or games with large graphics. Also you'll probably want to destroy objects and use "recreate initial objects" for unloading/loading instead of just setting their opacity to 0 or whatever like you mentioned.

My background layers are static as mentioned before and I'm also not using 100 different building/tree sprites, I'm rather re-using existing ones and randomize the frames. So my background does always look different but I'm going a little off topic with this. Also the only weblgl effects that I'm using are bound to a layer that is on top of all others, "outline" and a bit of "noise". I think this was the recommended method, instead of applying the effects to each sprite individually.

From a gameplay standpoint I would also chose to use system #3, not only because it's easy for me to do but also because I think that gameplay wise it would be better to have a giant game world instead of multiple smaller ones. I think that my player base would complain about the 5-10 sec. loading times, if I would use separated layouts.

My question is, how could I handle the loading times well? As far as I know C2 doesn't offer any "loading screen" feature, so this would mean that each time when such a giant layout has to be loaded in, it would display a black screen for about 10 seconds or even longer. I believe that a lot of players would think that the game crashed when waiting over 10 seconds for a layout to load it.
ImageImageImage
B
68
S
24
G
78
Posts: 693
Reputation: 45,301

Post » Mon Sep 26, 2016 8:20 am

TheRealDannyyy wrote:What exactly does this mean though?
For my understanding this would mean that C2 games load content like the old Super Mario games did?

shinkan wrote:For this one i think you are over complicating things a bit. All you have to do is store important stuff in arrays, dictionaries or whatever - which are global objects

There are a ton of instances and things that I have to consider though, besides the fact that I also have to sync it up on the connected peer.
I think for me this would be a pain to do, I'm not really good with "predictive" eventing that contains content from a lot of different instances.


1. No no, there is a difference between loading assets and rendering them. On start of the layout C2 loads every single object that is placed on that layout to the memory (objects created or spawned during the runtime are added to memory at runtime), but renders only what is in active viewport.

2. i think it could be even easier. Having for example one array for all enemies and their data (position, animation, HP, speed, etc etc) could let you easily sync it because all information you need are in one place. So from one and the same array you could send out data to the host and all the players.


TheRealDannyyy wrote:My question is, how could I handle the loading times well? As far as I know C2 doesn't offer any "loading screen" feature, so this would mean that each time when such a giant layout has to be loaded in, it would display a black screen for about 10 seconds or even longer. I believe that a lot of players would think that the game crashed when waiting over 10 seconds for a layout to load it.


You could do your own custom loading screens. Remember you don't have to load everything right on the start of the layout. You can make it in stages.

Maybe one day in C3 we would get an option to stream levels ;)
ImageImageImageImage
B
158
S
67
G
43
Posts: 2,603
Reputation: 36,003

Post » Mon Sep 26, 2016 8:29 am

shinkan wrote:You could do your own custom loading screens. Remember you don't have to load everything right on the start of the layout. You can make it in stages.
Maybe one day in C3 we would get an option to stream levels ;)

I have a startup loading screen but that is not my problem.
My problem would be the "rendering" into a layout which also takes quite a while on PC's with low/medium-end hardware.
So this "rendering" into a layout would require a loading screen that C2 doesn't offer I guess?
ImageImageImage
B
68
S
24
G
78
Posts: 693
Reputation: 45,301

Post » Mon Sep 26, 2016 8:43 am

That's why I meant by custom loading screen - the one made of sprite, spritefonts, text, or whatever you need... just to show that something is still going on at the background.
ImageImageImageImage
B
158
S
67
G
43
Posts: 2,603
Reputation: 36,003

Post » Mon Sep 26, 2016 8:47 am

shinkan wrote:That's why I meant by custom loading screen - the one made of sprite, spritefonts, text, or whatever you need... just to show that something is still going on at the background.

I get it, so you mean something like a "fake" loading screen. Well it's better than nothing I guess.
Thanks for the idea.
ImageImageImage
B
68
S
24
G
78
Posts: 693
Reputation: 45,301

Next

Return to Construct 2 General

Who is online

Users browsing this forum: No registered users and 4 guests