Flutter Web and Flame
Tags: flutter, productivity, webdev, dart
Published: 2020-05-03 12:39:19

*In case it helped :)*
<a href="https://www.buymeacoffee.com/aseemwangoo" target="_blank"><img src="https://www.buymeacoffee.com/assets/img/custom_images/orange_img.png" alt="Pass Me A Coffee!!" style="height: 41px !important;width: 174px !important;box-shadow: 0px 3px 2px 0px rgba(190, 190, 190, 0.5) !important;-webkit-box-shadow: 0px 3px 2px 0px rgba(190, 190, 190, 0.5) !important;" ></a>

<!-- wp:paragraph -->
<p>We will cover briefly about creating a game</p>
<!-- /wp:paragraph -->

<!-- wp:list {"ordered":true} -->
<ol><li><strong>Using </strong><a href="https://pub.dev/packages/flame" rel="noreferrer noopener" target="_blank"><strong>Flame</strong></a></li><li>This article is an overview of using Flame</li><li>Detailed explanation of Flame available <a href="https://flame-engine.org/docs/" rel="noreferrer noopener" target="_blank">here</a>.</li><li>Game assets were found at <a href="https://opengameart.org/" rel="noreferrer noopener" target="_blank">OpenGameArt</a></li></ol>
<!-- /wp:list -->

<!-- wp:quote -->
<blockquote class="wp-block-quote"><p>Note&nbsp;: For learning how to create a game, step by step, please follow <a href="https://jap.alekhin.io/2d-casual-mobile-game-tutorial-flame-flutter-part-1" rel="noreferrer noopener" target="_blank">this</a>. This game itself was created by following <a href="https://jap.alekhin.io/2d-casual-mobile-game-tutorial-flame-flutter-part-1" rel="noreferrer noopener" target="_blank">this</a>.</p></blockquote>
<!-- /wp:quote -->

<!-- wp:separator -->
<hr class="wp-block-separator"/>
<!-- /wp:separator -->

{% youtube uUVKxa-YrK8 %}

<!-- wp:paragraph -->
<p>Introducing Flame….</p>
<!-- /wp:paragraph -->

![Alt Text](https://dev-to-uploads.s3.amazonaws.com/i/9eo6o6p7m1ww944a6v4x.png)

<!-- wp:paragraph -->
<p>As per the documentation&nbsp;:</p>
<!-- /wp:paragraph -->

<!-- wp:quote -->
<blockquote class="wp-block-quote"><p>Flame is a minimalist Flutter game engine…</p></blockquote>
<!-- /wp:quote -->

####Article here: https://flatteredwithflutter.com/how-to-create-game-in-flutter-web-using-flame/

<!-- wp:paragraph -->
<p><strong>Steps&nbsp;:</strong></p>
<!-- /wp:paragraph -->

<!-- wp:list {"ordered":true} -->
<ol><li>Add this to your pubspec.yaml file</li></ol>
<!-- /wp:list -->

<!-- wp:code -->
<pre class="wp-block-code"><code>dependencies:  flame: ^0.19.1</code></pre>
<!-- /wp:code -->

<!-- wp:paragraph -->
<p>2. Create a <code><a rel="noreferrer noopener" href="https://pub.dev/documentation/flame/latest/game_game/game_game-library.html" target="_blank">Game</a></code> class</p>
<!-- /wp:paragraph -->

<!-- wp:paragraph -->
<p>In case of normal Flutter Apps, we tend to create a class which internally implements a <strong>MaterialApp</strong>,</p>
<!-- /wp:paragraph -->

<!-- wp:paragraph -->
<p>but here, we define a class named <code>GameTime</code> that extends Flame’s <code><a rel="noreferrer noopener" href="https://pub.dev/documentation/flame/latest/game_game/game_game-library.html" target="_blank">Game</a></code> class.</p>
<!-- /wp:paragraph -->

<!-- wp:preformatted -->
<pre class="wp-block-preformatted">class GameTime extends Game {
  void render(Canvas canvas) {}
  void update(double t) {}
}</pre>
<!-- /wp:preformatted -->

<!-- wp:paragraph -->
<p>3. Overview of <code>runApp</code></p>
<!-- /wp:paragraph -->

<!-- wp:preformatted -->
<pre class="wp-block-preformatted">void main() => runApp(GameScreen());

class GameScreen extends StatefulWidget {
  const GameScreen({Key key}) : super(key: key);
  static final _game = GameTime();

  @override
  _GameScreenState createState() => _GameScreenState();
}

class _GameScreenState extends State&lt;GameScreen> {
  @override
  Widget build(BuildContext context) => GameScreen._game.widget;
}
</pre>
<!-- /wp:preformatted -->

<!-- wp:paragraph -->
<p>This creates our page into a game&nbsp;!!</p>
<!-- /wp:paragraph -->

<!-- wp:paragraph -->
<p>Notice that, <code>GameTime</code> class itself has a <code>widget</code> property.</p>
<!-- /wp:paragraph -->

<!-- wp:paragraph -->
<p>As per the docs, <code>widget</code></p>
<!-- /wp:paragraph -->

<!-- wp:quote -->
<blockquote class="wp-block-quote"><p>Returns the game widget. Put this in your structure to start rendering and updating the game. You can add it directly to the runApp method or inside your widget structure.</p></blockquote>
<!-- /wp:quote -->

<!-- wp:heading {"level":4} -->
<h4>Our game — COVIDER&nbsp;!!!</h4>
<!-- /wp:heading -->

![Alt Text](https://dev-to-uploads.s3.amazonaws.com/i/ifmqcqkcbnfxsngih8i9.png)

<!-- wp:paragraph -->
<p>Rules&nbsp;:</p>
<!-- /wp:paragraph -->

<!-- wp:list {"ordered":true} -->
<ol><li>Tap on the virus to kill..</li><li>Each virus killed, you earn a point</li><li>There is a time associated with each virus, failing to kill in that time, you lose</li><li>Tap outside the virus, you also lose.</li></ol>
<!-- /wp:list -->

<!-- wp:paragraph -->
<p><a rel="noreferrer noopener" href="https://fir-signin-4477d.firebaseapp.com/#/" target="_blank"><strong>Play here</strong></a></p>
<!-- /wp:paragraph -->

<!-- wp:heading {"level":4} -->
<h4>Basics for&nbsp;COVIDER</h4>
<!-- /wp:heading -->

<!-- wp:paragraph -->
<p>Covider, (name of our game) is divided into</p>
<!-- /wp:paragraph -->

<!-- wp:list -->
<ul><li>Components</li><li>Controller</li><li>Views</li><li>Game Class&nbsp;, (<code>GameTime</code>class)</li></ul>
<!-- /wp:list -->

<!-- wp:paragraph -->
<p>Every game requires resources, assets, variables etc to be loaded before anything else happens,</p>
<!-- /wp:paragraph -->

<!-- wp:paragraph -->
<p>we do so by calling a function <code>initialize</code> inside the <code>GameTime</code> constructor</p>
<!-- /wp:paragraph -->

<!-- wp:preformatted -->
<pre class="wp-block-preformatted">GameTime() {
    initialize;
}

Future&lt;void> get initialize async {
  // YOUR STUFF HERE
}</pre>
<!-- /wp:preformatted -->

<!-- wp:heading {"level":4} -->
<h4>Current size of the&nbsp;screen</h4>
<!-- /wp:heading -->

<!-- wp:paragraph -->
<p>We make use of the Flame’s utility called <strong>initialDimensions…</strong></p>
<!-- /wp:paragraph -->

<!-- wp:preformatted -->
<pre class="wp-block-preformatted">final _size = await Flame.util.initialDimensions();</pre>
<!-- /wp:preformatted -->

<!-- wp:paragraph -->
<p>and pass this value inside the resize function.</p>
<!-- /wp:paragraph -->

<!-- wp:preformatted -->
<pre class="wp-block-preformatted">@override<br> void resize(Size size) {<br>   screenSize = size;<br>   super.resize(size);<br>}</pre>
<!-- /wp:preformatted -->

<!-- wp:paragraph -->
<p>hence, at every point of time, we have the latest screensize….</p>
<!-- /wp:paragraph -->

<!-- wp:heading {"level":4} -->
<h4>Draw Background…</h4>
<!-- /wp:heading -->

<!-- wp:paragraph -->
<p>First, we download an asset and put inside the <strong>pubspec.yaml</strong></p>
<!-- /wp:paragraph -->

<!-- wp:preformatted -->
<pre class="wp-block-preformatted">assets:<br>    - assets/images/<br>    - assets/images/bg/room.png</pre>
<!-- /wp:preformatted -->

<!-- wp:quote -->
<blockquote class="wp-block-quote"><p>Note: I had to specify the image path, to load, although I had already included the folder images</p></blockquote>
<!-- /wp:quote -->

<!-- wp:paragraph -->
<p>For loading images in Flame,</p>
<!-- /wp:paragraph -->

<!-- wp:preformatted -->
<pre class="wp-block-preformatted">Flame.images.loadAll(['bg/room.png'])</pre>
<!-- /wp:preformatted -->

<!-- wp:paragraph -->
<p>We have a class called <code>AssetsLoader</code> wherein we load assets…This is called in the initState of<code>GameScreen</code> ..</p>
<!-- /wp:paragraph -->

<!-- wp:preformatted -->
<pre class="wp-block-preformatted">class _GameScreenState extends State&lt;GameScreen&gt; {<br>  @override<br>  void initState() {<br>    super.initState();<br>    AssetsLoader.loadImages();<br>  }<br>}</pre>
<!-- /wp:preformatted -->

<!-- wp:paragraph -->
<p>Lets create a <strong>Background Component</strong>, to separate the background’s logic…</p>
<!-- /wp:paragraph -->

<!-- wp:preformatted -->
<pre class="wp-block-preformatted">class Background {
  Background({
    @required this.gameTime,
  }) {
    bgSprite = Sprite(GameUtils.roomImage);
    bgRect = Rect.fromLTWH(
      0.0,
      0.0,
      gameTime.screenSize.width,
      gameTime.screenSize.height,
    );
  }
  final GameTime gameTime;

  Rect bgRect;
  Sprite bgSprite;

  void render(Canvas c) {
    bgSprite.renderRect(c, bgRect);
  }
}</pre>
<!-- /wp:preformatted -->

<!-- wp:paragraph -->
<p><strong>Sprite</strong>&nbsp;: From flame package, used to draw onto the screen.</p>
<!-- /wp:paragraph -->

<!-- wp:paragraph -->
<p>In our case, we are passing the image, which we downloaded earlier, using <code>bgSprite.</code></p>
<!-- /wp:paragraph -->

<!-- wp:paragraph -->
<p><strong>renderRect&nbsp;: </strong>To render the background image.</p>
<!-- /wp:paragraph -->

<!-- wp:paragraph -->
<p>We create a rectangle, which is of the same dimensions as our screen. It takes in two parameters, <code>canvas</code> and the <code>rectangle</code> to be drawn…</p>
<!-- /wp:paragraph -->

<!-- wp:separator -->
<hr class="wp-block-separator"/>
<!-- /wp:separator -->

<!-- wp:paragraph -->
<p>Now for rendering the background inside <code>GameTime</code>….</p>
<!-- /wp:paragraph -->

<!-- wp:list {"ordered":true} -->
<ol><li>Initialize the instance of this component as</li></ol>
<!-- /wp:list -->

<!-- wp:preformatted -->
<pre class="wp-block-preformatted">Background background;</pre>
<!-- /wp:preformatted -->

<!-- wp:paragraph -->
<p>and inside the <code>GameTime</code> initialize function, write this</p>
<!-- /wp:paragraph -->

<!-- wp:preformatted -->
<pre class="wp-block-preformatted">background = Background(gameTime: this);</pre>
<!-- /wp:preformatted -->

<!-- wp:paragraph -->
<p>This will make sure, we always have the background instance…..</p>
<!-- /wp:paragraph -->

<!-- wp:paragraph -->
<p>2. Inside the render method of <code>GameTime</code> ,</p>
<!-- /wp:paragraph -->

<!-- wp:preformatted -->
<pre class="wp-block-preformatted">@override<br>void render(Canvas canvas) {<br>  // RENDER BACKGROUND<br>  background.render(canvas);<br>}</pre>
<!-- /wp:preformatted -->

<!-- wp:paragraph -->
<p>this calls the render method of background, which we defined earlier…Your background is visible now….</p>
<!-- /wp:paragraph -->

<!-- wp:quote -->
<blockquote class="wp-block-quote"><p><strong>Note&nbsp;: Pretty much each component, uses this logic….</strong></p></blockquote>
<!-- /wp:quote -->

<!-- wp:separator -->
<hr class="wp-block-separator"/>
<!-- /wp:separator -->

<!-- wp:heading {"level":4} -->
<h4>Include Audio</h4>
<!-- /wp:heading -->

<!-- wp:paragraph -->
<p>We downloaded assets for hit, lose and background music, put inside the <strong>pubspec.yaml</strong><br></p>
<!-- /wp:paragraph -->

<!-- wp:preformatted -->
<pre class="wp-block-preformatted">assets:<br>    - assets/audio/bgm/bg_music.mp3<br>    - assets/audio/sfx/hit1.mp3<br>    - assets/audio/sfx/hit2.mp3<br>    - assets/audio/sfx/laugh.mp3</pre>
<!-- /wp:preformatted -->

<!-- wp:separator -->
<hr class="wp-block-separator"/>
<!-- /wp:separator -->

<!-- wp:paragraph -->
<p>Just like we loaded images, we now need to load sounds in Flame…</p>
<!-- /wp:paragraph -->

<!-- wp:preformatted -->
<pre class="wp-block-preformatted">static void loadSounds() {<br>   Flame.audio.disableLog();<br>   Flame.audio.loadAll(GameUtils.sounds);<br>}</pre>
<!-- /wp:preformatted -->

<!-- wp:paragraph -->
<p>where <strong>GameUtils.sounds</strong> is the list of string</p>
<!-- /wp:paragraph -->

<!-- wp:separator -->
<hr class="wp-block-separator"/>
<!-- /wp:separator -->

<!-- wp:paragraph -->
<p>Flame package comes with <a href="https://pub.dev/packages/audioplayers" rel="noreferrer noopener" target="_blank"><strong>AudioPlayer,</strong></a>meaning you don’t need to add any other package…</p>
<!-- /wp:paragraph -->

<!-- wp:list -->
<ul><li>Create a variable inside <code>GameTime</code></li></ul>
<!-- /wp:list -->

<!-- wp:preformatted -->
<pre class="wp-block-preformatted">AudioPlayer playBGM;</pre>
<!-- /wp:preformatted -->

<!-- wp:list -->
<ul><li>Inside <code>GameTime</code> initialize method</li></ul>
<!-- /wp:list -->

<!-- wp:preformatted -->
<pre class="wp-block-preformatted">// AUDIO PLAYERS<br>playBGM = await Flame.audio.loopLongAudio(GameUtils.bgMusic,   volume: 0.25);</pre>
<!-- /wp:preformatted -->

<!-- wp:paragraph -->
<p><strong>audio.loopLongAudio</strong>&nbsp;: allows for audios of any length to be played. We set the <strong>volume (optional param)</strong> to 0.25..</p>
<!-- /wp:paragraph -->

<!-- wp:quote -->
<blockquote class="wp-block-quote"><p>Note&nbsp;: This is used to play the background music</p></blockquote>
<!-- /wp:quote -->

<!-- wp:list -->
<ul><li>For one-time sounds, like hit or miss,</li></ul>
<!-- /wp:list -->

<!-- wp:separator -->
<hr class="wp-block-separator"/>
<!-- /wp:separator -->

<!-- wp:preformatted -->
<pre class="wp-block-preformatted">Flame.audio.play(GameUtils.laughSound); // LAUGH SOUND</pre>
<!-- /wp:preformatted -->

<!-- wp:paragraph -->
<p>use <strong>audio.play&nbsp;: </strong>makes uses of optimized features</p>
<!-- /wp:paragraph -->

<!-- wp:separator -->
<hr class="wp-block-separator"/>
<!-- /wp:separator -->

<!-- wp:heading {"level":4} -->
<h4>Draw Virus</h4>
<!-- /wp:heading -->

<!-- wp:paragraph -->
<p>We downloaded assets for fly-up and fly-down and put inside the <strong>pubspec.yaml</strong></p>
<!-- /wp:paragraph -->

<!-- wp:preformatted -->
<pre class="wp-block-preformatted">assets:<br>    - assets/images/virus/virus_fly_up.png<br>    - assets/images/virus/virus_fly_down.png</pre>
<!-- /wp:preformatted -->

<!-- wp:paragraph -->
<p>Lets create <strong>Virus Component..</strong></p>
<!-- /wp:paragraph -->

<!-- wp:preformatted -->
<pre class="wp-block-preformatted">class Virus {
  Virus({
    @required this.gameTime,
  })

  final GameTime gameTime;

  double get virusSpeed => gameTime.tileSize * 2;
 
  void render(Canvas c) {}

  void update(double t) {}

  void onTapDown() {}

  void get setupVirusLocation {}
}</pre>
<!-- /wp:preformatted -->

<!-- wp:paragraph -->
<p>and from this class, we created two other classes called <code>MovingVirus</code> and <code>MovingDragonVirus</code></p>
<!-- /wp:paragraph -->

<!-- wp:paragraph -->
<p><strong>MovingVirusComponent</strong></p>
<!-- /wp:paragraph -->

<!-- wp:preformatted -->
<pre class="wp-block-preformatted">class MovingVirus extends Virus {
  MovingVirus(
    GameTime gameTime,
    double left,
    double top,
  ) : super(gameTime: gameTime) {
    virusRect = Rect.fromLTWH(left, top, gameTime.tileSize, gameTime.tileSize);

    movingVirusSprite = &lt;Sprite>[];
    movingVirusSprite.add(Sprite(GameUtils.virusUp));
    movingVirusSprite.add(Sprite(GameUtils.virusDown));

deadVirusSprite = Sprite(GameUtils.virusDead);
  }
}</pre>
<!-- /wp:preformatted -->

<!-- wp:quote -->
<blockquote class="wp-block-quote"><p>Basically, for any flying motion, we always have used two images -(one for fly up) and (other for fly down). Then, adding to the sprites respectively..</p></blockquote>
<!-- /wp:quote -->

<!-- wp:paragraph -->
<p>In order to create different speeds for viruses, we override the <code>virusSpeed</code></p>
<!-- /wp:paragraph -->

<!-- wp:paragraph -->
<p><strong>MovingDragonVirusComponent</strong></p>
<!-- /wp:paragraph -->

<!-- wp:preformatted -->
<pre class="wp-block-preformatted">class MovingDragonVirus extends Virus {
  MovingDragonVirus(
    GameTime gameTime,
    double left,
    double top,
  ) : super(gameTime: gameTime) {
    virusRect = Rect.fromLTWH(
        left,
        top,
        gameTime.tileSize * GameUtils.maxVirusSize,
        gameTime.tileSize * GameUtils.maxVirusSize);

    movingVirusSprite = &lt;Sprite>[];
    movingVirusSprite.add(Sprite(GameUtils.dragonVirusUp));
    movingVirusSprite.add(Sprite(GameUtils.dragonVirusDown));

    deadVirusSprite = Sprite(GameUtils.virusDead);
  }

  @override
  double get virusSpeed => gameTime.tileSize * 5;
}</pre>
<!-- /wp:preformatted -->

<!-- wp:separator -->
<hr class="wp-block-separator"/>
<!-- /wp:separator -->

<!-- wp:quote -->
<blockquote class="wp-block-quote"><p>Note&nbsp;: For the motion of virus, kindly visit <a href="https://jap.alekhin.io/game-graphics-and-animation-tutorial-flame-flutter-part-2" rel="noreferrer noopener" target="_blank">here for detailed explanation</a>.</p></blockquote>
<!-- /wp:quote -->

<!-- wp:separator -->
<hr class="wp-block-separator"/>
<!-- /wp:separator -->

<!-- wp:heading {"level":4} -->
<h4>Storing High&nbsp;Scores…</h4>
<!-- /wp:heading -->

<!-- wp:paragraph -->
<p>For storing data, we made use of <code>dart:html</code>&nbsp;, which gives access to <a href="https://api.dart.dev/stable/2.5.0/dart-html/Window/localStorage.html" rel="noreferrer noopener" target="_blank"><strong>localStorage</strong></a></p>
<!-- /wp:paragraph -->

<!-- wp:paragraph -->
<p>For accessing the localStorage,</p>
<!-- /wp:paragraph -->

<!-- wp:preformatted -->
<pre class="wp-block-preformatted">final _localStorage = html.window.localStorage;</pre>
<!-- /wp:preformatted -->

<!-- wp:paragraph -->
<p>To visit localstorage, (screenshot from Chrome)</p>
<!-- /wp:paragraph -->

![Alt Text](https://dev-to-uploads.s3.amazonaws.com/i/28fffcs6k9il70q80kru.png)

<!-- wp:paragraph -->
<p>and we store the data inside it as a String….</p>
<!-- /wp:paragraph -->

_localStorage[‘$key’] = value;

*In case it helped :)*
<a href="https://www.buymeacoffee.com/aseemwangoo" target="_blank"><img src="https://www.buymeacoffee.com/assets/img/custom_images/orange_img.png" alt="Pass Me A Coffee!!" style="height: 41px !important;width: 174px !important;box-shadow: 0px 3px 2px 0px rgba(190, 190, 190, 0.5) !important;-webkit-box-shadow: 0px 3px 2px 0px rgba(190, 190, 190, 0.5) !important;" ></a>

Hosted URL : <a rel="noreferrer noopener" href="https://fir-signin-4477d.firebaseapp.com/#/" target="_blank">https://fir-signin-4477d.firebaseapp.com/#/</a>

<a rel="noreferrer noopener" href="https://github.com/AseemWangoo/experiments_with_web" target="_blank">Source code for Flutter Web App..</a></pre>
<!-- /wp:preformatted -->

{% youtube uUVKxa-YrK8 %}