3

Flutter & Flame — Step 2: Game basics

 2 years ago
source link: https://medium.com/flutter-community/flutter-flame-step-2-game-basics-48b4493424f3
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.
0*cHfVjyws5ASmQ_Cj
Photo by Riho Kroll on Unsplash

Flutter & Flame — Step 2: Game basics

A solid foundation for our game will ensure that we can utilize everything Flame offers us. The second article will focus on creating the base elements of our Moon lander game.

Check out part one

In case you want to check how to set up the project and get you ready to rumble, have a look at the first part of this series.

Introduction

This article will cover all basic elements you need in a game, like for the above first article we will keep working on our Moon lander game.
You can download the code that will be the result of this article, following the link below:

https://github.com/wolfenrain/moonlander/releases/tag/Article_2

0*pvV8TkfnPpymv6Li?q=20
null
Photo by Rami Al-zayat on Unsplash

Games are widgets too

Every game you run with Flame, is a widget in your Flutter application like any text or button. Because of this, you can also combine your game with other widgets.
The Widget in Flame we use to host our complete game is called GameWidget, in the most basic usage you just pass your game object to it and you are done.

GameWidget features

Of course, you can do a bit more with this Widget. You can pass different callbacks to this widget. The errorBuilder will only be used if there is an unhandled exception in your onLoad. In case you load multiple assets, like sprites and audio files, you might find the loadingBuilder a neat way to show something to your users while your game is loading. The code below shows an example on how to use this.

Another very neat way to structure screens of your game is using overlays. Overlays are screens or single widgets that can (as the name suggests) be added on top of your game. An overlay requires a name and a builder that returns the widget you want to show.
The code above shows that we create a map with the key that is our name for the overlay. The value of the map is the builder, it always gets a reference of the Flutter context and the game. The builder needs to return a widget, this can be any Flutter screen.

In the code snippet above, we show a basic pause menu using a Stack and some ElevatedButton. You can see that our game object that is passed as a reference offers a property called overlays. This object offers a method to handle overlays.
You can push overlays or check if one is currently active, if check the “Resume” you can see we remove the “pause” overlay. Keep in mind, pause is the name we used in the map while creating the GameWidget.

1*--H-5VI5ZCNNASbKeIczIQ.png?q=20
null
The pause menu, very rough but a “normal” Flutter screen on top of your game

What we have so far

We can add overlays to our game, and we know that our game itself is just another widget. Let’s add something to our game to trigger this overlay. If you recall the first series of this article, you might remember the RocketComponent below is our PauseComponent:

The first new part for the pause component is the usage of isHud. This flag tells Flame to ignore this component in case you work with a moving camera. This component will always be positioned static.

Important note from the docs: Do note that this behavior currently only works if the component is added directly to the root FlameGame.

Our PauseComponent extends the SpriteComponent and uses the Tappable and HasGameRef mixin. Thanks to the SpriteComponent Flame knows that we want to draw an image (a sprite) on the given Position (with the set size). Important to know, a SpriteComponent is still based on PositionComponent and includes all mechanics of its base component.

Mixins to the rescue

0*q0gO1Fl5cxQZb33_?q=20
null
Photo by Paul Keiffer on Unsplash

The two mixins we use are extensions to our class and inform the FlameGame class what capabilities we want to support for this component. Any button should have the possibility to get pressed, that's done by the Tappable mixin.

The above code shows how we override the onTapDown function and use it to toggle an overlay in our game. This method also shows why we used the second mixin HasGameRef it offers a clean way to get the current game instance your component is connected to.
Of course, you could add it to each component constructor as a parameter, but why inflating your code if you have this handy mixin.

In case you want to know what other mixins are available, try out the fantastic documentation of flame, the link below lists all articles using the word mixin:

Life cycle of a game

0*duGY6V_JXyq8XlC3?q=20
null
Photo by Tolga Ulkan on Unsplash

Like any Flutter app, your game has life cycle events. The image below (from here) shows the events for FlameGames

1*LeMwA2Fnk9Eb4xivX0LGVQ.png?q=20
null

If you create a new game object and attach it to a parent widget, it will call the onGameResize, onLoad (only the first time) and onMount. While the onLoad is running, the widget returned from the loadingBuilder is shown on the screen. If you look at the part of the main file code below, you can see an example usage for the onMount and onRemove events in the game class.

Depending on your game, you need more or less of these events, but it’s always good to understand the inner workings to use it the right way if you need it. We just start/stop listing to overlay changes in these methods.

It’s loading time — onLoad

The above snippet is the onLoad function of our game, it loads the sprites for our pause button and the rocket component. Besides the data loading, it also creates the initial components and registers the above-mentioned listener for the overlay change.
Loading assets (like images, sounds etc.) can take some time, make sure your game is responsive during the loading. In our case, we show a loading spinner
No worries if you do not yet understand what exactly is happening with sprites and animation here, we will cover this in depth in our next article.

