flame icon indicating copy to clipboard operation
flame copied to clipboard

Implement "Loader" component

Open st-pasha opened this issue 3 years ago • 5 comments

Problem to solve

Often, a game needs some time to load its resources/assets. In this case some kind of a loading screen is necessary.

Currently, the only support from Flame for this functionality comes from the loadingBuilder parameter of the GameWidget. However, this solution may be insufficient for the following reasons:

  • it is non-trivial to pass the loading progress from the game to the loading widget;
  • since the loading page is inherently dynamic, one may want to use Flame's rendering for it instead of relying on Flutter animations or custom painters;
  • it is unclear how to use the loadingBuilder (or whether it is even possible) to show loading progress when loading internal components, such as a particular level within the game.

Proposal

How exactly to solve this is TBD. Should this be a specialized Route within the Navigator? Or a mixin to a page? Or a page wrapper? Or a child?

I'd be interested to hear how people solve this problem right now.

st-pasha avatar Jul 02 '22 20:07 st-pasha

Hello, I find your proposal very interesting. I currently use LoadingBuilder dividing the game into several phases loading the assets at the beginning of each one of them.

On the other hand, there is another question in Flutter regarding the delay that exists when loading the images inside a Widget, such as a Scroll. For this there is the precacheImage() function that has the problem that can cause the system to crash. https://twitter.com/NorbertoMartnAf/status/1295749275243216896

nmarafo avatar Jul 15 '22 08:07 nmarafo

I'm not sure if a dedicated loading screen would be a good solution since it forces the developers to use that screen-based approach only. What about a fully styleable component which can be used (if wanted) on a loading screen (created and navigated to like all other screens with the new routing system) or anywhere else?

For not having tight coupling and therefore possibly having problems passing the loading progress to that new component i would use some event- or state-based approach (event_bus, ValueNotifier) if i had to develop that feature on my own.

rivella50 avatar Aug 23 '22 06:08 rivella50

@st-pasha I just made a draft implementation ☝🏽 It now works only with Flutter widgets but could be extended to work with widgets too. This is pure stream and subscriber in proxy widget with user-friendly notifier for user-made widget... a bit tricky inside but allows to hide all mess under hood =) Is the approach is ok, I will make PR and some code refactoring of course.

it is unclear how to use the loadingBuilder (or whether it is even possible) to show loading progress when loading internal components, such as a particular level within the game.

In my case loading a new game level means creating and loading new game class instance with new parameters, so loadingBuilder is called automatically as it was at first time

ASGAlex avatar Jan 26 '23 10:01 ASGAlex

So, the idea here was to specifically make a loader Component, as opposed to a loader widget. We already support loading widget -- via the loadingBuilder parameter -- though it doesn't accept progress indicator right now.

There are several reasons why we would want to have the LoaderComponent instead of (or in addition to) a loading widget:

  • The loading component can be used in any place where something is being loaded. This could be the game itself, a particular level, or encounter, or location, etc. For example, there could be a game where the player travels on a world map, encounters a monster, and then goes into special fighting UI -- the UI, and the monster assets need to be loaded at this moment. Or the player enters a dungeon, and now you need to load the dungeon visuals. Etc.
  • Using components should (in theory) be easier than using widgets. The Components are a familiar territory for Flame developers, whereas they may not know the Flutter framework very well.
  • Using a component should be more efficient. After all, the loading screen often has some assets of its own. For example, the custom font that would show the "Loading X%" message. Using this font from a component would be more efficient than loading it both into the widget and into the game.

In my case loading a new game level means creating and loading new game class instance with new parameters, so loadingBuilder is called automatically as it was at first time

This would not work in general: it requires everything before the loading screen to not be a Game, which may be applicable in some limited cases, but not in general. Ideally, we would want the solutions included in core Flame to be as general as possible.

st-pasha avatar Jan 26 '23 23:01 st-pasha

I just add here my own experience with loading progress with heavy calculations. In general you can't implement this feature and to keep backward compatibility because onLoad function going to become more and more blocking while amount of it's operations grows. No matter if you use sync API or something async like streams - widget's tree just do not rebuilds.

The thing that helps to solve the problem is moving initialization logic from onLoad to something like update. And dividing you loading process to small chunks that lasts no longer than 16ms. After every loading operation you check Stopwatch timer and execute next operation, if you have enough time. Otherwise you save the progress and allow Flame to complete game's lifecycle, Flutter rebuilds widget's tree then and in new update cycle you restores the progress and continue loading.

ASGAlex avatar Apr 09 '23 05:04 ASGAlex