diff --git a/STYLEGUIDE.md b/STYLEGUIDE.md index 8a471ce40..715f1962f 100644 --- a/STYLEGUIDE.md +++ b/STYLEGUIDE.md @@ -150,3 +150,16 @@ Avoid documentation that just repeats the obvious. For example, `void doStuff()` You should use `[]` (brackets) to link dartdoc elements that can be referenced on the same file. Also, consider adding a "See also" section to element documentation. + +## Examples + +There are a few things to consider when adding an example to the examples directory: + + - Put your example in a fitting section. + - Always add a description named `description` in the game object (if you have one). + - Always add that `description` to the `info` field of the story. + - Always add the path to the source of the example in `codeLink` on the story. + - Always have the game class (if there exists one) in the top of your examples file. + - Name your game class with a relevant name and suffix it with `Example`, for example + `AnimationExample`. + - The filename should be the game's (or example's) name, for example `move_effect_example.dart` \ No newline at end of file diff --git a/examples/assets/images/animations/ember.png b/examples/assets/images/animations/ember.png new file mode 100644 index 000000000..ded814a80 Binary files /dev/null and b/examples/assets/images/animations/ember.png differ diff --git a/examples/lib/commons/ember.dart b/examples/lib/commons/ember.dart new file mode 100644 index 000000000..d24a263e0 --- /dev/null +++ b/examples/lib/commons/ember.dart @@ -0,0 +1,27 @@ +import 'package:flame/components.dart'; +import 'package:flame/effects.dart'; +import 'package:flame/game.dart'; + +class Ember extends SpriteAnimationComponent + with HasGameRef { + Ember({Vector2? position, Vector2? size, int? priority}) + : super( + position: position, + size: size ?? Vector2.all(50), + priority: priority, + anchor: Anchor.center, + ); + + @override + Future onLoad() async { + await super.onLoad(); + animation = await gameRef.loadSpriteAnimation( + 'animations/ember.png', + SpriteAnimationData.sequenced( + amount: 3, + textureSize: Vector2.all(16), + stepTime: 0.15, + ), + ); + } +} diff --git a/examples/lib/main.dart b/examples/lib/main.dart index f78358536..3528ffd5b 100644 --- a/examples/lib/main.dart +++ b/examples/lib/main.dart @@ -11,7 +11,6 @@ import 'stories/parallax/parallax.dart'; import 'stories/rendering/rendering.dart'; import 'stories/sprites/sprites.dart'; import 'stories/system/system.dart'; -import 'stories/tile_maps/tile_maps.dart'; import 'stories/utils/utils.dart'; import 'stories/widgets/widgets.dart'; @@ -22,18 +21,17 @@ void main() async { ); addAnimationStories(dashbook); - addComponentsStories(dashbook); - addCollisionDetectionStories(dashbook); - addEffectsStories(dashbook); - addTileMapStories(dashbook); - addInputStories(dashbook); - addSpritesStories(dashbook); - addRenderingStories(dashbook); - addUtilsStories(dashbook); addCameraAndViewportStories(dashbook); + addCollisionDetectionStories(dashbook); + addComponentsStories(dashbook); + addEffectsStories(dashbook); + addInputStories(dashbook); addParallaxStories(dashbook); - addWidgetsStories(dashbook); + addRenderingStories(dashbook); + addSpritesStories(dashbook); addSystemStories(dashbook); + addUtilsStories(dashbook); + addWidgetsStories(dashbook); runApp(dashbook); } diff --git a/examples/lib/stories/animations/animation_group.dart b/examples/lib/stories/animations/animation_group_example.dart similarity index 81% rename from examples/lib/stories/animations/animation_group.dart rename to examples/lib/stories/animations/animation_group_example.dart index a6632d10d..c0063c1dc 100644 --- a/examples/lib/stories/animations/animation_group.dart +++ b/examples/lib/stories/animations/animation_group_example.dart @@ -10,6 +10,14 @@ enum RobotState { } class AnimationGroupExample extends FlameGame with TapDetector { + static const description = ''' + This example shows how to create a component that can be switched between + different states to change the animation that is playing.\n\n + + Usage: Click/tap and hold the screen to change state and then let go to go + back to the original animation. + '''; + late SpriteAnimationGroupComponent robot; @override diff --git a/examples/lib/stories/animations/animations.dart b/examples/lib/stories/animations/animations.dart index c26d007dd..db7035bc3 100644 --- a/examples/lib/stories/animations/animations.dart +++ b/examples/lib/stories/animations/animations.dart @@ -2,56 +2,28 @@ import 'package:dashbook/dashbook.dart'; import 'package:flame/game.dart'; import '../../commons/commons.dart'; -import 'animation_group.dart'; -import 'aseprite.dart'; -import 'basic.dart'; - -const basicInfo = ''' -Basic example of `SpriteAnimation`s use in Flame's `FlameGame` - -The snippet shows how an animation can be loaded and added to the game -``` -class MyGame extends FlameGame { - @override - Future onLoad() async { - final animation = await loadSpriteAnimation( - 'animations/chopper.png', - SpriteAnimationData.sequenced( - amount: 4, - textureSize: Vector2.all(48), - stepTime: 0.15, - ), - ); - - final animationComponent = SpriteAnimationComponent( - animation: animation, - size: Vector2.all(100.0), - ); - - add(animationComponent); - } -} -``` - -On this example, click or touch anywhere on the screen to dynamically add animations -'''; +import 'animation_group_example.dart'; +import 'aseprite_example.dart'; +import 'basic_animation_example.dart'; void addAnimationStories(Dashbook dashbook) { dashbook.storiesOf('Animations') ..add( 'Basic Animations', - (_) => GameWidget(game: BasicAnimations()), - codeLink: baseLink('animations/basic.dart'), - info: basicInfo, + (_) => GameWidget(game: BasicAnimationsExample()), + codeLink: baseLink('animations/basic_animation_example.dart'), + info: BasicAnimationsExample.description, ) ..add( 'Group animation', (_) => GameWidget(game: AnimationGroupExample()), - codeLink: baseLink('animations/aseprite.dart'), + codeLink: baseLink('animations/aseprite_example.dart'), + info: AnimationGroupExample.description, ) ..add( 'Aseprite', - (_) => GameWidget(game: Aseprite()), - codeLink: baseLink('animations/aseprite.dart'), + (_) => GameWidget(game: AsepriteExample()), + codeLink: baseLink('animations/aseprite_example.dart'), + info: AsepriteExample.description, ); } diff --git a/examples/lib/stories/animations/aseprite.dart b/examples/lib/stories/animations/aseprite_example.dart similarity index 72% rename from examples/lib/stories/animations/aseprite.dart rename to examples/lib/stories/animations/aseprite_example.dart index 9366f4ef2..ae1c42f1a 100644 --- a/examples/lib/stories/animations/aseprite.dart +++ b/examples/lib/stories/animations/aseprite_example.dart @@ -1,7 +1,12 @@ import 'package:flame/components.dart'; import 'package:flame/game.dart'; -class Aseprite extends FlameGame { +class AsepriteExample extends FlameGame { + static const String description = ''' + This example shows how to load animations from a asprite json file and a + sprite sheet. There is no interaction on this example. + '''; + @override Future onLoad() async { await super.onLoad(); diff --git a/examples/lib/stories/animations/basic.dart b/examples/lib/stories/animations/basic_animation_example.dart similarity index 55% rename from examples/lib/stories/animations/basic.dart rename to examples/lib/stories/animations/basic_animation_example.dart index bc7d2592a..ef8d6130b 100644 --- a/examples/lib/stories/animations/basic.dart +++ b/examples/lib/stories/animations/basic_animation_example.dart @@ -4,7 +4,40 @@ import 'package:flame/components.dart'; import 'package:flame/game.dart'; import 'package:flame/input.dart'; -class BasicAnimations extends FlameGame with TapDetector { +import '../../commons/ember.dart'; + +class BasicAnimationsExample extends FlameGame with TapDetector { + static const description = ''' + Basic example of `SpriteAnimation`s use in Flame's `FlameGame`\n\n + + The snippet shows how an animation can be loaded and added to the game + ``` + class MyGame extends FlameGame { + @override + Future onLoad() async { + final animation = await loadSpriteAnimation( + 'animations/chopper.png', + SpriteAnimationData.sequenced( + amount: 4, + textureSize: Vector2.all(48), + stepTime: 0.15, + ), + ); + + final animationComponent = SpriteAnimationComponent( + animation: animation, + size: Vector2.all(100.0), + ); + + add(animationComponent); + } + } + ``` + + On this example, click or touch anywhere on the screen to dynamically add + animations. + '''; + late Image creature; @override @@ -22,12 +55,12 @@ class BasicAnimations extends FlameGame with TapDetector { ); final spriteSize = Vector2.all(100.0); - final animationComponent2 = SpriteAnimationComponent( + final animationComponent = SpriteAnimationComponent( animation: animation, size: spriteSize, ); - animationComponent2.x = size.x / 2 - spriteSize.x; - animationComponent2.y = spriteSize.y; + animationComponent.x = size.x / 2 - spriteSize.x; + animationComponent.y = spriteSize.y; final reversedAnimationComponent = SpriteAnimationComponent( animation: animation.reversed(), @@ -36,8 +69,9 @@ class BasicAnimations extends FlameGame with TapDetector { reversedAnimationComponent.x = size.x / 2; reversedAnimationComponent.y = spriteSize.y; - add(animationComponent2); + add(animationComponent); add(reversedAnimationComponent); + add(Ember()..position = size / 2); } void addAnimation(Vector2 position) { diff --git a/examples/lib/stories/camera_and_viewport/camera_and_viewport.dart b/examples/lib/stories/camera_and_viewport/camera_and_viewport.dart index bd901b945..29514415c 100644 --- a/examples/lib/stories/camera_and_viewport/camera_and_viewport.dart +++ b/examples/lib/stories/camera_and_viewport/camera_and_viewport.dart @@ -2,18 +2,18 @@ import 'package:dashbook/dashbook.dart'; import 'package:flame/game.dart'; import '../../commons/commons.dart'; -import 'coordinate_systems.dart'; -import 'fixed_resolution.dart'; -import 'follow_object.dart'; -import 'zoom.dart'; +import 'coordinate_systems_example.dart'; +import 'fixed_resolution_example.dart'; +import 'follow_component_example.dart'; +import 'zoom_example.dart'; void addCameraAndViewportStories(Dashbook dashbook) { dashbook.storiesOf('Camera & Viewport') ..add( - 'Follow Object', + 'Follow Component', (context) { return GameWidget( - game: CameraAndViewportGame( + game: FollowComponentExample( viewportResolution: Vector2( context.numberProperty('viewport width', 500), context.numberProperty('viewport height', 500), @@ -21,17 +21,14 @@ void addCameraAndViewportStories(Dashbook dashbook) { ), ); }, - codeLink: baseLink('camera_and_viewport/follow_object.dart'), - info: '' - 'Move around with W, A, S, D and notice how the camera follows the white square.\n' - 'If you collide with the blue squares, the camera reference is changed from center to topCenter.\n' - 'The blue squares can also be clicked to show how the coordinate system respects the camera transformation.', + codeLink: baseLink('camera_and_viewport/follow_component_example.dart'), + info: FollowComponentExample.description, ) ..add( 'Zoom', (context) { return GameWidget( - game: ZoomGame( + game: ZoomExample( viewportResolution: Vector2( context.numberProperty('viewport width', 500), context.numberProperty('viewport height', 500), @@ -39,16 +36,14 @@ void addCameraAndViewportStories(Dashbook dashbook) { ), ); }, - codeLink: baseLink('camera_and_viewport/zoom.dart'), - info: '' - 'On web: use scroll to zoom in and out.\n' - 'On mobile: use scale gesture to zoom in and out.', + codeLink: baseLink('camera_and_viewport/zoom_example.dart'), + info: ZoomExample.description, ) ..add( 'Fixed Resolution viewport', (context) { return GameWidget( - game: FixedResolutionGame( + game: FixedResolutionExample( viewportResolution: Vector2( context.numberProperty('viewport width', 600), context.numberProperty('viewport height', 1024), @@ -56,16 +51,13 @@ void addCameraAndViewportStories(Dashbook dashbook) { ), ); }, - codeLink: baseLink('camera_and_viewport/fixed_resolution.dart'), - info: FixedResolutionGame.info, + codeLink: baseLink('camera_and_viewport/fixed_resolution_example.dart'), + info: FixedResolutionExample.description, ) ..add( 'Coordinate Systems', (context) => CoordinateSystemsWidget(), - codeLink: baseLink('camera_and_viewport/coordinate_systems.dart'), - info: '' - 'Displays event data in all 3 coordinte systems (global, widget and game).\n' - 'Use WASD to move the camera and Q/E to zoom in/out.\n' - 'Trigger events to see the coordinates on each coordinate space.', + codeLink: baseLink('camera_and_viewport/coordinate_systems_example.dart'), + info: CoordinateSystemsExample.description, ); } diff --git a/examples/lib/stories/camera_and_viewport/coordinate_systems.dart b/examples/lib/stories/camera_and_viewport/coordinate_systems_example.dart similarity index 94% rename from examples/lib/stories/camera_and_viewport/coordinate_systems.dart rename to examples/lib/stories/camera_and_viewport/coordinate_systems_example.dart index b9363a0e3..4cda5b9c0 100644 --- a/examples/lib/stories/camera_and_viewport/coordinate_systems.dart +++ b/examples/lib/stories/camera_and_viewport/coordinate_systems_example.dart @@ -8,94 +8,21 @@ import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; import 'package:flutter/widgets.dart'; -/// A simple widget that "wraps" a Flame game with some Containers -/// on each direction (top, bottom, left and right) and allow adding -/// or removing containers. -class CoordinateSystemsWidget extends StatefulWidget { - @override - State createState() { - return _CoordinateSystemsState(); - } -} - -class _CoordinateSystemsState extends State { - /// The number of blocks in each direction (top, left, right, bottom). - List blocks = [1, 1, 1, 1]; - - @override - Widget build(BuildContext context) { - return Column( - mainAxisAlignment: MainAxisAlignment.spaceEvenly, - crossAxisAlignment: CrossAxisAlignment.stretch, - children: [ - ...createBlocks(index: 0, rotated: false, start: true), - Expanded( - child: Row( - mainAxisAlignment: MainAxisAlignment.spaceEvenly, - crossAxisAlignment: CrossAxisAlignment.stretch, - children: [ - ...createBlocks(index: 1, rotated: true, start: true), - Expanded( - child: GameWidget(game: CoordinateSystemsGame()), - ), - ...createBlocks(index: 2, rotated: true, start: false), - ], - ), - ), - ...createBlocks(index: 3, rotated: false, start: false), - ], - ); - } - - /// Just creates a list of Widgets + the "add" button - List createBlocks({ - /// Index on the [blocks] array - required int index, - - /// If true, render vertical text - required bool rotated, - - /// Whether to render the "add" button before or after - required bool start, - }) { - final add = Container( - child: Center( - child: TextButton( - child: const Text('+'), - onPressed: () => setState(() => blocks[index]++), - ), - ), - margin: const EdgeInsets.all(32), - ); - return [ - if (start) add, - for (int i = 1; i <= blocks[index]; i++) - GestureDetector( - child: Container( - child: Center( - child: RotatedBox( - quarterTurns: rotated ? 1 : 0, - child: Text('Block $i'), - ), - ), - margin: const EdgeInsets.all(32), - ), - onTap: () => setState(() => blocks[index]--), - ), - if (!start) add, - ]; - } -} - /// A game that allows for camera control and displays Tap, Drag & Scroll /// events information on the screen, to allow exploration of the 3 coordinate /// systems of Flame (global, widget, game). -class CoordinateSystemsGame extends FlameGame +class CoordinateSystemsExample extends FlameGame with MultiTouchTapDetector, MultiTouchDragDetector, ScrollDetector, KeyboardEvents { + static const String description = ''' + Displays event data in all 3 coordinate systems (global, widget and game). + Use WASD to move the camera and Q/E to zoom in/out. + Trigger events to see the coordinates on each coordinate space. + '''; + static final _borderPaint = Paint() ..style = PaintingStyle.stroke ..strokeWidth = 2 @@ -234,3 +161,82 @@ class CoordinateSystemsGame extends FlameGame return KeyEventResult.handled; } } + +/// A simple widget that "wraps" a Flame game with some Containers +/// on each direction (top, bottom, left and right) and allow adding +/// or removing containers. +class CoordinateSystemsWidget extends StatefulWidget { + @override + State createState() { + return _CoordinateSystemsState(); + } +} + +class _CoordinateSystemsState extends State { + /// The number of blocks in each direction (top, left, right, bottom). + List blocks = [1, 1, 1, 1]; + + @override + Widget build(BuildContext context) { + return Column( + mainAxisAlignment: MainAxisAlignment.spaceEvenly, + crossAxisAlignment: CrossAxisAlignment.stretch, + children: [ + ...createBlocks(index: 0, rotated: false, start: true), + Expanded( + child: Row( + mainAxisAlignment: MainAxisAlignment.spaceEvenly, + crossAxisAlignment: CrossAxisAlignment.stretch, + children: [ + ...createBlocks(index: 1, rotated: true, start: true), + Expanded( + child: GameWidget(game: CoordinateSystemsExample()), + ), + ...createBlocks(index: 2, rotated: true, start: false), + ], + ), + ), + ...createBlocks(index: 3, rotated: false, start: false), + ], + ); + } + + /// Just creates a list of Widgets + the "add" button + List createBlocks({ + /// Index on the [blocks] array + required int index, + + /// If true, render vertical text + required bool rotated, + + /// Whether to render the "add" button before or after + required bool start, + }) { + final add = Container( + child: Center( + child: TextButton( + child: const Text('+'), + onPressed: () => setState(() => blocks[index]++), + ), + ), + margin: const EdgeInsets.all(32), + ); + return [ + if (start) add, + for (int i = 1; i <= blocks[index]; i++) + GestureDetector( + child: Container( + child: Center( + child: RotatedBox( + quarterTurns: rotated ? 1 : 0, + child: Text('Block $i'), + ), + ), + margin: const EdgeInsets.all(32), + ), + onTap: () => setState(() => blocks[index]--), + ), + if (!start) add, + ]; + } +} diff --git a/examples/lib/stories/camera_and_viewport/fixed_resolution.dart b/examples/lib/stories/camera_and_viewport/fixed_resolution_example.dart similarity index 72% rename from examples/lib/stories/camera_and_viewport/fixed_resolution.dart rename to examples/lib/stories/camera_and_viewport/fixed_resolution_example.dart index 1b4920623..1536c857a 100644 --- a/examples/lib/stories/camera_and_viewport/fixed_resolution.dart +++ b/examples/lib/stories/camera_and_viewport/fixed_resolution_example.dart @@ -5,17 +5,18 @@ import 'package:flame/game.dart'; import 'package:flame/input.dart'; import 'package:flame/palette.dart'; -class FixedResolutionGame extends FlameGame with ScrollDetector, ScaleDetector { - static const info = ''' - This example shows how to create a viewport with a fixed resolution. - It is useful when you want the visible part of the game to be the same on all - devices no matter the actual screen size of the device. - Resize the window or change device orientation to see the difference. +class FixedResolutionExample extends FlameGame + with ScrollDetector, ScaleDetector { + static const description = ''' + This example shows how to create a viewport with a fixed resolution. + It is useful when you want the visible part of the game to be the same on + all devices no matter the actual screen size of the device. + Resize the window or change device orientation to see the difference. '''; final Vector2 viewportResolution; - FixedResolutionGame({ + FixedResolutionExample({ required this.viewportResolution, }); diff --git a/examples/lib/stories/camera_and_viewport/follow_component_example.dart b/examples/lib/stories/camera_and_viewport/follow_component_example.dart new file mode 100644 index 000000000..4cfded1b0 --- /dev/null +++ b/examples/lib/stories/camera_and_viewport/follow_component_example.dart @@ -0,0 +1,192 @@ +import 'dart:math'; + +import 'package:flame/components.dart'; +import 'package:flame/effects.dart'; +import 'package:flame/extensions.dart'; +import 'package:flame/game.dart'; +import 'package:flame/geometry.dart'; +import 'package:flame/input.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter/services.dart'; + +import '../../commons/ember.dart'; + +class FollowComponentExample extends FlameGame + with HasCollidables, HasTappables, HasKeyboardHandlerComponents { + static const String description = ''' + Move around with W, A, S, D and notice how the camera follows the ember + sprite.\n + If you collide with the gray squares, the camera reference is changed from + center to topCenter.\n + The gray squares can also be clicked to show how the coordinate system + respects the camera transformation. + '''; + + late MovableEmber ember; + final Vector2 viewportResolution; + + FollowComponentExample({ + required this.viewportResolution, + }); + + @override + Future onLoad() async { + await super.onLoad(); + camera.viewport = FixedResolutionViewport(viewportResolution); + add(Map()); + + add(ember = MovableEmber()); + camera.speed = 1; + camera.followComponent(ember, worldBounds: Map.bounds); + + for (var i = 0; i < 30; i++) { + add(Rock(Vector2(Map.genCoord(), Map.genCoord()))); + } + } +} + +class MovableEmber extends Ember + with HasHitboxes, Collidable, KeyboardHandler { + static const double speed = 300; + static final TextPaint textRenderer = TextPaint( + style: const TextStyle(color: Colors.white70, fontSize: 12), + ); + + final Vector2 velocity = Vector2.zero(); + late final TextComponent positionText; + late final Vector2 textPosition; + + MovableEmber() : super(priority: 2); + + @override + Future onLoad() async { + await super.onLoad(); + positionText = TextComponent( + textRenderer: textRenderer, + position: (size / 2)..y = size.y / 2 + 30, + anchor: Anchor.center, + ); + add(positionText); + addHitbox(HitboxCircle()); + } + + @override + void update(double dt) { + super.update(dt); + final deltaPosition = velocity * (speed * dt); + position.add(deltaPosition); + positionText.text = '(${x.toInt()}, ${y.toInt()})'; + } + + @override + void onCollision(Set points, Collidable other) { + if (other is Rock) { + gameRef.camera.setRelativeOffset(Anchor.topCenter); + } + } + + @override + void onCollisionEnd(Collidable other) { + if (other is Rock) { + gameRef.camera.setRelativeOffset(Anchor.center); + } + } + + @override + bool onKeyEvent(RawKeyEvent event, Set keysPressed) { + final isKeyDown = event is RawKeyDownEvent; + + final bool handled; + if (event.logicalKey == LogicalKeyboardKey.keyA) { + velocity.x = isKeyDown ? -1 : 0; + handled = true; + } else if (event.logicalKey == LogicalKeyboardKey.keyD) { + velocity.x = isKeyDown ? 1 : 0; + handled = true; + } else if (event.logicalKey == LogicalKeyboardKey.keyW) { + velocity.y = isKeyDown ? -1 : 0; + handled = true; + } else if (event.logicalKey == LogicalKeyboardKey.keyS) { + velocity.y = isKeyDown ? 1 : 0; + handled = true; + } else { + handled = false; + } + + if (handled) { + angle = -velocity.angleToSigned(Vector2(1, 0)); + return false; + } else { + return super.onKeyEvent(event, keysPressed); + } + } +} + +class Map extends Component { + static const double size = 1500; + static const Rect bounds = Rect.fromLTWH(-size, -size, 2 * size, 2 * size); + + static final Paint _paintBorder = Paint() + ..color = Colors.white12 + ..strokeWidth = 10 + ..style = PaintingStyle.stroke; + static final Paint _paintBg = Paint()..color = const Color(0xFF333333); + + static final _rng = Random(); + + late final List _paintPool; + late final List _rectPool; + + Map() : super(priority: 0) { + _paintPool = List.generate( + (size / 50).ceil(), + (_) => PaintExtension.random(rng: _rng) + ..style = PaintingStyle.stroke + ..strokeWidth = 2, + growable: false, + ); + _rectPool = List.generate( + (size / 50).ceil(), + (i) => Rect.fromCircle(center: Offset.zero, radius: size - i * 50), + growable: false, + ); + } + + @override + void render(Canvas canvas) { + canvas.drawRect(bounds, _paintBg); + canvas.drawRect(bounds, _paintBorder); + for (var i = 0; i < (size / 50).ceil(); i++) { + canvas.drawCircle(Offset.zero, size - i * 50, _paintPool[i]); + canvas.drawRect(_rectPool[i], _paintBorder); + } + } + + static double genCoord() { + return -size + _rng.nextDouble() * (2 * size); + } +} + +class Rock extends SpriteComponent + with HasGameRef, HasHitboxes, Collidable, Tappable { + Rock(Vector2 position) + : super( + position: position, + size: Vector2.all(50), + priority: 1, + ); + + @override + Future onLoad() async { + await super.onLoad(); + sprite = await gameRef.loadSprite('nine-box.png'); + //paint = Paint()..color = Colors.transparent; + addHitbox(HitboxRectangle()); + } + + @override + bool onTapDown(_) { + add(ColorEffect(color: Colors.green, duration: 0.5, isAlternating: true)); + return true; + } +} diff --git a/examples/lib/stories/camera_and_viewport/follow_object.dart b/examples/lib/stories/camera_and_viewport/follow_object.dart deleted file mode 100644 index 36a882229..000000000 --- a/examples/lib/stories/camera_and_viewport/follow_object.dart +++ /dev/null @@ -1,158 +0,0 @@ -import 'dart:math'; - -import 'package:flame/components.dart'; -import 'package:flame/game.dart'; -import 'package:flame/input.dart'; -import 'package:flutter/material.dart'; -import 'package:flutter/services.dart'; - -import '../../commons/square_component.dart'; - -final R = Random(); - -class MovableSquare extends SquareComponent - with Collidable, HasGameRef, KeyboardHandler { - static const double speed = 300; - static final TextPaint textRenderer = TextPaint( - style: const TextStyle(fontSize: 12), - ); - - final Vector2 velocity = Vector2.zero(); - late Timer timer; - - MovableSquare() : super(priority: 1); - - @override - Future onLoad() async { - await super.onLoad(); - timer = Timer(3.0) - ..stop() - ..onTick = () { - gameRef.camera.setRelativeOffset(Anchor.center); - }; - } - - @override - void update(double dt) { - super.update(dt); - timer.update(dt); - - final ds = velocity * (speed * dt); - position.add(ds); - } - - @override - void render(Canvas c) { - final text = '(${x.toInt()}, ${y.toInt()})'; - textRenderer.render(c, text, size / 2, anchor: Anchor.center); - } - - @override - void onCollision(Set points, Collidable other) { - if (other is Rock) { - gameRef.camera.setRelativeOffset(Anchor.topCenter); - timer.start(); - } - } - - @override - bool onKeyEvent(RawKeyEvent event, Set keysPressed) { - final isKeyDown = event is RawKeyDownEvent; - - if (event.logicalKey == LogicalKeyboardKey.keyA) { - velocity.x = isKeyDown ? -1 : 0; - return false; - } else if (event.logicalKey == LogicalKeyboardKey.keyD) { - velocity.x = isKeyDown ? 1 : 0; - return false; - } else if (event.logicalKey == LogicalKeyboardKey.keyW) { - velocity.y = isKeyDown ? -1 : 0; - return false; - } else if (event.logicalKey == LogicalKeyboardKey.keyS) { - velocity.y = isKeyDown ? 1 : 0; - return false; - } - - return super.onKeyEvent(event, keysPressed); - } -} - -class Map extends Component { - static const double S = 1500; - static const Rect bounds = Rect.fromLTWH(-S, -S, 2 * S, 2 * S); - - static final Paint _paintBorder = Paint() - ..color = const Color(0xFFFFFFFF) - ..strokeWidth = 10 - ..style = PaintingStyle.stroke; - static final Paint _paintBg = Paint()..color = const Color(0xFF333333); - - Map() : super(priority: 0); - - @override - void render(Canvas canvas) { - canvas.drawRect(bounds, _paintBg); - canvas.drawRect(bounds, _paintBorder); - } - - static double genCoord() { - return -S + R.nextDouble() * (2 * S); - } -} - -class Rock extends SquareComponent with Collidable, Tappable { - static final unpressedPaint = Paint()..color = const Color(0xFF2222FF); - static final pressedPaint = Paint()..color = const Color(0xFF414175); - - Rock(Vector2 position) - : super( - position: position, - size: 50, - priority: 2, - paint: unpressedPaint, - ); - - @override - bool onTapDown(_) { - paint = pressedPaint; - return true; - } - - @override - bool onTapUp(_) { - paint = unpressedPaint; - return true; - } - - @override - bool onTapCancel() { - paint = unpressedPaint; - return true; - } -} - -class CameraAndViewportGame extends FlameGame - with HasCollidables, HasTappables, HasKeyboardHandlerComponents { - late MovableSquare square; - - final Vector2 viewportResolution; - - CameraAndViewportGame({ - required this.viewportResolution, - }); - - @override - Future onLoad() async { - await super.onLoad(); - camera.viewport = FixedResolutionViewport(viewportResolution); - add(Map()); - - add(square = MovableSquare()); - camera.speed = 1; - camera.followComponent(square, worldBounds: Map.bounds); - - for (var i = 0; i < 30; i++) { - add(Rock(Vector2(Map.genCoord(), Map.genCoord()))); - } - } -} diff --git a/examples/lib/stories/camera_and_viewport/zoom.dart b/examples/lib/stories/camera_and_viewport/zoom_example.dart similarity index 83% rename from examples/lib/stories/camera_and_viewport/zoom.dart rename to examples/lib/stories/camera_and_viewport/zoom_example.dart index 6dbc13a7b..9255de6b1 100644 --- a/examples/lib/stories/camera_and_viewport/zoom.dart +++ b/examples/lib/stories/camera_and_viewport/zoom_example.dart @@ -2,13 +2,18 @@ import 'package:flame/components.dart'; import 'package:flame/game.dart'; import 'package:flame/input.dart'; -class ZoomGame extends FlameGame with ScrollDetector, ScaleDetector { +class ZoomExample extends FlameGame with ScrollDetector, ScaleDetector { + static const String description = ''' + On web: use scroll to zoom in and out.\n + On mobile: use scale gesture to zoom in and out. + '''; + final Vector2 viewportResolution; late SpriteComponent flame; Vector2? lastScale; - ZoomGame({ + ZoomExample({ required this.viewportResolution, }); diff --git a/examples/lib/stories/collision_detection/circles.dart b/examples/lib/stories/collision_detection/circles_example.dart similarity index 100% rename from examples/lib/stories/collision_detection/circles.dart rename to examples/lib/stories/collision_detection/circles_example.dart diff --git a/examples/lib/stories/collision_detection/collidable_animation.dart b/examples/lib/stories/collision_detection/collidable_animation_example.dart similarity index 100% rename from examples/lib/stories/collision_detection/collidable_animation.dart rename to examples/lib/stories/collision_detection/collidable_animation_example.dart diff --git a/examples/lib/stories/collision_detection/collision_detection.dart b/examples/lib/stories/collision_detection/collision_detection.dart index 3ca073719..aeccb8ee8 100644 --- a/examples/lib/stories/collision_detection/collision_detection.dart +++ b/examples/lib/stories/collision_detection/collision_detection.dart @@ -2,35 +2,36 @@ import 'package:dashbook/dashbook.dart'; import 'package:flame/game.dart'; import '../../commons/commons.dart'; -import 'circles.dart'; -import 'collidable_animation.dart'; -import 'multiple_shapes.dart'; -import 'simple_shapes.dart'; +import 'circles_example.dart'; +import 'collidable_animation_example.dart'; +import 'multiple_shapes_example.dart'; +import 'simple_shapes_example.dart'; void addCollisionDetectionStories(Dashbook dashbook) { dashbook.storiesOf('Collision Detection') ..add( 'Collidable AnimationComponent', (_) => GameWidget(game: CollidableAnimationExample()), - codeLink: baseLink('collision_detection/collidable_animation.dart'), + codeLink: + baseLink('collision_detection/collidable_animation_example.dart'), info: CollidableAnimationExample.description, ) ..add( 'Circles', (_) => GameWidget(game: CirclesExample()), - codeLink: baseLink('collision_detection/circles.dart'), + codeLink: baseLink('collision_detection/circles_example.dart'), info: CirclesExample.description, ) ..add( 'Multiple shapes', (_) => GameWidget(game: MultipleShapesExample()), - codeLink: baseLink('collision_detection/multiple_shapes.dart'), + codeLink: baseLink('collision_detection/multiple_shapes_example.dart'), info: MultipleShapesExample.description, ) ..add( 'Simple Shapes', (_) => GameWidget(game: SimpleShapesExample()), - codeLink: baseLink('collision_detection/simple_shapes.dart'), + codeLink: baseLink('collision_detection/simple_shapes_example.dart'), info: SimpleShapesExample.description, ); } diff --git a/examples/lib/stories/collision_detection/multiple_shapes.dart b/examples/lib/stories/collision_detection/multiple_shapes_example.dart similarity index 100% rename from examples/lib/stories/collision_detection/multiple_shapes.dart rename to examples/lib/stories/collision_detection/multiple_shapes_example.dart diff --git a/examples/lib/stories/collision_detection/simple_shapes.dart b/examples/lib/stories/collision_detection/simple_shapes_example.dart similarity index 99% rename from examples/lib/stories/collision_detection/simple_shapes.dart rename to examples/lib/stories/collision_detection/simple_shapes_example.dart index deceeff58..9c88d4374 100644 --- a/examples/lib/stories/collision_detection/simple_shapes.dart +++ b/examples/lib/stories/collision_detection/simple_shapes_example.dart @@ -17,6 +17,7 @@ class SimpleShapesExample extends FlameGame with HasTappables { tap on an already existing shape it will remove that shape and replace it with a new one. '''; + final _rng = Random(); MyShapeComponent randomShape(Vector2 position) { diff --git a/examples/lib/stories/components/components.dart b/examples/lib/stories/components/components.dart index 7d0765130..6cb161005 100644 --- a/examples/lib/stories/components/components.dart +++ b/examples/lib/stories/components/components.dart @@ -2,33 +2,35 @@ import 'package:dashbook/dashbook.dart'; import 'package:flame/game.dart'; import '../../commons/commons.dart'; -import 'composability.dart'; -import 'debug.dart'; -import 'game_in_game.dart'; -import 'priority.dart'; +import 'composability_example.dart'; +import 'debug_example.dart'; +import 'game_in_game_example.dart'; +import 'priority_example.dart'; void addComponentsStories(Dashbook dashbook) { dashbook.storiesOf('Components') ..add( 'Composability', - (_) => GameWidget(game: Composability()), - codeLink: baseLink('components/composability.dart'), + (_) => GameWidget(game: ComposabilityExample()), + codeLink: baseLink('components/composability_example.dart'), + info: ComposabilityExample.description, ) ..add( 'Priority', - (_) => GameWidget(game: Priority()), - codeLink: baseLink('components/priority.dart'), - info: priorityInfo, + (_) => GameWidget(game: PriorityExample()), + codeLink: baseLink('components/priority_example.dart'), + info: PriorityExample.description, ) ..add( 'Debug', - (_) => GameWidget(game: DebugGame()), - codeLink: baseLink('components/debug.dart'), + (_) => GameWidget(game: DebugExample()), + codeLink: baseLink('components/debug_example.dart'), + info: DebugExample.description, ) ..add( 'Game-in-game', - (_) => GameWidget(game: GameInGame()), - codeLink: baseLink('components/game_in_game.dart'), - info: gameInGameInfo, + (_) => GameWidget(game: GameInGameExample()), + codeLink: baseLink('components/game_in_game_example.dart'), + info: GameInGameExample.description, ); } diff --git a/examples/lib/stories/components/composability.dart b/examples/lib/stories/components/composability.dart deleted file mode 100644 index 85978bd6f..000000000 --- a/examples/lib/stories/components/composability.dart +++ /dev/null @@ -1,57 +0,0 @@ -import 'package:flame/components.dart'; -import 'package:flame/game.dart'; -import 'package:flame/input.dart'; - -class Square extends PositionComponent { - Square(Vector2 position, Vector2 size, {double angle = 0}) - : super( - position: position, - size: size, - angle: angle, - ); -} - -class ParentSquare extends Square with HasGameRef { - ParentSquare(Vector2 position, Vector2 size) : super(position, size); - - @override - Future onLoad() async { - super.onLoad(); - createChildren(); - } - - void createChildren() { - // All positions here are in relation to the parent's position - final children = [ - Square(Vector2(100, 100), Vector2(50, 50), angle: 2), - Square(Vector2(160, 100), Vector2(50, 50), angle: 3), - Square(Vector2(170, 150), Vector2(50, 50), angle: 4), - Square(Vector2(70, 200), Vector2(50, 50), angle: 5), - ]; - - addAll(children); - } -} - -// This class only has `HasDraggables` since the game-in-game example -// moves a draggable component to this game. -class Composability extends FlameGame with HasDraggables { - late ParentSquare parentSquare; - - @override - bool debugMode = true; - - @override - Future onLoad() async { - await super.onLoad(); - parentSquare = ParentSquare(Vector2.all(200), Vector2.all(300)) - ..anchor = Anchor.center; - add(parentSquare); - } - - @override - void update(double dt) { - super.update(dt); - parentSquare.angle += dt; - } -} diff --git a/examples/lib/stories/components/composability_example.dart b/examples/lib/stories/components/composability_example.dart new file mode 100644 index 000000000..57ce12d88 --- /dev/null +++ b/examples/lib/stories/components/composability_example.dart @@ -0,0 +1,72 @@ +import 'package:flame/components.dart'; +import 'package:flame/game.dart'; +import 'package:flame/input.dart'; + +// This class only has `HasDraggables` since the game-in-game example moves a +// draggable component to this game. +class ComposabilityExample extends FlameGame with HasDraggables { + static const String description = ''' + In this example we showcase how you can add children to a component and how + they transform together with their parent, if the parent is a + `PositionComponent`. This example is not interactive. + '''; + + late ParentSquare parentSquare; + + @override + bool debugMode = true; + + @override + Future onLoad() async { + await super.onLoad(); + parentSquare = ParentSquare(Vector2.all(200), Vector2.all(300)) + ..anchor = Anchor.center; + add(parentSquare); + } + + @override + void update(double dt) { + super.update(dt); + parentSquare.angle += dt; + } +} + +class ParentSquare extends RectangleComponent with HasGameRef { + ParentSquare(Vector2 position, Vector2 size) + : super(position: position, size: size); + + @override + Future onLoad() async { + super.onLoad(); + createChildren(); + } + + void createChildren() { + // All positions here are in relation to the parent's position + const childSize = 50.0; + final children = [ + RectangleComponent.square( + position: Vector2(100, 100), + size: childSize, + angle: 2, + ), + RectangleComponent.square( + position: Vector2(160, 100), + size: childSize, + angle: 3, + ), + RectangleComponent.square( + position: Vector2(170, 150), + size: childSize, + angle: 4, + ), + RectangleComponent.square( + position: Vector2(70, 200), + size: childSize, + angle: 5, + ), + ]; + + addAll(children); + } +} diff --git a/examples/lib/stories/components/debug.dart b/examples/lib/stories/components/debug_example.dart similarity index 72% rename from examples/lib/stories/components/debug.dart rename to examples/lib/stories/components/debug_example.dart index c6cb4b1fa..605ff2299 100644 --- a/examples/lib/stories/components/debug.dart +++ b/examples/lib/stories/components/debug_example.dart @@ -4,13 +4,60 @@ import 'package:flame/components.dart'; import 'package:flame/game.dart'; import 'package:flutter/material.dart'; -class LogoCompomnent extends SpriteComponent with HasGameRef { +class DebugExample extends FlameGame with FPSCounter { + static const String description = ''' + In this example we show what you will see when setting `debugMode = true` on + your game. It is a non-interactive example. + '''; + + static final fpsTextPaint = TextPaint( + style: const TextStyle(color: Color(0xFFFFFFFF)), + ); + + @override + bool debugMode = true; + + @override + Future onLoad() async { + await super.onLoad(); + final flameLogo = await loadSprite('flame.png'); + + final flame1 = LogoComponent(flameLogo); + flame1.x = 100; + flame1.y = 400; + + final flame2 = LogoComponent(flameLogo); + flame2.x = 100; + flame2.y = 400; + flame2.yDirection = -1; + + final flame3 = LogoComponent(flameLogo); + flame3.x = 100; + flame3.y = 400; + flame3.xDirection = -1; + + add(flame1); + add(flame2); + add(flame3); + } + + @override + void render(Canvas canvas) { + super.render(canvas); + + if (debugMode) { + fpsTextPaint.render(canvas, fps(120).toString(), Vector2(0, 50)); + } + } +} + +class LogoComponent extends SpriteComponent with HasGameRef { static const int speed = 150; int xDirection = 1; int yDirection = 1; - LogoCompomnent(Sprite sprite) : super(sprite: sprite, size: sprite.srcSize); + LogoComponent(Sprite sprite) : super(sprite: sprite, size: sprite.srcSize); @override void update(double dt) { @@ -33,45 +80,3 @@ class LogoCompomnent extends SpriteComponent with HasGameRef { } } } - -class DebugGame extends FlameGame with FPSCounter { - static final fpsTextPaint = TextPaint( - style: const TextStyle(color: Color(0xFFFFFFFF)), - ); - - @override - bool debugMode = true; - - @override - Future onLoad() async { - await super.onLoad(); - final flameLogo = await loadSprite('flame.png'); - - final flame1 = LogoCompomnent(flameLogo); - flame1.x = 100; - flame1.y = 400; - - final flame2 = LogoCompomnent(flameLogo); - flame2.x = 100; - flame2.y = 400; - flame2.yDirection = -1; - - final flame3 = LogoCompomnent(flameLogo); - flame3.x = 100; - flame3.y = 400; - flame3.xDirection = -1; - - add(flame1); - add(flame2); - add(flame3); - } - - @override - void render(Canvas canvas) { - super.render(canvas); - - if (debugMode) { - fpsTextPaint.render(canvas, fps(120).toString(), Vector2(0, 50)); - } - } -} diff --git a/examples/lib/stories/components/game_in_game.dart b/examples/lib/stories/components/game_in_game.dart deleted file mode 100644 index 71a6aac36..000000000 --- a/examples/lib/stories/components/game_in_game.dart +++ /dev/null @@ -1,46 +0,0 @@ -import 'package:flame/components.dart'; -import 'package:flame/game.dart'; - -import '../input/draggables.dart'; -import 'composability.dart'; - -const gameInGameInfo = ''' -This example shows two games having another game as a parent. -One game contains draggable components and the other is a rotating square with -other square children. -After 5 seconds, one of the components from the game with draggable squares -changes its parent from its original game to the component that is rotating. -After another 5 seconds it changes back to its original parent, and so on. -'''; - -class GameChangeTimer extends TimerComponent with HasGameRef { - GameChangeTimer() : super(period: 5, repeat: true); - - @override - void onTick() { - final child = gameRef.draggablesGame.square; - final newParent = child.parent == gameRef.draggablesGame - ? gameRef.composedGame.parentSquare - : gameRef.draggablesGame; - child.changeParent(newParent); - } -} - -class GameInGame extends FlameGame with HasDraggables { - @override - bool debugMode = true; - late final Composability composedGame; - late final DraggablesGame draggablesGame; - - @override - Future onLoad() async { - await super.onLoad(); - composedGame = Composability(); - draggablesGame = DraggablesGame(zoom: 1.0); - - await add(composedGame); - await add(draggablesGame); - - add(GameChangeTimer()); - } -} diff --git a/examples/lib/stories/components/game_in_game_example.dart b/examples/lib/stories/components/game_in_game_example.dart new file mode 100644 index 000000000..f6721e7d8 --- /dev/null +++ b/examples/lib/stories/components/game_in_game_example.dart @@ -0,0 +1,47 @@ +import 'package:flame/components.dart'; +import 'package:flame/game.dart'; + +import '../input/draggables_example.dart'; +import 'composability_example.dart'; + +class GameInGameExample extends FlameGame with HasDraggables { + static const String description = ''' + This example shows two games having another game as a parent. + One game contains draggable components and the other is a rotating square + with other square children. + After 5 seconds, one of the components from the game with draggable squares + changes its parent from its original game to the component that is rotating. + After another 5 seconds it changes back to its original parent, and so on. + '''; + + @override + bool debugMode = true; + late final ComposabilityExample composedGame; + late final DraggablesExample draggablesGame; + + @override + Future onLoad() async { + await super.onLoad(); + composedGame = ComposabilityExample(); + draggablesGame = DraggablesExample(zoom: 1.0); + + await add(composedGame); + await add(draggablesGame); + + add(GameChangeTimer()); + } +} + +class GameChangeTimer extends TimerComponent + with HasGameRef { + GameChangeTimer() : super(period: 5, repeat: true); + + @override + void onTick() { + final child = gameRef.draggablesGame.square; + final newParent = child.parent == gameRef.draggablesGame + ? gameRef.composedGame.parentSquare + : gameRef.draggablesGame; + child.changeParent(newParent); + } +} diff --git a/examples/lib/stories/components/priority.dart b/examples/lib/stories/components/priority_example.dart similarity index 53% rename from examples/lib/stories/components/priority.dart rename to examples/lib/stories/components/priority_example.dart index 3834bbbd8..db1ddf3d1 100644 --- a/examples/lib/stories/components/priority.dart +++ b/examples/lib/stories/components/priority_example.dart @@ -1,40 +1,14 @@ -import 'dart:ui'; - import 'package:flame/components.dart'; import 'package:flame/extensions.dart'; import 'package:flame/game.dart'; import 'package:flame/input.dart'; -const priorityInfo = ''' -On this example, click on the square to bring them to the front by changing the -priority. -'''; +class PriorityExample extends FlameGame with HasTappables { + static const String description = ''' + On this example, click on the square to bring them to the front by changing + the priority. + '''; -class Square extends PositionComponent with HasGameRef, Tappable { - late final Paint paint; - - Square(Vector2 position) { - this.position.setFrom(position); - size.setValues(100, 100); - paint = PaintExtension.random(withAlpha: 0.9, base: 100); - } - - @override - bool onTapDown(TapDownInfo info) { - final topComponent = gameRef.children.last; - if (topComponent != this) { - gameRef.children.changePriority(this, topComponent.priority + 1); - } - return false; - } - - @override - void render(Canvas canvas) { - canvas.drawRect(size.toRect(), paint); - } -} - -class Priority extends FlameGame with HasTappables { @override Future onLoad() async { await super.onLoad(); @@ -47,3 +21,22 @@ class Priority extends FlameGame with HasTappables { addAll(squares); } } + +class Square extends RectangleComponent + with HasGameRef, Tappable { + Square(Vector2 position) + : super( + position: position, + size: Vector2.all(100), + paint: PaintExtension.random(withAlpha: 0.9, base: 100), + ); + + @override + bool onTapDown(TapDownInfo info) { + final topComponent = gameRef.children.last; + if (topComponent != this) { + gameRef.children.changePriority(this, topComponent.priority + 1); + } + return false; + } +} diff --git a/examples/lib/stories/effects/color_effect.dart b/examples/lib/stories/effects/color_effect_example.dart similarity index 69% rename from examples/lib/stories/effects/color_effect.dart rename to examples/lib/stories/effects/color_effect_example.dart index be30d657f..8014c33df 100644 --- a/examples/lib/stories/effects/color_effect.dart +++ b/examples/lib/stories/effects/color_effect_example.dart @@ -5,7 +5,13 @@ import 'package:flame/effects.dart'; import 'package:flame/game.dart'; import 'package:flame/input.dart'; -class ColorEffectGame extends FlameGame with TapDetector { +class ColorEffectExample extends FlameGame with TapDetector { + static const String description = ''' + In this example you can see the color of the sprite (the Flame logo) pulse + towards a more green color and then back again. This is a non-interactive + example. + '''; + @override Future onLoad() async { await super.onLoad(); diff --git a/examples/lib/stories/effects/combined_effect.dart b/examples/lib/stories/effects/combined_effect_example.dart similarity index 85% rename from examples/lib/stories/effects/combined_effect.dart rename to examples/lib/stories/effects/combined_effect_example.dart index 511570d56..0a8f342c2 100644 --- a/examples/lib/stories/effects/combined_effect.dart +++ b/examples/lib/stories/effects/combined_effect_example.dart @@ -9,7 +9,12 @@ import '../../commons/square_component.dart'; final green = Paint()..color = const Color(0xAA338833); final red = Paint()..color = const Color(0xAA883333); -class CombinedEffectGame extends FlameGame with TapDetector { +class CombinedEffectExample extends FlameGame with TapDetector { + static const String description = ''' + In this example we show how multiple effects can be combined into one effect + with the `CombinedEffect`. Click anywhere on the screen to run the effect. + '''; + late SquareComponent greenSquare, redSquare; @override diff --git a/examples/lib/stories/effects/effects.dart b/examples/lib/stories/effects/effects.dart index 6ea492d84..40a13aa14 100644 --- a/examples/lib/stories/effects/effects.dart +++ b/examples/lib/stories/effects/effects.dart @@ -2,74 +2,73 @@ import 'package:dashbook/dashbook.dart'; import 'package:flame/game.dart'; import '../../commons/commons.dart'; -import 'color_effect.dart'; -import 'combined_effect.dart'; -import 'infinite_effect.dart'; -import 'move_effect.dart'; +import 'color_effect_example.dart'; +import 'combined_effect_example.dart'; +import 'infinite_effect_example.dart'; import 'move_effect_example.dart'; -import 'opacity_effect.dart'; +import 'old_move_effect_example.dart'; +import 'old_rotate_effect_example.dart'; +import 'opacity_effect_example.dart'; import 'remove_effect_example.dart'; -import 'rotate_effect.dart'; import 'rotate_effect_example.dart'; -import 'scale_effect.dart'; -import 'sequence_effect.dart'; -import 'size_effect.dart'; - -const scaleInfo = ''' -The `ScaleEffect` scales up the canvas before drawing the components and its -children. -In this example you can tap the screen and the component will scale up or down, -depending on its current state. -'''; +import 'scale_effect_example.dart'; +import 'sequence_effect_example.dart'; +import 'size_effect_example.dart'; void addEffectsStories(Dashbook dashbook) { dashbook.storiesOf('Effects') ..add( 'Size Effect', - (_) => GameWidget(game: SizeEffectGame()), - codeLink: baseLink('effects/size_effect.dart'), - info: sizeInfo, + (_) => GameWidget(game: SizeEffectExample()), + codeLink: baseLink('effects/size_effect_example.dart'), + info: SizeEffectExample.description, ) ..add( 'Scale Effect', - (_) => GameWidget(game: ScaleEffectGame()), - codeLink: baseLink('effects/scale_effect.dart'), - info: scaleInfo, + (_) => GameWidget(game: ScaleEffectExample()), + codeLink: baseLink('effects/scale_effect_example.dart'), + info: ScaleEffectExample.description, ) ..add( 'Move Effect', - (_) => GameWidget(game: MoveEffectGame()), - codeLink: baseLink('effects/move_effect.dart'), + (_) => GameWidget(game: OldMoveEffectExample()), + codeLink: baseLink('effects/old_move_effect_example.dart'), + info: OldMoveEffectExample.description, ) ..add( 'Rotate Effect', - (_) => GameWidget(game: RotateEffectGame()), - codeLink: baseLink('effects/rotate_effect.dart'), + (_) => GameWidget(game: OldRotateEffectExample()), + codeLink: baseLink('effects/old_rotate_effect_example.dart'), + info: OldRotateEffectExample.description, ) ..add( 'Sequence Effect', - (_) => GameWidget(game: SequenceEffectGame()), - codeLink: baseLink('effects/sequence_effect.dart'), + (_) => GameWidget(game: SequenceEffectExample()), + codeLink: baseLink('effects/sequence_effect_example.dart'), + info: SequenceEffectExample.description, ) ..add( 'Combined Effect', - (_) => GameWidget(game: CombinedEffectGame()), - codeLink: baseLink('effects/combined_effect.dart'), + (_) => GameWidget(game: CombinedEffectExample()), + codeLink: baseLink('effects/combined_effect_example.dart'), + info: CombinedEffectExample.description, ) ..add( 'Infinite Effect', - (_) => GameWidget(game: InfiniteEffectGame()), - codeLink: baseLink('effects/infinite_effect.dart'), + (_) => GameWidget(game: InfiniteEffectExample()), + codeLink: baseLink('effects/infinite_effect_example.dart'), + info: InfiniteEffectExample.description, ) ..add( 'Opacity Effect', - (_) => GameWidget(game: OpacityEffectGame()), - codeLink: baseLink('effects/opacity_effect.dart'), + (_) => GameWidget(game: OpacityEffectExample()), + codeLink: baseLink('effects/opacity_effect_example.dart'), + info: OpacityEffectExample.description, ) ..add( 'Color Effect', - (_) => GameWidget(game: ColorEffectGame()), - codeLink: baseLink('effects/color_effect.dart'), + (_) => GameWidget(game: ColorEffectExample()), + codeLink: baseLink('effects/color_effect_example.dart'), ) ..add( 'Move Effect (v2)', diff --git a/examples/lib/stories/effects/infinite_effect.dart b/examples/lib/stories/effects/infinite_effect_example.dart similarity index 79% rename from examples/lib/stories/effects/infinite_effect.dart rename to examples/lib/stories/effects/infinite_effect_example.dart index d313f76b3..8a488cd02 100644 --- a/examples/lib/stories/effects/infinite_effect.dart +++ b/examples/lib/stories/effects/infinite_effect_example.dart @@ -12,11 +12,12 @@ final green = Paint()..color = const Color(0xAA338833); final red = Paint()..color = const Color(0xAA883333); final orange = Paint()..color = const Color(0xAABB6633); -SquareComponent makeSquare(Paint paint) { - return SquareComponent(position: Vector2.all(100), paint: paint); -} +class InfiniteEffectExample extends FlameGame with TapDetector { + static const String description = ''' + In this example we show how effects can run in infinity with the + `isInfinite: true` argument. Click on the screen to start the effects. + '''; -class InfiniteEffectGame extends FlameGame with TapDetector { late SquareComponent greenSquare; late SquareComponent redSquare; late SquareComponent orangeSquare; @@ -24,6 +25,11 @@ class InfiniteEffectGame extends FlameGame with TapDetector { @override Future onLoad() async { await super.onLoad(); + + SquareComponent makeSquare(Paint paint) { + return SquareComponent(position: Vector2.all(100), paint: paint); + } + add(greenSquare = makeSquare(green)); add(redSquare = makeSquare(red)); add(orangeSquare = makeSquare(orange)); diff --git a/examples/lib/stories/effects/move_effect_example.dart b/examples/lib/stories/effects/move_effect_example.dart index 2043cd9b0..bde33e84b 100644 --- a/examples/lib/stories/effects/move_effect_example.dart +++ b/examples/lib/stories/effects/move_effect_example.dart @@ -29,11 +29,11 @@ class MoveEffectExample extends FlameGame { final paint1 = Paint() ..style = PaintingStyle.stroke ..strokeWidth = 5.0 - ..color = const Color(0xFFFF6622); + ..color = Colors.deepOrange; final paint2 = Paint() ..style = PaintingStyle.stroke ..strokeWidth = 5.0 - ..color = const Color(0xFF22FF66); + ..color = Colors.greenAccent; add( SquareComponent(position: Vector2(20, 50), size: 20, paint: paint1) @@ -102,7 +102,7 @@ class MoveEffectExample extends FlameGame { for (var i = 0; i < 20; i++) { add( SquareComponent(size: 10) - ..paint = (Paint()..color = const Color(0xFF00FFF7)) + ..paint = (Paint()..color = Colors.tealAccent) ..add( MoveEffect.along( path2, diff --git a/examples/lib/stories/effects/move_effect.dart b/examples/lib/stories/effects/old_move_effect_example.dart similarity index 69% rename from examples/lib/stories/effects/move_effect.dart rename to examples/lib/stories/effects/old_move_effect_example.dart index c005aa406..92955c131 100644 --- a/examples/lib/stories/effects/move_effect.dart +++ b/examples/lib/stories/effects/old_move_effect_example.dart @@ -6,7 +6,14 @@ import 'package:flame/input.dart'; import '../../commons/square_component.dart'; -class MoveEffectGame extends FlameGame with TapDetector { +class OldMoveEffectExample extends FlameGame with TapDetector { + static const String description = ''' + This example showcases the `MoveEffect`. Click somewhere on the screen and + the white square will go there and then it will follow the path that is + laid out by the white circular markers. After it has finished the path it + waits for a bit and then returns to its original position. + '''; + late SquareComponent square; final List path = [ diff --git a/examples/lib/stories/effects/rotate_effect.dart b/examples/lib/stories/effects/old_rotate_effect_example.dart similarity index 71% rename from examples/lib/stories/effects/rotate_effect.dart rename to examples/lib/stories/effects/old_rotate_effect_example.dart index ca2819fb0..d86cc2c93 100644 --- a/examples/lib/stories/effects/rotate_effect.dart +++ b/examples/lib/stories/effects/old_rotate_effect_example.dart @@ -8,7 +8,12 @@ import 'package:flutter/material.dart'; import '../../commons/square_component.dart'; -class RotateEffectGame extends FlameGame with TapDetector { +class OldRotateEffectExample extends FlameGame with TapDetector { + static const String description = ''' + In this example we simply rotate the square around it's anchor (which is in + the top left corner) when the screen is clicked. + '''; + late SquareComponent square; @override diff --git a/examples/lib/stories/effects/opacity_effect.dart b/examples/lib/stories/effects/opacity_effect_example.dart similarity index 73% rename from examples/lib/stories/effects/opacity_effect.dart rename to examples/lib/stories/effects/opacity_effect_example.dart index c165e56f4..4579f8874 100644 --- a/examples/lib/stories/effects/opacity_effect.dart +++ b/examples/lib/stories/effects/opacity_effect_example.dart @@ -3,7 +3,13 @@ import 'package:flame/effects.dart'; import 'package:flame/game.dart'; import 'package:flame/input.dart'; -class OpacityEffectGame extends FlameGame with TapDetector { +class OpacityEffectExample extends FlameGame with TapDetector { + static const String description = ''' + In this example we show how the `OpacityEffect` can be used in two ways. + The right flame will constantly pulse in and out of opacity and the left + flame will change opacity when you click the screen. + '''; + late final SpriteComponent sprite; @override @@ -26,7 +32,7 @@ class OpacityEffectGame extends FlameGame with TapDetector { )..add( OpacityEffect( opacity: 0, - duration: 0.5, + duration: 1.5, isInfinite: true, isAlternating: true, ), diff --git a/examples/lib/stories/effects/scale_effect.dart b/examples/lib/stories/effects/scale_effect_example.dart similarity index 74% rename from examples/lib/stories/effects/scale_effect.dart rename to examples/lib/stories/effects/scale_effect_example.dart index 60b937513..a73a9f3fe 100644 --- a/examples/lib/stories/effects/scale_effect.dart +++ b/examples/lib/stories/effects/scale_effect_example.dart @@ -8,7 +8,14 @@ import 'package:flutter/material.dart'; import '../../commons/square_component.dart'; -class ScaleEffectGame extends FlameGame with TapDetector { +class ScaleEffectExample extends FlameGame with TapDetector { + static const String description = ''' + The `ScaleEffect` scales up the canvas before drawing the components and its + children. + In this example you can tap the screen and the component will scale up or down, + depending on its current state. + '''; + late SquareComponent square; bool grow = true; diff --git a/examples/lib/stories/effects/sequence_effect.dart b/examples/lib/stories/effects/sequence_effect_example.dart similarity index 83% rename from examples/lib/stories/effects/sequence_effect.dart rename to examples/lib/stories/effects/sequence_effect_example.dart index 4ed6e55be..2d24715f1 100644 --- a/examples/lib/stories/effects/sequence_effect.dart +++ b/examples/lib/stories/effects/sequence_effect_example.dart @@ -8,7 +8,13 @@ import '../../commons/square_component.dart'; final green = Paint()..color = const Color(0xAA338833); -class SequenceEffectGame extends FlameGame with TapDetector { +class SequenceEffectExample extends FlameGame with TapDetector { + static const String description = ''' + In this example we show how different effects can be chained in the + `SequenceEffect`. Click anywhere on the screen to start the effects + sequence. + '''; + late SquareComponent greenSquare; @override diff --git a/examples/lib/stories/effects/size_effect.dart b/examples/lib/stories/effects/size_effect_example.dart similarity index 73% rename from examples/lib/stories/effects/size_effect.dart rename to examples/lib/stories/effects/size_effect_example.dart index 272c53e37..46f499abd 100644 --- a/examples/lib/stories/effects/size_effect.dart +++ b/examples/lib/stories/effects/size_effect_example.dart @@ -8,14 +8,14 @@ import 'package:flutter/material.dart'; import '../../commons/square_component.dart'; -const sizeInfo = ''' -The `SizeEffect` changes the size of the component, the sizes of the children -will stay the same. -In this example you can tap the screen and the component will size up or down, -depending on its current state. -'''; +class SizeEffectExample extends FlameGame with TapDetector { + static const String description = ''' + The `SizeEffect` changes the size of the component, the sizes of the + children will stay the same. + In this example you can tap the screen and the component will size up or + down, depending on its current state. + '''; -class SizeEffectGame extends FlameGame with TapDetector { late SquareComponent square; bool grow = true; diff --git a/examples/lib/stories/input/draggables.dart b/examples/lib/stories/input/draggables_example.dart similarity index 75% rename from examples/lib/stories/input/draggables.dart rename to examples/lib/stories/input/draggables_example.dart index fbd34163c..dc43a6364 100644 --- a/examples/lib/stories/input/draggables.dart +++ b/examples/lib/stories/input/draggables_example.dart @@ -4,9 +4,32 @@ import 'package:flame/game.dart'; import 'package:flame/input.dart'; import 'package:flutter/material.dart' show Colors; +import '../../commons/ember.dart'; + +class DraggablesExample extends FlameGame with HasDraggables { + static const String description = ''' + In this example we show you can use the `Draggable` mixin on + `PositionComponent`s. Drag around the Embers and see their position + changing. + '''; + + final double zoom; + late final DraggableSquare square; + + DraggablesExample({required this.zoom}); + + @override + Future onLoad() async { + await super.onLoad(); + camera.zoom = zoom; + add(square = DraggableSquare()); + add(DraggableSquare()..y = 350); + } +} + // Note: this component does not consider the possibility of multiple // simultaneous drags with different pointerIds. -class DraggableSquare extends PositionComponent with Draggable, HasGameRef { +class DraggableSquare extends Ember with Draggable { @override bool debugMode = true; @@ -21,7 +44,7 @@ class DraggableSquare extends PositionComponent with Draggable, HasGameRef { @override void update(double dt) { super.update(dt); - debugColor = isDragged && parent is DraggablesGame + debugColor = isDragged && parent is DraggablesExample ? Colors.greenAccent : Colors.purple; } @@ -34,7 +57,7 @@ class DraggableSquare extends PositionComponent with Draggable, HasGameRef { @override bool onDragUpdate(int pointerId, DragUpdateInfo info) { - if (parent is! DraggablesGame) { + if (parent is! DraggablesExample) { return true; } final dragDeltaPosition = this.dragDeltaPosition; @@ -58,18 +81,3 @@ class DraggableSquare extends PositionComponent with Draggable, HasGameRef { return false; } } - -class DraggablesGame extends FlameGame with HasDraggables { - final double zoom; - late final DraggableSquare square; - - DraggablesGame({required this.zoom}); - - @override - Future onLoad() async { - await super.onLoad(); - camera.zoom = zoom; - add(square = DraggableSquare()); - add(DraggableSquare()..y = 350); - } -} diff --git a/examples/lib/stories/input/hoverables.dart b/examples/lib/stories/input/hoverables_example.dart similarity index 78% rename from examples/lib/stories/input/hoverables.dart rename to examples/lib/stories/input/hoverables_example.dart index 13adbbaac..2a3a183d3 100644 --- a/examples/lib/stories/input/hoverables.dart +++ b/examples/lib/stories/input/hoverables_example.dart @@ -4,6 +4,25 @@ import 'package:flame/game.dart'; import 'package:flame/input.dart'; import 'package:flutter/material.dart'; +class HoverablesExample extends FlameGame with HasHoverables, TapDetector { + static const String description = ''' + This example shows how to use `Hoverable`s.\n\n + Add more squares by clicking and hover them to change their color. + '''; + + @override + Future onLoad() async { + await super.onLoad(); + add(HoverableSquare(Vector2(200, 500))); + add(HoverableSquare(Vector2(700, 300))); + } + + @override + void onTapDown(TapDownInfo info) { + add(HoverableSquare(info.eventPosition.game)); + } +} + class HoverableSquare extends PositionComponent with Hoverable { static final Paint _white = Paint()..color = const Color(0xFFFFFFFF); static final Paint _grey = Paint()..color = const Color(0xFFA5A5A5); @@ -18,17 +37,3 @@ class HoverableSquare extends PositionComponent with Hoverable { canvas.drawRect(size.toRect(), isHovered ? _grey : _white); } } - -class HoverablesGame extends FlameGame with HasHoverables, TapDetector { - @override - Future onLoad() async { - await super.onLoad(); - add(HoverableSquare(Vector2(200, 500))); - add(HoverableSquare(Vector2(700, 300))); - } - - @override - void onTapDown(TapDownInfo info) { - add(HoverableSquare(info.eventPosition.game)); - } -} diff --git a/examples/lib/stories/input/input.dart b/examples/lib/stories/input/input.dart index 809e0e010..7673e2f35 100644 --- a/examples/lib/stories/input/input.dart +++ b/examples/lib/stories/input/input.dart @@ -3,94 +3,100 @@ import 'package:flame/game.dart'; import 'package:flutter/material.dart'; import '../../commons/commons.dart'; -import 'draggables.dart'; -import 'hoverables.dart'; -import 'joystick.dart'; -import 'joystick_advanced.dart'; -import 'keyboard.dart'; -import 'mouse_cursor.dart'; -import 'mouse_movement.dart'; -import 'multitap.dart'; -import 'multitap_advanced.dart'; -import 'overlapping_tappables.dart'; -import 'scroll.dart'; -import 'tappables.dart'; +import 'draggables_example.dart'; +import 'hoverables_example.dart'; +import 'joystick_advanced_example.dart'; +import 'joystick_example.dart'; +import 'keyboard_example.dart'; +import 'mouse_cursor_example.dart'; +import 'mouse_movement_example.dart'; +import 'multitap_advanced_example.dart'; +import 'multitap_example.dart'; +import 'overlapping_tappables_example.dart'; +import 'scroll_example.dart'; +import 'tappables_example.dart'; void addInputStories(Dashbook dashbook) { dashbook.storiesOf('Input') ..add( 'Keyboard', - (_) => GameWidget(game: KeyboardGame()), - codeLink: baseLink('input/keyboard.dart'), - info: keyboardInfo, + (_) => GameWidget(game: KeyboardExample()), + codeLink: baseLink('input/keyboard_example.dart'), + info: KeyboardExample.description, ) ..add( 'Mouse Movement', - (_) => GameWidget(game: MouseMovementGame()), - codeLink: baseLink('input/mouse_movement.dart'), + (_) => GameWidget(game: MouseMovementExample()), + codeLink: baseLink('input/mouse_movement_example.dart'), + info: MouseMovementExample.description, ) ..add( 'Mouse Cursor', (_) => GameWidget( - game: MouseCursorGame(), + game: MouseCursorExample(), mouseCursor: SystemMouseCursors.move, ), - codeLink: baseLink('input/mouse_cursor.dart'), - info: ''' - Example showcasing the ability to change the game cursor in runtime - hover the little square to see the cursor changing - ''', + codeLink: baseLink('input/mouse_cursor_example.dart'), + info: MouseCursorExample.description, ) ..add( 'Scroll', - (_) => GameWidget(game: ScrollGame()), - codeLink: baseLink('input/scroll.dart'), + (_) => GameWidget(game: ScrollExample()), + codeLink: baseLink('input/scroll_example.dart'), + info: ScrollExample.description, ) ..add( 'Multitap', - (_) => GameWidget(game: MultitapGame()), - codeLink: baseLink('input/multitap.dart'), + (_) => GameWidget(game: MultitapExample()), + codeLink: baseLink('input/multitap_example.dart'), + info: MultitapExample.description, ) ..add( 'Multitap Advanced', - (_) => GameWidget(game: MultitapAdvancedGame()), - codeLink: baseLink('input/multitap_advanced.dart'), + (_) => GameWidget(game: MultitapAdvancedExample()), + codeLink: baseLink('input/multitap_advanced_example.dart'), + info: MultitapAdvancedExample.description, ) ..add( 'Tappables', - (_) => GameWidget(game: TappablesGame()), - codeLink: baseLink('input/tappables.dart'), + (_) => GameWidget(game: TappablesExample()), + codeLink: baseLink('input/tappables_example.dart'), + info: TappablesExample.description, ) ..add( - 'Overlaping Tappables', - (_) => GameWidget(game: OverlappingTappablesGame()), - codeLink: baseLink('input/overlaping_tappables.dart'), + 'Overlapping Tappables', + (_) => GameWidget(game: OverlappingTappablesExample()), + codeLink: baseLink('input/overlapping_tappables_example.dart'), + info: OverlappingTappablesExample.description, ) ..add( 'Draggables', (context) { return GameWidget( - game: DraggablesGame( + game: DraggablesExample( zoom: context.listProperty('zoom', 1, [0.5, 1, 1.5]), ), ); }, - codeLink: baseLink('input/draggables.dart'), + codeLink: baseLink('input/draggables_example.dart'), + info: DraggablesExample.description, ) ..add( 'Hoverables', - (_) => GameWidget(game: HoverablesGame()), - codeLink: baseLink('input/hoverables.dart'), - info: 'Add more squares by clicking. Hover squares to change colors.', + (_) => GameWidget(game: HoverablesExample()), + codeLink: baseLink('input/hoverables_example.dart'), + info: HoverablesExample.description, ) ..add( 'Joystick', - (_) => GameWidget(game: JoystickGame()), - codeLink: baseLink('input/joystick.dart'), + (_) => GameWidget(game: JoystickExample()), + codeLink: baseLink('input/joystick_example.dart'), + info: JoystickExample.description, ) ..add( 'Joystick Advanced', - (_) => GameWidget(game: JoystickAdvancedGame()), - codeLink: baseLink('input/joystick_advanced.dart'), + (_) => GameWidget(game: JoystickAdvancedExample()), + codeLink: baseLink('input/joystick_advanced_example.dart'), + info: JoystickAdvancedExample.description, ); } diff --git a/examples/lib/stories/input/joystick_advanced.dart b/examples/lib/stories/input/joystick_advanced_example.dart similarity index 87% rename from examples/lib/stories/input/joystick_advanced.dart rename to examples/lib/stories/input/joystick_advanced_example.dart index 84ec6acb4..9aa6dd2cb 100644 --- a/examples/lib/stories/input/joystick_advanced.dart +++ b/examples/lib/stories/input/joystick_advanced_example.dart @@ -12,7 +12,18 @@ import 'package:flutter/painting.dart'; import 'joystick_player.dart'; -class JoystickAdvancedGame extends FlameGame with HasDraggables, HasTappables { +class JoystickAdvancedExample extends FlameGame + with HasDraggables, HasTappables { + static const String description = ''' + In this example we showcase how to use the joystick by creating + `SpriteComponent`s that serve as the joystick's knob and background. + We also showcase the `HudButtonComponent` which is a button that has its + position defined by margins to the edges, which can be useful when making + controller buttons.\n\n + Steer the player by using the joystick and flip and rotate it by pressing + the buttons. + '''; + late final JoystickPlayer player; late final JoystickComponent joystick; late final TextComponent speedText; diff --git a/examples/lib/stories/input/joystick.dart b/examples/lib/stories/input/joystick_example.dart similarity index 73% rename from examples/lib/stories/input/joystick.dart rename to examples/lib/stories/input/joystick_example.dart index 47a36775f..92babfbbb 100644 --- a/examples/lib/stories/input/joystick.dart +++ b/examples/lib/stories/input/joystick_example.dart @@ -6,7 +6,13 @@ import 'package:flutter/painting.dart'; import 'joystick_player.dart'; -class JoystickGame extends FlameGame with HasDraggables { +class JoystickExample extends FlameGame with HasDraggables { + static const String description = ''' + In this example we showcase how to use the joystick by creating simple + `CircleComponent`s that serve as the joystick's knob and background. + Steer the player by using the joystick. + '''; + late final JoystickPlayer player; late final JoystickComponent joystick; diff --git a/examples/lib/stories/input/keyboard_example.dart b/examples/lib/stories/input/keyboard_example.dart new file mode 100644 index 000000000..6df6efed3 --- /dev/null +++ b/examples/lib/stories/input/keyboard_example.dart @@ -0,0 +1,57 @@ +import 'dart:ui'; + +import 'package:flame/game.dart'; +import 'package:flame/input.dart'; +import 'package:flame/palette.dart'; +import 'package:flutter/services.dart'; +import 'package:flutter/widgets.dart'; + +import '../../commons/ember.dart'; + +class KeyboardExample extends FlameGame with KeyboardEvents { + static const String description = ''' + Example showcasing how to act on keyboard events. + It also briefly showcases how to create a game without the FlameGame. + Usage: Use A S D F to steer Ember. + '''; + + static final Paint white = BasicPalette.white.paint(); + static const int speed = 200; + + late final Ember ember; + final Vector2 velocity = Vector2(0, 0); + + @override + Future onLoad() async { + await super.onLoad(); + ember = Ember(position: size / 2, size: Vector2.all(100)); + add(ember); + } + + @override + void update(double dt) { + super.update(dt); + final displacement = velocity * (speed * dt); + ember.position.add(displacement); + } + + @override + KeyEventResult onKeyEvent( + RawKeyEvent event, + Set keysPressed, + ) { + final isKeyDown = event is RawKeyDownEvent; + + if (event.logicalKey == LogicalKeyboardKey.keyA) { + velocity.x = isKeyDown ? -1 : 0; + } else if (event.logicalKey == LogicalKeyboardKey.keyD) { + velocity.x = isKeyDown ? 1 : 0; + } else if (event.logicalKey == LogicalKeyboardKey.keyW) { + velocity.y = isKeyDown ? -1 : 0; + } else if (event.logicalKey == LogicalKeyboardKey.keyS) { + velocity.y = isKeyDown ? 1 : 0; + } + + return super.onKeyEvent(event, keysPressed); + } +} diff --git a/examples/lib/stories/input/mouse_cursor.dart b/examples/lib/stories/input/mouse_cursor_example.dart similarity index 84% rename from examples/lib/stories/input/mouse_cursor.dart rename to examples/lib/stories/input/mouse_cursor_example.dart index 69e6e0fa9..0c53b216a 100644 --- a/examples/lib/stories/input/mouse_cursor.dart +++ b/examples/lib/stories/input/mouse_cursor_example.dart @@ -5,7 +5,12 @@ import 'package:flame/palette.dart'; import 'package:flutter/material.dart'; import 'package:flutter/widgets.dart'; -class MouseCursorGame extends FlameGame with MouseMovementDetector { +class MouseCursorExample extends FlameGame with MouseMovementDetector { + static const String description = ''' + Example showcasing the ability to change the game cursor in runtime + hover the little square to see the cursor changing + '''; + static const speed = 200; static final Paint _blue = BasicPalette.blue.paint(); static final Paint _white = BasicPalette.white.paint(); diff --git a/examples/lib/stories/input/mouse_movement.dart b/examples/lib/stories/input/mouse_movement_example.dart similarity index 75% rename from examples/lib/stories/input/mouse_movement.dart rename to examples/lib/stories/input/mouse_movement_example.dart index 8bd550b36..5799ca118 100644 --- a/examples/lib/stories/input/mouse_movement.dart +++ b/examples/lib/stories/input/mouse_movement_example.dart @@ -4,7 +4,13 @@ import 'package:flame/input.dart'; import 'package:flame/palette.dart'; import 'package:flutter/material.dart'; -class MouseMovementGame extends FlameGame with MouseMovementDetector { +class MouseMovementExample extends FlameGame with MouseMovementDetector { + static const String description = ''' + In this example we show how you can use `MouseMovementDetector`.\n\n + Move around the mouse on the canvas and the white square will follow it and + turn into blue if it reaches the mouse, or the edge of the canvas. + '''; + static const speed = 200; static final Paint _blue = BasicPalette.blue.paint(); static final Paint _white = BasicPalette.white.paint(); diff --git a/examples/lib/stories/input/multitap_advanced.dart b/examples/lib/stories/input/multitap_advanced_example.dart similarity index 84% rename from examples/lib/stories/input/multitap_advanced.dart rename to examples/lib/stories/input/multitap_advanced_example.dart index ddc16fe07..19515c199 100644 --- a/examples/lib/stories/input/multitap_advanced.dart +++ b/examples/lib/stories/input/multitap_advanced_example.dart @@ -5,8 +5,14 @@ import 'package:flame/palette.dart'; import 'package:flutter/material.dart'; /// Showcases how to mix two advanced detectors -class MultitapAdvancedGame extends FlameGame +class MultitapAdvancedExample extends FlameGame with MultiTouchTapDetector, MultiTouchDragDetector { + static const String description = ''' + This showcases the use of both `MultiTouchTapDetector` and + `MultiTouchDragDetector` simultaneously. Drag multiple fingers on the screen + to see rectangles of different sizes being drawn. + '''; + static final whitePaint = BasicPalette.white.paint(); static final tapSize = Vector2.all(50); diff --git a/examples/lib/stories/input/multitap.dart b/examples/lib/stories/input/multitap_example.dart similarity index 74% rename from examples/lib/stories/input/multitap.dart rename to examples/lib/stories/input/multitap_example.dart index cfd3260a6..2a1d4c5b5 100644 --- a/examples/lib/stories/input/multitap.dart +++ b/examples/lib/stories/input/multitap_example.dart @@ -5,7 +5,13 @@ import 'package:flame/palette.dart'; import 'package:flutter/material.dart'; /// Includes an example including advanced detectors -class MultitapGame extends FlameGame with MultiTouchTapDetector { +class MultitapExample extends FlameGame with MultiTouchTapDetector { + static const String description = ''' + In this example we showcase the multi touch capabilities + Touch multiple places on the screen and you will see multiple squares drawn, + one under each finger. + '''; + static final whitePaint = BasicPalette.white.paint(); static final tapSize = Vector2.all(50); diff --git a/examples/lib/stories/input/overlapping_tappables.dart b/examples/lib/stories/input/overlapping_tappables_example.dart similarity index 57% rename from examples/lib/stories/input/overlapping_tappables.dart rename to examples/lib/stories/input/overlapping_tappables_example.dart index 9cff7ff76..b6ad6413b 100644 --- a/examples/lib/stories/input/overlapping_tappables.dart +++ b/examples/lib/stories/input/overlapping_tappables_example.dart @@ -1,22 +1,30 @@ import 'package:flame/components.dart'; import 'package:flame/extensions.dart'; import 'package:flame/game.dart'; -import 'package:flutter/material.dart'; -class TappableSquare extends PositionComponent with Tappable { - Paint currentPaint; - - TappableSquare({Vector2? position}) - : currentPaint = PaintExtension.random(withAlpha: 0.9, base: 100), - super( - position: position ?? Vector2.all(100), - size: Vector2.all(100), - ); +class OverlappingTappablesExample extends FlameGame with HasTappables { + static const String description = ''' + In this example we show how you can stop event propagation to the components + by returning `false` in the overridden event handler method. In this case we + use `onTapUp`, `onTapDown` and `onTapCancel` from the `Tappable mixin. + '''; @override - void render(Canvas canvas) { - canvas.drawRect(size.toRect(), currentPaint); + Future onLoad() async { + await super.onLoad(); + add(TappableSquare(position: Vector2(100, 100))); + add(TappableSquare(position: Vector2(150, 150))); + add(TappableSquare(position: Vector2(100, 200))); } +} + +class TappableSquare extends RectangleComponent with Tappable { + TappableSquare({Vector2? position}) + : super( + position: position ?? Vector2.all(100), + size: Vector2.all(100), + paint: PaintExtension.random(withAlpha: 0.9, base: 100), + ); @override bool onTapUp(_) { @@ -34,13 +42,3 @@ class TappableSquare extends PositionComponent with Tappable { return false; } } - -class OverlappingTappablesGame extends FlameGame with HasTappables { - @override - Future onLoad() async { - await super.onLoad(); - add(TappableSquare(position: Vector2(100, 100))); - add(TappableSquare(position: Vector2(150, 150))); - add(TappableSquare(position: Vector2(100, 200))); - } -} diff --git a/examples/lib/stories/input/scroll.dart b/examples/lib/stories/input/scroll_example.dart similarity index 78% rename from examples/lib/stories/input/scroll.dart rename to examples/lib/stories/input/scroll_example.dart index bed254a38..0c65d69d5 100644 --- a/examples/lib/stories/input/scroll.dart +++ b/examples/lib/stories/input/scroll_example.dart @@ -4,7 +4,13 @@ import 'package:flame/input.dart'; import 'package:flame/palette.dart'; import 'package:flutter/material.dart'; -class ScrollGame extends FlameGame with ScrollDetector { +class ScrollExample extends FlameGame with ScrollDetector { + static const String description = ''' + In this example we show how to use the `ScrollDetector`.\n\n + Scroll within the canvas (both horizontally and vertically) and the white + square will move around. + '''; + static const speed = 2000.0; final _size = Vector2.all(50); final _paint = BasicPalette.white.paint(); diff --git a/examples/lib/stories/input/tappables.dart b/examples/lib/stories/input/tappables_example.dart similarity index 77% rename from examples/lib/stories/input/tappables.dart rename to examples/lib/stories/input/tappables_example.dart index 2181e294c..9b5d4be9c 100644 --- a/examples/lib/stories/input/tappables.dart +++ b/examples/lib/stories/input/tappables_example.dart @@ -3,6 +3,21 @@ import 'package:flame/extensions.dart'; import 'package:flame/game.dart'; import 'package:flutter/material.dart'; +class TappablesExample extends FlameGame with HasTappables { + static const String description = ''' + In this example we show the `Tappable` mixin functionality. You can add the + `Tappable` mixin to any `PositionComponent`.\n\n + Tap the squares to see them change their angle around their anchor. + '''; + + @override + Future onLoad() async { + await super.onLoad(); + add(TappableSquare()..anchor = Anchor.center); + add(TappableSquare()..y = 350); + } +} + class TappableSquare extends PositionComponent with Tappable { static final Paint _white = Paint()..color = const Color(0xFFFFFFFF); static final Paint _grey = Paint()..color = const Color(0xFFA5A5A5); @@ -39,12 +54,3 @@ class TappableSquare extends PositionComponent with Tappable { return true; } } - -class TappablesGame extends FlameGame with HasTappables { - @override - Future onLoad() async { - await super.onLoad(); - add(TappableSquare()..anchor = Anchor.center); - add(TappableSquare()..y = 350); - } -} diff --git a/examples/lib/stories/parallax/advanced.dart b/examples/lib/stories/parallax/advanced_parallax_example.dart similarity index 80% rename from examples/lib/stories/parallax/advanced.dart rename to examples/lib/stories/parallax/advanced_parallax_example.dart index f5c699cee..a8ba8ff11 100644 --- a/examples/lib/stories/parallax/advanced.dart +++ b/examples/lib/stories/parallax/advanced_parallax_example.dart @@ -2,7 +2,11 @@ import 'package:flame/components.dart'; import 'package:flame/game.dart'; import 'package:flame/parallax.dart'; -class AdvancedParallaxGame extends FlameGame { +class AdvancedParallaxExample extends FlameGame { + static const String description = ''' + Shows how to create a parallax with different velocity deltas on each layer. + '''; + final _layersMeta = { 'parallax/bg.png': 1.0, 'parallax/mountain-far.png': 1.5, diff --git a/examples/lib/stories/parallax/animation.dart b/examples/lib/stories/parallax/animation_parallax_example.dart similarity index 87% rename from examples/lib/stories/parallax/animation.dart rename to examples/lib/stories/parallax/animation_parallax_example.dart index fe9b1ddc8..c37641de5 100644 --- a/examples/lib/stories/parallax/animation.dart +++ b/examples/lib/stories/parallax/animation_parallax_example.dart @@ -3,7 +3,11 @@ import 'package:flame/game.dart'; import 'package:flame/parallax.dart'; import 'package:flutter/painting.dart'; -class AnimationParallaxGame extends FlameGame { +class AnimationParallaxExample extends FlameGame { + static const String description = ''' + Shows how to use animations in a `ParallaxComponent`. + '''; + @override Future onLoad() async { await super.onLoad(); diff --git a/examples/lib/stories/parallax/basic.dart b/examples/lib/stories/parallax/basic_parallax_example.dart similarity index 79% rename from examples/lib/stories/parallax/basic.dart rename to examples/lib/stories/parallax/basic_parallax_example.dart index dbe705bc1..ea95481c3 100644 --- a/examples/lib/stories/parallax/basic.dart +++ b/examples/lib/stories/parallax/basic_parallax_example.dart @@ -2,7 +2,11 @@ import 'package:flame/components.dart'; import 'package:flame/game.dart'; import 'package:flame/parallax.dart'; -class BasicParallaxGame extends FlameGame { +class BasicParallaxExample extends FlameGame { + static const String description = ''' + Shows the simplest way to use a fullscreen `ParallaxComponent`. + '''; + final _imageNames = [ ParallaxImageData('parallax/bg.png'), ParallaxImageData('parallax/mountain-far.png'), diff --git a/examples/lib/stories/parallax/component.dart b/examples/lib/stories/parallax/component_parallax_example.dart similarity index 78% rename from examples/lib/stories/parallax/component.dart rename to examples/lib/stories/parallax/component_parallax_example.dart index 83ddd9d42..62ffe22ff 100644 --- a/examples/lib/stories/parallax/component.dart +++ b/examples/lib/stories/parallax/component_parallax_example.dart @@ -2,7 +2,12 @@ import 'package:flame/components.dart'; import 'package:flame/game.dart'; import 'package:flame/parallax.dart'; -class ComponentParallaxGame extends FlameGame { +class ComponentParallaxExample extends FlameGame { + static const String description = ''' + Shows how to do initiation and loading of assets from within an extended + `ParallaxComponent`, + '''; + @override Future onLoad() async { await super.onLoad(); @@ -10,7 +15,7 @@ class ComponentParallaxGame extends FlameGame { } } -class MyParallaxComponent extends ParallaxComponent { +class MyParallaxComponent extends ParallaxComponent { @override Future onLoad() async { await super.onLoad(); diff --git a/examples/lib/stories/parallax/no_fcs.dart b/examples/lib/stories/parallax/no_fcs_parallax_example.dart similarity index 68% rename from examples/lib/stories/parallax/no_fcs.dart rename to examples/lib/stories/parallax/no_fcs_parallax_example.dart index f21beb195..d1bca73f7 100644 --- a/examples/lib/stories/parallax/no_fcs.dart +++ b/examples/lib/stories/parallax/no_fcs_parallax_example.dart @@ -4,12 +4,15 @@ import 'package:flame/game.dart'; import 'package:flame/parallax.dart'; import 'package:flutter/material.dart'; -/// This examples serves to test the Parallax feature outside of the -/// Flame Component System (FCS), use the other files in this folder -/// for examples on how to use parallax with FCS -/// FCS is only used when you extend FlameGame, not Game, -/// like we do in this example. -class NoFCSParallaxGame with Loadable, Game { +class NoFCSParallaxExample with Loadable, Game { + static const String description = ''' + This examples serves to test the Parallax feature outside of the Flame + Component System (FCS), use the other files in this folder for examples on + how to use parallax with FCS.\n + FCS is only used when you extend FlameGame, not when you only use the Game + mixin, like we do in this example. + '''; + late Parallax parallax; @override diff --git a/examples/lib/stories/parallax/parallax.dart b/examples/lib/stories/parallax/parallax.dart index c33ac5461..b2de15f4e 100644 --- a/examples/lib/stories/parallax/parallax.dart +++ b/examples/lib/stories/parallax/parallax.dart @@ -4,60 +4,57 @@ import 'package:flame/parallax.dart'; import 'package:flutter/painting.dart'; import '../../commons/commons.dart'; -import 'advanced.dart'; -import 'animation.dart'; -import 'basic.dart'; -import 'component.dart'; -import 'no_fcs.dart'; -import 'sandbox_layer.dart'; -import 'small_parallax.dart'; +import 'advanced_parallax_example.dart'; +import 'animation_parallax_example.dart'; +import 'basic_parallax_example.dart'; +import 'component_parallax_example.dart'; +import 'no_fcs_parallax_example.dart'; +import 'sandbox_layer_parallax_example.dart'; +import 'small_parallax_example.dart'; void addParallaxStories(Dashbook dashbook) { dashbook.storiesOf('Parallax') ..add( 'Basic', - (_) => GameWidget(game: BasicParallaxGame()), - codeLink: baseLink('parallax/basic.dart'), - info: 'Shows the simplest way to use a fullscreen ParallaxComponent', + (_) => GameWidget(game: BasicParallaxExample()), + codeLink: baseLink('parallax/basic_animation_example.dart'), + info: BasicParallaxExample.description, ) ..add( 'Component', - (_) => GameWidget(game: ComponentParallaxGame()), - codeLink: baseLink('parallax/component.dart'), - info: 'Shows how to do initiation and loading of assets from within an ' - 'extended ParallaxComponent', + (_) => GameWidget(game: ComponentParallaxExample()), + codeLink: baseLink('parallax/component_parallax_example.dart'), + info: ComponentParallaxExample.description, ) ..add( 'Animation', - (_) => GameWidget(game: AnimationParallaxGame()), - codeLink: baseLink('parallax/animation.dart'), - info: 'Shows how to use animations in a parallax', + (_) => GameWidget(game: AnimationParallaxExample()), + codeLink: baseLink('parallax/animation_parallax_example.dart'), + info: AnimationParallaxExample.description, ) ..add( 'Non-fullscreen', - (_) => GameWidget(game: SmallParallaxGame()), - codeLink: baseLink('parallax/small_parallax.dart'), - info: 'Shows how to create a smaller parallax in the center of the ' - 'screen', + (_) => GameWidget(game: SmallParallaxExample()), + codeLink: baseLink('parallax/small_parallax_example.dart'), + info: SmallParallaxExample.description, ) ..add( 'No FCS', - (_) => GameWidget(game: NoFCSParallaxGame()), - codeLink: baseLink('parallax/no_fcs.dart'), - info: "Shows how to use the parallax without Flame's component system", + (_) => GameWidget(game: NoFCSParallaxExample()), + codeLink: baseLink('parallax/no_fcs_parallax_example.dart'), + info: NoFCSParallaxExample.description, ) ..add( 'Advanced', - (_) => GameWidget(game: AdvancedParallaxGame()), - codeLink: baseLink('parallax/advanced.dart'), - info: 'Shows how to create a parallax with different velocity deltas on ' - 'each layer', + (_) => GameWidget(game: AdvancedParallaxExample()), + codeLink: baseLink('parallax/advanced_parallax_example.dart'), + info: AdvancedParallaxExample.description, ) ..add( 'Layer sandbox', (context) { return GameWidget( - game: SandBoxLayerParallaxGame( + game: SandboxLayerParallaxExample( planeSpeed: Vector2( context.numberProperty('plane x speed', 0), context.numberProperty('plane y speed', 0), @@ -89,8 +86,7 @@ void addParallaxStories(Dashbook dashbook) { ), ); }, - codeLink: baseLink('parallax/sandbox_layer.dart'), - info: 'In this example, properties of a layer can be changed to preview ' - 'the different combination of values', + codeLink: baseLink('parallax/sandbox_layer_parallax_example.dart'), + info: SandboxLayerParallaxExample.description, ); } diff --git a/examples/lib/stories/parallax/sandbox_layer.dart b/examples/lib/stories/parallax/sandbox_layer_parallax_example.dart similarity index 85% rename from examples/lib/stories/parallax/sandbox_layer.dart rename to examples/lib/stories/parallax/sandbox_layer_parallax_example.dart index 5a8172067..9993ddf68 100644 --- a/examples/lib/stories/parallax/sandbox_layer.dart +++ b/examples/lib/stories/parallax/sandbox_layer_parallax_example.dart @@ -3,13 +3,19 @@ import 'package:flame/game.dart'; import 'package:flame/parallax.dart'; import 'package:flutter/painting.dart'; -class SandBoxLayerParallaxGame extends FlameGame { +class SandboxLayerParallaxExample extends FlameGame { + static const String description = ''' + In this example, properties of a layer can be changed to preview the + different combination of values. You can change the properties by pressing + the pen in the upper right corner. + '''; + final Vector2 planeSpeed; final ImageRepeat planeRepeat; final LayerFill planeFill; final Alignment planeAlignment; - SandBoxLayerParallaxGame({ + SandboxLayerParallaxExample({ required this.planeSpeed, required this.planeRepeat, required this.planeFill, diff --git a/examples/lib/stories/parallax/small_parallax.dart b/examples/lib/stories/parallax/small_parallax_example.dart similarity index 75% rename from examples/lib/stories/parallax/small_parallax.dart rename to examples/lib/stories/parallax/small_parallax_example.dart index dd1bb1fde..78c8ff28d 100644 --- a/examples/lib/stories/parallax/small_parallax.dart +++ b/examples/lib/stories/parallax/small_parallax_example.dart @@ -2,7 +2,11 @@ import 'package:flame/components.dart'; import 'package:flame/game.dart'; import 'package:flame/parallax.dart'; -class SmallParallaxGame extends FlameGame { +class SmallParallaxExample extends FlameGame { + static const String description = ''' + Shows how to create a smaller parallax in the center of the screen. + '''; + @override Future onLoad() async { await super.onLoad(); @@ -17,8 +21,8 @@ class SmallParallaxGame extends FlameGame { size: Vector2.all(200), baseVelocity: Vector2(20, 0), velocityMultiplierDelta: Vector2(1.8, 1.0), - ) - ..position = size / 2; + ); + component.position = size / 2; add(component); } } diff --git a/examples/lib/stories/rendering/flip.dart b/examples/lib/stories/rendering/flip.dart deleted file mode 100644 index 5127b1a71..000000000 --- a/examples/lib/stories/rendering/flip.dart +++ /dev/null @@ -1,46 +0,0 @@ -import 'dart:ui'; - -import 'package:flame/components.dart'; -import 'package:flame/game.dart'; - -class FlipSpriteGame extends FlameGame { - @override - Future onLoad() async { - await super.onLoad(); - final image = await images.load('animations/chopper.png'); - - final regular = buildAnimationComponent(image); - regular.y = 100; - add(regular); - - final flipX = buildAnimationComponent(image); - flipX.y = 300; - flipX.flipHorizontally(); - add(flipX); - - final flipY = buildAnimationComponent(image); - flipY.y = 500; - flipY.flipVertically(); - add(flipY); - } - - SpriteAnimationComponent buildAnimationComponent(Image image) { - final ac = SpriteAnimationComponent( - animation: buildAnimation(image), - size: Vector2.all(100), - ); - ac.x = size.x / 2 - ac.x / 2; - return ac; - } - - SpriteAnimation buildAnimation(Image image) { - return SpriteAnimation.fromFrameData( - image, - SpriteAnimationData.sequenced( - amount: 4, - textureSize: Vector2.all(48), - stepTime: 0.15, - ), - ); - } -} diff --git a/examples/lib/stories/rendering/flip_sprite_example.dart b/examples/lib/stories/rendering/flip_sprite_example.dart new file mode 100644 index 000000000..612b5e1c2 --- /dev/null +++ b/examples/lib/stories/rendering/flip_sprite_example.dart @@ -0,0 +1,32 @@ +import 'package:flame/components.dart'; +import 'package:flame/game.dart'; + +import '../../commons/ember.dart'; + +class FlipSpriteExample extends FlameGame { + static const String description = ''' + In this example we show how you can flip components horizontally and + vertically. + '''; + + @override + Future onLoad() async { + await super.onLoad(); + + final regular = Ember(position: Vector2(size.x / 2 - 100, 200)); + add(regular); + + final flipX = Ember(position: Vector2(size.x / 2 - 100, 400)); + flipX.flipHorizontally(); + add(flipX); + + final flipY = Ember(position: Vector2(size.x / 2 + 100, 200)); + flipY.flipVertically(); + add(flipY); + + final flipWithRotation = Ember(position: Vector2(size.x / 2 + 100, 400)) + ..angle = 2; + flipWithRotation.flipVertically(); + add(flipWithRotation); + } +} diff --git a/examples/lib/stories/tile_maps/isometric_tile_map.dart b/examples/lib/stories/rendering/isometric_tile_map_example.dart similarity index 69% rename from examples/lib/stories/tile_maps/isometric_tile_map.dart rename to examples/lib/stories/rendering/isometric_tile_map_example.dart index 550afcfd8..ca145a83d 100644 --- a/examples/lib/stories/tile_maps/isometric_tile_map.dart +++ b/examples/lib/stories/rendering/isometric_tile_map_example.dart @@ -7,45 +7,29 @@ import 'package:flame/input.dart'; import 'package:flame/sprite.dart'; import 'package:flutter/material.dart' hide Image; -const x = 500.0; -const y = 500.0; -final topLeft = Vector2(x, y); +class IsometricTileMapExample extends FlameGame with MouseMovementDetector { + static const String description = ''' + Shows an example of how to use the `IsometricTileMapComponent`.\n\n + Move the mouse over the board to see a selector appearing on the tiles. + '''; -const scale = 2.0; -const srcTileSize = 32.0; -const destTileSize = scale * srcTileSize; + final topLeft = Vector2.all(500); -final originColor = Paint()..color = const Color(0xFFFF00FF); -final originColor2 = Paint()..color = const Color(0xFFAA55FF); + static const scale = 2.0; + static const srcTileSize = 32.0; + static const destTileSize = scale * srcTileSize; -const halfSize = true; -const tileHeight = scale * (halfSize ? 8.0 : 16.0); -const suffix = halfSize ? '-short' : ''; + static const halfSize = true; + static const tileHeight = scale * (halfSize ? 8.0 : 16.0); + static const suffix = halfSize ? '-short' : ''; -class Selector extends SpriteComponent { - bool show = false; + final originColor = Paint()..color = const Color(0xFFFF00FF); + final originColor2 = Paint()..color = const Color(0xFFAA55FF); - Selector(double s, Image image) - : super( - sprite: Sprite(image, srcSize: Vector2.all(32.0)), - size: Vector2.all(s), - ); - - @override - void render(Canvas canvas) { - if (!show) { - return; - } - - super.render(canvas); - } -} - -class IsometricTileMapGame extends FlameGame with MouseMovementDetector { late IsometricTileMapComponent base; late Selector selector; - IsometricTileMapGame(); + IsometricTileMapExample(); @override Future onLoad() async { @@ -69,9 +53,8 @@ class IsometricTileMapGame extends FlameGame with MouseMovementDetector { matrix, destTileSize: Vector2.all(destTileSize), tileHeight: tileHeight, - ) - ..x = x - ..y = y, + position: topLeft, + ), ); final selectorImage = await images.load('tile_maps/selector$suffix.png'); @@ -83,7 +66,7 @@ class IsometricTileMapGame extends FlameGame with MouseMovementDetector { super.render(canvas); canvas.renderPoint(topLeft, size: 5, paint: originColor); canvas.renderPoint( - Vector2(x, y - tileHeight), + topLeft.clone()..y -= tileHeight, size: 5, paint: originColor2, ); @@ -97,3 +80,22 @@ class IsometricTileMapGame extends FlameGame with MouseMovementDetector { selector.position.setFrom(topLeft + base.getBlockRenderPosition(block)); } } + +class Selector extends SpriteComponent { + bool show = true; + + Selector(double s, Image image) + : super( + sprite: Sprite(image, srcSize: Vector2.all(32.0)), + size: Vector2.all(s), + ); + + @override + void render(Canvas canvas) { + if (!show) { + return; + } + + super.render(canvas); + } +} diff --git a/examples/lib/stories/rendering/layers.dart b/examples/lib/stories/rendering/layers_example.dart similarity index 90% rename from examples/lib/stories/rendering/layers.dart rename to examples/lib/stories/rendering/layers_example.dart index 49f472572..11fcb88bc 100644 --- a/examples/lib/stories/rendering/layers.dart +++ b/examples/lib/stories/rendering/layers_example.dart @@ -4,6 +4,36 @@ import 'package:flame/components.dart'; import 'package:flame/game.dart'; import 'package:flame/layers.dart'; +class LayerExample extends FlameGame { + static const String description = ''' + In this example we show how layers can be used to produce a shadow effect. + '''; + + late Layer gameLayer; + late Layer backgroundLayer; + + @override + Future onLoad() async { + await super.onLoad(); + final playerSprite = Sprite(await images.load('layers/player.png')); + final enemySprite = Sprite(await images.load('layers/enemy.png')); + final backgroundSprite = Sprite(await images.load('layers/background.png')); + + gameLayer = GameLayer(playerSprite, enemySprite); + backgroundLayer = BackgroundLayer(backgroundSprite); + } + + @override + void render(Canvas canvas) { + super.render(canvas); + gameLayer.render(canvas); + backgroundLayer.render(canvas); + } + + @override + Color backgroundColor() => const Color(0xFF38607C); +} + class GameLayer extends DynamicLayer { final Sprite playerSprite; final Sprite enemySprite; @@ -43,29 +73,3 @@ class BackgroundLayer extends PreRenderedLayer { ); } } - -class LayerGame extends FlameGame { - late Layer gameLayer; - late Layer backgroundLayer; - - @override - Future onLoad() async { - await super.onLoad(); - final playerSprite = Sprite(await images.load('layers/player.png')); - final enemySprite = Sprite(await images.load('layers/enemy.png')); - final backgroundSprite = Sprite(await images.load('layers/background.png')); - - gameLayer = GameLayer(playerSprite, enemySprite); - backgroundLayer = BackgroundLayer(backgroundSprite); - } - - @override - void render(Canvas canvas) { - super.render(canvas); - gameLayer.render(canvas); - backgroundLayer.render(canvas); - } - - @override - Color backgroundColor() => const Color(0xFF38607C); -} diff --git a/examples/lib/stories/rendering/nine_tile_box_example.dart b/examples/lib/stories/rendering/nine_tile_box_example.dart new file mode 100644 index 000000000..37868e340 --- /dev/null +++ b/examples/lib/stories/rendering/nine_tile_box_example.dart @@ -0,0 +1,39 @@ +import 'package:flame/components.dart'; +import 'package:flame/game.dart'; +import 'package:flame/input.dart'; +import 'package:flutter/material.dart'; + +class NineTileBoxExample extends FlameGame with TapDetector, DoubleTapDetector { + static const String description = ''' + If you want to create a background for something that can stretch you can + use the `NineTileBox` which is showcased here.\n\n + Tap to make the box bigger and double tap to make it smaller. + '''; + + late NineTileBox nineTileBox; + final Vector2 boxSize = Vector2.all(300); + + @override + Future onLoad() async { + await super.onLoad(); + final sprite = Sprite(await images.load('nine-box.png')); + nineTileBox = NineTileBox(sprite, tileSize: 8, destTileSize: 24); + } + + @override + void render(Canvas canvas) { + super.render(canvas); + final position = (size - boxSize) / 2; + nineTileBox.draw(canvas, position, boxSize); + } + + @override + void onTap() { + boxSize.scale(1.2); + } + + @override + void onDoubleTap() { + boxSize.scale(0.8); + } +} diff --git a/examples/lib/stories/utils/particles.dart b/examples/lib/stories/rendering/particles_example.dart similarity index 98% rename from examples/lib/stories/utils/particles.dart rename to examples/lib/stories/rendering/particles_example.dart index 65e9dc677..5144903d5 100644 --- a/examples/lib/stories/utils/particles.dart +++ b/examples/lib/stories/rendering/particles_example.dart @@ -9,7 +9,11 @@ import 'package:flame/sprite.dart'; import 'package:flame/timer.dart' as flame_timer; import 'package:flutter/material.dart' hide Image; -class ParticlesGame extends FlameGame with FPSCounter { +class ParticlesExample extends FlameGame with FPSCounter { + static const String description = ''' + In this example we show how to render a lot of different particles. + '''; + /// Defines dimensions of the sample /// grid to be displayed on the screen, /// 5x5 in this particular case @@ -531,7 +535,7 @@ class ParticlesGame extends FlameGame with FPSCounter { Future loadGame() async { WidgetsFlutterBinding.ensureInitialized(); - return ParticlesGame(); + return ParticlesExample(); } /// A curve which maps sinus output (-1..1,0..pi) diff --git a/examples/lib/stories/rendering/rendering.dart b/examples/lib/stories/rendering/rendering.dart index 9c5d42918..475d1e27b 100644 --- a/examples/lib/stories/rendering/rendering.dart +++ b/examples/lib/stories/rendering/rendering.dart @@ -2,25 +2,49 @@ import 'package:dashbook/dashbook.dart'; import 'package:flame/game.dart'; import '../../commons/commons.dart'; -import 'flip.dart'; -import 'layers.dart'; -import 'text.dart'; +import 'flip_sprite_example.dart'; +import 'isometric_tile_map_example.dart'; +import 'layers_example.dart'; +import 'nine_tile_box_example.dart'; +import 'particles_example.dart'; +import 'text_example.dart'; void addRenderingStories(Dashbook dashbook) { dashbook.storiesOf('Rendering') ..add( 'Text', - (_) => GameWidget(game: TextGame()), - codeLink: baseLink('rendering/text.dart'), + (_) => GameWidget(game: TextExample()), + codeLink: baseLink('rendering/text_example.dart'), + info: TextExample.description, + ) + ..add( + 'Isometric Tile Map', + (_) => GameWidget(game: IsometricTileMapExample()), + codeLink: baseLink('tile_maps/isometric_tile_map_example.dart'), + info: IsometricTileMapExample.description, + ) + ..add( + 'Nine Tile Box', + (_) => GameWidget(game: NineTileBoxExample()), + codeLink: baseLink('utils/nine_tile_box_example.dart'), + info: NineTileBoxExample.description, ) ..add( 'Flip Sprite', - (_) => GameWidget(game: FlipSpriteGame()), - codeLink: baseLink('rendering/flip.dart'), + (_) => GameWidget(game: FlipSpriteExample()), + codeLink: baseLink('rendering/flip_sprite_example.dart'), + info: FlipSpriteExample.description, ) ..add( 'Layers', - (_) => GameWidget(game: LayerGame()), - codeLink: baseLink('rendering/layers.dart'), + (_) => GameWidget(game: LayerExample()), + codeLink: baseLink('rendering/layers_example.dart'), + info: LayerExample.description, + ) + ..add( + 'Particles', + (_) => GameWidget(game: ParticlesExample()), + codeLink: baseLink('utils/particles_example.dart'), + info: ParticlesExample.description, ); } diff --git a/examples/lib/stories/rendering/text.dart b/examples/lib/stories/rendering/text_example.dart similarity index 94% rename from examples/lib/stories/rendering/text.dart rename to examples/lib/stories/rendering/text_example.dart index 32d941f0d..04badb9fc 100644 --- a/examples/lib/stories/rendering/text.dart +++ b/examples/lib/stories/rendering/text_example.dart @@ -5,6 +5,53 @@ import 'package:flame/game.dart'; import 'package:flame/palette.dart'; import 'package:flutter/material.dart'; +class TextExample extends FlameGame { + static const String description = ''' + In this example we show different ways of rendering text. + '''; + + @override + Future onLoad() async { + await super.onLoad(); + add( + TextComponent(text: 'Hello, Flame', textRenderer: _regular) + ..anchor = Anchor.topCenter + ..x = size.x / 2 + ..y = 32.0, + ); + + add( + TextComponent(text: 'Text with shade', textRenderer: _shaded) + ..anchor = Anchor.topRight + ..position = size - Vector2.all(100), + ); + + add( + TextComponent(text: 'center', textRenderer: _tiny) + ..anchor = Anchor.center + ..position.setFrom(size / 2), + ); + + add( + TextComponent(text: 'bottomRight', textRenderer: _tiny) + ..anchor = Anchor.bottomRight + ..position.setFrom(size), + ); + + add( + MyTextBox( + '"This is our world now. The world of the electron and the switch; ' + 'the beauty of the baud. We exist without nationality, skin color, ' + 'or religious bias. You wage wars, murder, cheat, lie to us and try ' + "to make us believe it's for our own good, yet we're the " + 'criminals. Yes, I am a criminal. My crime is that of curiosity."', + ) + ..anchor = Anchor.bottomLeft + ..y = size.y, + ); + } +} + final _regularTextStyle = TextStyle(fontSize: 18, color: BasicPalette.white.color); final _regular = TextPaint(style: _regularTextStyle); @@ -46,46 +93,3 @@ class MyTextBox extends TextBoxComponent { c.drawRect(rect, Paint()..color = Colors.white10); } } - -class TextGame extends FlameGame { - @override - Future onLoad() async { - await super.onLoad(); - add( - TextComponent(text: 'Hello, Flame', textRenderer: _regular) - ..anchor = Anchor.topCenter - ..x = size.x / 2 - ..y = 32.0, - ); - - add( - TextComponent(text: 'Text with shade', textRenderer: _shaded) - ..anchor = Anchor.topRight - ..position = size - Vector2.all(100), - ); - - add( - TextComponent(text: 'center', textRenderer: _tiny) - ..anchor = Anchor.center - ..position.setFrom(size / 2), - ); - - add( - TextComponent(text: 'bottomRight', textRenderer: _tiny) - ..anchor = Anchor.bottomRight - ..position.setFrom(size), - ); - - add( - MyTextBox( - '"This is our world now. The world of the electron and the switch; ' - 'the beauty of the baud. We exist without nationality, skin color, ' - 'or religious bias. You wage wars, murder, cheat, lie to us and try ' - "to make us believe it's for our own good, yet we're the " - 'criminals. Yes, I am a criminal. My crime is that of curiosity."', - ) - ..anchor = Anchor.bottomLeft - ..y = size.y, - ); - } -} diff --git a/examples/lib/stories/sprites/base64.dart b/examples/lib/stories/sprites/base64_sprite_example.dart similarity index 73% rename from examples/lib/stories/sprites/base64.dart rename to examples/lib/stories/sprites/base64_sprite_example.dart index 5463a8d86..35c3020eb 100644 --- a/examples/lib/stories/sprites/base64.dart +++ b/examples/lib/stories/sprites/base64_sprite_example.dart @@ -1,7 +1,12 @@ import 'package:flame/components.dart'; import 'package:flame/game.dart'; -class Base64SpriteGame extends FlameGame { +class Base64SpriteExample extends FlameGame { + static const String description = ''' + In this example we load a sprite from the a base64 string and put it into a + `SpriteComponent`. + '''; + @override Future onLoad() async { await super.onLoad(); @@ -11,8 +16,9 @@ class Base64SpriteGame extends FlameGame { add( SpriteComponent.fromImage( image, - position: Vector2.all(100), + position: size / 2, size: Vector2.all(100), + anchor: Anchor.center, ), ); } diff --git a/examples/lib/stories/sprites/basic.dart b/examples/lib/stories/sprites/basic_sprite_example.dart similarity index 54% rename from examples/lib/stories/sprites/basic.dart rename to examples/lib/stories/sprites/basic_sprite_example.dart index ad5d4326b..bf80d9b82 100644 --- a/examples/lib/stories/sprites/basic.dart +++ b/examples/lib/stories/sprites/basic_sprite_example.dart @@ -1,7 +1,12 @@ import 'package:flame/components.dart'; import 'package:flame/game.dart'; -class BasicSpriteGame extends FlameGame { +class BasicSpriteExample extends FlameGame { + static const String description = ''' + In this example we load a sprite from the assets folder and put it into a + `SpriteComponent`. + '''; + @override Future onLoad() async { await super.onLoad(); @@ -9,8 +14,9 @@ class BasicSpriteGame extends FlameGame { add( SpriteComponent( sprite: sprite, - position: Vector2.all(100), + position: size / 2, size: sprite.srcSize * 2, + anchor: Anchor.center, ), ); } diff --git a/examples/lib/stories/sprites/sprite_group.dart b/examples/lib/stories/sprites/sprite_group_example.dart similarity index 78% rename from examples/lib/stories/sprites/sprite_group.dart rename to examples/lib/stories/sprites/sprite_group_example.dart index d5aecb6a3..0efe5741d 100644 --- a/examples/lib/stories/sprites/sprite_group.dart +++ b/examples/lib/stories/sprites/sprite_group_example.dart @@ -3,6 +3,25 @@ import 'package:flame/game.dart'; enum ButtonState { unpressed, pressed } +class SpriteGroupExample extends FlameGame with HasTappables { + static const String description = ''' + In this example we show how a `SpriteGroupComponent` can be used to create + a button which displays different sprites depending on whether it is pressed + or not. + '''; + + @override + Future onLoad() async { + await super.onLoad(); + add( + ButtonComponent() + ..position = size / 2 + ..size = Vector2(200, 50) + ..anchor = Anchor.center, + ); + } +} + class ButtonComponent extends SpriteGroupComponent with HasGameRef, Tappable { @override @@ -44,15 +63,3 @@ class ButtonComponent extends SpriteGroupComponent return true; } } - -class SpriteGroupExample extends FlameGame with HasTappables { - @override - Future onLoad() async { - await super.onLoad(); - add( - ButtonComponent() - ..position = Vector2(100, 100) - ..size = Vector2(200, 50), - ); - } -} diff --git a/examples/lib/stories/sprites/spritebatch.dart b/examples/lib/stories/sprites/spritebatch_example.dart similarity index 83% rename from examples/lib/stories/sprites/spritebatch.dart rename to examples/lib/stories/sprites/spritebatch_example.dart index 6d98ecb29..6c694c56b 100644 --- a/examples/lib/stories/sprites/spritebatch.dart +++ b/examples/lib/stories/sprites/spritebatch_example.dart @@ -5,7 +5,12 @@ import 'package:flame/game.dart'; import 'package:flame/sprite.dart'; import 'package:flutter/material.dart'; -class SpritebatchGame extends FlameGame { +class SpritebatchExample extends FlameGame { + static const String description = ''' + In this example we show how to render many sprites in a batch for + efficiency, this is done with `SpriteBatch` and the `SpriteBatchComponent`. + '''; + @override Future onLoad() async { await super.onLoad(); diff --git a/examples/lib/stories/sprites/spritebatch_auto_load.dart b/examples/lib/stories/sprites/spritebatch_load_example.dart similarity index 79% rename from examples/lib/stories/sprites/spritebatch_auto_load.dart rename to examples/lib/stories/sprites/spritebatch_load_example.dart index eaea18f73..7e0191e71 100644 --- a/examples/lib/stories/sprites/spritebatch_auto_load.dart +++ b/examples/lib/stories/sprites/spritebatch_load_example.dart @@ -5,8 +5,22 @@ import 'package:flame/game.dart'; import 'package:flame/sprite.dart'; import 'package:flutter/material.dart'; +class SpritebatchLoadExample extends FlameGame { + static const String description = ''' + In this example we do the same thing as in the normal sprite batch example, + but in this example the logic and loading is moved into a component that + extends `SpriteBatchComponent`. + '''; + + @override + Future onLoad() async { + await super.onLoad(); + add(MySpriteBatchComponent()); + } +} + class MySpriteBatchComponent extends SpriteBatchComponent - with HasGameRef { + with HasGameRef { @override Future onLoad() async { await super.onLoad(); @@ -42,11 +56,3 @@ class MySpriteBatchComponent extends SpriteBatchComponent } } } - -class SpritebatchAutoLoadGame extends FlameGame { - @override - Future onLoad() async { - await super.onLoad(); - add(MySpriteBatchComponent()); - } -} diff --git a/examples/lib/stories/sprites/sprites.dart b/examples/lib/stories/sprites/sprites.dart index 75d46de99..fad119ad2 100644 --- a/examples/lib/stories/sprites/sprites.dart +++ b/examples/lib/stories/sprites/sprites.dart @@ -2,43 +2,49 @@ import 'package:dashbook/dashbook.dart'; import 'package:flame/game.dart'; import '../../commons/commons.dart'; -import 'base64.dart'; -import 'basic.dart'; -import 'sprite_group.dart'; -import 'spritebatch.dart'; -import 'spritebatch_auto_load.dart'; -import 'spritesheet.dart'; +import 'base64_sprite_example.dart'; +import 'basic_sprite_example.dart'; +import 'sprite_group_example.dart'; +import 'spritebatch_example.dart'; +import 'spritebatch_load_example.dart'; +import 'spritesheet_example.dart'; void addSpritesStories(Dashbook dashbook) { dashbook.storiesOf('Sprites') ..add( 'Basic Sprite', - (_) => GameWidget(game: BasicSpriteGame()), - codeLink: baseLink('sprites/basic.dart'), + (_) => GameWidget(game: BasicSpriteExample()), + codeLink: baseLink('sprites/basic_animation_example.dart'), + info: BasicSpriteExample.description, ) ..add( 'Base64 Sprite', - (_) => GameWidget(game: Base64SpriteGame()), - codeLink: baseLink('sprites/base64.dart'), + (_) => GameWidget(game: Base64SpriteExample()), + codeLink: baseLink('sprites/base64_sprite_example.dart'), + info: Base64SpriteExample.description, ) ..add( 'Spritesheet', - (_) => GameWidget(game: SpritesheetGame()), - codeLink: baseLink('sprites/spritesheet.dart'), + (_) => GameWidget(game: SpritesheetExample()), + codeLink: baseLink('sprites/spritesheet_example.dart'), + info: SpritesheetExample.description, ) ..add( 'Spritebatch', - (_) => GameWidget(game: SpritebatchGame()), - codeLink: baseLink('sprites/spritebatch.dart'), + (_) => GameWidget(game: SpritebatchExample()), + codeLink: baseLink('sprites/spritebatch_example.dart'), + info: SpritebatchExample.description, ) ..add( 'Spritebatch Auto Load', - (_) => GameWidget(game: SpritebatchAutoLoadGame()), - codeLink: baseLink('sprites/spritebatch_auto_load.dart'), + (_) => GameWidget(game: SpritebatchLoadExample()), + codeLink: baseLink('sprites/spritebatch_load_example.dart'), + info: SpritebatchLoadExample.description, ) ..add( 'SpriteGroup', (_) => GameWidget(game: SpriteGroupExample()), - codeLink: baseLink('sprites/sprite_group.dart'), + codeLink: baseLink('sprites/sprite_group_example.dart'), + info: SpriteGroupExample.description, ); } diff --git a/examples/lib/stories/sprites/spritesheet.dart b/examples/lib/stories/sprites/spritesheet_example.dart similarity index 91% rename from examples/lib/stories/sprites/spritesheet.dart rename to examples/lib/stories/sprites/spritesheet_example.dart index 2b9105ab5..668ffc89c 100644 --- a/examples/lib/stories/sprites/spritesheet.dart +++ b/examples/lib/stories/sprites/spritesheet_example.dart @@ -2,7 +2,12 @@ import 'package:flame/components.dart'; import 'package:flame/game.dart'; import 'package:flame/sprite.dart'; -class SpritesheetGame extends FlameGame { +class SpritesheetExample extends FlameGame { + static const String description = ''' + In this example we show how to load images and how to create animations from + sprite sheets. + '''; + @override Future onLoad() async { await super.onLoad(); diff --git a/examples/lib/stories/widgets/overlay.dart b/examples/lib/stories/system/overlays_example.dart similarity index 73% rename from examples/lib/stories/widgets/overlay.dart rename to examples/lib/stories/system/overlays_example.dart index d040c2c9e..9d694e072 100644 --- a/examples/lib/stories/widgets/overlay.dart +++ b/examples/lib/stories/system/overlays_example.dart @@ -4,30 +4,13 @@ import 'package:flame/game.dart'; import 'package:flame/input.dart'; import 'package:flutter/material.dart'; -Widget _pauseMenuBuilder(BuildContext buildContext, ExampleGame game) { - return Center( - child: Container( - width: 100, - height: 100, - color: const Color(0xFFFF0000), - child: const Center( - child: Text('Paused'), - ), - ), - ); -} +class OverlaysExample extends FlameGame with TapDetector { + static const String description = ''' + In this example we show how the overlays system can be used.\n\n + If you tap the canvas the game will start and if you tap it again it will + pause. + '''; -Widget overlayBuilder(DashbookContext ctx) { - return GameWidget( - game: ExampleGame()..paused = true, - overlayBuilderMap: const { - 'PauseMenu': _pauseMenuBuilder, - }, - initialActiveOverlays: const ['PauseMenu'], - ); -} - -class ExampleGame extends FlameGame with TapDetector { @override Future onLoad() async { await super.onLoad(); @@ -62,3 +45,26 @@ class ExampleGame extends FlameGame with TapDetector { } } } + +Widget _pauseMenuBuilder(BuildContext buildContext, OverlaysExample game) { + return Center( + child: Container( + width: 100, + height: 100, + color: Colors.orange, + child: const Center( + child: Text('Paused'), + ), + ), + ); +} + +Widget overlayBuilder(DashbookContext ctx) { + return GameWidget( + game: OverlaysExample()..paused = true, + overlayBuilderMap: const { + 'PauseMenu': _pauseMenuBuilder, + }, + initialActiveOverlays: const ['PauseMenu'], + ); +} diff --git a/examples/lib/stories/system/pause_resume_game.dart b/examples/lib/stories/system/pause_resume_example.dart similarity index 65% rename from examples/lib/stories/system/pause_resume_game.dart rename to examples/lib/stories/system/pause_resume_example.dart index d828f864e..398472a6c 100644 --- a/examples/lib/stories/system/pause_resume_game.dart +++ b/examples/lib/stories/system/pause_resume_example.dart @@ -2,15 +2,18 @@ import 'package:flame/components.dart'; import 'package:flame/game.dart'; import 'package:flame/input.dart'; -class PauseResumeGame extends FlameGame with TapDetector, DoubleTapDetector { - static const info = ''' - Demonstrate how to use the pause and resume engine methods and paused attribute. +class PauseResumeExample extends FlameGame with TapDetector, DoubleTapDetector { + static const description = ''' + Demonstrate how to use the pause and resume engine methods and paused + attribute. - Tap on the screen to toggle the execution of the engine using the `resumeEngine` and - `pauseEngine` + Tap on the screen to toggle the execution of the engine using the + `resumeEngine` and `pauseEngine`. - Double Tap on the screen to toggle the execution of the engine using the `paused` attribute + Double Tap on the screen to toggle the execution of the engine using the + `paused` attribute. '''; + @override Future onLoad() async { await super.onLoad(); diff --git a/examples/lib/stories/system/system.dart b/examples/lib/stories/system/system.dart index 65ac982e9..112e8aa8c 100644 --- a/examples/lib/stories/system/system.dart +++ b/examples/lib/stories/system/system.dart @@ -2,13 +2,28 @@ import 'package:dashbook/dashbook.dart'; import 'package:flame/game.dart'; import '../../commons/commons.dart'; -import 'pause_resume_game.dart'; +import 'overlays_example.dart'; +import 'pause_resume_example.dart'; +import 'without_flamegame_example.dart'; void addSystemStories(Dashbook dashbook) { - dashbook.storiesOf('System').add( - 'Pause/resume engine', - (_) => GameWidget(game: PauseResumeGame()), - codeLink: baseLink('system/pause_resume_game.dart'), - info: PauseResumeGame.info, - ); + dashbook.storiesOf('System') + ..add( + 'Pause/resume engine', + (_) => GameWidget(game: PauseResumeExample()), + codeLink: baseLink('system/pause_resume_example.dart'), + info: PauseResumeExample.description, + ) + ..add( + 'Overlay', + overlayBuilder, + codeLink: baseLink('widgets/overlays_example.dart'), + info: OverlaysExample.description, + ) + ..add( + 'Without FlameGame', + (_) => GameWidget(game: NoFlameGameExample()), + codeLink: baseLink('utils/without_flamegame_example.dart'), + info: NoFlameGameExample.description, + ); } diff --git a/examples/lib/stories/input/keyboard.dart b/examples/lib/stories/system/without_flamegame_example.dart similarity index 81% rename from examples/lib/stories/input/keyboard.dart rename to examples/lib/stories/system/without_flamegame_example.dart index 6c610d4ff..85c5942d1 100644 --- a/examples/lib/stories/input/keyboard.dart +++ b/examples/lib/stories/system/without_flamegame_example.dart @@ -6,13 +6,13 @@ import 'package:flame/palette.dart'; import 'package:flutter/services.dart'; import 'package:flutter/widgets.dart'; -const keyboardInfo = ''' -Example showcasing how to act on keyboard events. -It also briefly showcases how to create a game without the FlameGame. -Usage: Use A S D F to steer the rectangle. -'''; +class NoFlameGameExample with Loadable, Game, KeyboardEvents { + static const String description = ''' + This example showcases how to create a game without the FlameGame. + It also briefly showcases how to act on keyboard events. + Usage: Use A S D F to steer the rectangle. + '''; -class KeyboardGame with Loadable, Game, KeyboardEvents { static final Paint white = BasicPalette.white.paint(); static const int speed = 200; diff --git a/examples/lib/stories/tile_maps/tile_maps.dart b/examples/lib/stories/tile_maps/tile_maps.dart deleted file mode 100644 index e25cc339c..000000000 --- a/examples/lib/stories/tile_maps/tile_maps.dart +++ /dev/null @@ -1,13 +0,0 @@ -import 'package:dashbook/dashbook.dart'; -import 'package:flame/game.dart'; - -import '../../commons/commons.dart'; -import 'isometric_tile_map.dart'; - -void addTileMapStories(Dashbook dashbook) { - dashbook.storiesOf('Tile Maps').add( - 'Isometric Tile Map', - (_) => GameWidget(game: IsometricTileMapGame()), - codeLink: baseLink('tile_maps/isometric_tile_map.dart'), - ); -} diff --git a/examples/lib/stories/utils/nine_tile_box.dart b/examples/lib/stories/utils/nine_tile_box.dart deleted file mode 100644 index 4e83aa864..000000000 --- a/examples/lib/stories/utils/nine_tile_box.dart +++ /dev/null @@ -1,23 +0,0 @@ -import 'package:flame/components.dart'; -import 'package:flame/game.dart'; -import 'package:flutter/material.dart'; - -class NineTileBoxGame extends FlameGame { - late NineTileBox nineTileBox; - - @override - Future onLoad() async { - await super.onLoad(); - final sprite = Sprite(await images.load('nine-box.png')); - nineTileBox = NineTileBox(sprite, tileSize: 8, destTileSize: 24); - } - - @override - void render(Canvas canvas) { - super.render(canvas); - const length = 300.0; - final boxSize = Vector2.all(length); - final position = (size - boxSize) / 2; - nineTileBox.draw(canvas, position, boxSize); - } -} diff --git a/examples/lib/stories/utils/timer_component.dart b/examples/lib/stories/utils/timer_component.dart deleted file mode 100644 index 7fb6ccf05..000000000 --- a/examples/lib/stories/utils/timer_component.dart +++ /dev/null @@ -1,39 +0,0 @@ -import 'package:flame/components.dart'; -import 'package:flame/game.dart'; -import 'package:flame/input.dart'; -import 'package:flutter/material.dart'; - -class RenderedTimeComponent extends TimerComponent { - final TextPaint textPaint = TextPaint( - style: const TextStyle(color: Colors.white), - ); - - final double yOffset; - - RenderedTimeComponent(double period, {this.yOffset = 150}) - : super( - period: period, - removeOnFinish: true, - ); - - @override - void render(Canvas canvas) { - textPaint.render( - canvas, - 'Elapsed time: ${timer.current.toStringAsFixed(3)}', - Vector2(10, yOffset), - ); - } -} - -class TimerComponentGame extends FlameGame with TapDetector, DoubleTapDetector { - @override - void onTap() { - add(RenderedTimeComponent(1)); - } - - @override - void onDoubleTap() { - add(RenderedTimeComponent(5, yOffset: 180)); - } -} diff --git a/examples/lib/stories/utils/timer_component_example.dart b/examples/lib/stories/utils/timer_component_example.dart new file mode 100644 index 000000000..96a4b8f63 --- /dev/null +++ b/examples/lib/stories/utils/timer_component_example.dart @@ -0,0 +1,53 @@ +import 'package:flame/components.dart'; +import 'package:flame/game.dart'; +import 'package:flame/input.dart'; +import 'package:flutter/material.dart'; + +class TimerComponentExample extends FlameGame + with TapDetector, DoubleTapDetector { + static const String description = ''' + This examples showcases the `TimerComponent`.\n\n + Tap to start a timer that lives for one second and double tap to start + another timer that lives for 5 seconds. + '''; + + RenderedTimeComponent? tapComponent; + RenderedTimeComponent? doubleTapComponent; + + @override + void onTap() { + tapComponent?.removeFromParent(); + tapComponent = RenderedTimeComponent(1); + add(tapComponent!); + } + + @override + void onDoubleTap() { + doubleTapComponent?.removeFromParent(); + doubleTapComponent = RenderedTimeComponent(5, yOffset: 180); + add(doubleTapComponent!); + } +} + +class RenderedTimeComponent extends TimerComponent { + final TextPaint textPaint = TextPaint( + style: const TextStyle(color: Colors.white, fontSize: 20), + ); + + final double yOffset; + + RenderedTimeComponent(double period, {this.yOffset = 150}) + : super( + period: period, + removeOnFinish: true, + ); + + @override + void render(Canvas canvas) { + textPaint.render( + canvas, + 'Elapsed time: ${timer.current.toStringAsFixed(3)}', + Vector2(30, yOffset), + ); + } +} diff --git a/examples/lib/stories/utils/timer.dart b/examples/lib/stories/utils/timer_example.dart similarity index 63% rename from examples/lib/stories/utils/timer.dart rename to examples/lib/stories/utils/timer_example.dart index 3ae45c397..334f297e9 100644 --- a/examples/lib/stories/utils/timer.dart +++ b/examples/lib/stories/utils/timer_example.dart @@ -3,9 +3,15 @@ import 'package:flame/input.dart'; import 'package:flame/timer.dart'; import 'package:flutter/material.dart'; -class TimerGame extends FlameGame with TapDetector { +class TimerExample extends FlameGame with TapDetector { + static const String description = ''' + This example shows how to use the `Timer`.\n\n + Tap down to start the countdown timer, it will then count to 5 and then stop + until you tap the canvas again and it restarts. + '''; + final TextPaint textConfig = TextPaint( - style: const TextStyle(color: Colors.white), + style: const TextStyle(color: Colors.white, fontSize: 20), ); late Timer countdown; late Timer interval; @@ -15,7 +21,7 @@ class TimerGame extends FlameGame with TapDetector { @override Future onLoad() async { await super.onLoad(); - countdown = Timer(2); + countdown = Timer(5); interval = Timer( 1, onTick: () => elapsedSecs += 1, @@ -41,9 +47,9 @@ class TimerGame extends FlameGame with TapDetector { super.render(canvas); textConfig.render( canvas, - 'Countdown: ${countdown.current}', - Vector2(10, 100), + 'Countdown: ${countdown.current.toStringAsPrecision(3)}', + Vector2(30, 100), ); - textConfig.render(canvas, 'Elapsed time: $elapsedSecs', Vector2(10, 150)); + textConfig.render(canvas, 'Elapsed time: $elapsedSecs', Vector2(30, 130)); } } diff --git a/examples/lib/stories/utils/utils.dart b/examples/lib/stories/utils/utils.dart index 836986102..9b2d24686 100644 --- a/examples/lib/stories/utils/utils.dart +++ b/examples/lib/stories/utils/utils.dart @@ -2,31 +2,21 @@ import 'package:dashbook/dashbook.dart'; import 'package:flame/game.dart'; import '../../commons/commons.dart'; -import 'nine_tile_box.dart'; -import 'particles.dart'; -import 'timer.dart'; -import 'timer_component.dart'; +import 'timer_component_example.dart'; +import 'timer_example.dart'; void addUtilsStories(Dashbook dashbook) { dashbook.storiesOf('Utils') - ..add( - 'Nine Tile Box', - (_) => GameWidget(game: NineTileBoxGame()), - codeLink: baseLink('utils/nine_tile_box.dart'), - ) ..add( 'Timer', - (_) => GameWidget(game: TimerGame()), - codeLink: baseLink('utils/timer.dart'), + (_) => GameWidget(game: TimerExample()), + codeLink: baseLink('utils/timer_example.dart'), + info: TimerExample.description, ) ..add( 'Timer Component', - (_) => GameWidget(game: TimerComponentGame()), - codeLink: baseLink('utils/timer_component.dart'), - ) - ..add( - 'Particles', - (_) => GameWidget(game: ParticlesGame()), - codeLink: baseLink('utils/particles.dart'), + (_) => GameWidget(game: TimerComponentExample()), + codeLink: baseLink('utils/timer_component_example.dart'), + info: TimerComponentExample.description, ); } diff --git a/examples/lib/stories/widgets/custom_painter_component.dart b/examples/lib/stories/widgets/custom_painter_example.dart similarity index 84% rename from examples/lib/stories/widgets/custom_painter_component.dart rename to examples/lib/stories/widgets/custom_painter_example.dart index 4fe7e6414..ed27ec9f1 100644 --- a/examples/lib/stories/widgets/custom_painter_component.dart +++ b/examples/lib/stories/widgets/custom_painter_example.dart @@ -4,9 +4,35 @@ import 'package:flame/game.dart'; import 'package:flame/input.dart'; import 'package:flutter/material.dart'; +class CustomPainterExample extends FlameGame with TapDetector { + static const description = ''' + Example demonstration of how to use the CustomPainterComponent. + + On the screen you can see a component using a custom painter being + rendered on a FlameGame, and if you tap, that same painter is used to + show the happy face on a widget overlay. + '''; + + @override + Future onLoad() async { + await super.onLoad(); + + add(Player()); + } + + @override + void onTap() { + if (overlays.isActive('HappyFace')) { + overlays.remove('HappyFace'); + } else { + overlays.add('HappyFace'); + } + } +} + Widget customPainterBuilder(DashbookContext ctx) { return GameWidget( - game: CustomPainterGame(), + game: CustomPainterExample(), overlayBuilderMap: { 'HappyFace': (context, game) { return Center( @@ -82,7 +108,8 @@ class PlayerCustomPainter extends CustomPainter { } } -class Player extends CustomPainterComponent with HasGameRef { +class Player extends CustomPainterComponent + with HasGameRef { static const speed = 150; int direction = 1; @@ -109,29 +136,3 @@ class Player extends CustomPainterComponent with HasGameRef { } } } - -class CustomPainterGame extends FlameGame with TapDetector { - static const info = ''' - Example demonstration how to use the CustomPainterComponent. - - On the screen you can see a component using a custom painter being - rendered on a FlameGame, and if you tap, that same painter is used to - show the happy face on a widget overlay. - '''; - - @override - Future onLoad() async { - await super.onLoad(); - - add(Player()); - } - - @override - void onTap() { - if (overlays.isActive('HappyFace')) { - overlays.remove('HappyFace'); - } else { - overlays.add('HappyFace'); - } - } -} diff --git a/examples/lib/stories/widgets/nine_tile_box.dart b/examples/lib/stories/widgets/nine_tile_box_example.dart similarity index 100% rename from examples/lib/stories/widgets/nine_tile_box.dart rename to examples/lib/stories/widgets/nine_tile_box_example.dart diff --git a/examples/lib/stories/widgets/sprite_widget_section.dart b/examples/lib/stories/widgets/partial_sprite_widget_example.dart similarity index 92% rename from examples/lib/stories/widgets/sprite_widget_section.dart rename to examples/lib/stories/widgets/partial_sprite_widget_example.dart index ca8f06e54..70fe889b0 100644 --- a/examples/lib/stories/widgets/sprite_widget_section.dart +++ b/examples/lib/stories/widgets/partial_sprite_widget_example.dart @@ -6,7 +6,7 @@ import 'package:flutter/widgets.dart'; final anchorOptions = Anchor.values.map((e) => e.name).toList(); -Widget spriteSectionWidgetBuilder(DashbookContext ctx) { +Widget partialSpriteWidgetBuilder(DashbookContext ctx) { return Container( width: ctx.numberProperty('container width', 400), height: ctx.numberProperty('container height', 200), diff --git a/examples/lib/stories/widgets/sprite_animation_widget.dart b/examples/lib/stories/widgets/sprite_animation_widget_example.dart similarity index 100% rename from examples/lib/stories/widgets/sprite_animation_widget.dart rename to examples/lib/stories/widgets/sprite_animation_widget_example.dart diff --git a/examples/lib/stories/widgets/sprite_button.dart b/examples/lib/stories/widgets/sprite_button_example.dart similarity index 100% rename from examples/lib/stories/widgets/sprite_button.dart rename to examples/lib/stories/widgets/sprite_button_example.dart diff --git a/examples/lib/stories/widgets/sprite_widget.dart b/examples/lib/stories/widgets/sprite_widget_example.dart similarity index 100% rename from examples/lib/stories/widgets/sprite_widget.dart rename to examples/lib/stories/widgets/sprite_widget_example.dart diff --git a/examples/lib/stories/widgets/widgets.dart b/examples/lib/stories/widgets/widgets.dart index 17caad09f..d9b8e0189 100644 --- a/examples/lib/stories/widgets/widgets.dart +++ b/examples/lib/stories/widgets/widgets.dart @@ -1,13 +1,12 @@ import 'package:dashbook/dashbook.dart'; import '../../commons/commons.dart'; -import 'custom_painter_component.dart'; -import 'nine_tile_box.dart'; -import 'overlay.dart'; -import 'sprite_animation_widget.dart'; -import 'sprite_button.dart'; -import 'sprite_widget.dart'; -import 'sprite_widget_section.dart'; +import 'custom_painter_example.dart'; +import 'nine_tile_box_example.dart'; +import 'partial_sprite_widget_example.dart'; +import 'sprite_animation_widget_example.dart'; +import 'sprite_button_example.dart'; +import 'sprite_widget_example.dart'; void addWidgetsStories(Dashbook dashbook) { dashbook.storiesOf('Widgets') @@ -15,37 +14,57 @@ void addWidgetsStories(Dashbook dashbook) { ..add( 'Nine Tile Box', nineTileBoxBuilder, - codeLink: baseLink('widgets/nine_tile_box.dart'), + codeLink: baseLink('widgets/nine_tile_box_example.dart'), + info: ''' + If you want to create a background for something that can stretch you + can use the `NineTileBox` which is showcased here, don't forget to check + out the settings on the pen icon. + ''', ) ..add( 'Sprite Button', spriteButtonBuilder, - codeLink: baseLink('widgets/sprite_button.dart'), + codeLink: baseLink('widgets/sprite_button_example.dart'), + info: ''' + If you want to use sprites as a buttons within the flutter widget tree + you can create a `SpriteButton`, don't forget to check out the settings + on the pen icon. + ''', ) ..add( 'Sprite Widget (full image)', spriteWidgetBuilder, - codeLink: baseLink('widgets/sprite_widget.dart'), + codeLink: baseLink('widgets/sprite_widget_example.dart'), + info: ''' + If you want to use a sprite within the flutter widget tree + you can create a `SpriteWidget`, don't forget to check out the settings + on the pen icon. + ''', ) ..add( 'Sprite Widget (section of image)', - spriteSectionWidgetBuilder, - codeLink: baseLink('widgets/sprite_widget_section.dart'), + partialSpriteWidgetBuilder, + codeLink: baseLink('widgets/partial_sprite_widget_example.dart'), + info: ''' + In this example we show how you can render only parts of a sprite within + a `SpriteWidget`, don't forget to check out the settings on the pen + icon. + ''', ) ..add( 'Sprite Animation Widget', spriteAnimationWidgetBuilder, - codeLink: baseLink('widgets/sprite_animation_widget.dart'), - ) - ..add( - 'Overlay', - overlayBuilder, - codeLink: baseLink('widgets/overlay.dart'), + codeLink: baseLink('widgets/sprite_animation_widget_example.dart'), + info: ''' + If you want to use a sprite animation directly on the flutter widget + tree you can create a `SpriteAnimationWidget`, don't forget to check out + the settings on the pen icon. + ''', ) ..add( 'CustomPainterComponent', customPainterBuilder, - codeLink: baseLink('widgets/custom_painter_component.dart'), - info: CustomPainterGame.info, + codeLink: baseLink('widgets/custom_painter_example.dart'), + info: CustomPainterExample.description, ); }