Components have a life cycle too

Not only your game has life cycle events, every component has them as well. Each component could load assets or run code to prepare your game logic.

Preview version

0*7BbuCMR6FPTeaDJF?q=20
null
Photo by NordWood Themes on Unsplash

While writing this article, we are using a preview version of Flame (1.0.0-releasecandidate.16), and we encounter a bug.
If you check the onOverlayChanged method shown below, you can see we use the build in pauseEngine and resumeEngine methods.

But our game did resume once we rebuild the widget (for example showing an overlay), after a quick search via GitHub we found out it was a known issue and will be addressed with an upcoming update:

End of the story: open-source rocks, keep your dependency updated regularly!

If you encounter an issue or need help, you can use GitHub or check the Flame Discord server. The community is always a good help and has many talented people to support each other.

Flame Discord

Flame GitHub

Render and Update loop

0*sderj89H5SlGQ7rv?q=20
null
Photo by Shubham Dhage on Unsplash

Part of the life cycle events are render and update, these functions are called for every tick of your game (in the best case 60 times a second, to get you a 60fps game).

Render

The job of the render function is to draw your game, to accomplish this it provides a Canvas object that offers a wide range of methods to generate content on the screen.

Update

The update-method is used to change the state (for example, move a character) of your game and its component.
Important here is that this has to happen in relation to the current FPS of your game. To make this work, the update method provides a parameter that contains the delta of milliseconds since the last call (it’s a double).

Good to know

You don’t have to override this functions for every component or game, it always depends on what you want to achieve. Look at our PauseComponent, we don’t use any of the above two events, still it’s working.
Magic? No!
Flame takes care of the basics, if you have a SpriteComponent the render event will draw it with the selected position and size, nothing you need to take care of.

Hint: If you have to override the events because you need custom behavior, don’t forget to call the method on your parent class with super.XXX();

Code, let the rocket fly

0*mCGxJf2oXuGEIHv3?q=20
null
Photo by John Baker on Unsplash

To make sure we have some visual progress, the last part of this article will roughly describe what we are doing to get a rocket on the screen including a simple animation.

Web support

Since the first Article we added web support to our MoonLander game, we will use this in the beginning to quickly add functions and test them. Of course, at the end the game will be for Android and iOS.

For now, I recommend launching the game in Chrome

All new RocketComponent

1*wRozGVFCrRqbP8JAgW-7VA.gif?q=20
null
Our little rocket, sprite source

The new RocketComponent uses the SpriteAnimationGroupComponent as a parent class and the following mixins Hitbox, Collidable, KeyboardHandler.

The parent class allows us to have multiple sprite-based animations, the Hitbox and Collidable are still there from the first article. The only new mixin is the KeyboardHandler, based on the name you might guess it allows us to react to keyboard events (key up/down etc.).

We will cover all the details about the components and game elements in our next article, but we can already extract some learnings from the code.

Debug your game

You might have noticed the pink box and numbers around the rocket in the above GIF. This is due to the debug flag set to true, the paint object used is the debugPaint. If you check our main.dart file, you can see that we set the debug mode based on the kDebugMode variable. kDebugMode is set by Flutter automatically and will inform Flame to render additional debug information (hitbox, position etc.)

A custom update function

Inside our update function, we change the position of our rocket and change the animation state (details are in the next article). As mentioned before, all of these changes use the dt parameter to align changes to the current FPS of the game.

Life cycle — onLoad

That's another override we learned in this article, the custom onLoad first calls the super onLoad followed by the custom code that should run every time a RocketComponent is created.

onKeyEvent — like onTapDown

Like the pause component, we want to react to input, in this case the key presses. For now, you just need to know that this function is not by default part of the component and will be called to handle input changes. Right now, it's used to change the animation of our rocket.

Try moon lander & what’s next

0*Ep1Ls4izO4hEX15u?q=20
null
Photo by Brett Jordan on Unsplash

If you run the game in Chrome you will see the rocket slowly decent, you can turn the rocket by pressing left or right arrow on your keyboard (actually not turn, just change the animation) and bring it back to idle state with any other key.

In the next article we will focus on the components including animations and proper controls. We will also address a way to keep your game consistent on multiple screen resolutions.

Now you

As a little challenge until the next article, try to let the rocket not only look in a direction, but also fly there! Use the update method to change the position based on key presses.

Important links

Sprite for the rocket: https://tavino.itch.io/spaceship

Find other game assets: https://itch.io/game-assets

GitHub Project link: https://github.com/wolfenrain/moonlander

Follow Flutter Community on Twitter: https://www.twitter.com/FlutterComm


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK