LCOV - code coverage report
Current view: top level - lib/src/game - game.dart (source / functions) Hit Total Coverage
Test: lcov.info Lines: 26 60 43.3 %
Date: 2021-08-10 15:50:53 Functions: 0 0 -

          Line data    Source code
       1             : import 'dart:async';
       2             : import 'dart:ui';
       3             : 
       4             : import 'package:flutter/rendering.dart';
       5             : import 'package:flutter/scheduler.dart';
       6             : import 'package:flutter/services.dart';
       7             : import 'package:flutter/widgets.dart';
       8             : 
       9             : import '../assets/assets_cache.dart';
      10             : import '../assets/images.dart';
      11             : import '../extensions/offset.dart';
      12             : import '../extensions/vector2.dart';
      13             : import '../sprite.dart';
      14             : import '../sprite_animation.dart';
      15             : import 'game_render_box.dart';
      16             : import 'mixins/keyboard.dart';
      17             : import 'projector.dart';
      18             : 
      19             : /// Represents a generic game.
      20             : ///
      21             : /// Subclass this to implement the [update] and [render] methods.
      22             : /// Flame will deal with calling these methods properly when the game's widget is rendered.
      23             : abstract class Game extends Projector {
      24             :   final images = Images();
      25             :   final assets = AssetsCache();
      26             : 
      27             :   /// Just a reference back to the render box that is kept up to date by the engine.
      28             :   GameRenderBox? _gameRenderBox;
      29             : 
      30             :   /// Currently attached build context. Can be null if not attached.
      31          10 :   BuildContext? get buildContext => _gameRenderBox?.buildContext;
      32             : 
      33             :   /// Whether the game widget was attached to the Flutter tree.
      34          10 :   bool get isAttached => buildContext != null;
      35             : 
      36             :   /// Current size of the game as provided by the framework; it will be null if layout has not been computed yet.
      37             :   ///
      38             :   /// Use [size] and [hasLayout] for safe access.
      39             :   Vector2? _size;
      40             : 
      41             :   /// Current game viewport size, updated every resize via the [onResize] method hook
      42           0 :   Vector2 get size {
      43           0 :     assertHasLayout();
      44           0 :     return _size!;
      45             :   }
      46             : 
      47             :   /// Indicates if the this game instance had its layout layed into the GameWidget
      48             :   /// Only this is true, the game is ready to have its size used or in the case
      49             :   /// of a BaseGame, to receive components.
      50          48 :   bool get hasLayout => _size != null;
      51             : 
      52             :   /// Returns the game background color.
      53             :   /// By default it will return a black color.
      54             :   /// It cannot be changed at runtime, because the game widget does not get rebuild when this value changes.
      55           8 :   Color backgroundColor() => const Color(0xFF000000);
      56             : 
      57             :   /// Implement this method to update the game state, given the time [dt] that has passed since the last update.
      58             :   ///
      59             :   /// Keep the updates as short as possible. [dt] is in seconds, with microseconds precision.
      60             :   void update(double dt);
      61             : 
      62             :   /// Implement this method to render the current game state in the [canvas].
      63             :   void render(Canvas canvas);
      64             : 
      65             :   /// This is the resize hook; every time the game widget is resized, this hook is called.
      66             :   ///
      67             :   /// The default implementation just sets the new size on the size field
      68          25 :   @mustCallSuper
      69             :   void onResize(Vector2 size) {
      70         100 :     _size = (_size ?? Vector2.zero())..setFrom(size);
      71             :   }
      72             : 
      73          24 :   @protected
      74             :   void assertHasLayout() {
      75             :     assert(
      76          24 :       hasLayout,
      77             :       '"size" is not ready yet. Did you try to access it on the Game constructor? Use the "onLoad" method instead.',
      78             :     );
      79             :   }
      80             : 
      81             :   /// This is the lifecycle state change hook; every time the game is resumed, paused or suspended, this is called.
      82             :   ///
      83             :   /// The default implementation does nothing; override to use the hook.
      84             :   /// Check [AppLifecycleState] for details about the events received.
      85           0 :   void lifecycleStateChange(AppLifecycleState state) {}
      86             : 
      87             :   /// Use for calculating the FPS.
      88           0 :   void onTimingsCallback(List<FrameTiming> timings) {}
      89             : 
      90           0 :   void _handleKeyEvent(RawKeyEvent e) {
      91           0 :     (this as KeyboardEvents).onKeyEvent(e);
      92             :   }
      93             : 
      94             :   /// Marks game as not attached tto any widget tree.
      95             :   ///
      96             :   /// Should be called manually.
      97           2 :   void attach(PipelineOwner owner, GameRenderBox gameRenderBox) {
      98           2 :     if (isAttached) {
      99           0 :       throw UnsupportedError('''
     100             :       Game attachment error:
     101             :       A game instance can only be attached to one widget at a time.
     102             :       ''');
     103             :     }
     104           2 :     _gameRenderBox = gameRenderBox;
     105           2 :     onAttach();
     106             :   }
     107             : 
     108             :   // Called when the Game widget is attached
     109           2 :   @mustCallSuper
     110             :   void onAttach() {
     111           2 :     if (this is KeyboardEvents) {
     112           0 :       RawKeyboard.instance.addListener(_handleKeyEvent);
     113             :     }
     114             :   }
     115             : 
     116             :   /// Marks game as not attached tto any widget tree.
     117             :   ///
     118             :   /// Should not be called manually.
     119           2 :   void detach() {
     120           2 :     _gameRenderBox = null;
     121           2 :     _size = null;
     122           2 :     onDetach();
     123             :   }
     124             : 
     125             :   // Called when the Game widget is detached
     126           2 :   @mustCallSuper
     127             :   void onDetach() {
     128             :     // Keeping this here, because if we leave this on HasWidgetsOverlay
     129             :     // and somebody overrides this and forgets to call the stream close
     130             :     // we can face some leaks.
     131           2 :     if (this is KeyboardEvents) {
     132           0 :       RawKeyboard.instance.removeListener(_handleKeyEvent);
     133             :     }
     134           4 :     images.clearCache();
     135             :   }
     136             : 
     137             :   /// Converts a global coordinate (i.e. w.r.t. the app itself) to a local
     138             :   /// coordinate (i.e. w.r.t. he game widget).
     139             :   /// If the widget occupies the whole app ("full screen" games), or is not
     140             :   /// attached to Flutter, this operation is the identity.
     141           4 :   Vector2 convertGlobalToLocalCoordinate(Vector2 point) {
     142           4 :     if (!isAttached) {
     143           4 :       return point.clone();
     144             :     }
     145           0 :     return _gameRenderBox!.globalToLocal(point.toOffset()).toVector2();
     146             :   }
     147             : 
     148             :   /// Converts a local coordinate (i.e. w.r.t. the game widget) to a global
     149             :   /// coordinate (i.e. w.r.t. the app itself).
     150             :   /// If the widget occupies the whole app ("full screen" games), or is not
     151             :   /// attached to Flutter, this operation is the identity.
     152           0 :   Vector2 convertLocalToGlobalCoordinate(Vector2 point) {
     153           0 :     if (!isAttached) {
     154           0 :       return point.clone();
     155             :     }
     156           0 :     return _gameRenderBox!.localToGlobal(point.toOffset()).toVector2();
     157             :   }
     158             : 
     159           0 :   @override
     160             :   Vector2 unprojectVector(Vector2 vector) => vector;
     161             : 
     162           0 :   @override
     163             :   Vector2 projectVector(Vector2 vector) => vector;
     164             : 
     165           0 :   @override
     166             :   Vector2 unscaleVector(Vector2 vector) => vector;
     167             : 
     168           0 :   @override
     169             :   Vector2 scaleVector(Vector2 vector) => vector;
     170             : 
     171             :   /// Utility method to load and cache the image for a sprite based on its options
     172           0 :   Future<Sprite> loadSprite(
     173             :     String path, {
     174             :     Vector2? srcSize,
     175             :     Vector2? srcPosition,
     176             :   }) {
     177           0 :     return Sprite.load(
     178             :       path,
     179             :       srcPosition: srcPosition,
     180             :       srcSize: srcSize,
     181           0 :       images: images,
     182             :     );
     183             :   }
     184             : 
     185             :   /// Utility method to load and cache the image for a sprite animation based on its options
     186           0 :   Future<SpriteAnimation> loadSpriteAnimation(
     187             :     String path,
     188             :     SpriteAnimationData data,
     189             :   ) {
     190           0 :     return SpriteAnimation.load(
     191             :       path,
     192             :       data,
     193           0 :       images: images,
     194             :     );
     195             :   }
     196             : 
     197             :   /// Flag to tell the game loop if it should start running upon creation
     198             :   bool runOnCreation = true;
     199             : 
     200             :   /// Pauses the engine game loop execution
     201           0 :   void pauseEngine() => pauseEngineFn?.call();
     202             : 
     203             :   /// Resumes the engine game loop execution
     204           0 :   void resumeEngine() => resumeEngineFn?.call();
     205             : 
     206             :   VoidCallback? pauseEngineFn;
     207             :   VoidCallback? resumeEngineFn;
     208             : 
     209             :   /// Use this method to load the assets need for the game instance to run
     210           8 :   Future<void> onLoad() async {}
     211             : 
     212             :   /// A property that stores an [ActiveOverlaysNotifier]
     213             :   ///
     214             :   /// This is useful to render widgets above a game, like a pause menu for example.
     215             :   /// Overlays visible or hidden via [overlays].add or [overlays].remove, respectively.
     216             :   ///
     217             :   /// Ex:
     218             :   /// ```
     219             :   /// final pauseOverlayIdentifier = 'PauseMenu';
     220             :   /// overlays.add(pauseOverlayIdentifier); // marks 'PauseMenu' to be rendered.
     221             :   /// overlays.remove(pauseOverlayIdentifier); // marks 'PauseMenu' to not be rendered.
     222             :   /// ```
     223             :   ///
     224             :   /// See also:
     225             :   /// - GameWidget
     226             :   /// - [Game.overlays]
     227             :   final overlays = ActiveOverlaysNotifier();
     228             : }
     229             : 
     230             : /// A [ChangeNotifier] used to control the visibility of overlays on a [Game] instance.
     231             : ///
     232             : /// To learn more, see:
     233             : /// - [Game.overlays]
     234             : class ActiveOverlaysNotifier extends ChangeNotifier {
     235             :   final Set<String> _activeOverlays = {};
     236             : 
     237             :   /// Mark a, overlay to be rendered.
     238             :   ///
     239             :   /// See also:
     240             :   /// - GameWidget
     241             :   /// - [Game.overlays]
     242           0 :   bool add(String overlayName) {
     243           0 :     final setChanged = _activeOverlays.add(overlayName);
     244             :     if (setChanged) {
     245           0 :       notifyListeners();
     246             :     }
     247             :     return setChanged;
     248             :   }
     249             : 
     250             :   /// Mark a, overlay to not be rendered.
     251             :   ///
     252             :   /// See also:
     253             :   /// - GameWidget
     254             :   /// - [Game.overlays]
     255           0 :   bool remove(String overlayName) {
     256           0 :     final hasRemoved = _activeOverlays.remove(overlayName);
     257             :     if (hasRemoved) {
     258           0 :       notifyListeners();
     259             :     }
     260             :     return hasRemoved;
     261             :   }
     262             : 
     263             :   /// A [Set] of the active overlay names.
     264          16 :   Set<String> get value => _activeOverlays;
     265             : 
     266             :   /// Returns if the given [overlayName] is active
     267           0 :   bool isActive(String overlayName) => _activeOverlays.contains(overlayName);
     268             : }

Generated by: LCOV version 1.15