diff --git a/doc/flame/camera_and_viewport.md b/doc/flame/camera_and_viewport.md index 1f6d1dc5d1..7fd5a97537 100644 --- a/doc/flame/camera_and_viewport.md +++ b/doc/flame/camera_and_viewport.md @@ -1,4 +1,9 @@ -# Camera and Viewport +# Camera and Viewport (Deprecated) + +```{note} +This document describes the deprecated Camera API. The new `CameraComponent` approach +is described in [](camera_component.md). +``` When rendering on Flutter, the regular coordinate space used are logical pixels. That means one pixel for Flutter is already not necessarily one real pixel on the device, because of the [device's @@ -104,7 +109,7 @@ class MyGame extends FlameGame { final someVector = Vector2(100, 100); @override void onLoad() { - camera.followVector2(someVector); + oldCamera.followVector2(someVector); } } diff --git a/doc/flame/camera_component.md b/doc/flame/camera_component.md index 7c4041d935..3bbc5c15a1 100644 --- a/doc/flame/camera_component.md +++ b/doc/flame/camera_component.md @@ -2,13 +2,13 @@ ```{note} This document describes a new camera API. The more traditional approach -(which will be deprecated) for handling a camera is described in +(which is deprecated) for handling a camera is described in [](camera_and_viewport.md). ``` -Camera-as-a-component is an alternative way of structuring a game, an approach -that allows more flexibility in placing the camera, or even having more than -one camera simultaneously. +Camera-as-a-component is the new way of structuring a game, an approach that +allows more flexibility in placing the camera, or even having more than one +camera simultaneously. In order to understand how this approach works, imagine that your game world is an entity that exists *somewhere* independently from your application. Imagine @@ -33,8 +33,10 @@ the viewing location and angle. This component should be used to host all other components that comprise your game world. The main property of the `World` class is that it does not render -through traditional means -- instead, create one or more [](#cameracomponent)s -to "look at" the world. +through traditional means -- instead it is rendered by one or more +[](#cameracomponent)s to "look at" the world. In the `FlameGame` class there is +one `World` called `world` which is added by default and paired together with +the default `CameraComponent` called `camera`. A game can have multiple `World` instances that can be rendered either at the same time, or at different times. For example, if you have two worlds A and B @@ -46,6 +48,23 @@ Just like with most `Component`s, children can be added to `World` by using the `children` argument in its constructor, or by using the `add` or `addAll` methods. +For many games you want to extend the world and create your logic in there, +such a game structure could look like this: + +```dart +void main() { + runApp(GameWidget(FlameGame(world: MyWorld()))); +} + +class MyWorld extends World { + @override + Future onLoad() async { + // Load all the assets that are needed in this world + // and add components etc. + } +} +``` + ## CameraComponent @@ -54,6 +73,10 @@ reference to a `World` instance during construction; however later the target world can be replaced with another one. Multiple cameras can observe the same world at the same time. +There is a default `CameraComponent` called `camera` on the `FlameGame` class +which is paired together with the default `world`, so you don't need to create +or add your own `CameraComponent` if your game doesn't need to. + A `CameraComponent` has two other components inside: a [](#viewport) and a [](#viewfinder). Unlike the `World` object, the camera owns the viewport and the viewfinder, which means those components are children of the camera. @@ -65,6 +88,21 @@ cases where the rendering of a component depends on the camera settings. For example, some components may decide to skip rendering themselves and their children if they are outside of the camera's viewport. +The `FlameGame` class has a `camera` field in its constructor, so you can set +what type of default camera that you want like this for example: + +```dart +void main() { + runApp( + GameWidget( + FlameGame( + camera: CameraComponent.withFixedResolution(width: 800, height: 600), + ), + ), + ); +} +``` + ### CameraComponent.withFixedResolution() @@ -191,9 +229,9 @@ if (!camera.canSee(component)) { ``` -## Comparison to the traditional camera +## Comparison to the deprecated camera -Compared to the normal [Camera](camera_and_viewport.md), the `CameraComponent` +Compared to the deprecated [Camera](camera_and_viewport.md), the `CameraComponent` has several advantages: - Multiple cameras can be added to the game at the same time; diff --git a/doc/flame/flame.md b/doc/flame/flame.md index c540c54abc..ba7b401f60 100644 --- a/doc/flame/flame.md +++ b/doc/flame/flame.md @@ -8,13 +8,13 @@ - [Platforms](platforms.md) - [Collision Detection](collision_detection.md) - [Effects](effects.md) -- [Camera & Viewport](camera_and_viewport.md) - [Camera Component](camera_component.md) - [Inputs](inputs/inputs.md) - [Rendering](rendering/rendering.md) - [Layout](layout/layout.md) - [Overlays](overlays.md) - [Other](other/other.md) +- [Camera & Viewport (deprecated)](camera_and_viewport.md) ```{toctree} :hidden: @@ -27,11 +27,11 @@ Router Platforms Collision Detection Effects -Camera & Viewport Camera Component Inputs Rendering Layout Overlays Other +Camera & Viewport ``` diff --git a/doc/flame/game.md b/doc/flame/game.md index 9b08ee77c9..d4a5d2c093 100644 --- a/doc/flame/game.md +++ b/doc/flame/game.md @@ -1,15 +1,15 @@ # FlameGame -`FlameGame` is the most commonly used `Game` class in Flame. - The `FlameGame` class implements a `Component` based `Game`. It has a tree of components and calls the `update` and `render` methods of all components that have been added to the game. -We refer to this component-based system as the Flame Component System (FCS). Throughout the +We refer to this component-based system as the Flame Component System (FCS). Throughout the documentation, FCS is used to reference this system. Components can be added to the `FlameGame` directly in the constructor with the named `children` -argument, or from anywhere else with the `add`/`addAll` methods. +argument, or from anywhere else with the `add`/`addAll` methods. Most of the time however, you want +to add your children to a `World`, the default world exist under `FlameGame.world` and you add +components to it just like you would to any other component. A simple `FlameGame` implementation that adds two components, one in `onLoad` and one directly in the constructor can look like this: @@ -29,7 +29,7 @@ class MyCrate extends SpriteComponent { } } -class MyGame extends FlameGame { +class MyWorld extends World { @override Future onLoad() async { await add(MyCrate()); @@ -37,11 +37,9 @@ class MyGame extends FlameGame { } void main() { - final myGame = MyGame(); + final myGame = FlameGame(world: MyWorld()); runApp( - GameWidget( - game: myGame, - ), + GameWidget(game: myGame), ); } ``` @@ -56,7 +54,7 @@ constructor. To remove components from the list on a `FlameGame` the `remove` or `removeAll` methods can be used. The first can be used if you just want to remove one component, and the second can be used when you -want to remove a list of components. +want to remove a list of components. These methods exist on all `Component`s, including the world. ## Game Loop @@ -77,13 +75,8 @@ Every time the game needs to be resized, for example when the orientation is cha will call all of the `Component`s `onGameResize` methods and it will also pass this information to the camera and viewport. -The `FlameGame.camera` controls which point in the coordinate space should be the top-left of the -screen (it defaults to [0,0] like a regular `Canvas`). - -```{note} -Utilizing `FlameGame.camera.gameSize` in the `onGameResize` event should be done -after the call to `super.onGameResize(canvasSize);`. -``` +The `FlameGame.camera` controls which point in the coordinate space that should be at the anchor of +your viewfinder, [0,0] is in the center (`Anchor.center`) of the viewport by default. ## Lifecycle @@ -178,8 +171,8 @@ class MyGame extends FlameGame with SingleGameInstance { ```{include} diagrams/low_level_game_api.md ``` -The `Game` class is a low-level API that can be used when you want to implement the functionality of -how the game engine should be structured. `Game` does not implement any `update` or +The abstract `Game` class is a low-level API that can be used when you want to implement the +functionality of how the game engine should be structured. `Game` does not implement any `update` or `render` function for example. The class also has the lifecycle methods `onLoad`, `onMount` and `onRemove` in it, which are @@ -226,9 +219,10 @@ A Flame `Game` can be paused and resumed in two ways: - With the use of the `pauseEngine` and `resumeEngine` methods. - By changing the `paused` attribute. -When pausing a Flame `Game`, the `GameLoop` is effectively paused, meaning that no updates or new -renders will happen until it is resumed. +When pausing a `Game`, the `GameLoop` is effectively paused, meaning that no updates or new renders +will happen until it is resumed. -While the game is paused, it is possible to advanced it frame by frame using the `stepEngine` method. +While the game is paused, it is possible to advanced it frame by frame using the `stepEngine` +method. It might not be much useful in the final game, but can be very helpful in inspecting game state step by step during the development cycle. diff --git a/doc/tutorials/platformer/app/lib/ember_quest.dart b/doc/tutorials/platformer/app/lib/ember_quest.dart index d9f582b206..fbf866aa96 100644 --- a/doc/tutorials/platformer/app/lib/ember_quest.dart +++ b/doc/tutorials/platformer/app/lib/ember_quest.dart @@ -24,9 +24,6 @@ class EmberQuestGame extends FlameGame double cloudSpeed = 0.0; double objectSpeed = 0.0; - final world = World(); - late final CameraComponent cameraComponent; - @override Future onLoad() async { //debugMode = true; // Uncomment to see the bounding boxes @@ -39,9 +36,7 @@ class EmberQuestGame extends FlameGame 'star.png', 'water_enemy.png', ]); - cameraComponent = CameraComponent(world: world); - cameraComponent.viewfinder.anchor = Anchor.topLeft; - addAll([cameraComponent, world]); + camera.viewfinder.anchor = Anchor.topLeft; initializeGame(loadHud: true); } @@ -112,7 +107,7 @@ class EmberQuestGame extends FlameGame ); world.add(_ember); if (loadHud) { - cameraComponent.viewport.add(Hud()); + camera.viewport.add(Hud()); } } diff --git a/examples/games/padracing/lib/padracing_game.dart b/examples/games/padracing/lib/padracing_game.dart index cb385ade5f..91bb728d7c 100644 --- a/examples/games/padracing/lib/padracing_game.dart +++ b/examples/games/padracing/lib/padracing_game.dart @@ -58,7 +58,7 @@ class PadRacingGame extends Forge2DGame with KeyboardEvents { @override Future onLoad() async { super.onLoad(); - cameraComponent.removeFromParent(); + camera.removeFromParent(); children.register(); final walls = createWalls(trackSize); diff --git a/examples/lib/main.dart b/examples/lib/main.dart index 04b89c0035..f4fb18a896 100644 --- a/examples/lib/main.dart +++ b/examples/lib/main.dart @@ -30,6 +30,7 @@ import 'package:examples/stories/layout/layout.dart'; import 'package:examples/stories/parallax/parallax.dart'; import 'package:examples/stories/rendering/rendering.dart'; import 'package:examples/stories/sprites/sprites.dart'; +import 'package:examples/stories/structure/structure.dart'; import 'package:examples/stories/svg/svg.dart'; import 'package:examples/stories/system/system.dart'; import 'package:examples/stories/tiled/tiled.dart'; @@ -71,6 +72,9 @@ void runAsDashbook() { // Some small sample games addGameStories(dashbook); + // Show some different ways of structuring games + addStructureStories(dashbook); + // Feature examples addAudioStories(dashbook); addAnimationStories(dashbook); diff --git a/examples/lib/stories/animations/benchmark_example.dart b/examples/lib/stories/animations/benchmark_example.dart index eec7eda14e..04d2c28e0f 100644 --- a/examples/lib/stories/animations/benchmark_example.dart +++ b/examples/lib/stories/animations/benchmark_example.dart @@ -17,14 +17,9 @@ starts to drop in FPS, this is without any sprite batching and such. final counterPrefix = 'Animations: '; final Random random = Random(); - final world = World(); - late final CameraComponent cameraComponent; - @override Future onLoad() async { - cameraComponent = CameraComponent(world: world); - addAll([cameraComponent, world]); - await cameraComponent.viewport.addAll([ + await camera.viewport.addAll([ FpsTextComponent( position: size - Vector2(0, 50), anchor: Anchor.bottomRight, diff --git a/examples/lib/stories/bridge_libraries/flame_forge2d/blob_example.dart b/examples/lib/stories/bridge_libraries/flame_forge2d/blob_example.dart index 768762c261..a1225f9b76 100644 --- a/examples/lib/stories/bridge_libraries/flame_forge2d/blob_example.dart +++ b/examples/lib/stories/bridge_libraries/flame_forge2d/blob_example.dart @@ -21,7 +21,7 @@ class BlobWorld extends Forge2DWorld with TapCallbacks, HasGameReference { @override Future onLoad() async { - super.onLoad(); + await super.onLoad(); final blobCenter = Vector2(0, -30); final blobRadius = Vector2.all(6.0); addAll(createBoundaries(game)); diff --git a/examples/lib/stories/bridge_libraries/flame_forge2d/camera_example.dart b/examples/lib/stories/bridge_libraries/flame_forge2d/camera_example.dart index 55c6ba2274..941423dba5 100644 --- a/examples/lib/stories/bridge_libraries/flame_forge2d/camera_example.dart +++ b/examples/lib/stories/bridge_libraries/flame_forge2d/camera_example.dart @@ -18,6 +18,6 @@ class CameraExampleWorld extends DominoExampleWorld { final position = info.localPosition; final pizza = Pizza(position); add(pizza); - pizza.mounted.whenComplete(() => game.cameraComponent.follow(pizza)); + pizza.mounted.whenComplete(() => game.camera.follow(pizza)); } } diff --git a/examples/lib/stories/bridge_libraries/flame_forge2d/joints/pulley_joint.dart b/examples/lib/stories/bridge_libraries/flame_forge2d/joints/pulley_joint.dart index 56c9771fee..9108e64099 100644 --- a/examples/lib/stories/bridge_libraries/flame_forge2d/joints/pulley_joint.dart +++ b/examples/lib/stories/bridge_libraries/flame_forge2d/joints/pulley_joint.dart @@ -14,7 +14,7 @@ class PulleyJointExample extends Forge2DGame { @override Future onLoad() async { super.onLoad(); - final distanceFromCenter = cameraComponent.visibleWorldRect.width / 5; + final distanceFromCenter = camera.visibleWorldRect.width / 5; final firstPulley = Ball( Vector2(-distanceFromCenter, -10), diff --git a/examples/lib/stories/bridge_libraries/flame_forge2d/raycast_example.dart b/examples/lib/stories/bridge_libraries/flame_forge2d/raycast_example.dart index c2bf236666..c31017687d 100644 --- a/examples/lib/stories/bridge_libraries/flame_forge2d/raycast_example.dart +++ b/examples/lib/stories/bridge_libraries/flame_forge2d/raycast_example.dart @@ -60,8 +60,8 @@ class RaycastExample extends Forge2DGame with MouseMovementDetector { void onMouseMove(PointerHoverInfo info) { final rayStart = screenToWorld( Vector2( - cameraComponent.viewport.size.x / 4, - cameraComponent.viewport.size.y / 2, + camera.viewport.size.x / 4, + camera.viewport.size.y / 2, ), ); diff --git a/examples/lib/stories/bridge_libraries/flame_forge2d/utils/boundaries.dart b/examples/lib/stories/bridge_libraries/flame_forge2d/utils/boundaries.dart index b369d4a90a..7c02dc02c7 100644 --- a/examples/lib/stories/bridge_libraries/flame_forge2d/utils/boundaries.dart +++ b/examples/lib/stories/bridge_libraries/flame_forge2d/utils/boundaries.dart @@ -2,7 +2,7 @@ import 'package:flame/extensions.dart'; import 'package:flame_forge2d/flame_forge2d.dart'; List createBoundaries(Forge2DGame game, {double? strokeWidth}) { - final visibleRect = game.cameraComponent.visibleWorldRect; + final visibleRect = game.camera.visibleWorldRect; final topLeft = visibleRect.topLeft.toVector2(); final topRight = visibleRect.topRight.toVector2(); final bottomRight = visibleRect.bottomRight.toVector2(); diff --git a/examples/lib/stories/camera_and_viewport/camera_component_properties_example.dart b/examples/lib/stories/camera_and_viewport/camera_component_properties_example.dart index 93f34cce08..17d9c0732c 100644 --- a/examples/lib/stories/camera_and_viewport/camera_component_properties_example.dart +++ b/examples/lib/stories/camera_and_viewport/camera_component_properties_example.dart @@ -21,7 +21,15 @@ class CameraComponentPropertiesExample extends FlameGame { Click at any point within the viewport to create a circle there. '''; - CameraComponent? _camera; + CameraComponentPropertiesExample() + : super( + camera: CameraComponent( + viewport: FixedSizeViewport(200, 200)..add(ViewportFrame()), + ) + ..viewfinder.zoom = 5 + ..viewfinder.anchor = const Anchor(0.25, 0.75), + ); + late RectangleComponent _cullRect; @override @@ -29,14 +37,7 @@ class CameraComponentPropertiesExample extends FlameGame { @override Future onLoad() async { - final world = World(); world.add(Background()); - _camera = CameraComponent( - world: world, - viewport: FixedSizeViewport(200, 200)..add(ViewportFrame()), - ) - ..viewfinder.zoom = 5 - ..viewfinder.anchor = const Anchor(0.25, 0.75); _cullRect = RectangleComponent.fromRect( Rect.zero, paint: Paint() @@ -44,10 +45,8 @@ class CameraComponentPropertiesExample extends FlameGame { ..strokeWidth = 0.25 ..color = const Color(0xaaffff00), ); - await add(world); - await add(_camera!); await world.add(_cullRect); - _camera!.mounted.then((_) { + camera.mounted.then((_) { updateSize(canvasSize); }); } @@ -55,13 +54,12 @@ class CameraComponentPropertiesExample extends FlameGame { @override void onGameResize(Vector2 size) { super.onGameResize(size); - if (_camera != null) { + if (camera.isMounted) { updateSize(size); } } void updateSize(Vector2 size) { - final camera = _camera!; camera.viewport.anchor = Anchor.center; camera.viewport.size = size * 0.7; camera.viewport.position = size * 0.6; diff --git a/examples/lib/stories/camera_and_viewport/coordinate_systems_example.dart b/examples/lib/stories/camera_and_viewport/coordinate_systems_example.dart index bcd7a48304..d9337d13db 100644 --- a/examples/lib/stories/camera_and_viewport/coordinate_systems_example.dart +++ b/examples/lib/stories/camera_and_viewport/coordinate_systems_example.dart @@ -33,13 +33,9 @@ class CoordinateSystemsExample extends FlameGame String? lastEventDescription; final cameraPosition = Vector2.zero(); final cameraVelocity = Vector2.zero(); - late final CameraComponent cameraComponent; - final world = World(); @override Future onLoad() async { - cameraComponent = CameraComponent(world: world); - addAll([world, cameraComponent]); final rectanglePosition = canvasSize / 4; final rectangleSize = Vector2.all(20); final positions = [ @@ -69,8 +65,8 @@ class CoordinateSystemsExample extends FlameGame ); _text.render( canvas, - 'Camera: ${cameraComponent.viewfinder.position}, ' - 'zoom: ${cameraComponent.viewfinder.zoom}', + 'Camera: ${camera.viewfinder.position}, ' + 'zoom: ${camera.viewfinder.zoom}', Vector2(canvasSize.x - 5, 5.0), anchor: Anchor.topRight, ); @@ -125,7 +121,7 @@ class CoordinateSystemsExample extends FlameGame 'Global: ${info.eventPosition.global}', 'Widget: ${info.eventPosition.widget}', 'Game: ${info.eventPosition.game}', - 'Camera: ${cameraComponent.viewfinder.position}', + 'Camera: ${camera.viewfinder.position}', if (info is DragUpdateInfo) ...[ 'Delta', 'Global: ${info.delta.global}', @@ -146,7 +142,7 @@ class CoordinateSystemsExample extends FlameGame // just make it look pretty cameraPosition.x = _roundDouble(cameraPosition.x, 5); cameraPosition.y = _roundDouble(cameraPosition.y, 5); - cameraComponent.viewfinder.position = cameraPosition; + camera.viewfinder.position = cameraPosition; } /// Round [val] up to [places] decimal places. @@ -173,9 +169,9 @@ class CoordinateSystemsExample extends FlameGame cameraVelocity.y = isKeyDown ? 1 : 0; } else if (isKeyDown) { if (event.logicalKey == LogicalKeyboardKey.keyQ) { - cameraComponent.viewfinder.zoom *= 2; + camera.viewfinder.zoom *= 2; } else if (event.logicalKey == LogicalKeyboardKey.keyE) { - cameraComponent.viewfinder.zoom /= 2; + camera.viewfinder.zoom /= 2; } } diff --git a/examples/lib/stories/camera_and_viewport/follow_component_example.dart b/examples/lib/stories/camera_and_viewport/follow_component_example.dart index 60233f7e5a..8a0d7b0db2 100644 --- a/examples/lib/stories/camera_and_viewport/follow_component_example.dart +++ b/examples/lib/stories/camera_and_viewport/follow_component_example.dart @@ -27,22 +27,20 @@ class FollowComponentExample extends FlameGame late MovableEmber ember; final Vector2 viewportResolution; - late final CameraComponent cameraComponent; @override Future onLoad() async { final world = World(); - cameraComponent = CameraComponent.withFixedResolution( + camera = CameraComponent.withFixedResolution( width: viewportResolution.x, height: viewportResolution.y, world: world, ); - addAll([world, cameraComponent]); world.add(Map()); world.add(ember = MovableEmber()); - cameraComponent.setBounds(Map.bounds); - cameraComponent.follow(ember, maxSpeed: 250); + camera.setBounds(Map.bounds); + camera.follow(ember, maxSpeed: 250); world.addAll( List.generate(30, (_) => Rock(Map.generateCoordinates())), diff --git a/examples/lib/stories/camera_and_viewport/zoom_example.dart b/examples/lib/stories/camera_and_viewport/zoom_example.dart index 93bcd2e60b..1ab2064079 100644 --- a/examples/lib/stories/camera_and_viewport/zoom_example.dart +++ b/examples/lib/stories/camera_and_viewport/zoom_example.dart @@ -14,19 +14,16 @@ class ZoomExample extends FlameGame with ScrollDetector, ScaleDetector { final Vector2 viewportResolution; late SpriteComponent flame; - late final CameraComponent cameraComponent; @override Future onLoad() async { final flameSprite = await loadSprite('flame.png'); - final world = World(); - cameraComponent = CameraComponent.withFixedResolution( + camera = CameraComponent.withFixedResolution( world: world, width: viewportResolution.x, height: viewportResolution.y, ); - addAll([world, cameraComponent]); world.add( flame = SpriteComponent( @@ -37,16 +34,14 @@ class ZoomExample extends FlameGame with ScrollDetector, ScaleDetector { } void clampZoom() { - cameraComponent.viewfinder.zoom = - cameraComponent.viewfinder.zoom.clamp(0.05, 3.0); + camera.viewfinder.zoom = camera.viewfinder.zoom.clamp(0.05, 3.0); } static const zoomPerScrollUnit = 0.02; @override void onScroll(PointerScrollInfo info) { - cameraComponent.viewfinder.zoom += - info.scrollDelta.game.y.sign * zoomPerScrollUnit; + camera.viewfinder.zoom += info.scrollDelta.game.y.sign * zoomPerScrollUnit; clampZoom(); } @@ -54,18 +49,18 @@ class ZoomExample extends FlameGame with ScrollDetector, ScaleDetector { @override void onScaleStart(_) { - startZoom = cameraComponent.viewfinder.zoom; + startZoom = camera.viewfinder.zoom; } @override void onScaleUpdate(ScaleUpdateInfo info) { final currentScale = info.scale.global; if (!currentScale.isIdentity()) { - cameraComponent.viewfinder.zoom = startZoom * currentScale.y; + camera.viewfinder.zoom = startZoom * currentScale.y; clampZoom(); } else { final delta = info.delta.game; - cameraComponent.viewfinder.position.translate(-delta.x, -delta.y); + camera.viewfinder.position.translate(-delta.x, -delta.y); } } } diff --git a/examples/lib/stories/collision_detection/quadtree_example.dart b/examples/lib/stories/collision_detection/quadtree_example.dart index 838bf0db2d..d851a29b49 100644 --- a/examples/lib/stories/collision_detection/quadtree_example.dart +++ b/examples/lib/stories/collision_detection/quadtree_example.dart @@ -39,14 +39,12 @@ Press T button to toggle player to collide with other objects. static const mapSize = 300; static const bricksCount = 8000; - late final CameraComponent cameraComponent; late final Player player; final staticLayer = StaticLayer(); @override Future onLoad() async { super.onLoad(); - final world = World(); const mapWidth = mapSize * tileSize; const mapHeight = mapSize * tileSize; @@ -82,12 +80,11 @@ Press T button to toggle player to collide with other objects. } staticLayer.reRender(); - cameraComponent = CameraComponent.withFixedResolution( + camera = CameraComponent.withFixedResolution( world: world, width: 500, height: 250, ); - addAll([world, cameraComponent]); player = Player( position: Vector2.all(mapSize * tileSize / 2), @@ -95,7 +92,7 @@ Press T button to toggle player to collide with other objects. priority: 2, ); world.add(player); - cameraComponent.follow(player); + camera.follow(player); final brick = Brick( position: player.position.translated(0, -tileSize * 2), @@ -132,7 +129,7 @@ Press T button to toggle player to collide with other objects. world.add(QuadTreeDebugComponent(collisionDetection)); world.add(LayerComponent(staticLayer)); - cameraComponent.viewport.add(FpsTextComponent()); + camera.viewport.add(FpsTextComponent()); } final elapsedMicroseconds = []; @@ -194,9 +191,8 @@ Press T button to toggle player to collide with other objects. @override void onScroll(PointerScrollInfo info) { - cameraComponent.viewfinder.zoom += info.scrollDelta.game.y.sign * 0.08; - cameraComponent.viewfinder.zoom = - cameraComponent.viewfinder.zoom.clamp(0.05, 5.0); + camera.viewfinder.zoom += info.scrollDelta.game.y.sign * 0.08; + camera.viewfinder.zoom = camera.viewfinder.zoom.clamp(0.05, 5.0); } } diff --git a/examples/lib/stories/collision_detection/raycast_max_distance_example.dart b/examples/lib/stories/collision_detection/raycast_max_distance_example.dart index ec40f3ef23..f689a77643 100644 --- a/examples/lib/stories/collision_detection/raycast_max_distance_example.dart +++ b/examples/lib/stories/collision_detection/raycast_max_distance_example.dart @@ -17,8 +17,6 @@ This examples showcases how raycast APIs can be used to detect hits within certa late Ray2 _ray; late _Character _character; - late CameraComponent cameraComponent; - final world = World(); final _result = RaycastResult(); final _text = TextComponent( @@ -34,12 +32,11 @@ This examples showcases how raycast APIs can be used to detect hits within certa @override void onLoad() { - cameraComponent = CameraComponent.withFixedResolution( + camera = CameraComponent.withFixedResolution( world: world, width: 320, height: 180, ); - addAll([cameraComponent, world]); _addMovingWall(); @@ -84,8 +81,8 @@ This examples showcases how raycast APIs can be used to detect hits within certa void update(double dt) { collisionDetection.raycast(_ray, maxDistance: _maxDistance, out: _result); if (_result.isActive) { - if (cameraComponent.viewfinder.children.query().isEmpty) { - cameraComponent.viewfinder.add( + if (camera.viewfinder.children.query().isEmpty) { + camera.viewfinder.add( MoveEffect.by( Vector2(5, 5), PerlinNoiseEffectController(duration: 0.2, frequency: 400), diff --git a/examples/lib/stories/components/look_at_example.dart b/examples/lib/stories/components/look_at_example.dart index 0ff55d2147..090241c786 100644 --- a/examples/lib/stories/components/look_at_example.dart +++ b/examples/lib/stories/components/look_at_example.dart @@ -16,8 +16,7 @@ class LookAtExample extends FlameGame { 'oriented in the desired direction if the image is not facing the ' 'correct direction.'; - final world = _TapWorld(); - late final CameraComponent cameraComponent; + LookAtExample() : super(world: _TapWorld()); late SpriteAnimationComponent _chopper1; late SpriteAnimationComponent _chopper2; @@ -27,9 +26,6 @@ class LookAtExample extends FlameGame { @override Future onLoad() async { - cameraComponent = CameraComponent(world: world); - addAll([cameraComponent, world]); - final spriteSheet = SpriteSheet( image: await images.load('animations/chopper.png'), srcSize: Vector2.all(48), diff --git a/examples/lib/stories/components/look_at_smooth_example.dart b/examples/lib/stories/components/look_at_smooth_example.dart index 36d015e20a..7d1e8ee470 100644 --- a/examples/lib/stories/components/look_at_smooth_example.dart +++ b/examples/lib/stories/components/look_at_smooth_example.dart @@ -17,8 +17,7 @@ class LookAtSmoothExample extends FlameGame { 'oriented in the desired direction if the image is not facing the ' 'correct direction.'; - final world = _TapWorld(); - late final CameraComponent cameraComponent; + LookAtSmoothExample() : super(world: _TapWorld()); late SpriteAnimationComponent _chopper1; late SpriteAnimationComponent _chopper2; @@ -28,9 +27,6 @@ class LookAtSmoothExample extends FlameGame { @override Future onLoad() async { - cameraComponent = CameraComponent(world: world); - addAll([cameraComponent, world]); - final spriteSheet = SpriteSheet( image: await images.load('animations/chopper.png'), srcSize: Vector2.all(48), diff --git a/examples/lib/stories/components/time_scale_example.dart b/examples/lib/stories/components/time_scale_example.dart index 60638bb214..34a00012ed 100644 --- a/examples/lib/stories/components/time_scale_example.dart +++ b/examples/lib/stories/components/time_scale_example.dart @@ -14,6 +14,14 @@ class TimeScaleExample extends FlameGame static const description = 'This example shows how time scale can be used to control game speed.'; + TimeScaleExample() + : super( + camera: CameraComponent.withFixedResolution( + width: 640, + height: 360, + ), + ); + final gameSpeedText = TextComponent( text: 'Time Scale: 1', textRenderer: TextPaint( @@ -31,17 +39,8 @@ class TimeScaleExample extends FlameGame @override Color backgroundColor() => const Color.fromARGB(255, 88, 114, 97); - final world = World(); - late final CameraComponent cameraComponent; - @override Future onLoad() async { - cameraComponent = CameraComponent.withFixedResolution( - world: world, - width: 640, - height: 360, - ); - addAll([world, cameraComponent]); final spriteSheet = SpriteSheet( image: await images.load('animations/chopper.png'), srcSize: Vector2.all(48), diff --git a/examples/lib/stories/effects/effect_controllers_example.dart b/examples/lib/stories/effects/effect_controllers_example.dart index f00be8b082..a79a2c327a 100644 --- a/examples/lib/stories/effects/effect_controllers_example.dart +++ b/examples/lib/stories/effects/effect_controllers_example.dart @@ -20,19 +20,20 @@ class EffectControllersExample extends FlameGame { delayed. '''; - final world = World(); - late final CameraComponent cameraComponent; + EffectControllersExample() + : super( + camera: CameraComponent.withFixedResolution( + width: 400, + height: 600, + ), + world: _EffectControllerWorld(), + ); +} +class _EffectControllerWorld extends World { @override void onLoad() { - cameraComponent = CameraComponent.withFixedResolution( - world: world, - width: 400, - height: 600, - ); - addAll([world, cameraComponent]); - - world.add( + add( RectangleComponent.square( position: Vector2(-140, 0), size: 20, @@ -43,7 +44,7 @@ class EffectControllersExample extends FlameGame { ), ), ); - world.add( + add( RectangleComponent.square( position: Vector2(-50, 0), size: 20, @@ -60,7 +61,7 @@ class EffectControllersExample extends FlameGame { ]), ); - world.add( + add( RectangleComponent.square( position: Vector2(50, 0), size: 20, @@ -72,7 +73,7 @@ class EffectControllersExample extends FlameGame { ), ), ); - world.add( + add( RectangleComponent.square( position: Vector2(140, 0), size: 10, diff --git a/examples/lib/stories/effects/move_effect_example.dart b/examples/lib/stories/effects/move_effect_example.dart index 025b603211..290064c941 100644 --- a/examples/lib/stories/effects/move_effect_example.dart +++ b/examples/lib/stories/effects/move_effect_example.dart @@ -22,19 +22,19 @@ class MoveEffectExample extends FlameGame { an arbitrary path using `MoveEffect.along`. '''; - final world = World(); - late final CameraComponent cameraComponent; + MoveEffectExample() + : super( + camera: CameraComponent.withFixedResolution( + width: 400, + height: 600, + )..viewfinder.anchor = Anchor.topLeft, + world: _MoveEffectWorld(), + ); +} +class _MoveEffectWorld extends World { @override void onLoad() { - cameraComponent = CameraComponent.withFixedResolution( - world: world, - width: 400, - height: 600, - ); - cameraComponent.viewfinder.anchor = Anchor.topLeft; - addAll([world, cameraComponent]); - final paint1 = Paint() ..style = PaintingStyle.stroke ..strokeWidth = 5.0 @@ -46,7 +46,7 @@ class MoveEffectExample extends FlameGame { final paint3 = Paint()..color = const Color(0xffb372dc); // Red square, moving back and forth - world.add( + add( RectangleComponent.square( position: Vector2(20, 50), size: 20, @@ -65,7 +65,7 @@ class MoveEffectExample extends FlameGame { ); // Green square, moving and jumping - world.add( + add( RectangleComponent.square( position: Vector2(20, 150), size: 20, @@ -97,7 +97,7 @@ class MoveEffectExample extends FlameGame { ); // Purple square, vibrating from two noise controllers. - world.add( + add( RectangleComponent.square( size: 15, position: Vector2(40, 240), @@ -123,7 +123,7 @@ class MoveEffectExample extends FlameGame { // A circle of moving rectangles. final path2 = Path()..addOval(const Rect.fromLTRB(80, 230, 320, 470)); for (var i = 0; i < 20; i++) { - world.add( + add( RectangleComponent.square(size: 10) ..position = Vector2(i * 10, 0) ..paint = (Paint()..color = Colors.tealAccent) @@ -150,7 +150,7 @@ class MoveEffectExample extends FlameGame { path1.lineTo(x, y); } for (var i = 0; i < 40; i++) { - world.add( + add( CircleComponent(radius: 5) ..position = Vector2(i * 10, 0) ..add( diff --git a/examples/lib/stories/effects/remove_effect_example.dart b/examples/lib/stories/effects/remove_effect_example.dart index 4f587ee749..af7c22ade4 100644 --- a/examples/lib/stories/effects/remove_effect_example.dart +++ b/examples/lib/stories/effects/remove_effect_example.dart @@ -12,22 +12,23 @@ class RemoveEffectExample extends FlameGame { disappear after a 0.5 second delay. '''; - final world = World(); - late final CameraComponent cameraComponent; + RemoveEffectExample() + : super( + camera: CameraComponent.withFixedResolution( + width: 400, + height: 600, + )..viewfinder.anchor = Anchor.topLeft, + world: _RemoveEffectWorld(), + ); +} +class _RemoveEffectWorld extends World { @override void onLoad() { - super.onMount(); - cameraComponent = CameraComponent.withFixedResolution( - world: world, - width: 400, - height: 600, - ); - cameraComponent.viewfinder.anchor = Anchor.topLeft; - addAll([cameraComponent, world]); + super.onLoad(); final rng = Random(); for (var i = 0; i < 20; i++) { - world.add(_RandomCircle.random(rng)); + add(_RandomCircle.random(rng)); } } } diff --git a/examples/lib/stories/effects/rotate_effect_example.dart b/examples/lib/stories/effects/rotate_effect_example.dart index 51d32dd6f6..b4586e7b9d 100644 --- a/examples/lib/stories/effects/rotate_effect_example.dart +++ b/examples/lib/stories/effects/rotate_effect_example.dart @@ -17,20 +17,21 @@ class RotateEffectExample extends FlameGame { add small amounts of wobble, creating quasi-chaotic movement. '''; - final world = World(); - late final CameraComponent cameraComponent; + RotateEffectExample() + : super( + camera: CameraComponent.withFixedResolution( + width: 400, + height: 600, + )..viewfinder.anchor = Anchor.topLeft, + world: _RotateEffectWorld(), + ); +} +class _RotateEffectWorld extends World { @override void onLoad() { - cameraComponent = CameraComponent.withFixedResolution( - world: world, - width: 400, - height: 600, - ); - addAll([cameraComponent, world]); - final compass = Compass(size: 200); - world.add(compass); + add(compass); compass.rim.add( RotateEffect.by( diff --git a/examples/lib/stories/experimental/camera_follow_and_world_bounds.dart b/examples/lib/stories/experimental/camera_follow_and_world_bounds.dart index 4ad657b123..d327c9687e 100644 --- a/examples/lib/stories/experimental/camera_follow_and_world_bounds.dart +++ b/examples/lib/stories/experimental/camera_follow_and_world_bounds.dart @@ -20,14 +20,11 @@ class CameraFollowAndWorldBoundsExample extends FlameGame @override Future onLoad() async { - final world = World()..addToParent(this); - final camera = CameraComponent(world: world); final player = Player()..position = Vector2(250, 0); camera ..viewfinder.visibleGameSize = Vector2(400, 100) ..follow(player, horizontalOnly: true) ..setBounds(Rectangle.fromLTRB(190, -50, 810, 50)); - add(camera); world.add(Ground()); world.add(player); } diff --git a/examples/lib/stories/input/draggables_example.dart b/examples/lib/stories/input/draggables_example.dart index 493b98377d..1d66cf59ea 100644 --- a/examples/lib/stories/input/draggables_example.dart +++ b/examples/lib/stories/input/draggables_example.dart @@ -1,5 +1,4 @@ import 'package:examples/commons/ember.dart'; -import 'package:flame/camera.dart'; import 'package:flame/events.dart'; import 'package:flame/game.dart'; import 'package:flutter/material.dart' show Colors; @@ -13,17 +12,12 @@ class DraggablesExample extends FlameGame { DraggablesExample({required this.zoom}); - final world = World(); - late final CameraComponent cameraComponent; final double zoom; late final DraggableEmber square; @override Future onLoad() async { - cameraComponent = CameraComponent(world: world); - cameraComponent.viewfinder.zoom = zoom; - addAll([cameraComponent, world]); - + camera.viewfinder.zoom = zoom; world.add(square = DraggableEmber()); world.add(DraggableEmber()..y = 350); } diff --git a/examples/lib/stories/input/joystick_advanced_example.dart b/examples/lib/stories/input/joystick_advanced_example.dart index c1125ada89..4b18fb910b 100644 --- a/examples/lib/stories/input/joystick_advanced_example.dart +++ b/examples/lib/stories/input/joystick_advanced_example.dart @@ -25,21 +25,15 @@ class JoystickAdvancedExample extends FlameGame with HasCollisionDetection { late final TextComponent speedText; late final TextComponent directionText; - final world = World(); - late final CameraComponent cameraComponent; - @override Future onLoad() async { - cameraComponent = CameraComponent(world: world); - addAll([cameraComponent, world]); - final image = await images.load('joystick.png'); final sheet = SpriteSheet.fromColumnsAndRows( image: image, columns: 6, rows: 1, ); - world.add(ScreenHitbox()..anchor = cameraComponent.viewfinder.anchor); + world.add(ScreenHitbox()..anchor = camera.viewfinder.anchor); joystick = JoystickComponent( knob: SpriteComponent( sprite: sheet.getSpriteById(1), @@ -185,14 +179,14 @@ class JoystickAdvancedExample extends FlameGame with HasCollisionDetection { )..add(directionText); world.add(player); - cameraComponent.viewport.add(joystick); - cameraComponent.viewport.add(flipButton); - cameraComponent.viewport.add(flopButton); - cameraComponent.viewport.add(buttonComponent); - cameraComponent.viewport.add(spriteButtonComponent); - cameraComponent.viewport.add(shapeButton); - cameraComponent.viewport.add(speedWithMargin); - cameraComponent.viewport.add(directionWithMargin); + camera.viewport.add(joystick); + camera.viewport.add(flipButton); + camera.viewport.add(flopButton); + camera.viewport.add(buttonComponent); + camera.viewport.add(spriteButtonComponent); + camera.viewport.add(shapeButton); + camera.viewport.add(speedWithMargin); + camera.viewport.add(directionWithMargin); } @override diff --git a/examples/lib/stories/rendering/particles_interactive_example.dart b/examples/lib/stories/rendering/particles_interactive_example.dart index 15667b36a4..b08ff2a224 100644 --- a/examples/lib/stories/rendering/particles_interactive_example.dart +++ b/examples/lib/stories/rendering/particles_interactive_example.dart @@ -15,26 +15,18 @@ class ParticlesInteractiveExample extends FlameGame with PanDetector { final random = Random(); final Tween noise = Tween(begin: -1, end: 1); final ColorTween colorTween; - final double zoom; - final world = World(); - late final CameraComponent cameraComponent; ParticlesInteractiveExample({ required Color from, required Color to, - required this.zoom, - }) : colorTween = ColorTween(begin: from, end: to); - - @override - Future onLoad() async { - cameraComponent = CameraComponent.withFixedResolution( - world: world, - width: 400, - height: 600, - ); - addAll([cameraComponent, world]); - cameraComponent.viewfinder.zoom = zoom; - } + required double zoom, + }) : colorTween = ColorTween(begin: from, end: to), + super( + camera: CameraComponent.withFixedResolution( + width: 400, + height: 600, + )..viewfinder.zoom = zoom, + ); @override void onPanUpdate(DragUpdateInfo info) { diff --git a/examples/lib/stories/structure/levels.dart b/examples/lib/stories/structure/levels.dart new file mode 100644 index 0000000000..15a9f960d3 --- /dev/null +++ b/examples/lib/stories/structure/levels.dart @@ -0,0 +1,150 @@ +import 'package:examples/commons/ember.dart'; +import 'package:flame/components.dart'; +import 'package:flame/effects.dart'; +import 'package:flame/events.dart'; +import 'package:flame/experimental.dart'; +import 'package:flame/game.dart'; +import 'package:flame/input.dart'; +import 'package:flutter/material.dart'; + +class LevelsExample extends FlameGame { + static const String description = ''' + In this example we showcase how you can utilize World components as levels. + Press the different buttons in the bottom to change levels and press in the + center to add new Ember's. You can see how level 1-3 keeps their state, + meanwhile the one called Resettable always resets. + '''; + + LevelsExample() : super(world: ResettableLevel()); + + late final TextComponent header; + + @override + Future onLoad() async { + header = TextComponent( + text: 'test', + position: Vector2(size.x / 2, 50), + anchor: Anchor.center, + ); + // If you have a lot of HUDs you could also create separate viewports for + // each level and then just change them from within the world's onLoad with: + // game.cameraComponent.viewport = Level1Viewport(); + final viewport = camera.viewport; + viewport.add(header); + final levels = [Level1(), Level2(), Level3()]; + viewport.addAll( + [ + LevelButton( + 'Level 1', + onPressed: () => world = levels[0], + position: Vector2(size.x / 2 - 210, size.y - 50), + ), + LevelButton( + 'Level 2', + onPressed: () => world = levels[1], + position: Vector2(size.x / 2 - 70, size.y - 50), + ), + LevelButton( + 'Level 3', + onPressed: () => world = levels[2], + position: Vector2(size.x / 2 + 70, size.y - 50), + ), + LevelButton( + 'Resettable', + onPressed: () => world = ResettableLevel(), + position: Vector2(size.x / 2 + 210, size.y - 50), + ), + ], + ); + } +} + +class ResettableLevel extends Level { + @override + Future onLoad() async { + add( + Ember() + ..add( + ScaleEffect.by( + Vector2.all(3), + EffectController(duration: 1, alternate: true, infinite: true), + ), + ), + ); + game.header.text = 'Resettable'; + } +} + +class Level1 extends Level { + @override + Future onLoad() async { + add(Ember()); + game.header.text = 'Level 1'; + } +} + +class Level2 extends Level { + @override + Future onLoad() async { + add(Ember(position: Vector2(-100, 0))); + add(Ember(position: Vector2(100, 0))); + game.header.text = 'Level 2'; + } +} + +class Level3 extends Level { + @override + Future onLoad() async { + add(Ember(position: Vector2(-100, -50))); + add(Ember(position: Vector2(100, -50))); + add(Ember(position: Vector2(0, 50))); + game.header.text = 'Level 3'; + } +} + +class Level extends World with HasGameReference, TapCallbacks { + @override + void onTapDown(TapDownEvent event) { + add(Ember(position: event.localPosition)); + } +} + +class LevelButton extends ButtonComponent { + LevelButton(String text, {super.onPressed, super.position}) + : super( + button: ButtonBackground(Colors.white), + buttonDown: ButtonBackground(Colors.orangeAccent), + children: [ + TextComponent( + text: text, + position: Vector2(60, 20), + anchor: Anchor.center, + ), + ], + size: Vector2(120, 40), + anchor: Anchor.center, + ); +} + +class ButtonBackground extends PositionComponent with HasAncestor { + ButtonBackground(Color color) { + _paint.color = color; + } + + @override + void onMount() { + super.onMount(); + size = ancestor.size; + } + + late final _background = RRect.fromRectAndRadius( + size.toRect(), + const Radius.circular(5), + ); + final _paint = Paint()..style = PaintingStyle.stroke; + + @override + void render(Canvas canvas) { + canvas.drawRRect(_background, _paint); + } +} diff --git a/examples/lib/stories/structure/structure.dart b/examples/lib/stories/structure/structure.dart new file mode 100644 index 0000000000..e49660181e --- /dev/null +++ b/examples/lib/stories/structure/structure.dart @@ -0,0 +1,13 @@ +import 'package:dashbook/dashbook.dart'; +import 'package:examples/commons/commons.dart'; +import 'package:examples/stories/structure/levels.dart'; +import 'package:flame/game.dart'; + +void addStructureStories(Dashbook dashbook) { + dashbook.storiesOf('Structure').add( + 'Levels', + (_) => GameWidget(game: LevelsExample()), + info: LevelsExample.description, + codeLink: baseLink('structure/levels.dart'), + ); +} diff --git a/examples/lib/stories/svg/svg_component.dart b/examples/lib/stories/svg/svg_component.dart index 545b05bf9d..fd368864ea 100644 --- a/examples/lib/stories/svg/svg_component.dart +++ b/examples/lib/stories/svg/svg_component.dart @@ -1,8 +1,8 @@ import 'dart:math'; import 'package:flame/components.dart'; +import 'package:flame/events.dart'; import 'package:flame/game.dart'; -import 'package:flame/input.dart'; import 'package:flame_svg/flame_svg.dart'; class Player extends SvgComponent with HasGameRef { @@ -67,8 +67,7 @@ class Balloons extends SvgComponent with HasGameRef { } } -class SvgComponentExample extends FlameGame - with TapDetector, DoubleTapDetector { +class SvgComponentExample extends FlameGame { static const description = ''' Simple game showcasing how to use SVGs inside a flame game. This game uses several SVGs for its graphics. Click or touch the screen to make the @@ -76,24 +75,27 @@ class SvgComponentExample extends FlameGame clicked position. '''; + SvgComponentExample() + : super( + camera: CameraComponent.withFixedResolution( + width: 400, + height: 600, + ), + world: _SvgComponentWorld(), + ); +} + +class _SvgComponentWorld extends World with TapCallbacks, DoubleTapCallbacks { late Player player; - final world = World(); - late final CameraComponent cameraComponent; @override Future? onLoad() async { await super.onLoad(); - cameraComponent = CameraComponent.withFixedResolution( - world: world, - width: 400, - height: 600, - ); - addAll([cameraComponent, world]); - world.add(player = Player()); - world.add(Background()); + add(player = Player()); + add(Background()); - world.addAll([ + addAll([ Balloons(position: Vector2(-10, -20)), Balloons(position: Vector2(-100, -150)), Balloons(position: Vector2(-200, -140)), @@ -103,12 +105,12 @@ class SvgComponentExample extends FlameGame } @override - void onTapUp(TapUpInfo info) { - player.destination = info.eventPosition.game; + void onTapUp(TapUpEvent info) { + player.destination = info.localPosition; } @override - void onDoubleTapDown(TapDownInfo info) { - add(Balloons()..position = info.eventPosition.game); + void onDoubleTapDown(DoubleTapDownEvent info) { + add(Balloons()..position = info.localPosition); } } diff --git a/packages/flame/lib/src/camera/camera_component.dart b/packages/flame/lib/src/camera/camera_component.dart index 783a0b1f39..0481771412 100644 --- a/packages/flame/lib/src/camera/camera_component.dart +++ b/packages/flame/lib/src/camera/camera_component.dart @@ -15,7 +15,7 @@ import 'package:flame/src/effects/move_effect.dart'; import 'package:flame/src/effects/move_to_effect.dart'; import 'package:flame/src/effects/provider_interfaces.dart'; import 'package:flame/src/experimental/geometry/shapes/shape.dart'; -import 'package:meta/meta.dart'; +import 'package:flame/src/game/flame_game.dart'; import 'package:vector_math/vector_math_64.dart'; /// [CameraComponent] is a component through which a [World] is observed. @@ -30,7 +30,8 @@ import 'package:vector_math/vector_math_64.dart'; /// main game world. However, additional cameras may also be used for some /// special effects. These extra cameras may be placed either in parallel with /// the main camera, or within the world. It is even possible to create a camera -/// that looks at itself. +/// that looks at itself. [FlameGame] has one [CameraComponent] added by default +/// which is called just [FlameGame.camera]. /// /// Since [CameraComponent] is a [Component], it is possible to attach other /// components to it. In particular, adding components directly to the camera is @@ -45,13 +46,15 @@ class CameraComponent extends Component { Viewport? viewport, Viewfinder? viewfinder, List? hudComponents, - }) : viewport = (viewport ?? MaxViewport())..addAll(hudComponents ?? []), - viewfinder = viewfinder ?? Viewfinder(), + }) : _viewport = (viewport ?? MaxViewport())..addAll(hudComponents ?? []), + _viewfinder = viewfinder ?? Viewfinder(), // The priority is set to the max here to avoid some bugs for the users, // if they for example would add any components that modify positions // before the CameraComponent, since it then will render the positions // of the last tick each tick. - super(priority: 0x7fffffff); + super(priority: 0x7fffffff) { + addAll([_viewport, _viewfinder]); + } /// Create a camera that shows a portion of the game world of fixed size /// [width] x [height]. @@ -83,7 +86,15 @@ class CameraComponent extends Component { /// the world can be observed. The viewport's size is equal to or smaller /// than the size of the game canvas. If it is smaller, then the viewport's /// position specifies where exactly it is placed on the canvas. - final Viewport viewport; + Viewport get viewport => _viewport; + set viewport(Viewport newViewport) { + _viewport.removeFromParent(); + _viewport = newViewport; + add(_viewport); + _viewfinder.updateTransform(); + } + + Viewport _viewport; /// The [viewfinder] controls which part of the world is seen through the /// viewport. @@ -92,7 +103,14 @@ class CameraComponent extends Component { /// center of the viewport. In addition, viewfinder controls the zoom level /// (i.e. how much of the world is seen through the viewport), and, /// optionally, rotation. - final Viewfinder viewfinder; + Viewfinder get viewfinder => _viewfinder; + set viewfinder(Viewfinder newViewfinder) { + _viewfinder.removeFromParent(); + _viewfinder = newViewfinder; + add(_viewfinder); + } + + Viewfinder _viewfinder; /// Special component that is designed to be the root of a game world. /// @@ -121,8 +139,8 @@ class CameraComponent extends Component { /// after the camera was fully mounted. Rect get visibleWorldRect { assert( - viewport.isLoaded && viewfinder.isLoaded, - 'This property cannot be accessed before the camera is loaded. ' + parent != null, + "This property can't be accessed before the camera is added to the game. " 'If you are using visibleWorldRect from another component (for example ' 'the World), make sure that the CameraComponent is added before that ' 'Component.', @@ -130,12 +148,6 @@ class CameraComponent extends Component { return viewfinder.visibleWorldRect; } - @mustCallSuper - @override - Future onLoad() async { - await addAll([viewport, viewfinder]); - } - /// Renders the [world] as seen through this camera. /// /// If the world is not mounted yet, only the viewport HUD elements will be diff --git a/packages/flame/lib/src/camera/viewfinder.dart b/packages/flame/lib/src/camera/viewfinder.dart index 9e3e44380f..0d246d8d09 100644 --- a/packages/flame/lib/src/camera/viewfinder.dart +++ b/packages/flame/lib/src/camera/viewfinder.dart @@ -189,16 +189,17 @@ class Viewfinder extends Component void onLoad() { // This has to be done here and on onMount so that it is available for // the CameraComponent.visibleWorldRect calculation in onLoad of the game. - _initializeTransform(); + updateTransform(); } @mustCallSuper @override void onMount() { - _initializeTransform(); + updateTransform(); } - void _initializeTransform() { + @internal + void updateTransform() { assert( parent! is CameraComponent, 'Viewfinder can only be mounted to a CameraComponent', diff --git a/packages/flame/lib/src/camera/viewport.dart b/packages/flame/lib/src/camera/viewport.dart index 6942cd293c..248eef53f0 100644 --- a/packages/flame/lib/src/camera/viewport.dart +++ b/packages/flame/lib/src/camera/viewport.dart @@ -1,11 +1,11 @@ import 'dart:ui'; +import 'package:flame/game.dart'; import 'package:flame/src/anchor.dart'; import 'package:flame/src/camera/camera_component.dart'; import 'package:flame/src/components/core/component.dart'; import 'package:flame/src/effects/provider_interfaces.dart'; import 'package:meta/meta.dart'; -import 'package:vector_math/vector_math_64.dart'; /// [Viewport] is a part of a [CameraComponent] system. /// @@ -25,6 +25,7 @@ abstract class Viewport extends Component Viewport({super.children}); final Vector2 _size = Vector2.zero(); + bool _isInitialized = false; /// Position of the viewport's anchor in the parent's coordinate frame. /// @@ -54,7 +55,15 @@ abstract class Viewport extends Component /// Changing the size at runtime triggers the [onViewportResize] event. The /// size cannot be negative. @override - Vector2 get size => _size; + Vector2 get size { + if (!_isInitialized && camera.parent is FlameGame) { + // This is so that the size can be accessed before the viewport is fully + // mounted. + onGameResize((camera.parent! as FlameGame).canvasSize); + } + return _size; + } + @override set size(Vector2 value) { assert( @@ -62,7 +71,8 @@ abstract class Viewport extends Component "Viewport's size cannot be negative: $value", ); _size.setFrom(value); - if (isMounted) { + _isInitialized = true; + if (parent != null) { camera.viewfinder.onViewportResize(); } onViewportResize(); diff --git a/packages/flame/lib/src/collisions/hitboxes/screen_hitbox.dart b/packages/flame/lib/src/collisions/hitboxes/screen_hitbox.dart index 881e19a371..dcacb5c17f 100644 --- a/packages/flame/lib/src/collisions/hitboxes/screen_hitbox.dart +++ b/packages/flame/lib/src/collisions/hitboxes/screen_hitbox.dart @@ -21,7 +21,7 @@ class ScreenHitbox extends PositionComponent // TODO(Lukas): Pass in a CameraComponent and use the position of the // viewfinder, or only allow this to be attached to a viewport. // ignore: deprecated_member_use_from_same_package - position = gameRef.camera.unprojectVector(_zeroVector); + position = gameRef.oldCamera.unprojectVector(_zeroVector); } @override diff --git a/packages/flame/lib/src/components/mixins/component_viewport_margin.dart b/packages/flame/lib/src/components/mixins/component_viewport_margin.dart index c579d3cb42..8822f28599 100644 --- a/packages/flame/lib/src/components/mixins/component_viewport_margin.dart +++ b/packages/flame/lib/src/components/mixins/component_viewport_margin.dart @@ -68,7 +68,7 @@ mixin ComponentViewportMargin on PositionComponent, HasGameRef { void _updateMargins() { final screenSize = positionType == PositionType.viewport // ignore: deprecated_member_use_from_same_package - ? gameRef.camera.viewport.effectiveSize + ? gameRef.oldCamera.viewport.effectiveSize : gameRef.canvasSize; final margin = this.margin!; final x = margin.left != 0 diff --git a/packages/flame/lib/src/components/parallax_component.dart b/packages/flame/lib/src/components/parallax_component.dart index 53bad6ddad..0d1449376a 100644 --- a/packages/flame/lib/src/components/parallax_component.dart +++ b/packages/flame/lib/src/components/parallax_component.dart @@ -87,7 +87,7 @@ class ParallaxComponent extends PositionComponent } // TODO(Lukas): Use CameraComponent here instead. // ignore: deprecated_member_use_from_same_package - final newSize = gameRef.camera.viewport.effectiveSize; + final newSize = gameRef.oldCamera.viewport.effectiveSize; this.size.setFrom(newSize); parallax?.resize(newSize); } diff --git a/packages/flame/lib/src/events/component_mixins/drag_callbacks.dart b/packages/flame/lib/src/events/component_mixins/drag_callbacks.dart index d66417ad12..0660000c74 100644 --- a/packages/flame/lib/src/events/component_mixins/drag_callbacks.dart +++ b/packages/flame/lib/src/events/component_mixins/drag_callbacks.dart @@ -1,5 +1,5 @@ import 'package:flame/src/components/core/component.dart'; -import 'package:flame/src/events/flame_game_mixins/has_draggable_components.dart'; +import 'package:flame/src/events/flame_game_mixins/multi_drag_dispatcher.dart'; import 'package:flame/src/events/messages/drag_cancel_event.dart'; import 'package:flame/src/events/messages/drag_end_event.dart'; import 'package:flame/src/events/messages/drag_start_event.dart'; diff --git a/packages/flame/lib/src/events/component_mixins/tap_callbacks.dart b/packages/flame/lib/src/events/component_mixins/tap_callbacks.dart index 221d236769..e4250ff5c8 100644 --- a/packages/flame/lib/src/events/component_mixins/tap_callbacks.dart +++ b/packages/flame/lib/src/events/component_mixins/tap_callbacks.dart @@ -1,5 +1,5 @@ import 'package:flame/src/components/core/component.dart'; -import 'package:flame/src/events/flame_game_mixins/has_tappable_components.dart'; +import 'package:flame/src/events/flame_game_mixins/multi_tap_dispatcher.dart'; import 'package:flame/src/events/messages/tap_cancel_event.dart'; import 'package:flame/src/events/messages/tap_down_event.dart'; import 'package:flame/src/events/messages/tap_up_event.dart'; diff --git a/packages/flame/lib/src/events/flame_game_mixins/has_draggable_components.dart b/packages/flame/lib/src/events/flame_game_mixins/multi_drag_dispatcher.dart similarity index 100% rename from packages/flame/lib/src/events/flame_game_mixins/has_draggable_components.dart rename to packages/flame/lib/src/events/flame_game_mixins/multi_drag_dispatcher.dart diff --git a/packages/flame/lib/src/events/flame_game_mixins/has_tappable_components.dart b/packages/flame/lib/src/events/flame_game_mixins/multi_tap_dispatcher.dart similarity index 100% rename from packages/flame/lib/src/events/flame_game_mixins/has_tappable_components.dart rename to packages/flame/lib/src/events/flame_game_mixins/multi_tap_dispatcher.dart diff --git a/packages/flame/lib/src/events/tagged_component.dart b/packages/flame/lib/src/events/tagged_component.dart index 0a309da036..b1b4dbb627 100644 --- a/packages/flame/lib/src/events/tagged_component.dart +++ b/packages/flame/lib/src/events/tagged_component.dart @@ -1,6 +1,6 @@ import 'package:flame/src/components/core/component.dart'; -import 'package:flame/src/events/flame_game_mixins/has_draggable_components.dart'; -import 'package:flame/src/events/flame_game_mixins/has_tappable_components.dart'; +import 'package:flame/src/events/flame_game_mixins/multi_drag_dispatcher.dart'; +import 'package:flame/src/events/flame_game_mixins/multi_tap_dispatcher.dart'; import 'package:meta/meta.dart'; /// [TaggedComponent] is a utility class that represents a pair of a component diff --git a/packages/flame/lib/src/game/camera/camera_wrapper.dart b/packages/flame/lib/src/game/camera/camera_wrapper.dart index eeb6e15421..339fc933d8 100644 --- a/packages/flame/lib/src/game/camera/camera_wrapper.dart +++ b/packages/flame/lib/src/game/camera/camera_wrapper.dart @@ -9,9 +9,9 @@ import 'package:meta/meta.dart'; /// using it in any code other than the FlameGame class is unsafe and /// not recommended. @internal -@Deprecated('Will be removed in Flame v2') +@Deprecated('Will be removed in Flame v1.10') class CameraWrapper { - @Deprecated('Will be removed in Flame v2') + @Deprecated('Will be removed in Flame v1.10') CameraWrapper(this.camera, this.world); final Camera camera; diff --git a/packages/flame/lib/src/game/flame_game.dart b/packages/flame/lib/src/game/flame_game.dart index 6efb77486f..746ed909da 100644 --- a/packages/flame/lib/src/game/flame_game.dart +++ b/packages/flame/lib/src/game/flame_game.dart @@ -1,5 +1,6 @@ // ignore_for_file: deprecated_member_use_from_same_package +import 'dart:async'; import 'dart:ui'; import 'package:flame/components.dart'; @@ -18,21 +19,70 @@ import 'package:meta/meta.dart'; /// /// This is the recommended base class to use for most games made with Flame. /// It is based on the Flame Component System (also known as FCS). -class FlameGame extends ComponentTreeRoot +class FlameGame extends ComponentTreeRoot with Game implements ReadOnlySizeProvider { FlameGame({ super.children, - Camera? camera, - }) { + W? world, + CameraComponent? camera, + Camera? oldCamera, + }) : assert( + world != null || W == World, + 'The generics type $W does not conform to the type of ' + '${world?.runtimeType ?? 'World'}.', + ), + _world = world ?? World() as W, + _camera = camera ?? CameraComponent() { assert( Component.staticGameInstance == null, '$this instantiated, while another game ${Component.staticGameInstance} ' 'declares itself to be a singleton', ); - _cameraWrapper = CameraWrapper(camera ?? Camera(), children); + _cameraWrapper = CameraWrapper(oldCamera ?? Camera(), children); + _camera.world = _world; + add(_camera); + add(_world); } + /// The [World] that the [camera] is rendering. + /// Inside of this world is where most of your components should be added. + /// + /// You don't have to add the world to the tree after setting it here, it is + /// done automatically. + W get world => _world; + set world(W newWorld) { + if (newWorld == _world) { + return; + } + _world.removeFromParent(); + camera.world = newWorld; + _world = newWorld; + if (_world.parent == null) { + add(_world); + } + } + + W _world; + + /// The component that is responsible for rendering your [world]. + /// + /// In this component you can set different viewports, viewfinders, follow + /// components, set bounds for where the camera can move etc. + /// + /// You don't have to add the CameraComponent to the tree after setting it + /// here, it is done automatically. + CameraComponent get camera => _camera; + set camera(CameraComponent newCameraComponent) { + _camera.removeFromParent(); + _camera = newCameraComponent; + if (_camera.parent == null) { + add(_camera); + } + } + + CameraComponent _camera; + late final CameraWrapper _cameraWrapper; @internal @@ -40,35 +90,22 @@ class FlameGame extends ComponentTreeRoot /// The camera translates the coordinate space after the viewport is applied. @Deprecated(''' - In the future (maybe as early as v1.9.0) this camera will be removed, - please use the CameraComponent instead. + In the future (maybe as early as v1.10.0) this camera will be removed, + please use the CameraComponent instead, which has a default camera at + `FlameGame.camera`. This is the simplest way of using the CameraComponent: - 1. Add variables for a CameraComponent and a World to your game class - - final world = World(); - late final CameraComponent cameraComponent; - - 2. In your `onLoad` method, initialize the cameraComponent and add the world - to it. - - @override - void onLoad() { - cameraComponent = CameraComponent(world: world); - addAll([cameraComponent, world]); - } - - 3. Instead of adding the root components directly to your game with `add`, + 1. Instead of adding the root components directly to your game with `add`, add them to the world. world.add(yourComponent); - 4. (Optional) If you want to add a HUD component, instead of using + 2. (Optional) If you want to add a HUD component, instead of using PositionType, add the component as a child of the viewport. - cameraComponent.viewport.add(yourHudComponent); + camera.viewport.add(yourHudComponent); ''') - Camera get camera => _cameraWrapper.camera; + Camera get oldCamera => _cameraWrapper.camera; /// This is overwritten to consider the viewport transformation. /// @@ -77,7 +114,7 @@ class FlameGame extends ComponentTreeRoot /// /// This does not match the Flutter widget size; for that see [canvasSize]. @override - Vector2 get size => camera.gameSize; + Vector2 get size => oldCamera.gameSize; @override @internal @@ -134,12 +171,13 @@ class FlameGame extends ComponentTreeRoot /// components and other methods. /// You can override it further to add more custom behavior, but you should /// seriously consider calling the super implementation as well. - /// This implementation also uses the current [camera] in order to transform - /// the coordinate system appropriately. + /// This implementation also uses the current [oldCamera] in order to + /// transform the coordinate system appropriately for those using the old + /// camera. @override @mustCallSuper void onGameResize(Vector2 size) { - camera.handleResize(size); + oldCamera.handleResize(size); super.onGameResize(size); // [onGameResize] is declared both in [Component] and in [Game]. Since // there is no way to explicitly call the [Component]'s implementation, @@ -183,12 +221,12 @@ class FlameGame extends ComponentTreeRoot } @override - Projector get viewportProjector => camera.viewport; + Projector get viewportProjector => oldCamera.viewport; @override - Projector get projector => camera.combinedProjector; + Projector get projector => oldCamera.combinedProjector; - /// Returns a [ComponentsNotifier] for the given type [T]. + /// Returns a [ComponentsNotifier] for the given type [W]. /// /// This method handles duplications, so there will never be /// more than one [ComponentsNotifier] for a given type, meaning diff --git a/packages/flame/test/camera/camera_component_test.dart b/packages/flame/test/camera/camera_component_test.dart index 7818e4dfba..4646c83013 100644 --- a/packages/flame/test/camera/camera_component_test.dart +++ b/packages/flame/test/camera/camera_component_test.dart @@ -181,20 +181,18 @@ void main() { }); testWithFlameGame('componentsAtPoint', (game) async { - final world = World(); - final camera = CameraComponent( - world: world, - viewport: FixedSizeViewport(600, 400), - ) - ..viewport.anchor = Anchor.center - ..viewport.position = Vector2(400, 300) - ..viewfinder.position = Vector2(100, 50); + game.camera.viewport = FixedSizeViewport(600, 400) + ..anchor = Anchor.center + ..position = Vector2(400, 300) + ..priority = -1; + game.camera.viewfinder.position = Vector2(100, 50); final component = PositionComponent( size: Vector2(300, 100), position: Vector2(50, 30), ); + final world = game.world; + final camera = game.camera; world.add(component); - game.addAll([world, camera]); await game.ready(); final nested = []; diff --git a/packages/flame/test/camera/viewports/max_viewport_test.dart b/packages/flame/test/camera/viewports/max_viewport_test.dart index 490e386a5e..716fe589b3 100644 --- a/packages/flame/test/camera/viewports/max_viewport_test.dart +++ b/packages/flame/test/camera/viewports/max_viewport_test.dart @@ -1,7 +1,7 @@ import 'package:flame/camera.dart'; +import 'package:flame/game.dart'; import 'package:flame_test/flame_test.dart'; import 'package:flutter_test/flutter_test.dart'; -import 'package:vector_math/vector_math_64.dart'; void main() { group('MaxViewport', () { @@ -41,9 +41,22 @@ void main() { final viewport = camera.viewport; expect(viewport, isA<_MyMaxViewport>()); - expect((viewport as _MyMaxViewport).onViewportResizeCalled, 3); + expect((viewport as _MyMaxViewport).onViewportResizeCalled, 2); game.onGameResize(Vector2(200, 200)); - expect(viewport.onViewportResizeCalled, 4); + expect(viewport.onViewportResizeCalled, 3); + }); + + testWithGame( + 'check that onViewportResize is called with game CameraComponent', () { + return FlameGame( + camera: CameraComponent(viewport: _MyMaxViewport()), + ); + }, (game) async { + final viewport = game.camera.viewport; + expect(viewport, isA<_MyMaxViewport>()); + expect((viewport as _MyMaxViewport).onViewportResizeCalled, 2); + game.onGameResize(Vector2(200, 200)); + expect(viewport.onViewportResizeCalled, 3); }); }); } diff --git a/packages/flame/test/camera/world_test.dart b/packages/flame/test/camera/world_test.dart index fd64e8951b..27a4becfb4 100644 --- a/packages/flame/test/camera/world_test.dart +++ b/packages/flame/test/camera/world_test.dart @@ -8,6 +8,7 @@ void main() { testWithFlameGame( 'by default it has a negative max 32bit int priority', (game) async { + game.removeAll(game.children); final world = World()..addToParent(game); final camera = CameraComponent(world: world)..addToParent(game); await game.ready(); @@ -20,6 +21,7 @@ void main() { testWithFlameGame( 'with a custom priority putting it in front of the camera in the tree', (game) async { + game.removeAll(game.children); final world = World(priority: 4); final camera = CameraComponent(world: world)..priority = 0; game.addAll([world, camera]); diff --git a/packages/flame/test/collisions/collision_detection_test.dart b/packages/flame/test/collisions/collision_detection_test.dart index 48e2d76796..133f60437d 100644 --- a/packages/flame/test/collisions/collision_detection_test.dart +++ b/packages/flame/test/collisions/collision_detection_test.dart @@ -804,6 +804,7 @@ void main() { }); testCollisionDetectionGame('circles as children', (game) async { + final world = game.world; final outerCircle = CircleHitbox( radius: 4.0, anchor: Anchor.center, @@ -820,7 +821,7 @@ void main() { position: Vector2.all(100), children: [innerCircle], ); - await game.ensureAddAll([outerContainer, innerContainer]); + await world.ensureAddAll([outerContainer, innerContainer]); final intersections = game.collisionDetection.intersections( innerCircle, outerCircle, @@ -971,6 +972,7 @@ void main() { 'circle enclosed by solid polygon defined in clockwise (wrong) order', _CollisionDetectionGame.new, (game) async { + final world = game.world; final polygonSize = Vector2.all(3); final innerCircle = CircleHitbox(); final outerPolygon = PolygonHitbox.relative( @@ -986,7 +988,7 @@ void main() { ], parentSize: polygonSize, )..isSolid = true; - await game.ensureAddAll([ + await world.ensureAddAll([ PositionComponent( position: Vector2.all(3), size: Vector2.all(1), @@ -1015,7 +1017,8 @@ void main() { runCollisionTestRegistry({ 'one hitbox': (collisionSystem) async { final game = collisionSystem as FlameGame; - await game.ensureAdd( + final world = game.world; + await world.ensureAdd( PositionComponent( children: [RectangleHitbox()], position: Vector2(100, 0), @@ -1030,13 +1033,14 @@ void main() { direction: Vector2(1, 0), ); final result = collisionSystem.collisionDetection.raycast(ray); - expect(result?.hitbox?.parent, game.children.first); + expect(result?.hitbox?.parent, world.children.first); expect(result?.reflectionRay?.origin, closeToVector(Vector2(50, 0))); expect(result?.reflectionRay?.direction, closeToVector(Vector2(-1, 0))); }, 'multiple hitboxes after each other': (collisionSystem) async { final game = collisionSystem as FlameGame; - await game.ensureAddAll([ + final world = game.world; + await world.ensureAddAll([ for (var i = 0.0; i < 10; i++) PositionComponent( position: Vector2.all(100 + i * 10), @@ -1050,7 +1054,7 @@ void main() { direction: Vector2.all(1)..normalize(), ); final result = collisionSystem.collisionDetection.raycast(ray); - expect(result?.hitbox?.parent, game.children.first); + expect(result?.hitbox?.parent, world.children.first); expect(result?.reflectionRay?.origin, closeToVector(Vector2.all(90))); expect( result?.reflectionRay?.direction, @@ -1060,7 +1064,8 @@ void main() { 'multiple hitboxes after each other with one ignored': (collisionSystem) async { final game = collisionSystem as FlameGame; - await game.ensureAddAll([ + final world = game.world; + await world.ensureAddAll([ for (var i = 0.0; i < 10; i++) PositionComponent( position: Vector2.all(100 + i * 10), @@ -1076,10 +1081,10 @@ void main() { final result = collisionSystem.collisionDetection.raycast( ray, ignoreHitboxes: [ - game.children.first.children.first as ShapeHitbox, + world.children.first.children.first as ShapeHitbox, ], ); - expect(result?.hitbox?.parent, game.children.toList()[1]); + expect(result?.hitbox?.parent, game.world.children.toList()[1]); expect( result?.reflectionRay?.origin, closeToVector(Vector2.all(100.5)), @@ -1091,7 +1096,8 @@ void main() { }, 'ray with origin on hitbox corner': (collisionSystem) async { final game = collisionSystem as FlameGame; - await game.ensureAddAll([ + final world = game.world; + await world.ensureAddAll([ PositionComponent( position: Vector2.all(10), size: Vector2.all(10), @@ -1103,7 +1109,7 @@ void main() { direction: Vector2.all(1)..normalize(), ); final result = collisionSystem.collisionDetection.raycast(ray); - expect(result?.hitbox?.parent, game.children.first); + expect(result?.hitbox?.parent, world.children.first); expect(result?.reflectionRay?.origin, closeToVector(Vector2(20, 20))); expect( result?.reflectionRay?.direction, @@ -1112,7 +1118,8 @@ void main() { }, 'raycast with maxDistance': (collisionSystem) async { final game = collisionSystem as FlameGame; - await game.ensureAddAll([ + final world = game.world; + await world.ensureAddAll([ PositionComponent( position: Vector2.all(20), size: Vector2.all(40), @@ -1141,7 +1148,7 @@ void main() { maxDistance: Vector2.all(10).length, out: result, ); - expect(result.hitbox?.parent, game.children.first); + expect(result.hitbox?.parent, world.children.first); }, }); @@ -1149,7 +1156,8 @@ void main() { runCollisionTestRegistry({ 'ray from within RectangleHitbox': (collisionSystem) async { final game = collisionSystem as FlameGame; - await game.ensureAddAll([ + final world = game.world; + await world.ensureAddAll([ PositionComponent( position: Vector2.all(0), size: Vector2.all(10), @@ -1161,7 +1169,7 @@ void main() { direction: Vector2.all(1)..normalize(), ); final result = collisionSystem.collisionDetection.raycast(ray); - expect(result?.hitbox?.parent, game.children.first); + expect(result?.hitbox?.parent, world.children.first); expect(result?.normal, closeToVector(Vector2(0, -1))); expect(result?.reflectionRay?.origin, closeToVector(Vector2(10, 10))); expect( @@ -1171,7 +1179,8 @@ void main() { }, 'ray from the left of RectangleHitbox': (collisionSystem) async { final game = collisionSystem as FlameGame; - await game.ensureAddAll([ + final world = game.world; + await world.ensureAddAll([ PositionComponent( position: Vector2.zero(), size: Vector2.all(10), @@ -1180,7 +1189,7 @@ void main() { await game.ready(); final ray = Ray2(origin: Vector2(-5, 5), direction: Vector2(1, 0)); final result = collisionSystem.collisionDetection.raycast(ray); - expect(result?.hitbox?.parent, game.children.first); + expect(result?.hitbox?.parent, world.children.first); expect(result?.reflectionRay?.origin, closeToVector(Vector2(0, 5))); expect( result?.reflectionRay?.direction, @@ -1189,7 +1198,8 @@ void main() { }, 'ray from the top of RectangleHitbox': (collisionSystem) async { final game = collisionSystem as FlameGame; - await game.ensureAddAll([ + final world = game.world; + await world.ensureAddAll([ PositionComponent( position: Vector2.zero(), size: Vector2.all(10), @@ -1198,7 +1208,7 @@ void main() { await game.ready(); final ray = Ray2(origin: Vector2(5, -5), direction: Vector2(0, 1)); final result = collisionSystem.collisionDetection.raycast(ray); - expect(result?.hitbox?.parent, game.children.first); + expect(result?.hitbox?.parent, world.children.first); expect(result?.reflectionRay?.origin, closeToVector(Vector2(5, 0))); expect( result?.reflectionRay?.direction, @@ -1207,7 +1217,8 @@ void main() { }, 'ray from the right of RectangleHitbox': (collisionSystem) async { final game = collisionSystem as FlameGame; - await game.ensureAddAll([ + final world = game.world; + await world.ensureAddAll([ PositionComponent( position: Vector2.zero(), size: Vector2.all(10), @@ -1216,7 +1227,7 @@ void main() { await game.ready(); final ray = Ray2(origin: Vector2(15, 5), direction: Vector2(-1, 0)); final result = collisionSystem.collisionDetection.raycast(ray); - expect(result?.hitbox?.parent, game.children.first); + expect(result?.hitbox?.parent, world.children.first); expect(result?.reflectionRay?.origin, closeToVector(Vector2(10, 5))); expect( result?.reflectionRay?.direction, @@ -1225,7 +1236,8 @@ void main() { }, 'ray from the bottom of RectangleHitbox': (collisionSystem) async { final game = collisionSystem as FlameGame; - await game.ensureAddAll([ + final world = game.world; + await world.ensureAddAll([ PositionComponent( position: Vector2.zero(), size: Vector2.all(10), @@ -1234,7 +1246,7 @@ void main() { await game.ready(); final ray = Ray2(origin: Vector2(5, 15), direction: Vector2(0, -1)); final result = collisionSystem.collisionDetection.raycast(ray); - expect(result?.hitbox?.parent, game.children.first); + expect(result?.hitbox?.parent, world.children.first); expect(result?.reflectionRay?.origin, closeToVector(Vector2(5, 10))); expect( result?.reflectionRay?.direction, @@ -1248,7 +1260,8 @@ void main() { runCollisionTestRegistry({ 'ray from top to bottom within CircleHitbox': (collisionSystem) async { final game = collisionSystem as FlameGame; - await game.ensureAddAll([ + final world = game.world; + await world.ensureAddAll([ PositionComponent( position: Vector2.zero(), size: Vector2.all(10), @@ -1257,7 +1270,7 @@ void main() { await game.ready(); final ray = Ray2(origin: Vector2(5, 4), direction: Vector2(0, 1)); final result = collisionSystem.collisionDetection.raycast(ray); - expect(result?.hitbox?.parent, game.children.first); + expect(result?.hitbox?.parent, world.children.first); expect(result?.normal, closeToVector(Vector2(0, -1))); expect(result?.reflectionRay?.origin, closeToVector(Vector2(5, 10))); expect( @@ -1268,7 +1281,8 @@ void main() { 'ray from bottom-right to top-left within CircleHitbox': (collisionSystem) async { final game = collisionSystem as FlameGame; - await game.ensureAddAll([ + final world = game.world; + await world.ensureAddAll([ PositionComponent( position: Vector2.zero(), size: Vector2.all(10), @@ -1280,7 +1294,7 @@ void main() { direction: Vector2.all(-1)..normalize(), ); final result = collisionSystem.collisionDetection.raycast(ray); - expect(result?.hitbox?.parent, game.children.first); + expect(result?.hitbox?.parent, world.children.first); expect(result?.normal, closeToVector(Vector2.all(0.707106781186547))); expect( result?.intersectionPoint, @@ -1294,7 +1308,8 @@ void main() { 'ray from bottom within CircleHitbox going down': (collisionSystem) async { final game = collisionSystem as FlameGame; - await game.ensureAddAll([ + final world = game.world; + await world.ensureAddAll([ PositionComponent( position: Vector2.zero(), size: Vector2.all(10), @@ -1304,7 +1319,7 @@ void main() { final direction = Vector2(0, 1); final ray = Ray2(origin: Vector2(5, 6), direction: direction); final result = collisionSystem.collisionDetection.raycast(ray); - expect(result?.hitbox?.parent, game.children.first); + expect(result?.hitbox?.parent, world.children.first); expect(result?.normal, closeToVector(Vector2(0, -1))); expect( result?.intersectionPoint, @@ -1317,7 +1332,8 @@ void main() { }, 'ray from the left of CircleHitbox': (collisionSystem) async { final game = collisionSystem as FlameGame; - await game.ensureAddAll([ + final world = game.world; + await world.ensureAddAll([ PositionComponent( position: Vector2.zero(), size: Vector2.all(10), @@ -1326,7 +1342,7 @@ void main() { await game.ready(); final ray = Ray2(origin: Vector2(-5, 5), direction: Vector2(1, 0)); final result = collisionSystem.collisionDetection.raycast(ray); - expect(result?.hitbox?.parent, game.children.first); + expect(result?.hitbox?.parent, world.children.first); expect(result?.reflectionRay?.origin, closeToVector(Vector2(0, 5))); expect( result?.reflectionRay?.direction, @@ -1335,7 +1351,8 @@ void main() { }, 'ray from the top of CircleHitbox': (collisionSystem) async { final game = collisionSystem as FlameGame; - await game.ensureAddAll([ + final world = game.world; + await world.ensureAddAll([ PositionComponent( position: Vector2.zero(), size: Vector2.all(10), @@ -1344,7 +1361,7 @@ void main() { await game.ready(); final ray = Ray2(origin: Vector2(5, -5), direction: Vector2(0, 1)); final result = collisionSystem.collisionDetection.raycast(ray); - expect(result?.hitbox?.parent, game.children.first); + expect(result?.hitbox?.parent, world.children.first); expect(result?.reflectionRay?.origin, closeToVector(Vector2(5, 0))); expect( result?.reflectionRay?.direction, @@ -1353,7 +1370,8 @@ void main() { }, 'ray from the right of CircleHitbox': (collisionSystem) async { final game = collisionSystem as FlameGame; - await game.ensureAddAll([ + final world = game.world; + await world.ensureAddAll([ PositionComponent( position: Vector2.zero(), size: Vector2.all(10), @@ -1362,7 +1380,7 @@ void main() { await game.ready(); final ray = Ray2(origin: Vector2(15, 5), direction: Vector2(-1, 0)); final result = collisionSystem.collisionDetection.raycast(ray); - expect(result?.hitbox?.parent, game.children.first); + expect(result?.hitbox?.parent, world.children.first); expect(result?.reflectionRay?.origin, closeToVector(Vector2(10, 5))); expect( result?.reflectionRay?.direction, @@ -1371,7 +1389,8 @@ void main() { }, 'ray from the bottom of CircleHitbox': (collisionSystem) async { final game = collisionSystem as FlameGame; - await game.ensureAddAll([ + final world = game.world; + await world.ensureAddAll([ PositionComponent( position: Vector2.zero(), size: Vector2.all(10), @@ -1380,7 +1399,7 @@ void main() { await game.ready(); final ray = Ray2(origin: Vector2(5, 15), direction: Vector2(0, -1)); final result = collisionSystem.collisionDetection.raycast(ray); - expect(result?.hitbox?.parent, game.children.first); + expect(result?.hitbox?.parent, world.children.first); expect(result?.reflectionRay?.origin, closeToVector(Vector2(5, 10))); expect( result?.reflectionRay?.direction, @@ -1389,11 +1408,12 @@ void main() { }, 'ray from the center of CircleHitbox': (collisionSystem) async { final game = collisionSystem as FlameGame; + final world = game.world; final positionComponent = PositionComponent( position: Vector2.zero(), size: Vector2.all(10), )..add(CircleHitbox()); - await game.ensureAdd(positionComponent); + await world.ensureAdd(positionComponent); await game.ready(); final ray = Ray2( @@ -1415,7 +1435,8 @@ void main() { runCollisionTestRegistry({ 'All directions and all hits': (collisionSystem) async { final game = collisionSystem as FlameGame; - await game.ensureAddAll([ + final world = game.world; + await world.ensureAddAll([ PositionComponent( position: Vector2(10, 0), size: Vector2.all(10), @@ -1444,7 +1465,8 @@ void main() { }, 'raycastAll with maxDistance': (collisionSystem) async { final game = collisionSystem as FlameGame; - await game.ensureAddAll([ + final world = game.world; + await world.ensureAddAll([ PositionComponent( position: Vector2(10, 0), size: Vector2.all(10), @@ -1487,7 +1509,8 @@ void main() { runCollisionTestRegistry({ 'All directions and all hits': (collisionSystem) async { final game = collisionSystem as FlameGame; - await game.ensureAddAll([ + final world = game.world; + await world.ensureAddAll([ PositionComponent( position: Vector2(10, 0), size: Vector2.all(10), @@ -1507,7 +1530,7 @@ void main() { ]); await game.ready(); final origin = Vector2.all(15); - final ignoreHitbox = game.children.first.children.first as ShapeHitbox; + final ignoreHitbox = world.children.first.children.first as ShapeHitbox; final results = collisionSystem.collisionDetection.raycastAll( origin, numberOfRays: 4, @@ -1524,12 +1547,13 @@ void main() { runCollisionTestRegistry({ 'on single circle': (collisionSystem) async { final game = collisionSystem as FlameGame; + final world = game.world; final circle = CircleComponent( radius: 10.0, position: Vector2.all(20), anchor: Anchor.center, )..add(CircleHitbox()); - await game.ensureAdd(circle); + await world.ensureAdd(circle); final ray = Ray2( origin: Vector2(0, 10), direction: Vector2.all(1.0)..normalize(), @@ -1545,12 +1569,13 @@ void main() { expect(results.first.normal, Vector2(-1, 0)); }, 'on single rectangle': (game) async { + final world = (game as FlameGame).world; final rectangle = RectangleComponent( position: Vector2.all(20), size: Vector2.all(20), anchor: Anchor.center, )..add(RectangleHitbox()); - await game.ensureAdd(rectangle); + await world.ensureAdd(rectangle); final ray = Ray2( origin: Vector2(0, 10), direction: Vector2.all(1.0)..normalize(), @@ -1566,12 +1591,13 @@ void main() { expect(results.first.normal, Vector2(-1, 0)); }, 'on single rectangle with ray with negative X': (game) async { + final world = (game as FlameGame).world; final rectangle = RectangleComponent( position: Vector2(-20, 40), size: Vector2.all(20), anchor: Anchor.center, )..add(RectangleHitbox()); - await game.ensureAdd(rectangle); + await world.ensureAdd(rectangle); final ray = Ray2( origin: Vector2(10, 20), direction: Vector2(-1, 1)..normalize(), @@ -1587,6 +1613,7 @@ void main() { expect(results.first.normal, Vector2(1, 0)); }, 'on two circles': (game) async { + final world = (game as FlameGame).world; final circle1 = CircleComponent( position: Vector2.all(20), radius: 10, @@ -1597,7 +1624,7 @@ void main() { radius: 10, anchor: Anchor.center, )..add(CircleHitbox()); - await game.ensureAddAll([circle1, circle2]); + await world.ensureAddAll([circle1, circle2]); final ray = Ray2( origin: Vector2(0, 10), direction: Vector2.all(1.0)..normalize(), @@ -1622,6 +1649,7 @@ void main() { expect(reflectionRay2?.direction, Vector2(1, 1)..normalize()); }, 'on two rectangles': (game) async { + final world = (game as FlameGame).world; final rectangle1 = RectangleComponent( position: Vector2.all(20), size: Vector2.all(20), @@ -1632,7 +1660,7 @@ void main() { size: Vector2.all(20), anchor: Anchor.center, )..add(RectangleHitbox()); - await game.ensureAddAll([rectangle1, rectangle2]); + await world.ensureAddAll([rectangle1, rectangle2]); final ray = Ray2( origin: Vector2(0, 10), direction: Vector2.all(1.0)..normalize(), @@ -1658,6 +1686,7 @@ void main() { expect(reflectionRay2?.direction, Vector2(1, 1)..normalize()); }, 'on two rectangles with one ignored': (game) async { + final world = (game as FlameGame).world; final rectangle1 = RectangleComponent( position: Vector2.all(20), size: Vector2.all(20), @@ -1668,13 +1697,13 @@ void main() { size: Vector2.all(20), anchor: Anchor.center, )..add(RectangleHitbox()); - await game.ensureAddAll([rectangle1, rectangle2]); + await world.ensureAddAll([rectangle1, rectangle2]); final ray = Ray2( origin: Vector2(0, 10), direction: Vector2.all(1.0)..normalize(), ); final ignoreHitbox = - game.children.toList()[1].children.first as ShapeHitbox; + world.children.toList()[1].children.first as ShapeHitbox; final results = game.collisionDetection .raytrace(ray, ignoreHitboxes: [ignoreHitbox]).toList(); expect(results.length, 1); @@ -1691,6 +1720,7 @@ void main() { expect(results2.length, 1); }, 'on a rectangle within another': (game) async { + final world = (game as FlameGame).world; final rectangle1 = RectangleComponent( position: Vector2.all(20), size: Vector2.all(20), @@ -1698,7 +1728,7 @@ void main() { final rectangle2 = RectangleComponent( size: Vector2.all(200), )..add(RectangleHitbox()); - await game.ensureAddAll([rectangle1, rectangle2]); + await world.ensureAddAll([rectangle1, rectangle2]); final ray = Ray2( origin: Vector2(20, 10), direction: Vector2.all(1.0)..normalize(), diff --git a/packages/flame/test/components/button_component_test.dart b/packages/flame/test/components/button_component_test.dart index 5cdc645a94..bb88d2bf01 100644 --- a/packages/flame/test/components/button_component_test.dart +++ b/packages/flame/test/components/button_component_test.dart @@ -1,7 +1,7 @@ import 'package:flame/components.dart'; import 'package:flame/game.dart'; import 'package:flame/input.dart'; -import 'package:flame/src/events/flame_game_mixins/has_tappable_components.dart'; +import 'package:flame/src/events/flame_game_mixins/multi_tap_dispatcher.dart'; import 'package:flame_test/flame_test.dart'; import 'package:flutter/material.dart'; import 'package:flutter_test/flutter_test.dart'; diff --git a/packages/flame/test/components/component_test.dart b/packages/flame/test/components/component_test.dart index 7567584974..8e11ee67d5 100644 --- a/packages/flame/test/components/component_test.dart +++ b/packages/flame/test/components/component_test.dart @@ -11,7 +11,7 @@ void main() { group('Lifecycle', () { testWithFlameGame('correct order', (game) async { final component = LifecycleComponent(); - await game.add(component); + await game.world.add(component); await game.ready(); expect( @@ -23,7 +23,7 @@ void main() { testWithFlameGame('component mounted completes', (game) async { final component = LifecycleComponent(); final mounted = component.mounted; - await game.add(component); + await game.world.add(component); await game.ready(); await expectLater(mounted, completes); @@ -35,10 +35,10 @@ void main() { (game) async { final component = LifecycleComponent(); final removed = component.removed; - await game.add(component); + await game.world.add(component); await game.ready(); - game.remove(component); + game.world.remove(component); game.update(0); await expectLater(removed, completes); @@ -49,11 +49,11 @@ void main() { 'component removed completes when set after game is ready', (game) async { final component = LifecycleComponent(); - await game.add(component); + await game.world.add(component); await game.ready(); final removed = component.removed; - game.remove(component); + game.world.remove(component); game.update(0); await expectLater(removed, completes); }, @@ -97,7 +97,7 @@ void main() { final parent = LifecycleComponent('parent'); final child = LifecycleComponent('child'); parent.add(child); - game.add(parent); + game.world.add(parent); var mounted = child.mounted; await game.ready(); @@ -118,7 +118,7 @@ void main() { (game) async { final parent = LifecycleComponent('parent'); final child = LifecycleComponent('child'); - game.add(parent); + game.world.add(parent); final mounted = child.mounted; await game.ready(); @@ -143,7 +143,7 @@ void main() { expect(child.events, contains('onMount')); mountedFutureCompleted = true; }); - game.add(child); + game.world.add(child); await game.ready(); expect(child.isMounted, true); await future; @@ -153,7 +153,7 @@ void main() { testWithFlameGame('component loaded completes', (game) async { final component = LifecycleComponent(); - await game.add(component); + await game.world.add(component); final loaded = component.loaded; await game.ready(); @@ -167,7 +167,7 @@ void main() { (game) async { final component = Component(); final loadedFuture = component.loaded; - game.add(component); + game.world.add(component); await game.ready(); expectLater(loadedFuture, completes); }, @@ -177,7 +177,7 @@ void main() { final parent = LifecycleComponent('parent'); final child = LifecycleComponent('child'); parent.add(child); - game.add(parent); + game.world.add(parent); await game.ready(); child.parent = game; game.update(0); @@ -209,12 +209,12 @@ void main() { final b = SlowComponent('B', 0.02); final c = SlowComponent('C', 0.05); final d = SlowComponent('D', 0); - game.add(a); - game.add(b); - game.add(c); - game.add(d); + game.world.add(a); + game.world.add(b); + game.world.add(c); + game.world.add(d); await game.ready(); - expect(game.children.toList(), equals([a, b, c, d])); + expect(game.world.children.toList(), equals([a, b, c, d])); }, ); @@ -252,12 +252,13 @@ void main() { (game) async { final parent = LifecycleComponent('parent'); final child = LifecycleComponent('child')..addToParent(parent); - await game.add(parent); + await game.world.add(parent); await game.ready(); expect(parent.isMounted, true); expect(child.isMounted, true); - expect(parent.parent, game); + expect(parent.parent, game.world); + expect(parent.parent?.parent, game); expect(child.parent, parent); parent.removeFromParent(); @@ -268,12 +269,13 @@ void main() { expect(parent.parent, isNull); expect(child.parent, isNull); - await game.add(parent); + await game.world.add(parent); await game.ready(); expect(parent.isMounted, true); expect(child.isMounted, true); - expect(parent.parent, game); + expect(parent.parent, game.world); + expect(parent.parent?.parent, game); expect(child.parent, parent); }, ); @@ -299,17 +301,17 @@ void main() { expect(game.isMounted, true); expect(child.isLoaded, false); expect(child.isMounted, false); - await game.add(child); + await game.world.add(child); expect(child.isLoaded, true); await tester.pump(); expect(child.isMounted, true); - expect(game.children.length, 1); + expect(game.world.children.length, 1); await tester.tap(find.text('Toggle')); await tester.pump(); expect(game.onDetachCalled, true); - expect(game.children.length, 1); + expect(game.world.children.length, 1); game.resetValues(); expect(game.isAttached, false); expect(game.isMounted, true); @@ -372,17 +374,23 @@ void main() { ); testWithFlameGame('children in the constructor', (game) async { - game.add( + game.world.add( Component( children: [ComponentA(), ComponentB()], ), ); await game.ready(); - expect(game.children.length, 1); - expect(game.children.first.children.length, 2); - expect(game.children.first.children.elementAt(0), isA()); - expect(game.children.first.children.elementAt(1), isA()); + expect(game.world.children.length, 1); + expect(game.world.children.first.children.length, 2); + expect( + game.world.children.first.children.elementAt(0), + isA(), + ); + expect( + game.world.children.first.children.elementAt(1), + isA(), + ); }); testWithFlameGame('add multiple children with addAll', (game) async { @@ -396,11 +404,11 @@ void main() { final component = TwoChildrenComponent( children: [ComponentA(), ComponentB()], ); - game.add(component); + game.world.add(component); await game.ready(); - expect(game.children.length, 1); - expect(game.children.first, component); + expect(game.world.children.length, 1); + expect(game.world.children.first, component); expect(component.children.length, 4); expect(component.children.elementAt(0), isA()); expect(component.children.elementAt(1), isA()); @@ -425,7 +433,7 @@ void main() { 'game resize while components are being added', (game) async { final component = ComponentWithSizeHistory(); - game.add(component); + game.world.add(component); expect(component.history, isEmpty); expect(component.isLoading, false); expect(component.isLoaded, true); @@ -488,7 +496,7 @@ void main() { expect(component.isMounted, false); expect(component.isLoaded, true); - expect(game.children.length, 0); + expect(game.world.children.length, 0); }, ); @@ -503,10 +511,10 @@ void main() { expect(component.countEvents('onRemove'), 1); expect(component.isMounted, false); - game.add(component); + game.world.add(component); await game.ready(); expect(component.countEvents('onRemove'), 1); - expect(game.children.length, 1); + expect(game.world.children.length, 1); }, ); @@ -514,7 +522,7 @@ void main() { 'try to remove a component before it was ever added', (game) async { expect( - () => game.remove(Component()), + () => game.world.remove(Component()), failsAssert( "Trying to remove a component that doesn't belong to any parent", ), @@ -533,7 +541,7 @@ void main() { failsAssert( 'Trying to remove a component that belongs to a different ' "parent: this = Instance of 'Component', component's parent = " - "Instance of 'FlameGame'", + "Instance of 'FlameGame'", ), ); }, @@ -549,7 +557,7 @@ void main() { expect(child.isMounted, false); child.removeFromParent(); - game.add(parent); + game.world.add(parent); await game.ready(); expect(child.isLoading, false); @@ -588,13 +596,13 @@ void main() { 'remove component immediately after adding', (game) async { final component = LifecycleComponent(); - game.add(component); + game.world.add(component); expect(component.isLoading, true); expect(component.isLoaded, false); - game.remove(component); + game.world.remove(component); await game.ready(); - expect(game.children.length, 0); + expect(game.world.children.length, 0); expect(component.isLoaded, true); expect(component.isMounted, false); // onRemove shouldn't be called because there was never an onMount @@ -606,13 +614,13 @@ void main() { 'remove slow-loading component immediately after adding', (game) async { final component = _SlowLoadingComponent(); - game.add(component); + game.world.add(component); expect(component.isLoading, true); expect(component.isLoaded, false); - game.remove(component); + game.world.remove(component); await game.ready(); - expect(game.children.length, 0); + expect(game.world.children.length, 0); expect(component.isMounted, false); }, ); @@ -621,10 +629,10 @@ void main() { 'component removes itself from onLoad', (game) async { final component = _SelfRemovingOnLoadComponent(); - game.add(component); + game.world.add(component); await game.ready(); - expect(game.children.length, 0); + expect(game.world.children.length, 0); expect(component.isLoaded, true); expect(component.isMounted, false); }, @@ -634,10 +642,10 @@ void main() { 'component removes itself from onMount', (game) async { final component = _SelfRemovingOnMountComponent(); - game.add(component); + game.world.add(component); await game.ready(); - expect(game.children.length, 0); + expect(game.world.children.length, 0); expect(component.isLoaded, true); expect(component.isMounted, false); }, @@ -647,16 +655,16 @@ void main() { 'Quickly removed component can be re-added', (game) async { final component = LifecycleComponent(); - game.add(component); - game.remove(component); + game.world.add(component); + game.world.remove(component); await game.ready(); component.events.add('--'); - expect(game.children.length, 0); - game.add(component); + expect(game.world.children.length, 0); + game.world.add(component); await game.ready(); - expect(game.children.length, 1); + expect(game.world.children.length, 1); expect(component.isMounted, true); expect(component.isLoaded, true); expect( @@ -676,7 +684,7 @@ void main() { final component2 = Component(); final component3 = Component(); component1.addAll([component2, component3]); - game.add(component1); + game.world.add(component1); await game.ready(); expect(component1.isMounted, true); @@ -690,14 +698,14 @@ void main() { expect(component2.isMounted, false); expect(component3.isMounted, false); - game.add(component1); - game.add(component2); + game.world.add(component1); + game.world.add(component2); await game.ready(); expect(component1.isMounted, true); expect(component2.isMounted, true); expect(component3.isMounted, true); - expect(game.children.length, 2); + expect(game.world.children.length, 2); expect(component1.children.length, 1); game.descendants().forEach((component) { @@ -712,8 +720,8 @@ void main() { game.pauseEngine(); final component = Component(); - await game.add(component); - game.remove(component); + await game.world.add(component); + game.world.remove(component); game.resumeEngine(); game.update(0); @@ -727,14 +735,17 @@ void main() { 10, _IdentifiableComponent.new, ); - game.addAll(components); + game.world.addAll(components); await game.ready(); - expect(game.children.length, 10); - game.removeWhere((c) => (c as _IdentifiableComponent).id.isEven); + expect(game.world.children.length, 10); + game.world.removeWhere( + (c) => (c as _IdentifiableComponent).id.isEven, + ); game.update(0); - expect(game.children.length, 5); + expect(game.world.children.length, 5); expect( - game.children.every((c) => (c as _IdentifiableComponent).id.isOdd), + game.world.children + .every((c) => (c as _IdentifiableComponent).id.isOdd), true, ); }, @@ -743,7 +754,7 @@ void main() { testWithFlameGame( 'removeWhere works before all components are mounted', (game) async { - game.add(_RemoveWhereComponent()); + game.world.add(_RemoveWhereComponent()); expect( () async { await game.ready(); @@ -773,30 +784,30 @@ void main() { }); testWithFlameGame('moving to sibling', (game) async { - final componentA = Component()..addToParent(game); - final componentB = Component()..addToParent(game); + final componentA = Component()..addToParent(game.world); + final componentB = Component()..addToParent(game.world); await game.ready(); - expect(game.children.toList(), [componentA, componentB]); + expect(game.world.children.toList(), [componentA, componentB]); expect(componentA.hasChildren, false); expect(componentB.hasChildren, false); componentA.parent = componentB; await game.ready(); - expect(game.children.toList(), [componentB]); + expect(game.world.children.toList(), [componentB]); expect(componentB.children.toList(), [componentA]); expect(componentA.parent, componentB); }); testWithFlameGame('moving to parent', (game) async { - final parent = Component()..addToParent(game); + final parent = Component()..addToParent(game.world); final child = Component()..addToParent(parent); await game.ready(); - expect(game.children.toList(), [parent]); + expect(game.world.children.toList(), [parent]); expect(parent.children.toList(), [child]); - child.parent = game; + child.parent = game.world; await game.ready(); - expect(game.children.toList(), [parent, child]); + expect(game.world.children.toList(), [parent, child]); expect(parent.children.toList(), isEmpty); }); }); @@ -805,16 +816,16 @@ void main() { testWithFlameGame( 'descendants in a deep component tree', (game) async { - expect(game.descendants().length, 0); + expect(game.world.descendants().length, 0); final component = Component()..add(Component()..add(Component())); - game.add(component); + game.world.add(component); expect(game.hasLifecycleEvents, true); - expect(game.descendants().length, 0); + expect(game.world.descendants().length, 0); await game.ready(); - expect(game.descendants().length, 3); + expect(game.world.descendants().length, 3); - final descendantsWithSelf = game.descendants(includeSelf: true); + final descendantsWithSelf = game.world.descendants(includeSelf: true); expect(descendantsWithSelf.length, 4); for (final component in descendantsWithSelf) { expect(component.findGame() != null, true); @@ -827,23 +838,27 @@ void main() { 'adding', (game) async { final component = Component()..add(Component()..add(Component())); - await game.add(component); + await game.world.add(component); await game.ready(); expect(game.hasLifecycleEvents, false); - game.add(Component()); + game.world.add(Component()); expect(game.hasLifecycleEvents, true); - expect(game.descendants().length, 3); + expect(game.world.descendants().length, 3); + // Remember that CameraComponent, Viewport, Viewfinder and World are + // added by default. + expect(game.descendants().length, 7); }, ); testWithFlameGame( 'order must adhere to the "depth-first search" algorithm', (game) async { - final componentA = Component()..addToParent(game); - final componentB = Component()..addToParent(game); - final componentC = Component()..addToParent(game); + final world = game.world; + final componentA = Component()..addToParent(world); + final componentB = Component()..addToParent(world); + final componentC = Component()..addToParent(world); final componentD = Component()..addToParent(componentB); final componentE = Component()..addToParent(componentB); final componentF = Component()..addToParent(componentE); @@ -858,20 +873,20 @@ void main() { componentC, ]; expect( - game.descendants().toList(), + world.descendants().toList(), expectedOrder, ); expect( - game.descendants(includeSelf: true).toList(), - [game, ...expectedOrder], + world.descendants(includeSelf: true).toList(), + [world, ...expectedOrder], ); expect( - game.descendants(reversed: true).toList(), + world.descendants(reversed: true).toList(), expectedOrder.reversed.toList(), ); expect( - game.descendants(reversed: true, includeSelf: true).toList(), - [...expectedOrder.reversed, game], + world.descendants(reversed: true, includeSelf: true).toList(), + [...expectedOrder.reversed, world], ); }, ); @@ -1022,21 +1037,22 @@ void main() { group('componentsAtPoint()', () { testWithFlameGame('nested components', (game) async { - final compA = PositionComponent() + final world = game.world; + final componentA = PositionComponent() ..size = Vector2(200, 150) ..scale = Vector2.all(2) ..position = Vector2(350, 50) - ..addToParent(game); - final compB = CircleComponent(radius: 10) + ..addToParent(world); + final componentB = CircleComponent(radius: 10) ..position = Vector2(150, 75) ..anchor = Anchor.center - ..addToParent(compA); + ..addToParent(componentA); await game.ready(); void matchComponentsAtPoint(Vector2 point, List<_Pair> expected) { final nested = []; var i = 0; - for (final component in game.componentsAtPoint(point, nested)) { + for (final component in world.componentsAtPoint(point, nested)) { expect(i, lessThan(expected.length)); expect(component, expected[i].component); expect(nested, expected[i].points); @@ -1046,25 +1062,31 @@ void main() { } matchComponentsAtPoint(Vector2(0, 0), [ - _Pair(game, [Vector2(0, 0)]), + _Pair(world, [Vector2(0, 0)]), ]); matchComponentsAtPoint(Vector2(400, 100), [ - _Pair(compA, [Vector2(400, 100), Vector2(25, 25)]), - _Pair(game, [Vector2(400, 100)]), + _Pair(componentA, [Vector2(400, 100), Vector2(25, 25)]), + _Pair(world, [Vector2(400, 100)]), ]); matchComponentsAtPoint(Vector2(650, 200), [ - _Pair(compB, [Vector2(650, 200), Vector2(150, 75), Vector2(10, 10)]), - _Pair(compA, [Vector2(650, 200), Vector2(150, 75)]), - _Pair(game, [Vector2(650, 200)]), + _Pair( + componentB, + [Vector2(650, 200), Vector2(150, 75), Vector2(10, 10)], + ), + _Pair(componentA, [Vector2(650, 200), Vector2(150, 75)]), + _Pair(world, [Vector2(650, 200)]), ]); matchComponentsAtPoint(Vector2(664, 214), [ - _Pair(compB, [Vector2(664, 214), Vector2(157, 82), Vector2(17, 17)]), - _Pair(compA, [Vector2(664, 214), Vector2(157, 82)]), - _Pair(game, [Vector2(664, 214)]), + _Pair( + componentB, + [Vector2(664, 214), Vector2(157, 82), Vector2(17, 17)], + ), + _Pair(componentA, [Vector2(664, 214), Vector2(157, 82)]), + _Pair(world, [Vector2(664, 214)]), ]); matchComponentsAtPoint(Vector2(664, 216), [ - _Pair(compA, [Vector2(664, 216), Vector2(157, 83)]), - _Pair(game, [Vector2(664, 216)]), + _Pair(componentA, [Vector2(664, 216), Vector2(157, 83)]), + _Pair(world, [Vector2(664, 216)]), ]); }); }); @@ -1089,7 +1111,7 @@ void main() { parent.debugMode = true; parent.add(child); - game.add(parent); + game.world.add(parent); await game.ready(); expect(child.debugMode, true); @@ -1105,7 +1127,7 @@ void main() { grandParent.add(parent); grandParent.debugMode = true; - game.add(grandParent); + game.world.add(grandParent); await game.ready(); expect(child.debugMode, true); @@ -1116,21 +1138,21 @@ void main() { testWithFlameGame( 'propagateToChildren visits children in the correct order', (game) async { - final component1 = IntComponent()..addToParent(game); - final component2 = IntComponent()..addToParent(game); + final component1 = IntComponent()..addToParent(game.world); + final component2 = IntComponent()..addToParent(game.world); final component3 = IntComponent()..addToParent(component2); final component4 = IntComponent()..addToParent(component2); await game.ready(); var order = 0; - game.propagateToChildren( + game.world.propagateToChildren( (component) { order += 1; if (component is IntComponent) { expect(component.value, 0); component.value = order; } else { - expect(component, equals(game)); + expect(component, equals(game.world)); expect(order, 5); } return true; @@ -1159,7 +1181,7 @@ void main() { self.children.elementAt(0).add(Component()); }, ); - game.add(component); + game.world.add(component); await game.ready(); expect(component.isMounted, true); @@ -1172,7 +1194,7 @@ void main() { 'Components can be retrieved via a named key', (game) async { final component = ComponentA(key: ComponentKey.named('A')); - game.add(component); + game.world.add(component); await game.ready(); expect(ComponentKey.named('A'), equals(ComponentKey.named('A'))); @@ -1190,8 +1212,8 @@ void main() { final component1 = ComponentA(key: key1); final component2 = ComponentA(key: key2); - game.add(component1); - game.add(component2); + game.world.add(component1); + game.world.add(component2); await game.ready(); expect(key1, isNot(equals(key2))); @@ -1210,7 +1232,7 @@ void main() { 'Components can be retrieved via their name', (game) async { final component = ComponentA(key: ComponentKey.named('A')); - game.add(component); + game.world.add(component); await game.ready(); final retrieved = game.findByKeyName('A'); @@ -1233,7 +1255,7 @@ void main() { final key = ComponentKey.unique(); final component = ComponentA(key: key); - game.add(component); + game.world.add(component); await game.ready(); final retrieved1 = game.findByKey(key); @@ -1253,8 +1275,8 @@ void main() { final component = ComponentA(key: ComponentKey.named('A')); final component2 = ComponentA(key: ComponentKey.named('A')); - game.add(component); - game.add(component2); + game.world.add(component); + game.world.add(component2); await expectLater( () => game.ready(), diff --git a/packages/flame/test/components/hud_button_component_test.dart b/packages/flame/test/components/hud_button_component_test.dart index cbfac6f52e..d1a856c172 100644 --- a/packages/flame/test/components/hud_button_component_test.dart +++ b/packages/flame/test/components/hud_button_component_test.dart @@ -1,7 +1,7 @@ import 'package:flame/components.dart'; import 'package:flame/extensions.dart'; import 'package:flame/input.dart'; -import 'package:flame/src/events/flame_game_mixins/has_tappable_components.dart'; +import 'package:flame/src/events/flame_game_mixins/multi_tap_dispatcher.dart'; import 'package:flame_test/flame_test.dart'; import 'package:flutter/material.dart'; import 'package:flutter_test/flutter_test.dart'; diff --git a/packages/flame/test/components/input/sprite_button_component_test.dart b/packages/flame/test/components/input/sprite_button_component_test.dart index 95e9587bd1..96b7b1f69e 100644 --- a/packages/flame/test/components/input/sprite_button_component_test.dart +++ b/packages/flame/test/components/input/sprite_button_component_test.dart @@ -4,7 +4,7 @@ import 'package:flame/game.dart'; import 'package:flame/input.dart'; import 'package:flame/src/anchor.dart'; import 'package:flame/src/components/sprite_group_component.dart'; -import 'package:flame/src/events/flame_game_mixins/has_tappable_components.dart'; +import 'package:flame/src/events/flame_game_mixins/multi_tap_dispatcher.dart'; import 'package:flame/src/spritesheet.dart'; import 'package:flame_test/flame_test.dart'; import 'package:flutter/material.dart'; diff --git a/packages/flame/test/components/joystick_component_test.dart b/packages/flame/test/components/joystick_component_test.dart index 15717aedd2..d107a3ef50 100644 --- a/packages/flame/test/components/joystick_component_test.dart +++ b/packages/flame/test/components/joystick_component_test.dart @@ -1,5 +1,5 @@ import 'package:flame/components.dart'; -import 'package:flame/src/events/flame_game_mixins/has_draggable_components.dart'; +import 'package:flame/src/events/flame_game_mixins/multi_drag_dispatcher.dart'; import 'package:flame_test/flame_test.dart'; import 'package:flutter/widgets.dart'; import 'package:test/test.dart'; diff --git a/packages/flame/test/components/mixins/draggable_test.dart b/packages/flame/test/components/mixins/draggable_test.dart index e83af3a4b2..c85b8c709c 100644 --- a/packages/flame/test/components/mixins/draggable_test.dart +++ b/packages/flame/test/components/mixins/draggable_test.dart @@ -67,7 +67,7 @@ void main() { ..height = 10; await game.ensureAdd(component); - game.camera.zoom = 1.5; + game.oldCamera.zoom = 1.5; game.onDragStart( 1, DragStartInfo.fromDetails( @@ -93,8 +93,8 @@ void main() { ..height = 10; await game.ensureAdd(component); - game.camera.zoom = 1.5; - game.camera.snapTo(Vector2.all(50)); + game.oldCamera.zoom = 1.5; + game.oldCamera.snapTo(Vector2.all(50)); game.onDragStart( 1, DragStartInfo.fromDetails( diff --git a/packages/flame/test/components/mixins/hoverable_test.dart b/packages/flame/test/components/mixins/hoverable_test.dart index 5e4c93f1dc..9c59dd4dfb 100644 --- a/packages/flame/test/components/mixins/hoverable_test.dart +++ b/packages/flame/test/components/mixins/hoverable_test.dart @@ -91,7 +91,7 @@ void main() { await game.ensureAdd(c); // component is now at the corner of the screen - game.camera.snapTo(Vector2(10, 20)); + game.oldCamera.snapTo(Vector2(10, 20)); _triggerMouseMove(game, 11, 21); expect(c.isHovered, false); diff --git a/packages/flame/test/components/mixins/tappable_test.dart b/packages/flame/test/components/mixins/tappable_test.dart index 240910a5e3..55219a83a2 100644 --- a/packages/flame/test/components/mixins/tappable_test.dart +++ b/packages/flame/test/components/mixins/tappable_test.dart @@ -1,7 +1,7 @@ import 'package:flame/components.dart'; import 'package:flame/events.dart'; import 'package:flame/game.dart'; -import 'package:flame/src/events/flame_game_mixins/has_tappable_components.dart'; +import 'package:flame/src/events/flame_game_mixins/multi_tap_dispatcher.dart'; import 'package:flame_test/flame_test.dart'; import 'package:flutter/gestures.dart'; import 'package:test/test.dart'; diff --git a/packages/flame/test/components/position_type_test.dart b/packages/flame/test/components/position_type_test.dart index fb1a80ab29..32a672e93d 100644 --- a/packages/flame/test/components/position_type_test.dart +++ b/packages/flame/test/components/position_type_test.dart @@ -22,7 +22,7 @@ void main() { ]); final canvas = MockCanvas(); - game.camera.snapTo(Vector2(12.0, 18.0)); + game.oldCamera.snapTo(Vector2(12.0, 18.0)); game.render(canvas); expect( @@ -40,13 +40,14 @@ void main() { testWithFlameGame( 'PositionType.viewport', (game) async { + game.removeAll([game.world, game.camera]); await game.ensureAddAll([ _MyComponent(4, positionType: PositionType.viewport), _MyComponent(1, positionType: PositionType.viewport), _MyComponent(2, positionType: PositionType.viewport), ]); final canvas = MockCanvas(); - game.camera.snapTo(Vector2(12.0, 18.0)); + game.oldCamera.snapTo(Vector2(12.0, 18.0)); game.render(canvas); expect( @@ -63,7 +64,8 @@ void main() { testWithFlameGame( 'viewport does not affect component with PositionType.widget', (game) async { - game.camera.viewport = FixedResolutionViewport(Vector2.all(50)); + game.removeAll([game.world, game.camera]); + game.oldCamera.viewport = FixedResolutionViewport(Vector2.all(50)); game.onGameResize(Vector2.all(200.0)); await game.ensureAdd( _MyComponent(0, positionType: PositionType.widget), @@ -81,10 +83,11 @@ void main() { testWithFlameGame( 'camera does not affect component with PositionType.widget', (game) async { + game.removeAll([game.world, game.camera]); await game.ensureAdd( _MyComponent(0, positionType: PositionType.widget), ); - game.camera.snapTo(Vector2(100, 100)); + game.oldCamera.snapTo(Vector2(100, 100)); final canvas = MockCanvas(); game.render(canvas); @@ -98,6 +101,7 @@ void main() { testWithFlameGame( 'Several static components', (game) async { + game.removeAll([game.world, game.camera]); await game.ensureAddAll([ _MyComponent(5, positionType: PositionType.widget), _MyComponent(1, positionType: PositionType.widget), @@ -105,7 +109,7 @@ void main() { ]); final canvas = MockCanvas(); - game.camera.snapTo(Vector2(12.0, 18.0)); + game.oldCamera.snapTo(Vector2(12.0, 18.0)); game.render(canvas); expect( @@ -122,6 +126,7 @@ void main() { testWithFlameGame( 'mixed', (game) async { + game.removeAll([game.world, game.camera]); await game.ensureAddAll([ _MyComponent(4), _MyComponent(1), @@ -134,7 +139,7 @@ void main() { ]); final canvas = MockCanvas(); - game.camera.snapTo(Vector2(12.0, 18.0)); + game.oldCamera.snapTo(Vector2(12.0, 18.0)); game.render(canvas); expect( diff --git a/packages/flame/test/components/priority_test.dart b/packages/flame/test/components/priority_test.dart index b182fab5d5..26636a8414 100644 --- a/packages/flame/test/components/priority_test.dart +++ b/packages/flame/test/components/priority_test.dart @@ -31,8 +31,8 @@ void main() { final priorityComponents = List.generate(10, _PriorityComponent.new) ..add(firstComponent); priorityComponents.shuffle(); - final components = game.children; - await game.ensureAddAll(priorityComponents); + final components = game.world.children; + await game.world.ensureAddAll(priorityComponents); componentsSorted(components); expect(components.first, firstComponent); firstComponent.priority = 11; @@ -48,8 +48,8 @@ void main() { final priorityComponents = List.generate(10, _PriorityComponent.new) ..add(firstComponent); priorityComponents.shuffle(); - final components = game.children; - await game.ensureAddAll(priorityComponents); + final components = game.world.children; + await game.world.ensureAddAll(priorityComponents); componentsSorted(components); expect(components.first, firstComponent); firstComponent.priority = 11; diff --git a/packages/flame/test/components/route_test.dart b/packages/flame/test/components/route_test.dart index 0e1105585e..f6096bcf66 100644 --- a/packages/flame/test/components/route_test.dart +++ b/packages/flame/test/components/route_test.dart @@ -416,14 +416,16 @@ void main() { ); testWithFlameGame('componentsAtPoint for opaque route', (game) async { + final initialComponent = PositionComponent(size: Vector2.all(100)); + final newComponent = PositionComponent(size: Vector2.all(100)); final router = RouterComponent( initialRoute: 'initial', routes: { 'initial': Route( - () => PositionComponent(size: Vector2.all(100)), + () => initialComponent, ), 'new': Route( - () => PositionComponent(size: Vector2.all(100)), + () => newComponent, ), }, )..addToParent(game); @@ -432,20 +434,26 @@ void main() { router.pushNamed('new'); await game.ready(); expect( - game.componentsAtPoint(Vector2(50, 50)).toList(), - [router.currentRoute.children.first, game], + game.componentsAtPoint(Vector2(50, 50)).contains(newComponent), + isTrue, + ); + expect( + game.componentsAtPoint(Vector2(50, 50)).contains(initialComponent), + isFalse, ); }); testWithFlameGame('componentsAtPoint for transparent route', (game) async { + final initialComponent = PositionComponent(size: Vector2.all(100)); + final newComponent = PositionComponent(size: Vector2.all(100)); final router = RouterComponent( initialRoute: 'initial', routes: { 'initial': Route( - () => PositionComponent(size: Vector2.all(100)), + () => initialComponent, ), 'new': Route( - () => PositionComponent(size: Vector2.all(100)), + () => newComponent, transparent: true, ), }, @@ -455,12 +463,12 @@ void main() { router.pushNamed('new'); await game.ready(); expect( - game.componentsAtPoint(Vector2(50, 50)).toList(), - [ - router.currentRoute.children.first, - router.previousRoute!.children.first, - game, - ], + game.componentsAtPoint(Vector2(50, 50)).contains(newComponent), + isTrue, + ); + expect( + game.componentsAtPoint(Vector2(50, 50)).contains(initialComponent), + isTrue, ); }); }); diff --git a/packages/flame/test/components/spawn_component_test.dart b/packages/flame/test/components/spawn_component_test.dart index 3f44f98696..91bf599d29 100644 --- a/packages/flame/test/components/spawn_component_test.dart +++ b/packages/flame/test/components/spawn_component_test.dart @@ -19,21 +19,22 @@ void main() { area: shape, random: random, ); - await game.ensureAdd(spawn); + final world = game.world; + await world.ensureAdd(spawn); game.update(0.5); - expect(game.children.length, 1); + expect(world.children.length, 1); game.update(0.5); game.update(0.0); - expect(game.children.length, 2); + expect(world.children.length, 2); game.update(1.0); game.update(0.0); - expect(game.children.length, 3); + expect(world.children.length, 3); for (var i = 0; i < 1000; i++) { game.update(random.nextDouble()); } expect( - game.children + world.children .query() .every((c) => shape.containsPoint(c.position)), isTrue, @@ -50,21 +51,22 @@ void main() { area: shape, random: random, ); - await game.ensureAdd(spawn); + final world = game.world; + await world.ensureAdd(spawn); game.update(0.5); - expect(game.children.length, 1); + expect(world.children.length, 1); game.update(0.5); game.update(0.0); - expect(game.children.length, 2); + expect(world.children.length, 2); game.update(1.0); game.update(0.0); - expect(game.children.length, 3); + expect(world.children.length, 3); for (var i = 0; i < 1000; i++) { game.update(random.nextDouble()); } expect( - game.children + world.children .query() .every((c) => shape.containsPoint(c.position)), isTrue, @@ -87,21 +89,22 @@ void main() { area: shape, random: random, ); - await game.ensureAdd(spawn); + final world = game.world; + await world.ensureAdd(spawn); game.update(0.5); - expect(game.children.length, 1); + expect(world.children.length, 1); game.update(0.5); game.update(0.0); - expect(game.children.length, 2); + expect(world.children.length, 2); game.update(1.0); game.update(0.0); - expect(game.children.length, 3); + expect(world.children.length, 3); for (var i = 0; i < 1000; i++) { game.update(random.nextDouble()); } expect( - game.children + world.children .query() .every((c) => shape.containsPoint(c.position)), isTrue, diff --git a/packages/flame/test/components/sprite_animation_component_test.dart b/packages/flame/test/components/sprite_animation_component_test.dart index 4c51faae8b..c8bcd2996f 100644 --- a/packages/flame/test/components/sprite_animation_component_test.dart +++ b/packages/flame/test/components/sprite_animation_component_test.dart @@ -72,14 +72,15 @@ Future main() async { removeOnFinish: true, ); - await game.ensureAdd(component); + final world = game.world; + await world.ensureAdd(component); - expect(game.children.length, 1); + expect(world.children.length, 1); game.update(2); // runs a cycle to remove the component game.update(0.1); - expect(game.children.length, 0); + expect(world.children.length, 0); }, ); @@ -100,17 +101,18 @@ Future main() async { removeOnFinish: true, ); - await game.ensureAdd(component); + final world = game.world; + await world.ensureAdd(component); - expect(component.parent, game); - expect(game.children.length, 1); + expect(component.parent, world); + expect(world.children.length, 1); game.update(2); - expect(component.parent, game); + expect(component.parent, world); // runs a cycle to remove the component, but failed game.update(0.1); - expect(game.children.length, 1); + expect(world.children.length, 1); }, ); @@ -131,17 +133,18 @@ Future main() async { removeOnFinish: false, ); - await game.ensureAdd(component); + final world = game.world; + await world.ensureAdd(component); - expect(component.parent, game); - expect(game.children.length, 1); + expect(component.parent, world); + expect(world.children.length, 1); game.update(2); - expect(component.parent, game); + expect(component.parent, world); // runs a cycle to remove the component, but failed game.update(0.1); - expect(game.children.length, 1); + expect(world.children.length, 1); }, ); @@ -163,17 +166,18 @@ Future main() async { removeOnFinish: false, ); - await game.ensureAdd(component); + final world = game.world; + await world.ensureAdd(component); - expect(component.parent, game); - expect(game.children.length, 1); + expect(component.parent, world); + expect(world.children.length, 1); game.update(2); - expect(component.parent, game); + expect(component.parent, world); // runs a cycle to remove the component, but failed game.update(0.1); - expect(game.children.length, 1); + expect(world.children.length, 1); }, ); @@ -194,17 +198,18 @@ Future main() async { playing: false, ); - await game.ensureAdd(component); + final world = game.world; + await world.ensureAdd(component); - expect(component.parent, game); - expect(game.children.length, 1); + expect(component.parent, world); + expect(world.children.length, 1); game.update(2); - expect(component.parent, game); + expect(component.parent, world); // runs a cycle to potentially remove the component game.update(0.1); - expect(game.children.length, 1); + expect(world.children.length, 1); }, ); }); diff --git a/packages/flame/test/components/sprite_animation_group_component_test.dart b/packages/flame/test/components/sprite_animation_group_component_test.dart index e7e08d36a3..b025f6a282 100644 --- a/packages/flame/test/components/sprite_animation_group_component_test.dart +++ b/packages/flame/test/components/sprite_animation_group_component_test.dart @@ -63,16 +63,17 @@ Future main() async { removeOnFinish: {_AnimationState.idle: true}, ); - await game.ensureAdd(component); - expect(component.parent, game); - expect(game.children.length, 1); + final world = game.world; + await world.ensureAdd(component); + expect(component.parent, world); + expect(world.children.length, 1); game.update(2); - expect(component.parent, game); + expect(component.parent, world); // runs a cycle and the component should still be there game.update(0.1); - expect(game.children.length, 1); + expect(world.children.length, 1); }); testWithFlameGame( @@ -92,14 +93,15 @@ Future main() async { current: _AnimationState.idle, ); - await game.ensureAdd(component); - expect(game.children.length, 1); + final world = game.world; + await world.ensureAdd(component); + expect(world.children.length, 1); game.update(2); // runs a cycle to remove the component game.update(0.1); - expect(game.children.length, 0); + expect(world.children.length, 0); }, ); @@ -121,16 +123,17 @@ Future main() async { current: _AnimationState.idle, ); - await game.ensureAdd(component); - expect(component.parent, game); - expect(game.children.length, 1); + final world = game.world; + await world.ensureAdd(component); + expect(component.parent, world); + expect(world.children.length, 1); game.update(2); - expect(component.parent, game); + expect(component.parent, world); // runs a cycle to remove the component, but failed game.update(0.1); - expect(game.children.length, 1); + expect(world.children.length, 1); }); testWithFlameGame( @@ -150,16 +153,17 @@ Future main() async { // when omitted, removeOnFinish is false for all states ); - await game.ensureAdd(component); - expect(component.parent, game); - expect(game.children.length, 1); + final world = game.world; + await world.ensureAdd(component); + expect(component.parent, world); + expect(world.children.length, 1); game.update(2); - expect(component.parent, game); + expect(component.parent, world); // runs a cycle to remove the component, but failed game.update(0.1); - expect(game.children.length, 1); + expect(world.children.length, 1); }); testWithFlameGame( @@ -180,17 +184,18 @@ Future main() async { current: _AnimationState.idle, ); - await game.ensureAdd(component); + final world = game.world; + await world.ensureAdd(component); - expect(component.parent, game); - expect(game.children.length, 1); + expect(component.parent, world); + expect(world.children.length, 1); game.update(2); - expect(component.parent, game); + expect(component.parent, world); // runs a cycle to remove the component, but failed game.update(0.1); - expect(game.children.length, 1); + expect(world.children.length, 1); }); }); diff --git a/packages/flame/test/components/timer_component_test.dart b/packages/flame/test/components/timer_component_test.dart index c5a00a57d0..f0cb919311 100644 --- a/packages/flame/test/components/timer_component_test.dart +++ b/packages/flame/test/components/timer_component_test.dart @@ -42,24 +42,26 @@ void main() { testWithFlameGame('never remove from the game when is repeating', (game) async { - game.add(_MyTimerComponent()); + final world = game.world; + world.add(_MyTimerComponent()); game.update(0); game.update(1.2); game.update(0); - expect(game.children.length, equals(1)); + expect(world.children.length, equals(1)); }); testWithFlameGame('is removed from the game when is finished', (game) async { - game.add(_NonRepeatingTimerComponent()); + final world = game.world; + world.add(_NonRepeatingTimerComponent()); game.update(0); game.update(1.2); game.update(0); - expect(game.children.length, equals(0)); + expect(world.children.length, equals(0)); }); testWithFlameGame('calls onTick when provided', (game) async { diff --git a/packages/flame/test/effects/remove_effect_test.dart b/packages/flame/test/effects/remove_effect_test.dart index f2a6b181b2..8ea74f2a00 100644 --- a/packages/flame/test/effects/remove_effect_test.dart +++ b/packages/flame/test/effects/remove_effect_test.dart @@ -6,9 +6,10 @@ import 'package:flutter_test/flutter_test.dart'; void main() { group('RemoveEffect', () { testWithFlameGame('no delay', (game) async { + final world = game.world; final component = Component(); - await game.ensureAdd(component); - expect(game.children.length, 1); + await world.ensureAdd(component); + expect(world.children.length, 1); // First `game.update()` invokes the destroy effect and schedules // `component` for deletion; second `game.update()` processes the deletion @@ -16,27 +17,29 @@ void main() { component.add(RemoveEffect()); game.update(0); game.update(0); - expect(game.children.length, 0); + expect(world.children.length, 0); }); testWithFlameGame('delayed', (game) async { + final world = game.world; final component = Component(); - await game.ensureAdd(component); - expect(game.children.length, 1); + await world.ensureAdd(component); + expect(world.children.length, 1); component.add(RemoveEffect(delay: 1)); game.update(0.5); game.update(0); - expect(game.children.length, 1); + expect(world.children.length, 1); game.update(0.5); game.update(0); - expect(game.children.length, 0); + expect(world.children.length, 0); }); testWithFlameGame('as a part of a sequence', (game) async { + final world = game.world; final component = PositionComponent(); - await game.ensureAdd(component); + await world.ensureAdd(component); component.add( SequenceEffect([ MoveByEffect(Vector2.all(10), EffectController(duration: 1)), @@ -44,12 +47,12 @@ void main() { ]), ); game.update(0); - expect(game.children.length, 1); + expect(world.children.length, 1); game.update(0.5); - expect(game.children.length, 1); + expect(world.children.length, 1); game.update(1.0); // This completes the move effect game.update(0); // This runs the remove effect - expect(game.children.length, 0); + expect(world.children.length, 0); }); }); } diff --git a/packages/flame/test/events/component_mixins/drag_callbacks_test.dart b/packages/flame/test/events/component_mixins/drag_callbacks_test.dart index 7063cefb2c..b1c6c0fc9b 100644 --- a/packages/flame/test/events/component_mixins/drag_callbacks_test.dart +++ b/packages/flame/test/events/component_mixins/drag_callbacks_test.dart @@ -1,7 +1,7 @@ import 'package:flame/components.dart'; import 'package:flame/events.dart'; import 'package:flame/game.dart'; -import 'package:flame/src/events/flame_game_mixins/has_draggable_components.dart'; +import 'package:flame/src/events/flame_game_mixins/multi_drag_dispatcher.dart'; import 'package:flame_test/flame_test.dart'; import 'package:flutter/gestures.dart'; import 'package:flutter_test/flutter_test.dart'; @@ -13,7 +13,7 @@ void main() { (game) async { await game.add(_DragCallbacksComponent()); await game.ready(); - expect(game.children.toList()[1], isA()); + expect(game.children.toList()[2], isA()); }, ); @@ -108,7 +108,7 @@ void main() { await tester.pumpWidget(GameWidget(game: game)); await tester.pump(); await tester.pump(); - expect(game.children.length, equals(2)); + expect(game.children.length, equals(4)); expect(component.isMounted, isTrue); await tester.dragFrom(const Offset(10, 10), const Offset(90, 90)); @@ -142,8 +142,8 @@ void main() { _DragCallbacksGame.new, (game) async { await game.ready(); - expect(game.children.length, equals(1)); - expect(game.children.first, isA()); + expect(game.children.length, equals(3)); + expect(game.children.elementAt(1), isA()); }, ); @@ -154,7 +154,7 @@ void main() { await tester.pumpWidget(GameWidget(game: game)); await tester.pump(); await tester.pump(); - expect(game.children.length, equals(1)); + expect(game.children.length, equals(3)); expect(game.isMounted, isTrue); await tester.dragFrom(const Offset(10, 10), const Offset(90, 90)); diff --git a/packages/flame/test/events/component_mixins/tap_callbacks_test.dart b/packages/flame/test/events/component_mixins/tap_callbacks_test.dart index c649f8253c..2f730b0141 100644 --- a/packages/flame/test/events/component_mixins/tap_callbacks_test.dart +++ b/packages/flame/test/events/component_mixins/tap_callbacks_test.dart @@ -1,7 +1,7 @@ import 'package:flame/components.dart'; import 'package:flame/events.dart'; import 'package:flame/game.dart'; -import 'package:flame/src/events/flame_game_mixins/has_tappable_components.dart'; +import 'package:flame/src/events/flame_game_mixins/multi_tap_dispatcher.dart'; import 'package:flame_test/flame_test.dart'; import 'package:flutter_test/flutter_test.dart'; @@ -12,7 +12,7 @@ void main() { (game) async { await game.ensureAdd(_TapCallbacksComponent()); await game.ready(); - expect(game.children.toList()[1], isA()); + expect(game.children.toList()[2], isA()); }); testWithFlameGame('tap event start', (game) async { @@ -123,7 +123,7 @@ void main() { await tester.pumpWidget(GameWidget(game: game)); await tester.pump(); await tester.pump(); - expect(game.children.length, 2); + expect(game.children.length, 4); expect(component.isMounted, isTrue); await tester.tapAt(const Offset(10, 10)); @@ -158,8 +158,8 @@ void main() { _TapCallbacksGame.new, (game) async { await game.ready(); - expect(game.children.length, equals(1)); - expect(game.children.first, isA()); + expect(game.children.length, equals(3)); + expect(game.children.elementAt(1), isA()); }, ); @@ -170,7 +170,7 @@ void main() { await tester.pumpWidget(GameWidget(game: game)); await tester.pump(); await tester.pump(); - expect(game.children.length, equals(1)); + expect(game.children.length, equals(3)); expect(game.isMounted, isTrue); await tester.tapAt(const Offset(10, 10)); diff --git a/packages/flame/test/events/flame_game_mixins/has_draggable_components_test.dart b/packages/flame/test/events/flame_game_mixins/has_draggable_components_test.dart index 0746fdb19e..bf2385a1e6 100644 --- a/packages/flame/test/events/flame_game_mixins/has_draggable_components_test.dart +++ b/packages/flame/test/events/flame_game_mixins/has_draggable_components_test.dart @@ -3,7 +3,7 @@ import 'package:flame/components.dart'; import 'package:flame/events.dart'; import 'package:flame/game.dart'; -import 'package:flame/src/events/flame_game_mixins/has_draggable_components.dart'; +import 'package:flame/src/events/flame_game_mixins/multi_drag_dispatcher.dart'; import 'package:flutter_test/flutter_test.dart'; void main() { @@ -28,9 +28,10 @@ void main() { await tester.pumpWidget(GameWidget(game: game)); await tester.pump(); await tester.pump(const Duration(milliseconds: 10)); - expect(game.children.length, 2); - expect(game.children.elementAt(0), isA<_DragCallbacksComponent>()); - expect(game.children.elementAt(1), isA()); + + expect(game.children.length, 4); + expect(game.children.elementAt(1), isA<_DragCallbacksComponent>()); + expect(game.children.elementAt(2), isA()); // regular drag await tester.timedDragFrom( @@ -70,8 +71,8 @@ void main() { await tester.pumpWidget(GameWidget(game: game)); await tester.pump(); await tester.pump(); - expect(game.children.length, 3); - expect(game.children.last, isA()); + expect(game.children.length, 5); + expect(game.children.elementAt(3), isA()); await tester.timedDragFrom( const Offset(20, 20), @@ -98,8 +99,8 @@ void main() { await tester.pumpWidget(GameWidget(game: game)); await tester.pump(); await tester.pump(); - expect(game.children.length, 2); - expect(game.children.last, isA()); + expect(game.children.length, 4); + expect(game.children.elementAt(2), isA()); await tester.timedDragFrom( const Offset(80, 80), @@ -145,8 +146,8 @@ void main() { await tester.pumpWidget(GameWidget(game: game)); await tester.pump(); await tester.pump(); - expect(game.children.length, 3); - expect(game.children.last, isA()); + expect(game.children.length, 5); + expect(game.children.elementAt(3), isA()); await tester.timedDragFrom( const Offset(50, 50), diff --git a/packages/flame/test/events/flame_game_mixins/has_tappable_components_test.dart b/packages/flame/test/events/flame_game_mixins/has_tappable_components_test.dart index 8bed3d7d1c..ea53dc5404 100644 --- a/packages/flame/test/events/flame_game_mixins/has_tappable_components_test.dart +++ b/packages/flame/test/events/flame_game_mixins/has_tappable_components_test.dart @@ -3,7 +3,7 @@ import 'package:flame/components.dart'; import 'package:flame/events.dart'; import 'package:flame/game.dart'; -import 'package:flame/src/events/flame_game_mixins/has_tappable_components.dart'; +import 'package:flame/src/events/flame_game_mixins/multi_tap_dispatcher.dart'; import 'package:flutter_test/flutter_test.dart'; void main() { @@ -30,7 +30,7 @@ void main() { await tester.pumpWidget(GameWidget(game: game)); await tester.pump(); await tester.pump(const Duration(milliseconds: 10)); - expect(game.children.length, 2); + expect(game.children.length, 4); // regular tap await tester.tapAt(const Offset(100, 100)); @@ -101,8 +101,8 @@ void main() { await tester.pumpWidget(GameWidget(game: game)); await tester.pump(); await tester.pump(); - expect(game.children.length, 2); - expect(game.children.first.children.length, 1); + expect(game.children.length, 4); + expect(game.children.elementAt(1).children.length, 1); await tester.longPressAt(const Offset(50, 50)); await tester.pump(const Duration(seconds: 1)); @@ -149,8 +149,8 @@ void main() { await tester.pumpWidget(GameWidget(game: game)); await tester.pump(); await tester.pump(); - expect(game.children.length, 2); - expect(game.children.first.children.length, 1); + expect(game.children.length, 4); + expect(game.children.elementAt(1).children.length, 1); await tester.longPressAt(const Offset(50, 50)); await tester.pump(const Duration(seconds: 1)); @@ -197,8 +197,8 @@ void main() { await tester.pumpWidget(GameWidget(game: game)); await tester.pump(); await tester.pump(); - expect(game.children.length, 2); - expect(game.children.first.children.length, 1); + expect(game.children.length, 4); + expect(game.children.elementAt(1).children.length, 1); await tester.tapAt(const Offset(200, 200)); await tester.pump(const Duration(seconds: 1)); @@ -254,8 +254,8 @@ void main() { await tester.pumpWidget(GameWidget(game: game)); await tester.pump(); await tester.pump(const Duration(milliseconds: 10)); - expect(game.children.length, 3); - expect(game.children.last, isA()); + expect(game.children.length, 5); + expect(game.children.elementAt(3), isA()); await tester.tapAt(const Offset(50, 50)); await tester.pump(const Duration(seconds: 1)); diff --git a/packages/flame/test/experimental/has_game_reference_test.dart b/packages/flame/test/experimental/has_game_reference_test.dart index b99a0bc270..24d5c4db0f 100644 --- a/packages/flame/test/experimental/has_game_reference_test.dart +++ b/packages/flame/test/experimental/has_game_reference_test.dart @@ -53,7 +53,8 @@ void main() { expect( () => component.game, failsAssert( - 'Found game of type FlameGame, while type _MyGame was expected', + 'Found game of type FlameGame, while type _MyGame was ' + 'expected', ), ); }, diff --git a/packages/flame/test/game/camera/camera_test.dart b/packages/flame/test/game/camera/camera_test.dart index 8df8cb6945..72fd01bac9 100644 --- a/packages/flame/test/game/camera/camera_test.dart +++ b/packages/flame/test/game/camera/camera_test.dart @@ -14,7 +14,7 @@ void main() { testWithFlameGame( 'default camera applies no translation', (game) async { - expect(game.camera.position, Vector2.zero()); + expect(game.oldCamera.position, Vector2.zero()); await game.ensureAdd(_TestComponent(Vector2.all(10.0))); @@ -33,15 +33,15 @@ void main() { testWithFlameGame( 'camera snap movement', (game) async { - expect(game.camera.position, Vector2.zero()); + expect(game.oldCamera.position, Vector2.zero()); await game.ensureAdd(_TestComponent(Vector2.all(10.0))); // this puts the top left of the screen on (4,4) - game.camera.moveTo(Vector2.all(4.0)); - game.camera.snap(); + game.oldCamera.moveTo(Vector2.all(4.0)); + game.oldCamera.snap(); // the component will now be at 10 - 4 = (6, 6) - expect(game.camera.position, Vector2.all(4.0)); + expect(game.oldCamera.position, Vector2.all(4.0)); final canvas = MockCanvas(); game.render(canvas); @@ -59,17 +59,17 @@ void main() { testWithFlameGame( 'camera smooth movement', (game) async { - game.camera.speed = 1; // 1 pixel per second - game.camera.moveTo(Vector2(0.0, 10.0)); + game.oldCamera.speed = 1; // 1 pixel per second + game.oldCamera.moveTo(Vector2(0.0, 10.0)); // nothing should change yet - expect(game.camera.position, Vector2.all(0.0)); + expect(game.oldCamera.position, Vector2.all(0.0)); game.update(2.0); // 2s - expect(game.camera.position, Vector2(0.0, 2.0)); + expect(game.oldCamera.position, Vector2(0.0, 2.0)); game.update(5.0); // 5s - expect(game.camera.position, Vector2(0.0, 7.0)); + expect(game.oldCamera.position, Vector2(0.0, 7.0)); game.update(100.0); // more than needed at once - expect(game.camera.position, Vector2(0.0, 10.0)); + expect(game.oldCamera.position, Vector2(0.0, 10.0)); }, ); @@ -80,15 +80,15 @@ void main() { final p = _TestComponent(Vector2.all(10.0))..anchor = Anchor.center; await game.ensureAdd(p); - game.camera.followComponent(p); + game.oldCamera.followComponent(p); - expect(game.camera.position, Vector2.all(0.0)); + expect(game.oldCamera.position, Vector2.all(0.0)); p.position.setValues(10.0, 20.0); // follow happens immediately because the object's movement is assumed // to be smooth. game.update(0); // (10,20) - half screen (50,50). - expect(game.camera.position, Vector2(-40, -30)); + expect(game.oldCamera.position, Vector2(-40, -30)); final canvas = MockCanvas(); game.render(canvas); @@ -112,15 +112,16 @@ void main() { final p = _TestComponent(Vector2.all(10.0))..anchor = Anchor.center; await game.ensureAdd(p); // this would be a typical vertical shoot-em-up - game.camera.followComponent(p, relativeOffset: const Anchor(0.5, 0.8)); + game.oldCamera + .followComponent(p, relativeOffset: const Anchor(0.5, 0.8)); - expect(game.camera.position, Vector2.all(0.0)); + expect(game.oldCamera.position, Vector2.all(0.0)); p.position.setValues(600.0, 2000.0); // follow happens immediately because the object's movement is assumed // to be smooth. game.update(0); // (600,2000) - fractional screen (50,80) - expect(game.camera.position, Vector2(550, 1920)); + expect(game.oldCamera.position, Vector2(550, 1920)); final canvas = MockCanvas(); game.render(canvas); @@ -142,31 +143,31 @@ void main() { final p = _TestComponent(Vector2.all(10.0))..anchor = Anchor.center; await game.ensureAdd(p); - game.camera.followComponent( + game.oldCamera.followComponent( p, worldBounds: const Rect.fromLTWH(-1000, -1000, 2000, 2000), ); p.position.setValues(600.0, 700.0); // well within bounds game.update(0); - expect(game.camera.position, Vector2(550, 650)); + expect(game.oldCamera.position, Vector2(550, 650)); // x ok, y starts to get to bounds p.position.setValues(600.0, 950.0); // right on the edge game.update(0); - expect(game.camera.position, Vector2(550, 900)); + expect(game.oldCamera.position, Vector2(550, 900)); p.position.setValues(600.0, 950.0); // stop advancing game.update(0); - expect(game.camera.position, Vector2(550, 900)); + expect(game.oldCamera.position, Vector2(550, 900)); p.position.setValues(-1100.0, 950.0); game.update(0); - expect(game.camera.position, Vector2(-1000, 900)); + expect(game.oldCamera.position, Vector2(-1000, 900)); p.position.setValues(1000.0, 1000.0); game.update(0); - expect(game.camera.position, Vector2(900, 900)); + expect(game.oldCamera.position, Vector2(900, 900)); }, ); @@ -177,7 +178,7 @@ void main() { final p = _TestComponent(Vector2.all(10.0))..anchor = Anchor.center; await game.ensureAdd(p); - game.camera.followComponent( + game.oldCamera.followComponent( p, worldBounds: const Rect.fromLTWH(0, 0, 100, 100), ); @@ -185,15 +186,15 @@ void main() { // In this case the camera will just center the world, no matter the // player position. game.update(0); - expect(game.camera.position, Vector2(50, 50)); + expect(game.oldCamera.position, Vector2(50, 50)); p.position.setValues(60.0, 50.0); game.update(0); - expect(game.camera.position, Vector2(50, 50)); + expect(game.oldCamera.position, Vector2(50, 50)); p.position.setValues(-10.0, -20.0); game.update(0); - expect(game.camera.position, Vector2(50, 50)); + expect(game.oldCamera.position, Vector2(50, 50)); }, ); @@ -201,29 +202,29 @@ void main() { 'camera follow with zoom', (game) async { game.onGameResize(Vector2.all(100.0)); - game.camera.zoom = 2; + game.oldCamera.zoom = 2; final p = _TestComponent(Vector2.all(10.0))..anchor = Anchor.center; await game.ensureAdd(p); - game.camera.followComponent( + game.oldCamera.followComponent( p, worldBounds: const Rect.fromLTWH(0, 0, 100, 100), ); game.update(0); - expect(game.camera.position, Vector2(0, 0)); + expect(game.oldCamera.position, Vector2(0, 0)); p.position.setValues(50.0, 60.0); game.update(0); - expect(game.camera.position, Vector2(25, 35)); + expect(game.oldCamera.position, Vector2(25, 35)); p.position.setValues(80.0, 90.0); game.update(0); - expect(game.camera.position, Vector2(50, 50)); + expect(game.oldCamera.position, Vector2(50, 50)); p.position.setValues(-10.0, -20.0); game.update(0); - expect(game.camera.position, Vector2(0, 0)); + expect(game.oldCamera.position, Vector2(0, 0)); }, ); @@ -232,20 +233,20 @@ void main() { (game) async { game.onGameResize(Vector2.all(200.0)); - game.camera.setRelativeOffset(Anchor.center); + game.oldCamera.setRelativeOffset(Anchor.center); game.update(0); - expect(game.camera.position, Vector2.zero()); + expect(game.oldCamera.position, Vector2.zero()); game.update(10000); - expect(game.camera.position, Vector2.all(-100.0)); + expect(game.oldCamera.position, Vector2.all(-100.0)); }, ); testWithFlameGame( 'camera zoom', (game) async { - game.camera.zoom = 2; + game.oldCamera.zoom = 2; final p = _TestComponent(Vector2.all(100.0))..anchor = Anchor.center; await game.ensureAdd(p); @@ -267,8 +268,8 @@ void main() { 'camera zoom with setRelativeOffset', (game) async { game.onGameResize(Vector2.all(200.0)); - game.camera.zoom = 2; - game.camera.setRelativeOffset(Anchor.center); + game.oldCamera.zoom = 2; + game.oldCamera.setRelativeOffset(Anchor.center); final p = _TestComponent(Vector2.all(100.0))..anchor = Anchor.center; game.add(p); @@ -285,14 +286,14 @@ void main() { ..drawRect(const Rect.fromLTWH(0, 0, 1, 1)) ..translate(0, 0), // reset camera ); - expect(game.camera.position, Vector2.all(-50.0)); + expect(game.oldCamera.position, Vector2.all(-50.0)); }, ); testWithFlameGame( 'camera shake should return to where it started', (game) async { - final camera = game.camera; + final camera = game.oldCamera; expect(camera.position, Vector2.zero()); camera.shake(duration: 9000); game.update(5000); @@ -305,14 +306,14 @@ void main() { testWithFlameGame( 'default ratio viewport + camera with world boundaries', (game) async { - game.camera.viewport = FixedResolutionViewport(Vector2.all(100)); + game.oldCamera.viewport = FixedResolutionViewport(Vector2.all(100)); game.onGameResize(Vector2.all(200.0)); expect(game.canvasSize, Vector2.all(200.00)); expect(game.size, Vector2.all(100.00)); final p = _TestComponent(Vector2.all(10.0))..anchor = Anchor.center; await game.ensureAdd(p); - game.camera.followComponent( + game.oldCamera.followComponent( p, // this could be a typical mario-like platformer, where the player is // more on the bottom left to allow the level to be seen @@ -321,23 +322,23 @@ void main() { ); game.update(0); - expect(game.camera.position, Vector2(0, 0)); + expect(game.oldCamera.position, Vector2(0, 0)); p.position.setValues(30.0, 0.0); game.update(0); - expect(game.camera.position, Vector2(5, 0)); + expect(game.oldCamera.position, Vector2(5, 0)); p.position.setValues(30.0, 100.0); game.update(0); - expect(game.camera.position, Vector2(5, 75)); + expect(game.oldCamera.position, Vector2(5, 75)); p.position.setValues(30.0, 1000.0); game.update(0); - expect(game.camera.position, Vector2(5, 900)); + expect(game.oldCamera.position, Vector2(5, 900)); p.position.setValues(950.0, 20.0); game.update(0); - expect(game.camera.position, Vector2(900, 0)); + expect(game.oldCamera.position, Vector2(900, 0)); }, ); }); diff --git a/packages/flame/test/game/camera/viewport_test.dart b/packages/flame/test/game/camera/viewport_test.dart index 7f6165da2b..4b7fdd3a1c 100644 --- a/packages/flame/test/game/camera/viewport_test.dart +++ b/packages/flame/test/game/camera/viewport_test.dart @@ -13,12 +13,12 @@ void main() { testWithFlameGame( 'fixed ratio viewport has perfect ratio', (game) async { - game.camera.viewport = FixedResolutionViewport(Vector2.all(50)); + game.oldCamera.viewport = FixedResolutionViewport(Vector2.all(50)); game.onGameResize(Vector2.all(200.0)); expect(game.canvasSize, Vector2.all(200.00)); expect(game.size, Vector2.all(50.00)); - final viewport = game.camera.viewport as FixedResolutionViewport; + final viewport = game.oldCamera.viewport as FixedResolutionViewport; expect(viewport.resizeOffset, Vector2.zero()); expect(viewport.scaledSize, Vector2(200.0, 200.0)); expect(viewport.scale, 4.0); @@ -41,12 +41,12 @@ void main() { testWithFlameGame( 'fixed ratio viewport maxes width', (game) async { - game.camera.viewport = FixedResolutionViewport(Vector2.all(50)); + game.oldCamera.viewport = FixedResolutionViewport(Vector2.all(50)); game.onGameResize(Vector2(100.0, 200.0)); expect(game.canvasSize, Vector2(100.0, 200.00)); expect(game.size, Vector2.all(50.00)); - final viewport = game.camera.viewport as FixedResolutionViewport; + final viewport = game.oldCamera.viewport as FixedResolutionViewport; expect(viewport.resizeOffset, Vector2(0, 50.0)); expect(viewport.scaledSize, Vector2(100.0, 100.0)); expect(viewport.scale, 2.0); @@ -70,12 +70,13 @@ void main() { testWithFlameGame( 'fixed ratio viewport maxes height', (game) async { - game.camera.viewport = FixedResolutionViewport(Vector2(100.0, 400.0)); + game.oldCamera.viewport = + FixedResolutionViewport(Vector2(100.0, 400.0)); game.onGameResize(Vector2(100.0, 200.0)); expect(game.canvasSize, Vector2(100.0, 200.00)); expect(game.size, Vector2(100.00, 400.0)); - final viewport = game.camera.viewport as FixedResolutionViewport; + final viewport = game.oldCamera.viewport as FixedResolutionViewport; expect(viewport.resizeOffset, Vector2(25.0, 0)); expect(viewport.scaledSize, Vector2(50.0, 200.0)); expect(viewport.scale, 0.5); diff --git a/packages/flame/test/game/flame_game_test.dart b/packages/flame/test/game/flame_game_test.dart index f4c8af6de1..7a203cc6f9 100644 --- a/packages/flame/test/game/flame_game_test.dart +++ b/packages/flame/test/game/flame_game_test.dart @@ -6,7 +6,7 @@ import 'package:collection/collection.dart'; import 'package:flame/components.dart'; import 'package:flame/events.dart'; import 'package:flame/game.dart'; -import 'package:flame/src/events/flame_game_mixins/has_tappable_components.dart'; +import 'package:flame/src/events/flame_game_mixins/multi_tap_dispatcher.dart'; import 'package:flame/src/game/game_render_box.dart'; import 'package:flame_test/flame_test.dart'; import 'package:flutter/rendering.dart'; @@ -29,7 +29,7 @@ void main() { testWithFlameGame('game resize in zoomed game', (game) async { game - ..camera.zoom = 10 + ..oldCamera.zoom = 10 ..onGameResize(Vector2(300, 200)); final component = ComponentWithSizeHistory(); await game.ensureAdd(component); @@ -159,12 +159,13 @@ void main() { testWithFlameGame( 'removes PositionComponent when removeFromParent is called', (game) async { + final world = game.world; final component = PositionComponent(); - await game.ensureAdd(component); - expect(game.children.length, equals(1)); + await world.ensureAdd(component); + expect(world.children.length, equals(1)); component.removeFromParent(); game.updateTree(0); - expect(game.children.isEmpty, equals(true)); + expect(world.children.isEmpty, equals(true)); }, ); @@ -172,13 +173,14 @@ void main() { 'can add a component to a game without a layout', (WidgetTester tester) async { final game = FlameGame(); - final component = Component()..addToParent(game); + final world = game.world; + final component = Component()..addToParent(world); expect(game.hasLayout, false); await tester.pumpWidget(GameWidget(game: game)); game.update(0); - expect(game.children.length, 1); - expect(game.children.first, component); + expect(world.children.length, 1); + expect(world.children.first, component); }, ); }); @@ -199,7 +201,7 @@ void main() { 'viewport only with scale projection (no camera)', (game) async { final viewport = FixedResolutionViewport(Vector2.all(100)); - game.camera.viewport = viewport; + game.oldCamera.viewport = viewport; game.onGameResize(Vector2(200, 200)); expect(viewport.scale, 2); expect(viewport.resizeOffset, Vector2.zero()); // no translation @@ -233,7 +235,7 @@ void main() { 'viewport only with translation projection (no camera)', (game) async { final viewport = FixedResolutionViewport(Vector2.all(100)); - game.camera.viewport = viewport; + game.oldCamera.viewport = viewport; game.onGameResize(Vector2(200, 100)); expect(viewport.scale, 1); // no scale expect(viewport.resizeOffset, Vector2(50, 0)); // y is unchanged @@ -281,7 +283,7 @@ void main() { 'viewport only with both scale and translation (no camera)', (game) async { final viewport = FixedResolutionViewport(Vector2.all(100)); - game.camera.viewport = viewport; + game.oldCamera.viewport = viewport; game.onGameResize(Vector2(200, 400)); expect(viewport.scale, 2); expect(viewport.resizeOffset, Vector2(0, 100)); // x is unchanged @@ -312,7 +314,7 @@ void main() { (game) async { game.onGameResize(Vector2.all(1)); - game.camera.zoom = 3; // 3x zoom + game.oldCamera.zoom = 3; // 3x zoom checkProjectorReversibility(game.projector); expect( @@ -342,7 +344,7 @@ void main() { game.onGameResize(Vector2.all(1)); // Top left corner of the screen is (50, 100). - game.camera.snapTo(Vector2(50, 100)); + game.oldCamera.snapTo(Vector2(50, 100)); checkProjectorReversibility(game.projector); expect( @@ -369,11 +371,11 @@ void main() { game.onGameResize(Vector2.all(10)); // No-op because the default is already top left. - game.camera.setRelativeOffset(Anchor.topLeft); + game.oldCamera.setRelativeOffset(Anchor.topLeft); // Top left corner of the screen is (-100, -100). - game.camera.snapTo(Vector2.all(-100)); + game.oldCamera.snapTo(Vector2.all(-100)); // Zoom is 2x, meaning every 1 unit you walk away of (-100, -100). - game.camera.zoom = 2; + game.oldCamera.zoom = 2; checkProjectorReversibility(game.projector); expect( @@ -395,8 +397,8 @@ void main() { // Note: in the current implementation, if we change the relative // position the zoom is still applied w.r.t. the top left of the // screen. - game.camera.setRelativeOffset(Anchor.center); - game.camera.snap(); + game.oldCamera.setRelativeOffset(Anchor.center); + game.oldCamera.snap(); // That means that the center would be -100, -100 if the zoom was 1 // meaning the topLeft will be (-105, -105) (regardless of zoom), @@ -423,9 +425,9 @@ void main() { testWithFlameGame('camera & viewport - two translations', (game) async { final viewport = FixedResolutionViewport(Vector2.all(100)); - game.camera.viewport = viewport; // default camera + game.oldCamera.viewport = viewport; // default camera game.onGameResize(Vector2(200, 100)); - game.camera.snapTo(Vector2(10, 100)); + game.oldCamera.snapTo(Vector2(10, 100)); expect(viewport.scale, 1); // no scale expect(viewport.resizeOffset, Vector2(50, 0)); // y is unchanged checkProjectorReversibility(game.projector); @@ -458,10 +460,10 @@ void main() { testWithFlameGame('camera zoom & viewport translation', (game) async { final viewport = FixedResolutionViewport(Vector2.all(100)); - game.camera.viewport = viewport; + game.oldCamera.viewport = viewport; game.onGameResize(Vector2(200, 100)); - game.camera.zoom = 2; - game.camera.snap(); + game.oldCamera.zoom = 2; + game.oldCamera.snap(); expect(viewport.scale, 1); // no scale expect(viewport.resizeOffset, Vector2(50, 0)); // y is unchanged checkProjectorReversibility(game.projector); @@ -492,14 +494,14 @@ void main() { 'camera translation & viewport scale+translation', (game) async { final viewport = FixedResolutionViewport(Vector2.all(100)); - game.camera.viewport = viewport; + game.oldCamera.viewport = viewport; game.onGameResize(Vector2(200, 400)); expect(viewport.scale, 2); expect(viewport.resizeOffset, Vector2(0, 100)); // x is unchanged // The camera should apply a (10, 10) translation on top of the // viewport. - game.camera.snapTo(Vector2.all(10)); + game.oldCamera.snapTo(Vector2.all(10)); checkProjectorReversibility(game.projector); @@ -528,15 +530,15 @@ void main() { 'camera & viewport scale/zoom + translation (cancel out scaling)', (game) async { final viewport = FixedResolutionViewport(Vector2.all(100)); - game.camera.viewport = viewport; + game.oldCamera.viewport = viewport; game.onGameResize(Vector2(200, 400)); expect(viewport.scale, 2); expect(viewport.resizeOffset, Vector2(0, 100)); // x is unchanged // The camera should apply a (10, 10) translation + a 0.5x zoom on top // of the viewport coordinate system. - game.camera.zoom = 0.5; - game.camera.snapTo(Vector2.all(10)); + game.oldCamera.zoom = 0.5; + game.oldCamera.snapTo(Vector2.all(10)); checkProjectorReversibility(game.projector); @@ -582,15 +584,15 @@ void main() { 'camera & viewport scale/zoom + translation', (game) async { final viewport = FixedResolutionViewport(Vector2.all(100)); - game.camera.viewport = viewport; + game.oldCamera.viewport = viewport; game.onGameResize(Vector2(200, 400)); expect(viewport.scale, 2); expect(viewport.resizeOffset, Vector2(0, 100)); // x is unchanged // The camera should apply a (50, 0) translation + 4x zoom on top of // the viewport coordinate system. - game.camera.zoom = 4; - game.camera.snapTo(Vector2(50, 0)); + game.oldCamera.zoom = 4; + game.oldCamera.snapTo(Vector2(50, 0)); checkProjectorReversibility(game.projector); @@ -635,17 +637,20 @@ void main() { 'children in the constructor', () { return FlameGame( - children: [_IndexedComponent(1), _IndexedComponent(2)], + world: World( + children: [_IndexedComponent(1), _IndexedComponent(2)], + ), ); }, (game) async { - game.add(_IndexedComponent(3)); - game.add(_IndexedComponent(4)); + final world = game.world; + world.add(_IndexedComponent(3)); + world.add(_IndexedComponent(4)); await game.ready(); - expect(game.children.length, 4); + expect(world.children.length, 4); expect( - game.children + world.children .whereType<_IndexedComponent>() .map((c) => c.index) .isSorted((a, b) => a.compareTo(b)), @@ -667,7 +672,7 @@ void main() { game.add(_IndexedComponent(6)); await game.ready(); - expect(game.children.length, 6); + expect(game.children.whereType<_IndexedComponent>().length, 6); expect( game.children .whereType<_IndexedComponent>() diff --git a/packages/flame/test/game/mixins/single_game_instance_test.dart b/packages/flame/test/game/mixins/single_game_instance_test.dart index 2622a62a6d..67d0dfd2cc 100644 --- a/packages/flame/test/game/mixins/single_game_instance_test.dart +++ b/packages/flame/test/game/mixins/single_game_instance_test.dart @@ -20,7 +20,7 @@ void main() { expect( FlameGame.new, failsAssert( - "Instance of 'FlameGame' instantiated, while another game " + "Instance of 'FlameGame' instantiated, while another game " "Instance of 'SingletonGame' declares itself to be a singleton", ), ); diff --git a/packages/flame_forge2d/example/lib/main.dart b/packages/flame_forge2d/example/lib/main.dart index 5448f4de5c..db4b0d42d1 100644 --- a/packages/flame_forge2d/example/lib/main.dart +++ b/packages/flame_forge2d/example/lib/main.dart @@ -14,13 +14,13 @@ class Forge2DExample extends Forge2DGame { Future onLoad() async { await super.onLoad(); - cameraComponent.viewport.add(FpsTextComponent()); + camera.viewport.add(FpsTextComponent()); world.add(Ball()); world.addAll(createBoundaries()); } List createBoundaries() { - final visibleRect = cameraComponent.visibleWorldRect; + final visibleRect = camera.visibleWorldRect; final topLeft = visibleRect.topLeft.toVector2(); final topRight = visibleRect.topRight.toVector2(); final bottomRight = visibleRect.bottomRight.toVector2(); diff --git a/packages/flame_forge2d/lib/body_component.dart b/packages/flame_forge2d/lib/body_component.dart index 0f496183d1..5eeba27214 100644 --- a/packages/flame_forge2d/lib/body_component.dart +++ b/packages/flame_forge2d/lib/body_component.dart @@ -54,7 +54,7 @@ abstract class BodyComponent extends Component } Forge2DWorld get world => game.world; - CameraComponent get camera => game.cameraComponent; + CameraComponent get camera => game.camera; Vector2 get center => body.worldCenter; @override diff --git a/packages/flame_forge2d/lib/forge2d_game.dart b/packages/flame_forge2d/lib/forge2d_game.dart index 3046ed4e73..81adc7e91b 100644 --- a/packages/flame_forge2d/lib/forge2d_game.dart +++ b/packages/flame_forge2d/lib/forge2d_game.dart @@ -1,54 +1,29 @@ -import 'dart:async'; - import 'package:flame/camera.dart'; import 'package:flame/game.dart'; import 'package:flame_forge2d/forge2d_world.dart'; -import 'package:flutter/foundation.dart'; import 'package:forge2d/forge2d.dart'; /// The base game class for creating games that uses the Forge2D physics engine. -class Forge2DGame extends FlameGame { +class Forge2DGame extends FlameGame { Forge2DGame({ + Forge2DWorld? world, + CameraComponent? cameraComponent, Vector2? gravity, ContactListener? contactListener, double zoom = 10, - Forge2DWorld? world, - }) : _world = (world?..setGravity(gravity)) ?? - Forge2DWorld( - gravity: gravity, - contactListener: contactListener, - ), - _initialZoom = zoom; - - /// The [Forge2DWorld] that the [cameraComponent] is rendering. - /// Inside of this world is where all your components should be added. - Forge2DWorld get world => _world; - set world(Forge2DWorld newWorld) { - cameraComponent.world = newWorld; - _world = newWorld; - } - - Forge2DWorld _world; - - CameraComponent cameraComponent = CameraComponent(); - - // TODO(spydon): Use a meterToPixels constant instead for rendering. - // (see #2613) - final double _initialZoom; - - @override - @mustCallSuper - FutureOr onLoad() async { - cameraComponent - ..world = world - ..viewfinder.zoom = _initialZoom; - add(cameraComponent); - add(world); - } + }) : super( + world: ((world?..setGravity(gravity)) ?? + Forge2DWorld( + gravity: gravity, + contactListener: contactListener, + )) as T, + camera: (cameraComponent ?? CameraComponent()) + ..viewfinder.zoom = zoom, + ); /// Takes a point in world coordinates and returns it in screen coordinates. Vector2 worldToScreen(Vector2 position) { - return cameraComponent.localToGlobal(position); + return camera.localToGlobal(position); } /// Takes a point in screen coordinates and returns it in world coordinates. @@ -56,6 +31,6 @@ class Forge2DGame extends FlameGame { /// Remember that if you are using this for your events you can most of the /// time just use `event.localPosition` directly instead. Vector2 screenToWorld(Vector2 position) { - return cameraComponent.globalToLocal(position); + return camera.globalToLocal(position); } } diff --git a/packages/flame_forge2d/test/body_component_test.dart b/packages/flame_forge2d/test/body_component_test.dart index e404b58b53..86a2404e76 100644 --- a/packages/flame_forge2d/test/body_component_test.dart +++ b/packages/flame_forge2d/test/body_component_test.dart @@ -55,7 +55,7 @@ void main() { ..paint = testPaint; await game.world.add(component); - game.cameraComponent.follow(component); + game.camera.follow(component); }, ); @@ -75,7 +75,7 @@ void main() { ..paint = testPaint; await game.world.add(component); - game.cameraComponent.follow(component); + game.camera.follow(component); }, verify: (game, tester) async { await expectLater( @@ -104,7 +104,7 @@ void main() { ..paint = testPaint; await game.world.add(component); - game.cameraComponent.follow(component); + game.camera.follow(component); // a PolygonShape contains point expect(component.containsPoint(Vector2.all(10)), isTrue); @@ -136,7 +136,7 @@ void main() { ..paint = testPaint; await game.world.add(component); - game.cameraComponent.follow(component); + game.camera.follow(component); }, verify: (game, tester) async { await expectLater( @@ -165,7 +165,7 @@ void main() { ..paint = testPaint; await game.world.add(component); - game.cameraComponent.follow(component); + game.camera.follow(component); }, verify: (game, tester) async { await expectLater( diff --git a/packages/flame_forge2d/test/forge2d_game_test.dart b/packages/flame_forge2d/test/forge2d_game_test.dart index a3f2cb62fd..d140b2f697 100644 --- a/packages/flame_forge2d/test/forge2d_game_test.dart +++ b/packages/flame_forge2d/test/forge2d_game_test.dart @@ -13,6 +13,7 @@ void main() { testWithGame('Center positioned camera should be zero in world', _TestForge2dGame.new, (game) async { final size = Vector2.all(100); + game.update(0); game.onGameResize(size); expect( game.screenToWorld(size / 2), @@ -26,7 +27,7 @@ void main() { game.onGameResize(size); expect( game.screenToWorld(Vector2.zero()), - -(size / 2) / game.cameraComponent.viewfinder.zoom, + -(size / 2) / game.camera.viewfinder.zoom, ); }); @@ -37,7 +38,7 @@ void main() { game.onGameResize(size); expect( game.screenToWorld(screenPosition), - (-size / 2 + screenPosition) / game.cameraComponent.viewfinder.zoom, + (-size / 2 + screenPosition) / game.camera.viewfinder.zoom, ); }); @@ -59,7 +60,7 @@ void main() { game.onGameResize(size); expect( game.worldToScreen(worldPosition), - (size / 2) + worldPosition * game.cameraComponent.viewfinder.zoom, + (size / 2) + worldPosition * game.camera.viewfinder.zoom, ); }); @@ -69,12 +70,12 @@ void main() { final worldPosition = Vector2(10, 30); final viewfinderPosition = Vector2(20, 10); game.onGameResize(size); - game.cameraComponent.viewfinder.position = viewfinderPosition; + game.camera.viewfinder.position = viewfinderPosition; expect( game.worldToScreen(worldPosition), (size / 2) + (worldPosition - viewfinderPosition) * - game.cameraComponent.viewfinder.zoom, + game.camera.viewfinder.zoom, ); }); }, diff --git a/packages/flame_isolate/example/lib/brains/worker_overmind.dart b/packages/flame_isolate/example/lib/brains/worker_overmind.dart index ce5d0e3cf6..7d518fe9ea 100755 --- a/packages/flame_isolate/example/lib/brains/worker_overmind.dart +++ b/packages/flame_isolate/example/lib/brains/worker_overmind.dart @@ -21,7 +21,7 @@ class WorkerOvermind extends Component @override Future onLoad() async { - game.cameraComponent.viewport.add(isolateHud = WorkerOvermindHud()); + game.camera.viewport.add(isolateHud = WorkerOvermindHud()); super.onLoad(); } diff --git a/packages/flame_isolate/example/lib/colonists_game.dart b/packages/flame_isolate/example/lib/colonists_game.dart index 66c09985b3..c3bb1649ac 100755 --- a/packages/flame_isolate/example/lib/colonists_game.dart +++ b/packages/flame_isolate/example/lib/colonists_game.dart @@ -17,19 +17,19 @@ import 'package:flutter/services.dart'; class ColonistsGame extends FlameGame with KeyboardEvents { final PositionComponent _cameraPosition = PositionComponent(); late final GameMap _currentMap; - final world = World(); - late final CameraComponent cameraComponent; + ColonistsGame() + : super( + camera: CameraComponent.withFixedResolution( + width: 400, + height: 600, + ), + ); @override Future onLoad() async { - cameraComponent = CameraComponent.withFixedResolution( - world: world, - width: 400, - height: 600, - ); - addAll([cameraComponent, world]); - cameraComponent.follow(_cameraPosition); - cameraComponent.viewfinder.zoom = 0.4; + super.onLoad(); + camera.follow(_cameraPosition); + camera.viewfinder.zoom = 0.4; await Flame.images.load('bread.png'); await Flame.images.load('worker.png'); @@ -78,14 +78,14 @@ class ColonistsGame extends FlameGame with KeyboardEvents { _leftForce = howMuch; } else if (event.data.logicalKey == LogicalKeyboardKey.numpadAdd && event is RawKeyDownEvent) { - cameraComponent.viewfinder.zoom = min( - cameraComponent.viewfinder.zoom + 0.1, + camera.viewfinder.zoom = min( + camera.viewfinder.zoom + 0.1, 5, ); } else if (event.data.logicalKey == LogicalKeyboardKey.numpadSubtract && event is RawKeyDownEvent) { - cameraComponent.viewfinder.zoom = max( - cameraComponent.viewfinder.zoom - 0.1, + camera.viewfinder.zoom = max( + camera.viewfinder.zoom - 0.1, 0.1, ); } @@ -101,7 +101,7 @@ class ColonistsGame extends FlameGame with KeyboardEvents { final step = direction..scale(cameraSpeed * dt * 4); _cameraPosition.position += step; if (workers.isNotEmpty) { - cameraComponent.follow(workers.first); + camera.follow(workers.first); } } diff --git a/packages/flame_lottie/test/flame_lottie_test.dart b/packages/flame_lottie/test/flame_lottie_test.dart index 2cf794551f..33ba59fce9 100644 --- a/packages/flame_lottie/test/flame_lottie_test.dart +++ b/packages/flame_lottie/test/flame_lottie_test.dart @@ -17,11 +17,10 @@ void main() { final lottieComponent = LottieComponent(composition); - await game.add(lottieComponent); + await game.world.add(lottieComponent); await game.ready(); - expect(game.children, isNotEmpty); - expect(game.children, [lottieComponent]); + expect(game.world.children, [lottieComponent]); }, ); diff --git a/packages/flame_test/example/lib/game.dart b/packages/flame_test/example/lib/game.dart index 120567e67c..fe210f175d 100644 --- a/packages/flame_test/example/lib/game.dart +++ b/packages/flame_test/example/lib/game.dart @@ -23,6 +23,6 @@ class Background extends SpriteComponent with HasGameRef { class MyGame extends FlameGame { @override Future onLoad() async { - await add(Background()); + await world.add(Background()); } } diff --git a/packages/flame_test/example/test/flame_test_test.dart b/packages/flame_test/example/test/flame_test_test.dart index 1b818565e6..33c0d9fd7b 100644 --- a/packages/flame_test/example/test/flame_test_test.dart +++ b/packages/flame_test/example/test/flame_test_test.dart @@ -11,7 +11,7 @@ void main() { 'can load the game', MyGame.new, (game) async { - expect(game.children.length, 1); + expect(game.world.children.length, 1); }, ); @@ -29,7 +29,6 @@ void main() { 'render the background correctly', setUp: (game, _) async { await game.ready(); - await game.ensureAdd(Background()); }, verify: (game, tester) async { await expectLater( diff --git a/packages/flame_test/example/test/goldens/game.png b/packages/flame_test/example/test/goldens/game.png index 87a1947aaf..ca7168f5c9 100644 Binary files a/packages/flame_test/example/test/goldens/game.png and b/packages/flame_test/example/test/goldens/game.png differ diff --git a/packages/flame_test/test/flame_async_test.dart b/packages/flame_test/test/flame_async_test.dart index 46f8bb60e8..9a1e4cab21 100644 --- a/packages/flame_test/test/flame_async_test.dart +++ b/packages/flame_test/test/flame_async_test.dart @@ -11,24 +11,25 @@ void main() { testWithFlameGame( 'runs all the async tests', (game) async { - await game.ensureAdd(Component()); + final world = game.world; + await world.ensureAdd(Component()); instructions++; - await game.ensureAdd(Component()); + await world.ensureAdd(Component()); instructions++; - await game.ensureAdd(Component()); + await world.ensureAdd(Component()); instructions++; - await game.ensureAdd(Component()); + await world.ensureAdd(Component()); instructions++; - await game.ensureAdd(Component()); + await world.ensureAdd(Component()); instructions++; - await game.ensureAdd(Component()); + await world.ensureAdd(Component()); instructions++; - await game.ensureAdd(Component()); + await world.ensureAdd(Component()); instructions++; - await game.ensureAdd(Component()); + await world.ensureAdd(Component()); instructions++; - expect(game.children.length, equals(8)); + expect(world.children.length, equals(8)); instructions++; }, ); diff --git a/packages/flame_tiled/example/lib/main.dart b/packages/flame_tiled/example/lib/main.dart index dec2b18c08..34c0eb92f2 100644 --- a/packages/flame_tiled/example/lib/main.dart +++ b/packages/flame_tiled/example/lib/main.dart @@ -12,30 +12,29 @@ void main() { class TiledGame extends FlameGame { late TiledComponent mapComponent; - final world = World(); - late final CameraComponent cameraComponent; + TiledGame() + : super( + camera: CameraComponent.withFixedResolution( + width: 16 * 28, + height: 16 * 14, + ), + ); @override Future onLoad() async { - cameraComponent = CameraComponent.withFixedResolution( - world: world, - width: 16 * 28, - height: 16 * 14, - ); - cameraComponent.viewfinder.zoom = 0.5; - cameraComponent.viewfinder.anchor = Anchor.topLeft; - cameraComponent.viewfinder.add( - MoveToEffect( - Vector2(1000, 0), - EffectController( - duration: 10, - alternate: true, - infinite: true, + camera.viewfinder + ..zoom = 0.5 + ..anchor = Anchor.topLeft + ..add( + MoveToEffect( + Vector2(1000, 0), + EffectController( + duration: 10, + alternate: true, + infinite: true, + ), ), - ), - ); - - addAll([cameraComponent, world]); + ); mapComponent = await TiledComponent.load('map.tmx', Vector2.all(16)); world.add(mapComponent); diff --git a/packages/flame_tiled/test/tiled_test.dart b/packages/flame_tiled/test/tiled_test.dart index 42fedc5e08..58b1df9cdc 100644 --- a/packages/flame_tiled/test/tiled_test.dart +++ b/packages/flame_tiled/test/tiled_test.dart @@ -447,15 +447,14 @@ void main() { Vector2(16, 16), ); - final world = World(children: [component]); - final cameraComponent = CameraComponent(world: world); - // Need to initialize a game and call `onLoad` and `onGameResize` to // get the camera and canvas sizes all initialized - final game = FlameGame(children: [world, cameraComponent]); - cameraComponent.viewfinder.anchor = Anchor.center; - cameraComponent.viewfinder.position = Vector2(150, 20); - cameraComponent.viewport.size = mapSizePx.clone(); + final game = FlameGame(); + game.onGameResize(Vector2.all(100)); + final camera = game.camera; + game.world.add(component); + camera.viewfinder.position = Vector2(150, 20); + camera.viewport.size = mapSizePx.clone(); game.onGameResize(mapSizePx); component.onGameResize(mapSizePx); await component.onLoad();