mirror of
				https://github.com/flame-engine/flame.git
				synced 2025-11-01 01:18:38 +08:00 
			
		
		
		
	refactor classes in game.dart to separate files
This commit is contained in:
		
							
								
								
									
										4
									
								
								doc/examples/particles/.gitignore
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										4
									
								
								doc/examples/particles/.gitignore
									
									
									
									
										vendored
									
									
								
							| @ -71,3 +71,7 @@ | |||||||
| !**/ios/**/default.pbxuser | !**/ios/**/default.pbxuser | ||||||
| !**/ios/**/default.perspectivev3 | !**/ios/**/default.perspectivev3 | ||||||
| !/packages/flutter_tools/test/data/dart_dependencies_test/**/.packages | !/packages/flutter_tools/test/data/dart_dependencies_test/**/.packages | ||||||
|  |  | ||||||
|  | .flutter-plugins-dependencies | ||||||
|  | macos | ||||||
|  | test | ||||||
|  | |||||||
| @ -217,7 +217,7 @@ class MyGame extends BaseGame { | |||||||
|     return Particle.generate( |     return Particle.generate( | ||||||
|       count: 5, |       count: 5, | ||||||
|       generator: (i) => MovingParticle( |       generator: (i) => MovingParticle( | ||||||
|         curve: Interval(.2, .6, curve: Curves.easeInOutCubic), |         curve: const Interval(.2, .6, curve: Curves.easeInOutCubic), | ||||||
|         to: randomCellOffset() * .5, |         to: randomCellOffset() * .5, | ||||||
|         child: CircleParticle( |         child: CircleParticle( | ||||||
|           radius: 5 + rnd.nextDouble() * 5, |           radius: 5 + rnd.nextDouble() * 5, | ||||||
|  | |||||||
							
								
								
									
										2
									
								
								doc/examples/sprites/.gitignore
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										2
									
								
								doc/examples/sprites/.gitignore
									
									
									
									
										vendored
									
									
								
							| @ -70,7 +70,5 @@ | |||||||
| !/packages/flutter_tools/test/data/dart_dependencies_test/**/.packages | !/packages/flutter_tools/test/data/dart_dependencies_test/**/.packages | ||||||
|  |  | ||||||
| macos | macos | ||||||
|  |  | ||||||
| test | test | ||||||
|  |  | ||||||
| .flutter-plugins-dependencies | .flutter-plugins-dependencies | ||||||
|  | |||||||
							
								
								
									
										3
									
								
								example/.gitignore
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										3
									
								
								example/.gitignore
									
									
									
									
										vendored
									
									
								
							| @ -71,3 +71,6 @@ build/ | |||||||
| !/packages/flutter_tools/test/data/dart_dependencies_test/**/.packages | !/packages/flutter_tools/test/data/dart_dependencies_test/**/.packages | ||||||
|  |  | ||||||
| .flutter-plugins-dependencies | .flutter-plugins-dependencies | ||||||
|  |  | ||||||
|  | macos | ||||||
|  | test | ||||||
|  | |||||||
| @ -2,7 +2,7 @@ import 'dart:ui'; | |||||||
|  |  | ||||||
| import 'package:flame/box2d/box2d_component.dart'; | import 'package:flame/box2d/box2d_component.dart'; | ||||||
| import 'package:flame/components/component.dart'; | import 'package:flame/components/component.dart'; | ||||||
| import 'package:flame/game.dart'; | import 'package:flame/game/base_game.dart'; | ||||||
|  |  | ||||||
| class Box2DGame extends BaseGame { | class Box2DGame extends BaseGame { | ||||||
|   final Box2DComponent box; |   final Box2DComponent box; | ||||||
|  | |||||||
| @ -2,7 +2,7 @@ import 'dart:ui'; | |||||||
|  |  | ||||||
| import 'package:flame/components/mixins/has_game_ref.dart'; | import 'package:flame/components/mixins/has_game_ref.dart'; | ||||||
| import 'package:flame/components/mixins/tapable.dart'; | import 'package:flame/components/mixins/tapable.dart'; | ||||||
| import 'package:flame/game.dart'; | import 'package:flame/game/base_game.dart'; | ||||||
| import 'package:ordered_set/comparing.dart'; | import 'package:ordered_set/comparing.dart'; | ||||||
| import 'package:ordered_set/ordered_set.dart'; | import 'package:ordered_set/ordered_set.dart'; | ||||||
|  |  | ||||||
|  | |||||||
| @ -1,2 +1,3 @@ | |||||||
| // Keeping compatible with earlier versions of Flame | // Keeping compatible with earlier versions of Flame | ||||||
| export './game/game.dart'; | export './game/game.dart'; | ||||||
|  | export './game/base_game.dart'; | ||||||
|  | |||||||
							
								
								
									
										194
									
								
								lib/game/base_game.dart
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										194
									
								
								lib/game/base_game.dart
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,194 @@ | |||||||
|  | import 'dart:math' as math; | ||||||
|  | import 'dart:ui'; | ||||||
|  |  | ||||||
|  | import 'package:flame/components/composed_component.dart'; | ||||||
|  | import 'package:flutter/gestures.dart'; | ||||||
|  | import 'package:flutter/rendering.dart'; | ||||||
|  | import 'package:flutter/widgets.dart' hide WidgetBuilder; | ||||||
|  | import 'package:flutter/foundation.dart'; | ||||||
|  | import 'package:ordered_set/comparing.dart'; | ||||||
|  | import 'package:ordered_set/ordered_set.dart'; | ||||||
|  |  | ||||||
|  | import '../components/component.dart'; | ||||||
|  | import '../components/mixins/has_game_ref.dart'; | ||||||
|  | import '../components/mixins/tapable.dart'; | ||||||
|  | import '../position.dart'; | ||||||
|  | import '../gestures.dart'; | ||||||
|  | import 'game.dart'; | ||||||
|  |  | ||||||
|  |  | ||||||
|  | /// This is a more complete and opinionated implementation of Game. | ||||||
|  | /// | ||||||
|  | /// It still needs to be subclasses to add your game logic, but the [update], [render] and [resize] methods have default implementations. | ||||||
|  | /// This is the recommended structure to use for most games. | ||||||
|  | /// It is based on the Component system. | ||||||
|  | abstract class BaseGame extends Game with TapDetector { | ||||||
|  |   /// The list of components to be updated and rendered by the base game. | ||||||
|  |   OrderedSet<Component> components = | ||||||
|  |   OrderedSet(Comparing.on((c) => c.priority())); | ||||||
|  |  | ||||||
|  |   /// Components added by the [addLater] method | ||||||
|  |   final List<Component> _addLater = []; | ||||||
|  |  | ||||||
|  |   /// Current screen size, updated every resize via the [resize] method hook | ||||||
|  |   Size size; | ||||||
|  |  | ||||||
|  |   /// Camera position; every non-HUD component is translated so that the camera position is the top-left corner of the screen. | ||||||
|  |   Position camera = Position.empty(); | ||||||
|  |  | ||||||
|  |   /// List of deltas used in debug mode to calculate FPS | ||||||
|  |   final List<double> _dts = []; | ||||||
|  |  | ||||||
|  |   Iterable<Tapable> get _tapableComponents => | ||||||
|  |       components.where((c) => c is Tapable).cast(); | ||||||
|  |  | ||||||
|  |   @override | ||||||
|  |   void onTapCancel() { | ||||||
|  |     _tapableComponents.forEach((c) => c.handleTapCancel()); | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   @override | ||||||
|  |   void onTapDown(TapDownDetails details) { | ||||||
|  |     _tapableComponents.forEach((c) => c.handleTapDown(details)); | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   @override | ||||||
|  |   void onTapUp(TapUpDetails details) { | ||||||
|  |     _tapableComponents.forEach((c) => c.handleTapUp(details)); | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   /// This method is called for every component added, both via [add] and [addLater] methods. | ||||||
|  |   /// | ||||||
|  |   /// You can use this to setup your mixins, pre-calculate stuff on every component, or anything you desire. | ||||||
|  |   /// By default, this calls the first time resize for every component, so don't forget to call super.preAdd when overriding. | ||||||
|  |   @mustCallSuper | ||||||
|  |   void preAdd(Component c) { | ||||||
|  |     if (debugMode() && c is PositionComponent) { | ||||||
|  |       c.debugMode = true; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     // first time resize | ||||||
|  |     if (size != null) { | ||||||
|  |       c.resize(size); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     if (c is HasGameRef) { | ||||||
|  |       (c as HasGameRef).gameRef = this; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     if (c is ComposedComponent) { | ||||||
|  |       c.components.forEach(preAdd); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     c.onMount(); | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   /// Adds a new component to the components list. | ||||||
|  |   /// | ||||||
|  |   /// Also calls [preAdd], witch in turn sets the current size on the component (because the resize hook won't be called until a new resize happens). | ||||||
|  |   void add(Component c) { | ||||||
|  |     preAdd(c); | ||||||
|  |     components.add(c); | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   /// Registers a component to be added on the components on the next tick. | ||||||
|  |   /// | ||||||
|  |   /// Use this to add components in places where a concurrent issue with the update method might happen. | ||||||
|  |   /// Also calls [preAdd] for the component added, immediately. | ||||||
|  |   void addLater(Component c) { | ||||||
|  |     preAdd(c); | ||||||
|  |     _addLater.add(c); | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   /// This implementation of render basically calls [renderComponent] for every component, making sure the canvas is reset for each one. | ||||||
|  |   /// | ||||||
|  |   /// You can override it further to add more custom behaviour. | ||||||
|  |   /// Beware of however you are rendering components if not using this; you must be careful to save and restore the canvas to avoid components messing up with each other. | ||||||
|  |   @override | ||||||
|  |   void render(Canvas canvas) { | ||||||
|  |     canvas.save(); | ||||||
|  |     components.forEach((comp) => renderComponent(canvas, comp)); | ||||||
|  |     canvas.restore(); | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   /// This renders a single component obeying BaseGame rules. | ||||||
|  |   /// | ||||||
|  |   /// It translates the camera unless hud, call the render method and restore the canvas. | ||||||
|  |   /// This makes sure the canvas is not messed up by one component and all components render independently. | ||||||
|  |   void renderComponent(Canvas canvas, Component c) { | ||||||
|  |     if (!c.loaded()) { | ||||||
|  |       return; | ||||||
|  |     } | ||||||
|  |     if (!c.isHud()) { | ||||||
|  |       canvas.translate(-camera.x, -camera.y); | ||||||
|  |     } | ||||||
|  |     c.render(canvas); | ||||||
|  |     canvas.restore(); | ||||||
|  |     canvas.save(); | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   /// This implementation of update updates every component in the list. | ||||||
|  |   /// | ||||||
|  |   /// It also actually adds the components that were added by the [addLater] method, and remove those that are marked for destruction via the [Component.destroy] method. | ||||||
|  |   /// You can override it further to add more custom behaviour. | ||||||
|  |   @override | ||||||
|  |   void update(double t) { | ||||||
|  |     components.addAll(_addLater); | ||||||
|  |     _addLater.clear(); | ||||||
|  |  | ||||||
|  |     components.forEach((c) => c.update(t)); | ||||||
|  |     components.removeWhere((c) => c.destroy()).forEach((c) => c.onDestroy()); | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   /// This implementation of resize passes the resize call along to every component in the list, enabling each one to make their decisions as how to handle the resize. | ||||||
|  |   /// | ||||||
|  |   /// It also updates the [size] field of the class to be used by later added components and other methods. | ||||||
|  |   /// You can override it further to add more custom behaviour, but you should seriously consider calling the super implementation as well. | ||||||
|  |   @override | ||||||
|  |   @mustCallSuper | ||||||
|  |   void resize(Size size) { | ||||||
|  |     this.size = size; | ||||||
|  |     components.forEach((c) => c.resize(size)); | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   /// Returns whether this [Game] is in debug mode or not. | ||||||
|  |   /// | ||||||
|  |   /// Returns `false` by default. Override to use the debug mode. | ||||||
|  |   /// In debug mode, the [recordDt] method actually records every `dt` for statistics. | ||||||
|  |   /// Then, you can use the [fps] method to check the game FPS. | ||||||
|  |   /// You can also use this value to enable other debug behaviors for your game, like bounding box rendering, for instance. | ||||||
|  |   bool debugMode() => false; | ||||||
|  |  | ||||||
|  |   /// This is a hook that comes from the RenderBox to allow recording of render times and statistics. | ||||||
|  |   @override | ||||||
|  |   void recordDt(double dt) { | ||||||
|  |     if (debugMode()) { | ||||||
|  |       _dts.add(dt); | ||||||
|  |     } | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   /// Returns the average FPS for the last [average] measures. | ||||||
|  |   /// | ||||||
|  |   /// The values are only saved if in debug mode (override [debugMode] to use this). | ||||||
|  |   /// Selects the last [average] dts, averages then, and returns the inverse value. | ||||||
|  |   /// So it's technically updates per second, but the relation between updates and renders is 1:1. | ||||||
|  |   /// Returns 0 if empty. | ||||||
|  |   double fps([int average = 1]) { | ||||||
|  |     final List<double> dts = _dts.sublist(math.max(0, _dts.length - average)); | ||||||
|  |     if (dts.isEmpty) { | ||||||
|  |       return 0.0; | ||||||
|  |     } | ||||||
|  |     final double dtSum = dts.reduce((s, t) => s + t); | ||||||
|  |     final double averageDt = dtSum / average; | ||||||
|  |     return 1 / averageDt; | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   /// Returns the current time in seconds with microseconds precision. | ||||||
|  |   /// | ||||||
|  |   /// This is compatible with the `dt` value used in the [update] method. | ||||||
|  |   double currentTime() { | ||||||
|  |     return DateTime.now().microsecondsSinceEpoch.toDouble() / | ||||||
|  |         Duration.microsecondsPerSecond; | ||||||
|  |   } | ||||||
|  | } | ||||||
|  |  | ||||||
							
								
								
									
										38
									
								
								lib/game/embedded_game_widget.dart
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										38
									
								
								lib/game/embedded_game_widget.dart
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,38 @@ | |||||||
|  | import 'package:flutter/rendering.dart'; | ||||||
|  | import 'package:flutter/widgets.dart' hide WidgetBuilder; | ||||||
|  |  | ||||||
|  | import '../position.dart'; | ||||||
|  |  | ||||||
|  | import 'game_render_box.dart'; | ||||||
|  | import 'game.dart'; | ||||||
|  |  | ||||||
|  | /// This a widget to embed a game inside the Widget tree. You can use it in pair with [SimpleGame] or any other more complex [Game], as desired. | ||||||
|  | /// | ||||||
|  | /// It handles for you positioning, size constraints and other factors that arise when your game is embedded within the component tree. | ||||||
|  | /// Provided it with a [Game] instance for your game and the optional size of the widget. | ||||||
|  | /// Creating this without a fixed size might mess up how other components are rendered with relation to this one in the tree. | ||||||
|  | /// You can bind Gesture Recognizers immediately around this to add controls to your widgets, with easy coordinate conversions. | ||||||
|  | class EmbeddedGameWidget extends LeafRenderObjectWidget { | ||||||
|  |   final Game game; | ||||||
|  |   final Position size; | ||||||
|  |  | ||||||
|  |   EmbeddedGameWidget(this.game, {this.size}); | ||||||
|  |  | ||||||
|  |   @override | ||||||
|  |   RenderBox createRenderObject(BuildContext context) { | ||||||
|  |     return RenderConstrainedBox( | ||||||
|  |         child: GameRenderBox(context, game), | ||||||
|  |         additionalConstraints: | ||||||
|  |         BoxConstraints.expand(width: size?.x, height: size?.y)); | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   @override | ||||||
|  |   void updateRenderObject( | ||||||
|  |       BuildContext context, RenderConstrainedBox renderBox) { | ||||||
|  |     renderBox | ||||||
|  |       ..child = GameRenderBox(context, game) | ||||||
|  |       ..additionalConstraints = | ||||||
|  |       BoxConstraints.expand(width: size?.x, height: size?.y); | ||||||
|  |   } | ||||||
|  | } | ||||||
|  |  | ||||||
| @ -1,25 +1,14 @@ | |||||||
| import 'dart:math' as math; |  | ||||||
| import 'dart:ui'; | import 'dart:ui'; | ||||||
| import 'dart:async'; | import 'dart:async'; | ||||||
|  |  | ||||||
| import 'package:flame/components/composed_component.dart'; |  | ||||||
| import 'package:flutter/gestures.dart'; |  | ||||||
| import 'package:flutter/rendering.dart'; | import 'package:flutter/rendering.dart'; | ||||||
| import 'package:flutter/scheduler.dart'; | import 'package:flutter/scheduler.dart'; | ||||||
| import 'package:flutter/widgets.dart' hide WidgetBuilder; | import 'package:flutter/widgets.dart' hide WidgetBuilder; | ||||||
| import 'package:flutter/foundation.dart'; | import 'package:flutter/foundation.dart'; | ||||||
| import 'package:flutter/services.dart'; | import 'package:flutter/services.dart'; | ||||||
| import 'package:ordered_set/comparing.dart'; |  | ||||||
| import 'package:ordered_set/ordered_set.dart'; |  | ||||||
|  |  | ||||||
| import '../components/component.dart'; |  | ||||||
| import '../components/mixins/has_game_ref.dart'; |  | ||||||
| import '../components/mixins/tapable.dart'; |  | ||||||
| import '../position.dart'; |  | ||||||
| import '../gestures.dart'; |  | ||||||
| import '../keyboard.dart'; | import '../keyboard.dart'; | ||||||
|  |  | ||||||
| import 'game_loop.dart'; |  | ||||||
| import 'widget_builder.dart'; | import 'widget_builder.dart'; | ||||||
|  |  | ||||||
| /// Represents a generic game. | /// Represents a generic game. | ||||||
| @ -55,7 +44,7 @@ abstract class Game { | |||||||
|   void lifecycleStateChange(AppLifecycleState state) {} |   void lifecycleStateChange(AppLifecycleState state) {} | ||||||
|  |  | ||||||
|   /// Used for debugging |   /// Used for debugging | ||||||
|   void _recordDt(double dt) {} |   void recordDt(double dt) {} | ||||||
|  |  | ||||||
|   /// Returns the game widget. Put this in your structure to start rendering and updating the game. |   /// 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 (if you use vanilla screens and widgets). |   /// You can add it directly to the runApp method or inside your widget structure (if you use vanilla screens and widgets). | ||||||
| @ -95,13 +84,13 @@ abstract class Game { | |||||||
|   bool runOnCreation = true; |   bool runOnCreation = true; | ||||||
|  |  | ||||||
|   /// Pauses the engine game loop execution |   /// Pauses the engine game loop execution | ||||||
|   void pauseEngine() => _pauseEngineFn?.call(); |   void pauseEngine() => pauseEngineFn?.call(); | ||||||
|  |  | ||||||
|   /// Resumes the engine game loop execution |   /// Resumes the engine game loop execution | ||||||
|   void resumeEngine() => _resumeEngineFn?.call(); |   void resumeEngine() => resumeEngineFn?.call(); | ||||||
|  |  | ||||||
|   VoidCallback _pauseEngineFn; |   VoidCallback pauseEngineFn; | ||||||
|   VoidCallback _resumeEngineFn; |   VoidCallback resumeEngineFn; | ||||||
| } | } | ||||||
|  |  | ||||||
| class OverlayWidget { | class OverlayWidget { | ||||||
| @ -127,289 +116,4 @@ mixin HasWidgetsOverlay on Game { | |||||||
|   } |   } | ||||||
| } | } | ||||||
|  |  | ||||||
| /// This is a more complete and opinionated implementation of Game. |  | ||||||
| /// |  | ||||||
| /// It still needs to be subclasses to add your game logic, but the [update], [render] and [resize] methods have default implementations. |  | ||||||
| /// This is the recommended structure to use for most games. |  | ||||||
| /// It is based on the Component system. |  | ||||||
| abstract class BaseGame extends Game with TapDetector { |  | ||||||
|   /// The list of components to be updated and rendered by the base game. |  | ||||||
|   OrderedSet<Component> components = |  | ||||||
|       OrderedSet(Comparing.on((c) => c.priority())); |  | ||||||
|  |  | ||||||
|   /// Components added by the [addLater] method |  | ||||||
|   final List<Component> _addLater = []; |  | ||||||
|  |  | ||||||
|   /// Current screen size, updated every resize via the [resize] method hook |  | ||||||
|   Size size; |  | ||||||
|  |  | ||||||
|   /// Camera position; every non-HUD component is translated so that the camera position is the top-left corner of the screen. |  | ||||||
|   Position camera = Position.empty(); |  | ||||||
|  |  | ||||||
|   /// List of deltas used in debug mode to calculate FPS |  | ||||||
|   final List<double> _dts = []; |  | ||||||
|  |  | ||||||
|   Iterable<Tapable> get _tapableComponents => |  | ||||||
|       components.where((c) => c is Tapable).cast(); |  | ||||||
|  |  | ||||||
|   @override |  | ||||||
|   void onTapCancel() { |  | ||||||
|     _tapableComponents.forEach((c) => c.handleTapCancel()); |  | ||||||
|   } |  | ||||||
|  |  | ||||||
|   @override |  | ||||||
|   void onTapDown(TapDownDetails details) { |  | ||||||
|     _tapableComponents.forEach((c) => c.handleTapDown(details)); |  | ||||||
|   } |  | ||||||
|  |  | ||||||
|   @override |  | ||||||
|   void onTapUp(TapUpDetails details) { |  | ||||||
|     _tapableComponents.forEach((c) => c.handleTapUp(details)); |  | ||||||
|   } |  | ||||||
|  |  | ||||||
|   /// This method is called for every component added, both via [add] and [addLater] methods. |  | ||||||
|   /// |  | ||||||
|   /// You can use this to setup your mixins, pre-calculate stuff on every component, or anything you desire. |  | ||||||
|   /// By default, this calls the first time resize for every component, so don't forget to call super.preAdd when overriding. |  | ||||||
|   @mustCallSuper |  | ||||||
|   void preAdd(Component c) { |  | ||||||
|     if (debugMode() && c is PositionComponent) { |  | ||||||
|       c.debugMode = true; |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     // first time resize |  | ||||||
|     if (size != null) { |  | ||||||
|       c.resize(size); |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     if (c is HasGameRef) { |  | ||||||
|       (c as HasGameRef).gameRef = this; |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     if (c is ComposedComponent) { |  | ||||||
|       c.components.forEach(preAdd); |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     c.onMount(); |  | ||||||
|   } |  | ||||||
|  |  | ||||||
|   /// Adds a new component to the components list. |  | ||||||
|   /// |  | ||||||
|   /// Also calls [preAdd], witch in turn sets the current size on the component (because the resize hook won't be called until a new resize happens). |  | ||||||
|   void add(Component c) { |  | ||||||
|     preAdd(c); |  | ||||||
|     components.add(c); |  | ||||||
|   } |  | ||||||
|  |  | ||||||
|   /// Registers a component to be added on the components on the next tick. |  | ||||||
|   /// |  | ||||||
|   /// Use this to add components in places where a concurrent issue with the update method might happen. |  | ||||||
|   /// Also calls [preAdd] for the component added, immediately. |  | ||||||
|   void addLater(Component c) { |  | ||||||
|     preAdd(c); |  | ||||||
|     _addLater.add(c); |  | ||||||
|   } |  | ||||||
|  |  | ||||||
|   /// This implementation of render basically calls [renderComponent] for every component, making sure the canvas is reset for each one. |  | ||||||
|   /// |  | ||||||
|   /// You can override it further to add more custom behaviour. |  | ||||||
|   /// Beware of however you are rendering components if not using this; you must be careful to save and restore the canvas to avoid components messing up with each other. |  | ||||||
|   @override |  | ||||||
|   void render(Canvas canvas) { |  | ||||||
|     canvas.save(); |  | ||||||
|     components.forEach((comp) => renderComponent(canvas, comp)); |  | ||||||
|     canvas.restore(); |  | ||||||
|   } |  | ||||||
|  |  | ||||||
|   /// This renders a single component obeying BaseGame rules. |  | ||||||
|   /// |  | ||||||
|   /// It translates the camera unless hud, call the render method and restore the canvas. |  | ||||||
|   /// This makes sure the canvas is not messed up by one component and all components render independently. |  | ||||||
|   void renderComponent(Canvas canvas, Component c) { |  | ||||||
|     if (!c.loaded()) { |  | ||||||
|       return; |  | ||||||
|     } |  | ||||||
|     if (!c.isHud()) { |  | ||||||
|       canvas.translate(-camera.x, -camera.y); |  | ||||||
|     } |  | ||||||
|     c.render(canvas); |  | ||||||
|     canvas.restore(); |  | ||||||
|     canvas.save(); |  | ||||||
|   } |  | ||||||
|  |  | ||||||
|   /// This implementation of update updates every component in the list. |  | ||||||
|   /// |  | ||||||
|   /// It also actually adds the components that were added by the [addLater] method, and remove those that are marked for destruction via the [Component.destroy] method. |  | ||||||
|   /// You can override it further to add more custom behaviour. |  | ||||||
|   @override |  | ||||||
|   void update(double t) { |  | ||||||
|     components.addAll(_addLater); |  | ||||||
|     _addLater.clear(); |  | ||||||
|  |  | ||||||
|     components.forEach((c) => c.update(t)); |  | ||||||
|     components.removeWhere((c) => c.destroy()).forEach((c) => c.onDestroy()); |  | ||||||
|   } |  | ||||||
|  |  | ||||||
|   /// This implementation of resize passes the resize call along to every component in the list, enabling each one to make their decisions as how to handle the resize. |  | ||||||
|   /// |  | ||||||
|   /// It also updates the [size] field of the class to be used by later added components and other methods. |  | ||||||
|   /// You can override it further to add more custom behaviour, but you should seriously consider calling the super implementation as well. |  | ||||||
|   @override |  | ||||||
|   @mustCallSuper |  | ||||||
|   void resize(Size size) { |  | ||||||
|     this.size = size; |  | ||||||
|     components.forEach((c) => c.resize(size)); |  | ||||||
|   } |  | ||||||
|  |  | ||||||
|   /// Returns whether this [Game] is in debug mode or not. |  | ||||||
|   /// |  | ||||||
|   /// Returns `false` by default. Override to use the debug mode. |  | ||||||
|   /// In debug mode, the [_recordDt] method actually records every `dt` for statistics. |  | ||||||
|   /// Then, you can use the [fps] method to check the game FPS. |  | ||||||
|   /// You can also use this value to enable other debug behaviors for your game, like bounding box rendering, for instance. |  | ||||||
|   bool debugMode() => false; |  | ||||||
|  |  | ||||||
|   /// This is a hook that comes from the RenderBox to allow recording of render times and statistics. |  | ||||||
|   @override |  | ||||||
|   void _recordDt(double dt) { |  | ||||||
|     if (debugMode()) { |  | ||||||
|       _dts.add(dt); |  | ||||||
|     } |  | ||||||
|   } |  | ||||||
|  |  | ||||||
|   /// Returns the average FPS for the last [average] measures. |  | ||||||
|   /// |  | ||||||
|   /// The values are only saved if in debug mode (override [debugMode] to use this). |  | ||||||
|   /// Selects the last [average] dts, averages then, and returns the inverse value. |  | ||||||
|   /// So it's technically updates per second, but the relation between updates and renders is 1:1. |  | ||||||
|   /// Returns 0 if empty. |  | ||||||
|   double fps([int average = 1]) { |  | ||||||
|     final List<double> dts = _dts.sublist(math.max(0, _dts.length - average)); |  | ||||||
|     if (dts.isEmpty) { |  | ||||||
|       return 0.0; |  | ||||||
|     } |  | ||||||
|     final double dtSum = dts.reduce((s, t) => s + t); |  | ||||||
|     final double averageDt = dtSum / average; |  | ||||||
|     return 1 / averageDt; |  | ||||||
|   } |  | ||||||
|  |  | ||||||
|   /// Returns the current time in seconds with microseconds precision. |  | ||||||
|   /// |  | ||||||
|   /// This is compatible with the `dt` value used in the [update] method. |  | ||||||
|   double currentTime() { |  | ||||||
|     return DateTime.now().microsecondsSinceEpoch.toDouble() / |  | ||||||
|         Duration.microsecondsPerSecond; |  | ||||||
|   } |  | ||||||
| } |  | ||||||
|  |  | ||||||
| /// This is a helper implementation of a [BaseGame] designed to allow to easily create a game with a single component. |  | ||||||
| /// |  | ||||||
| /// This is useful to add sprites, animations and other Flame components "directly" to your non-game Flutter widget tree, when combined with [EmbeddedGameWidget]. |  | ||||||
| class SimpleGame extends BaseGame { |  | ||||||
|   SimpleGame(Component c) { |  | ||||||
|     add(c); |  | ||||||
|   } |  | ||||||
| } |  | ||||||
|  |  | ||||||
| /// This a widget to embed a game inside the Widget tree. You can use it in pair with [SimpleGame] or any other more complex [Game], as desired. |  | ||||||
| /// |  | ||||||
| /// It handles for you positioning, size constraints and other factors that arise when your game is embedded within the component tree. |  | ||||||
| /// Provided it with a [Game] instance for your game and the optional size of the widget. |  | ||||||
| /// Creating this without a fixed size might mess up how other components are rendered with relation to this one in the tree. |  | ||||||
| /// You can bind Gesture Recognizers immediately around this to add controls to your widgets, with easy coordinate conversions. |  | ||||||
| class EmbeddedGameWidget extends LeafRenderObjectWidget { |  | ||||||
|   final Game game; |  | ||||||
|   final Position size; |  | ||||||
|  |  | ||||||
|   EmbeddedGameWidget(this.game, {this.size}); |  | ||||||
|  |  | ||||||
|   @override |  | ||||||
|   RenderBox createRenderObject(BuildContext context) { |  | ||||||
|     return RenderConstrainedBox( |  | ||||||
|         child: GameRenderBox(context, game), |  | ||||||
|         additionalConstraints: |  | ||||||
|             BoxConstraints.expand(width: size?.x, height: size?.y)); |  | ||||||
|   } |  | ||||||
|  |  | ||||||
|   @override |  | ||||||
|   void updateRenderObject( |  | ||||||
|       BuildContext context, RenderConstrainedBox renderBox) { |  | ||||||
|     renderBox |  | ||||||
|       ..child = GameRenderBox(context, game) |  | ||||||
|       ..additionalConstraints = |  | ||||||
|           BoxConstraints.expand(width: size?.x, height: size?.y); |  | ||||||
|   } |  | ||||||
| } |  | ||||||
|  |  | ||||||
| class GameRenderBox extends RenderBox with WidgetsBindingObserver { |  | ||||||
|   BuildContext context; |  | ||||||
|   Game game; |  | ||||||
|   GameLoop gameLoop; |  | ||||||
|  |  | ||||||
|   GameRenderBox(this.context, this.game) { |  | ||||||
|     gameLoop = GameLoop(gameLoopCallback); |  | ||||||
|   } |  | ||||||
|  |  | ||||||
|   @override |  | ||||||
|   bool get sizedByParent => true; |  | ||||||
|  |  | ||||||
|   @override |  | ||||||
|   void performResize() { |  | ||||||
|     super.performResize(); |  | ||||||
|     game.resize(constraints.biggest); |  | ||||||
|   } |  | ||||||
|  |  | ||||||
|   @override |  | ||||||
|   void attach(PipelineOwner owner) { |  | ||||||
|     super.attach(owner); |  | ||||||
|     game.onAttach(); |  | ||||||
|  |  | ||||||
|     game._pauseEngineFn = gameLoop.pause; |  | ||||||
|     game._resumeEngineFn = gameLoop.resume; |  | ||||||
|  |  | ||||||
|     if (game.runOnCreation) { |  | ||||||
|       gameLoop.scheduleTick(); |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     _bindLifecycleListener(); |  | ||||||
|   } |  | ||||||
|  |  | ||||||
|   @override |  | ||||||
|   void detach() { |  | ||||||
|     super.detach(); |  | ||||||
|     game.onDetach(); |  | ||||||
|     gameLoop.unscheduleTick(); |  | ||||||
|     _unbindLifecycleListener(); |  | ||||||
|   } |  | ||||||
|  |  | ||||||
|   void gameLoopCallback(double dt) { |  | ||||||
|     if (!attached) { |  | ||||||
|       return; |  | ||||||
|     } |  | ||||||
|     game._recordDt(dt); |  | ||||||
|     game.update(dt); |  | ||||||
|     markNeedsPaint(); |  | ||||||
|   } |  | ||||||
|  |  | ||||||
|   @override |  | ||||||
|   void paint(PaintingContext context, Offset offset) { |  | ||||||
|     context.canvas.save(); |  | ||||||
|     context.canvas.translate( |  | ||||||
|         game.builder.offset.dx + offset.dx, game.builder.offset.dy + offset.dy); |  | ||||||
|     game.render(context.canvas); |  | ||||||
|     context.canvas.restore(); |  | ||||||
|   } |  | ||||||
|  |  | ||||||
|   void _bindLifecycleListener() { |  | ||||||
|     WidgetsBinding.instance.addObserver(this); |  | ||||||
|   } |  | ||||||
|  |  | ||||||
|   void _unbindLifecycleListener() { |  | ||||||
|     WidgetsBinding.instance.removeObserver(this); |  | ||||||
|   } |  | ||||||
|  |  | ||||||
|   @override |  | ||||||
|   void didChangeAppLifecycleState(AppLifecycleState state) { |  | ||||||
|     game.lifecycleStateChange(state); |  | ||||||
|   } |  | ||||||
| } |  | ||||||
|  | |||||||
							
								
								
									
										82
									
								
								lib/game/game_render_box.dart
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										82
									
								
								lib/game/game_render_box.dart
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,82 @@ | |||||||
|  | import 'dart:ui'; | ||||||
|  |  | ||||||
|  | import 'package:flutter/gestures.dart'; | ||||||
|  | import 'package:flutter/rendering.dart'; | ||||||
|  | import 'package:flutter/scheduler.dart'; | ||||||
|  | import 'package:flutter/widgets.dart' hide WidgetBuilder; | ||||||
|  |  | ||||||
|  | import 'game_loop.dart'; | ||||||
|  | import 'game.dart'; | ||||||
|  |  | ||||||
|  | class GameRenderBox extends RenderBox with WidgetsBindingObserver { | ||||||
|  |   BuildContext context; | ||||||
|  |   Game game; | ||||||
|  |   GameLoop gameLoop; | ||||||
|  |  | ||||||
|  |   GameRenderBox(this.context, this.game) { | ||||||
|  |     gameLoop = GameLoop(gameLoopCallback); | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   @override | ||||||
|  |   bool get sizedByParent => true; | ||||||
|  |  | ||||||
|  |   @override | ||||||
|  |   void performResize() { | ||||||
|  |     super.performResize(); | ||||||
|  |     game.resize(constraints.biggest); | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   @override | ||||||
|  |   void attach(PipelineOwner owner) { | ||||||
|  |     super.attach(owner); | ||||||
|  |     game.onAttach(); | ||||||
|  |  | ||||||
|  |     game.pauseEngineFn = gameLoop.pause; | ||||||
|  |     game.resumeEngineFn = gameLoop.resume; | ||||||
|  |  | ||||||
|  |     if (game.runOnCreation) { | ||||||
|  |       gameLoop.scheduleTick(); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     _bindLifecycleListener(); | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   @override | ||||||
|  |   void detach() { | ||||||
|  |     super.detach(); | ||||||
|  |     game.onDetach(); | ||||||
|  |     gameLoop.unscheduleTick(); | ||||||
|  |     _unbindLifecycleListener(); | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   void gameLoopCallback(double dt) { | ||||||
|  |     if (!attached) { | ||||||
|  |       return; | ||||||
|  |     } | ||||||
|  |     game.recordDt(dt); | ||||||
|  |     game.update(dt); | ||||||
|  |     markNeedsPaint(); | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   @override | ||||||
|  |   void paint(PaintingContext context, Offset offset) { | ||||||
|  |     context.canvas.save(); | ||||||
|  |     context.canvas.translate( | ||||||
|  |         game.builder.offset.dx + offset.dx, game.builder.offset.dy + offset.dy); | ||||||
|  |     game.render(context.canvas); | ||||||
|  |     context.canvas.restore(); | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   void _bindLifecycleListener() { | ||||||
|  |     WidgetsBinding.instance.addObserver(this); | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   void _unbindLifecycleListener() { | ||||||
|  |     WidgetsBinding.instance.removeObserver(this); | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   @override | ||||||
|  |   void didChangeAppLifecycleState(AppLifecycleState state) { | ||||||
|  |     game.lifecycleStateChange(state); | ||||||
|  |   } | ||||||
|  | } | ||||||
							
								
								
									
										12
									
								
								lib/game/simple_game.dart
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										12
									
								
								lib/game/simple_game.dart
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,12 @@ | |||||||
|  | import 'package:flame/components/component.dart'; | ||||||
|  |  | ||||||
|  | import 'base_game.dart'; | ||||||
|  |  | ||||||
|  | /// This is a helper implementation of a [BaseGame] designed to allow to easily create a game with a single component. | ||||||
|  | /// | ||||||
|  | /// This is useful to add sprites, animations and other Flame components "directly" to your non-game Flutter widget tree, when combined with [EmbeddedGameWidget]. | ||||||
|  | class SimpleGame extends BaseGame { | ||||||
|  |   SimpleGame(Component c) { | ||||||
|  |     add(c); | ||||||
|  |   } | ||||||
|  | } | ||||||
| @ -1,5 +1,6 @@ | |||||||
| import 'package:flutter/widgets.dart'; | import 'package:flutter/widgets.dart'; | ||||||
| import '../gestures.dart'; | import '../gestures.dart'; | ||||||
|  | import 'embedded_game_widget.dart'; | ||||||
| import 'game.dart'; | import 'game.dart'; | ||||||
|  |  | ||||||
| class WidgetBuilder { | class WidgetBuilder { | ||||||
| @ -9,70 +10,53 @@ class WidgetBuilder { | |||||||
|     return GestureDetector( |     return GestureDetector( | ||||||
|       // Taps |       // Taps | ||||||
|       onTap: game is TapDetector ? () => (game as TapDetector).onTap() : null, |       onTap: game is TapDetector ? () => (game as TapDetector).onTap() : null, | ||||||
|       onTapCancel: game is TapDetector |       onTapCancel: game is TapDetector ? () => (game as TapDetector).onTapCancel() : null, | ||||||
|           ? () => (game as TapDetector).onTapCancel() |       onTapDown: | ||||||
|           : null, |           game is TapDetector ? (TapDownDetails d) => (game as TapDetector).onTapDown(d) : null, | ||||||
|       onTapDown: game is TapDetector |       onTapUp: game is TapDetector ? (TapUpDetails d) => (game as TapDetector).onTapUp(d) : null, | ||||||
|           ? (TapDownDetails d) => (game as TapDetector).onTapDown(d) |  | ||||||
|           : null, |  | ||||||
|       onTapUp: game is TapDetector |  | ||||||
|           ? (TapUpDetails d) => (game as TapDetector).onTapUp(d) |  | ||||||
|           : null, |  | ||||||
|  |  | ||||||
|       // Secondary taps |       // Secondary taps | ||||||
|       onSecondaryTapDown: game is SecondaryTapDetector |       onSecondaryTapDown: game is SecondaryTapDetector | ||||||
|           ? (TapDownDetails d) => |           ? (TapDownDetails d) => (game as SecondaryTapDetector).onSecondaryTapDown(d) | ||||||
|               (game as SecondaryTapDetector).onSecondaryTapDown(d) |  | ||||||
|           : null, |           : null, | ||||||
|       onSecondaryTapUp: game is SecondaryTapDetector |       onSecondaryTapUp: game is SecondaryTapDetector | ||||||
|           ? (TapUpDetails d) => |           ? (TapUpDetails d) => (game as SecondaryTapDetector).onSecondaryTapUp(d) | ||||||
|               (game as SecondaryTapDetector).onSecondaryTapUp(d) |  | ||||||
|           : null, |           : null, | ||||||
|       onSecondaryTapCancel: game is SecondaryTapDetector |       onSecondaryTapCancel: game is SecondaryTapDetector | ||||||
|           ? () => (game as SecondaryTapDetector).onSecondaryTapCancel() |           ? () => (game as SecondaryTapDetector).onSecondaryTapCancel() | ||||||
|           : null, |           : null, | ||||||
|  |  | ||||||
|       // Double tap |       // Double tap | ||||||
|       onDoubleTap: game is DoubleTapDetector |       onDoubleTap: | ||||||
|           ? () => (game as DoubleTapDetector).onDoubleTap() |           game is DoubleTapDetector ? () => (game as DoubleTapDetector).onDoubleTap() : null, | ||||||
|           : null, |  | ||||||
|  |  | ||||||
|       // Long presses |       // Long presses | ||||||
|       onLongPress: game is LongPressDetector |       onLongPress: | ||||||
|           ? () => (game as LongPressDetector).onLongPress() |           game is LongPressDetector ? () => (game as LongPressDetector).onLongPress() : null, | ||||||
|           : null, |  | ||||||
|       onLongPressStart: game is LongPressDetector |       onLongPressStart: game is LongPressDetector | ||||||
|           ? (LongPressStartDetails d) => |           ? (LongPressStartDetails d) => (game as LongPressDetector).onLongPressStart(d) | ||||||
|               (game as LongPressDetector).onLongPressStart(d) |  | ||||||
|           : null, |           : null, | ||||||
|       onLongPressMoveUpdate: game is LongPressDetector |       onLongPressMoveUpdate: game is LongPressDetector | ||||||
|           ? (LongPressMoveUpdateDetails d) => |           ? (LongPressMoveUpdateDetails d) => (game as LongPressDetector).onLongPressMoveUpdate(d) | ||||||
|               (game as LongPressDetector).onLongPressMoveUpdate(d) |  | ||||||
|           : null, |  | ||||||
|       onLongPressUp: game is LongPressDetector |  | ||||||
|           ? () => (game as LongPressDetector).onLongPressUp() |  | ||||||
|           : null, |           : null, | ||||||
|  |       onLongPressUp: | ||||||
|  |           game is LongPressDetector ? () => (game as LongPressDetector).onLongPressUp() : null, | ||||||
|       onLongPressEnd: game is LongPressDetector |       onLongPressEnd: game is LongPressDetector | ||||||
|           ? (LongPressEndDetails d) => |           ? (LongPressEndDetails d) => (game as LongPressDetector).onLongPressEnd(d) | ||||||
|               (game as LongPressDetector).onLongPressEnd(d) |  | ||||||
|           : null, |           : null, | ||||||
|  |  | ||||||
|       // Vertical drag |       // Vertical drag | ||||||
|       onVerticalDragDown: game is VerticalDragDetector |       onVerticalDragDown: game is VerticalDragDetector | ||||||
|           ? (DragDownDetails d) => |           ? (DragDownDetails d) => (game as VerticalDragDetector).onVerticalDragDown(d) | ||||||
|               (game as VerticalDragDetector).onVerticalDragDown(d) |  | ||||||
|           : null, |           : null, | ||||||
|       onVerticalDragStart: game is VerticalDragDetector |       onVerticalDragStart: game is VerticalDragDetector | ||||||
|           ? (DragStartDetails d) => |           ? (DragStartDetails d) => (game as VerticalDragDetector).onVerticalDragStart(d) | ||||||
|               (game as VerticalDragDetector).onVerticalDragStart(d) |  | ||||||
|           : null, |           : null, | ||||||
|       onVerticalDragUpdate: game is VerticalDragDetector |       onVerticalDragUpdate: game is VerticalDragDetector | ||||||
|           ? (DragUpdateDetails d) => |           ? (DragUpdateDetails d) => (game as VerticalDragDetector).onVerticalDragUpdate(d) | ||||||
|               (game as VerticalDragDetector).onVerticalDragUpdate(d) |  | ||||||
|           : null, |           : null, | ||||||
|       onVerticalDragEnd: game is VerticalDragDetector |       onVerticalDragEnd: game is VerticalDragDetector | ||||||
|           ? (DragEndDetails d) => |           ? (DragEndDetails d) => (game as VerticalDragDetector).onVerticalDragEnd(d) | ||||||
|               (game as VerticalDragDetector).onVerticalDragEnd(d) |  | ||||||
|           : null, |           : null, | ||||||
|       onVerticalDragCancel: game is VerticalDragDetector |       onVerticalDragCancel: game is VerticalDragDetector | ||||||
|           ? () => (game as VerticalDragDetector).onVerticalDragCancel() |           ? () => (game as VerticalDragDetector).onVerticalDragCancel() | ||||||
| @ -80,20 +64,16 @@ class WidgetBuilder { | |||||||
|  |  | ||||||
|       // Horizontal drag |       // Horizontal drag | ||||||
|       onHorizontalDragDown: game is HorizontalDragDetector |       onHorizontalDragDown: game is HorizontalDragDetector | ||||||
|           ? (DragDownDetails d) => |           ? (DragDownDetails d) => (game as HorizontalDragDetector).onHorizontalDragDown(d) | ||||||
|               (game as HorizontalDragDetector).onHorizontalDragDown(d) |  | ||||||
|           : null, |           : null, | ||||||
|       onHorizontalDragStart: game is HorizontalDragDetector |       onHorizontalDragStart: game is HorizontalDragDetector | ||||||
|           ? (DragStartDetails d) => |           ? (DragStartDetails d) => (game as HorizontalDragDetector).onHorizontalDragStart(d) | ||||||
|               (game as HorizontalDragDetector).onHorizontalDragStart(d) |  | ||||||
|           : null, |           : null, | ||||||
|       onHorizontalDragUpdate: game is HorizontalDragDetector |       onHorizontalDragUpdate: game is HorizontalDragDetector | ||||||
|           ? (DragUpdateDetails d) => |           ? (DragUpdateDetails d) => (game as HorizontalDragDetector).onHorizontalDragUpdate(d) | ||||||
|               (game as HorizontalDragDetector).onHorizontalDragUpdate(d) |  | ||||||
|           : null, |           : null, | ||||||
|       onHorizontalDragEnd: game is HorizontalDragDetector |       onHorizontalDragEnd: game is HorizontalDragDetector | ||||||
|           ? (DragEndDetails d) => |           ? (DragEndDetails d) => (game as HorizontalDragDetector).onHorizontalDragEnd(d) | ||||||
|               (game as HorizontalDragDetector).onHorizontalDragEnd(d) |  | ||||||
|           : null, |           : null, | ||||||
|       onHorizontalDragCancel: game is HorizontalDragDetector |       onHorizontalDragCancel: game is HorizontalDragDetector | ||||||
|           ? () => (game as HorizontalDragDetector).onHorizontalDragCancel() |           ? () => (game as HorizontalDragDetector).onHorizontalDragCancel() | ||||||
| @ -101,38 +81,29 @@ class WidgetBuilder { | |||||||
|  |  | ||||||
|       // Force presses |       // Force presses | ||||||
|       onForcePressStart: game is ForcePressDetector |       onForcePressStart: game is ForcePressDetector | ||||||
|           ? (ForcePressDetails d) => |           ? (ForcePressDetails d) => (game as ForcePressDetector).onForcePressStart(d) | ||||||
|               (game as ForcePressDetector).onForcePressStart(d) |  | ||||||
|           : null, |           : null, | ||||||
|       onForcePressPeak: game is ForcePressDetector |       onForcePressPeak: game is ForcePressDetector | ||||||
|           ? (ForcePressDetails d) => |           ? (ForcePressDetails d) => (game as ForcePressDetector).onForcePressPeak(d) | ||||||
|               (game as ForcePressDetector).onForcePressPeak(d) |  | ||||||
|           : null, |           : null, | ||||||
|       onForcePressUpdate: game is ForcePressDetector |       onForcePressUpdate: game is ForcePressDetector | ||||||
|           ? (ForcePressDetails d) => |           ? (ForcePressDetails d) => (game as ForcePressDetector).onForcePressUpdate(d) | ||||||
|               (game as ForcePressDetector).onForcePressUpdate(d) |  | ||||||
|           : null, |           : null, | ||||||
|       onForcePressEnd: game is ForcePressDetector |       onForcePressEnd: game is ForcePressDetector | ||||||
|           ? (ForcePressDetails d) => |           ? (ForcePressDetails d) => (game as ForcePressDetector).onForcePressEnd(d) | ||||||
|               (game as ForcePressDetector).onForcePressEnd(d) |  | ||||||
|           : null, |           : null, | ||||||
|  |  | ||||||
|       // Pan |       // Pan | ||||||
|       onPanDown: game is PanDetector |       onPanDown: | ||||||
|           ? (DragDownDetails d) => (game as PanDetector).onPanDown(d) |           game is PanDetector ? (DragDownDetails d) => (game as PanDetector).onPanDown(d) : null, | ||||||
|           : null, |       onPanStart: | ||||||
|       onPanStart: game is PanDetector |           game is PanDetector ? (DragStartDetails d) => (game as PanDetector).onPanStart(d) : null, | ||||||
|           ? (DragStartDetails d) => (game as PanDetector).onPanStart(d) |  | ||||||
|           : null, |  | ||||||
|       onPanUpdate: game is PanDetector |       onPanUpdate: game is PanDetector | ||||||
|           ? (DragUpdateDetails d) => (game as PanDetector).onPanUpdate(d) |           ? (DragUpdateDetails d) => (game as PanDetector).onPanUpdate(d) | ||||||
|           : null, |           : null, | ||||||
|       onPanEnd: game is PanDetector |       onPanEnd: | ||||||
|           ? (DragEndDetails d) => (game as PanDetector).onPanEnd(d) |           game is PanDetector ? (DragEndDetails d) => (game as PanDetector).onPanEnd(d) : null, | ||||||
|           : null, |       onPanCancel: game is PanDetector ? () => (game as PanDetector).onPanCancel() : null, | ||||||
|       onPanCancel: game is PanDetector |  | ||||||
|           ? () => (game as PanDetector).onPanCancel() |  | ||||||
|           : null, |  | ||||||
|  |  | ||||||
|       // Scales |       // Scales | ||||||
|       onScaleStart: game is ScaleDetector |       onScaleStart: game is ScaleDetector | ||||||
| @ -147,9 +118,7 @@ class WidgetBuilder { | |||||||
|  |  | ||||||
|       child: Container( |       child: Container( | ||||||
|           color: game.backgroundColor(), |           color: game.backgroundColor(), | ||||||
|           child: Directionality( |           child: Directionality(textDirection: TextDirection.ltr, child: EmbeddedGameWidget(game))), | ||||||
|               textDirection: TextDirection.ltr, |  | ||||||
|               child: EmbeddedGameWidget(game))), |  | ||||||
|     ); |     ); | ||||||
|   } |   } | ||||||
| } | } | ||||||
| @ -185,8 +154,7 @@ class _OverlayGameWidgetState extends State<OverlayGameWidget> { | |||||||
|   Widget build(BuildContext context) { |   Widget build(BuildContext context) { | ||||||
|     return Directionality( |     return Directionality( | ||||||
|         textDirection: TextDirection.ltr, |         textDirection: TextDirection.ltr, | ||||||
|         child: |         child: Stack(children: [widget.gameChild, ..._overlays.values.toList()])); | ||||||
|             Stack(children: [widget.gameChild, ..._overlays.values.toList()])); |  | ||||||
|   } |   } | ||||||
| } | } | ||||||
|  |  | ||||||
| @ -197,7 +165,6 @@ class OverlayWidgetBuilder extends WidgetBuilder { | |||||||
|   Widget build(Game game) { |   Widget build(Game game) { | ||||||
|     final container = super.build(game); |     final container = super.build(game); | ||||||
|  |  | ||||||
|     return OverlayGameWidget( |     return OverlayGameWidget(gameChild: container, game: game, key: UniqueKey()); | ||||||
|         gameChild: container, game: game, key: UniqueKey()); |  | ||||||
|   } |   } | ||||||
| } | } | ||||||
|  | |||||||
| @ -6,9 +6,10 @@ import 'package:flutter/services.dart'; | |||||||
| import 'package:flutter/widgets.dart' as widgets; | import 'package:flutter/widgets.dart' as widgets; | ||||||
|  |  | ||||||
| import 'animation.dart'; | import 'animation.dart'; | ||||||
|  | import 'game/embedded_game_widget.dart'; | ||||||
|  | import 'game/simple_game.dart'; | ||||||
| import 'sprite.dart'; | import 'sprite.dart'; | ||||||
| import 'components/animation_component.dart'; | import 'components/animation_component.dart'; | ||||||
| import 'game.dart'; |  | ||||||
| import 'position.dart'; | import 'position.dart'; | ||||||
|  |  | ||||||
| /// Some utilities that did not fit anywhere else. | /// Some utilities that did not fit anywhere else. | ||||||
|  | |||||||
| @ -4,10 +4,10 @@ import 'package:flame/components/composed_component.dart'; | |||||||
| import 'package:flame/components/mixins/has_game_ref.dart'; | import 'package:flame/components/mixins/has_game_ref.dart'; | ||||||
| import 'package:flame/components/mixins/resizable.dart'; | import 'package:flame/components/mixins/resizable.dart'; | ||||||
| import 'package:flame/components/mixins/tapable.dart'; | import 'package:flame/components/mixins/tapable.dart'; | ||||||
|  | import 'package:flame/game/base_game.dart'; | ||||||
| import 'package:flutter/gestures.dart'; | import 'package:flutter/gestures.dart'; | ||||||
| import 'package:test/test.dart'; | import 'package:test/test.dart'; | ||||||
|  |  | ||||||
| import 'package:flame/game.dart'; |  | ||||||
| import 'package:flame/components/component.dart'; | import 'package:flame/components/component.dart'; | ||||||
|  |  | ||||||
| class MyGame extends BaseGame {} | class MyGame extends BaseGame {} | ||||||
|  | |||||||
| @ -1,9 +1,9 @@ | |||||||
| import 'dart:ui'; | import 'dart:ui'; | ||||||
|  |  | ||||||
| import 'package:flame/components/mixins/has_game_ref.dart'; | import 'package:flame/components/mixins/has_game_ref.dart'; | ||||||
|  | import 'package:flame/game/base_game.dart'; | ||||||
| import 'package:test/test.dart'; | import 'package:test/test.dart'; | ||||||
|  |  | ||||||
| import 'package:flame/game.dart'; |  | ||||||
| import 'package:flame/components/component.dart'; | import 'package:flame/components/component.dart'; | ||||||
|  |  | ||||||
| class MyGame extends BaseGame { | class MyGame extends BaseGame { | ||||||
|  | |||||||
| @ -1,8 +1,8 @@ | |||||||
| import 'dart:ui'; | import 'dart:ui'; | ||||||
|  |  | ||||||
|  | import 'package:flame/game/base_game.dart'; | ||||||
| import 'package:test/test.dart'; | import 'package:test/test.dart'; | ||||||
|  |  | ||||||
| import 'package:flame/game.dart'; |  | ||||||
| import 'package:flame/components/component.dart'; | import 'package:flame/components/component.dart'; | ||||||
| import 'package:flame/components/mixins/resizable.dart'; | import 'package:flame/components/mixins/resizable.dart'; | ||||||
|  |  | ||||||
|  | |||||||
		Reference in New Issue
	
	Block a user
	 Erlend Fagerheim
					Erlend Fagerheim