mirror of
https://github.com/flame-engine/flame.git
synced 2025-11-02 03:15:43 +08:00
refactor: Game is now a class, not a mixin (#1751)
This simple refactor allows us to write class MyGame extends Game, instead of a more awkward class MyGame with Game. However, using ... with Game still continues to work, so no changes necessary for the users.
This commit is contained in:
@ -138,7 +138,7 @@ class MyGame extends FlameGame {
|
|||||||
anchor: Anchor.center,
|
anchor: Anchor.center,
|
||||||
);
|
);
|
||||||
add(player);
|
add(player);
|
||||||
|
|
||||||
camera.followComponent(player);
|
camera.followComponent(player);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -148,17 +148,20 @@ class MyGame extends FlameGame {
|
|||||||
### Using the camera with the Game class
|
### Using the camera with the Game class
|
||||||
|
|
||||||
If you are not using `FlameGame`, but instead are using the `Game` mixin, then you need to manage
|
If you are not using `FlameGame`, but instead are using the `Game` mixin, then you need to manage
|
||||||
calling certain camera methods yourself. Let's say we have the following game structure, and we
|
calling certain camera methods yourself. Let's say we have the following game structure, and we
|
||||||
want to add the camera functionality:
|
want to add the camera functionality:
|
||||||
|
|
||||||
```dart
|
```dart
|
||||||
class YourGame with Game {
|
class YourGame extends Game {
|
||||||
Camera? camera;
|
Camera? camera;
|
||||||
|
|
||||||
|
@override
|
||||||
Future<void> onLoad() async {}
|
Future<void> onLoad() async {}
|
||||||
|
|
||||||
|
@override
|
||||||
void render(Canvas canvas) {}
|
void render(Canvas canvas) {}
|
||||||
|
|
||||||
|
@override
|
||||||
void update(double dt) {}
|
void update(double dt) {}
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
@ -166,8 +169,7 @@ class YourGame with Game {
|
|||||||
We first create a new camera instance on load and assign our game as the reference:
|
We first create a new camera instance on load and assign our game as the reference:
|
||||||
|
|
||||||
```dart
|
```dart
|
||||||
// ...
|
@override
|
||||||
|
|
||||||
Future<void> onLoad() async {
|
Future<void> onLoad() async {
|
||||||
camera = Camera();
|
camera = Camera();
|
||||||
|
|
||||||
@ -179,51 +181,42 @@ We first create a new camera instance on load and assign our game as the referen
|
|||||||
|
|
||||||
// Rest of your on load code.
|
// Rest of your on load code.
|
||||||
}
|
}
|
||||||
|
|
||||||
// ...
|
|
||||||
```
|
```
|
||||||
|
|
||||||
The camera can also be made aware of which position to follow, this is an optional feature as you
|
The camera can also be made aware of which position to follow, this is an optional feature as you
|
||||||
can also use the camare for just moving,snapping or shaking.
|
can also use the camera for just moving,snapping or shaking.
|
||||||
|
|
||||||
To do this the `Camera` class provides multiple methods for it but let's showcase the simplest one
|
To do this the `Camera` class provides multiple methods for it but let's showcase the simplest one
|
||||||
and that is the `followVector2`:
|
and that is the `followVector2`:
|
||||||
|
|
||||||
```dart
|
```dart
|
||||||
// Somewhere in your code.
|
// Somewhere in your code:
|
||||||
|
|
||||||
camera?.followVector2(
|
camera?.followVector2(
|
||||||
yourPositionToFollow,
|
yourPositionToFollow,
|
||||||
worldBounds: yourWorldBounds, // Optional to pass, it will overwrite the previous bounds.
|
worldBounds: yourWorldBounds, // Optional to pass, it will overwrite the previous bounds.
|
||||||
);
|
);
|
||||||
```
|
```
|
||||||
|
|
||||||
Now that the camera is created and it is aware of both the world bounds and the position it should
|
Now that the camera is created and it is aware of both the world bounds and the position it should
|
||||||
follow, it can be used to translate the canvas in the render method:
|
follow, it can be used to translate the canvas in the render method:
|
||||||
|
|
||||||
```dart
|
```dart
|
||||||
// ...
|
@override
|
||||||
|
|
||||||
void render(Canvas canvas) {
|
void render(Canvas canvas) {
|
||||||
camera?.apply(canvas); // This will apply the camera transformation.
|
camera?.apply(canvas); // This will apply the camera transformation.
|
||||||
|
|
||||||
// Rest of your rendering code.
|
// Rest of your rendering code.
|
||||||
}
|
}
|
||||||
|
|
||||||
// ...
|
|
||||||
```
|
```
|
||||||
|
|
||||||
The only thing left to do is to call the `update` method on the `Camera` so it can smoothly follow
|
The only thing left to do is to call the `update` method on the `Camera` so it can smoothly follow
|
||||||
your given position:
|
your given position:
|
||||||
|
|
||||||
```dart
|
```dart
|
||||||
// ...
|
@override
|
||||||
|
|
||||||
void update(double dt) {
|
void update(double dt) {
|
||||||
camera?.update(dt);
|
camera?.update(dt);
|
||||||
|
|
||||||
// Rest of your update code.
|
// Rest of your update code.
|
||||||
}
|
}
|
||||||
|
|
||||||
// ...
|
|
||||||
```
|
```
|
||||||
|
|||||||
@ -41,10 +41,12 @@ main() {
|
|||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
**Note:** If you instantiate your game in a build method your game will be rebuilt every time the
|
```{note}
|
||||||
Flutter tree gets rebuilt, which usually is more often than you'd like. To avoid this, you can
|
If you instantiate your game in a build method your game will be rebuilt every time the
|
||||||
instead create an instance of your game first and reference it within your widget structure, like
|
Flutter tree gets rebuilt, which usually is more often than you'd like. To avoid this, you can
|
||||||
it is done in the example above.
|
instead create an instance of your game first and reference it within your widget structure, like
|
||||||
|
it is done in the example above.
|
||||||
|
```
|
||||||
|
|
||||||
To remove components from the list on a `FlameGame` the `remove` or `removeAll` methods can be used.
|
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
|
The first can be used if you just want to remove one component, and the second can be used when you
|
||||||
@ -103,7 +105,7 @@ just draw a background that covers the whole canvas if you would want it to chan
|
|||||||
## SingleGameInstance mixin
|
## SingleGameInstance mixin
|
||||||
|
|
||||||
An optional mixin `SingleGameInstance` can be applied to your game if you are making a single-game
|
An optional mixin `SingleGameInstance` can be applied to your game if you are making a single-game
|
||||||
application. This is a common scenario when building games: there is a single full-screen
|
application. This is a common scenario when building games: there is a single full-screen
|
||||||
`GameWidget` which hosts a single `Game` instance.
|
`GameWidget` which hosts a single `Game` instance.
|
||||||
|
|
||||||
Adding this mixin provides performance advantages in certain scenarios. In particular, a component's
|
Adding this mixin provides performance advantages in certain scenarios. In particular, a component's
|
||||||
@ -123,7 +125,7 @@ class MyGame extends FlameGame with SingleGameInstance {
|
|||||||
|
|
||||||

|

|
||||||
|
|
||||||
The `Game` mixin is a low-level API that can be used when you want to implement the functionality of
|
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
|
how the game engine should be structured. `Game` does not implement any `update` or
|
||||||
`render` function for example.
|
`render` function for example.
|
||||||
|
|
||||||
@ -133,13 +135,15 @@ called from the `GameWidget` (or another parent) when the game is loaded + mount
|
|||||||
called after `onLoad`) is called every time it is added to a new parent. `onRemove` is called when
|
called after `onLoad`) is called every time it is added to a new parent. `onRemove` is called when
|
||||||
the class is removed from a parent.
|
the class is removed from a parent.
|
||||||
|
|
||||||
**Note**: The `Game` mixin allows for more freedom of how to implement things, but you are also
|
```{note}
|
||||||
|
The `Game` class allows for more freedom of how to implement things, but you are also
|
||||||
missing out on all of the built-in features in Flame if you use it.
|
missing out on all of the built-in features in Flame if you use it.
|
||||||
|
```
|
||||||
|
|
||||||
An example of how a `Game` implementation could look like:
|
An example of how a `Game` implementation could look like:
|
||||||
|
|
||||||
```dart
|
```dart
|
||||||
class MyGameSubClass with Game {
|
class MyGameSubClass extends Game {
|
||||||
@override
|
@override
|
||||||
void render(Canvas canvas) {
|
void render(Canvas canvas) {
|
||||||
// ...
|
// ...
|
||||||
@ -151,7 +155,7 @@ class MyGameSubClass with Game {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
main() {
|
void main() {
|
||||||
final myGame = MyGameSubClass();
|
final myGame = MyGameSubClass();
|
||||||
runApp(
|
runApp(
|
||||||
GameWidget(
|
GameWidget(
|
||||||
@ -202,7 +206,7 @@ by setting a `overlayBuilderMap`.
|
|||||||
void main() {
|
void main() {
|
||||||
// Inside the game methods:
|
// Inside the game methods:
|
||||||
final pauseOverlayIdentifier = 'PauseMenu';
|
final pauseOverlayIdentifier = 'PauseMenu';
|
||||||
|
|
||||||
// Marks 'PauseMenu' to be rendered.
|
// Marks 'PauseMenu' to be rendered.
|
||||||
overlays.add(pauseOverlayIdentifier);
|
overlays.add(pauseOverlayIdentifier);
|
||||||
// Marks 'PauseMenu' to not be rendered.
|
// Marks 'PauseMenu' to not be rendered.
|
||||||
@ -226,8 +230,8 @@ Widget build(BuildContext context) {
|
|||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
The order of rendering for an overlay is determined by the order of the keys in the
|
The order of rendering for an overlay is determined by the order of the keys in the
|
||||||
`overlayBuilderMap`.
|
`overlayBuilderMap`.
|
||||||
|
|
||||||
An example of feature can be found
|
An example of feature can be found
|
||||||
[here](https://github.com/flame-engine/flame/blob/main/examples/lib/stories/system/overlays_example.dart).
|
[here](https://github.com/flame-engine/flame/blob/main/examples/lib/stories/system/overlays_example.dart).
|
||||||
|
|||||||
@ -2,7 +2,7 @@ import 'package:flame/extensions.dart';
|
|||||||
import 'package:flame/game.dart';
|
import 'package:flame/game.dart';
|
||||||
import 'package:flame/parallax.dart';
|
import 'package:flame/parallax.dart';
|
||||||
|
|
||||||
class NoFCSParallaxExample with Game {
|
class NoFCSParallaxExample extends Game {
|
||||||
static const String description = '''
|
static const String description = '''
|
||||||
This examples serves to test the Parallax feature outside of the Flame
|
This examples serves to test the Parallax feature outside of the Flame
|
||||||
Component System (FCS), use the other files in this folder for examples on
|
Component System (FCS), use the other files in this folder for examples on
|
||||||
|
|||||||
@ -4,7 +4,7 @@ import 'package:flame/palette.dart';
|
|||||||
import 'package:flutter/services.dart';
|
import 'package:flutter/services.dart';
|
||||||
import 'package:flutter/widgets.dart';
|
import 'package:flutter/widgets.dart';
|
||||||
|
|
||||||
class NoFlameGameExample with Game, KeyboardEvents {
|
class NoFlameGameExample extends Game with KeyboardEvents {
|
||||||
static const String description = '''
|
static const String description = '''
|
||||||
This example showcases how to create a game without the FlameGame.
|
This example showcases how to create a game without the FlameGame.
|
||||||
It also briefly showcases how to act on keyboard events.
|
It also briefly showcases how to act on keyboard events.
|
||||||
|
|||||||
@ -5,9 +5,9 @@ export 'src/extensions/vector2.dart';
|
|||||||
export 'src/game/camera/camera.dart';
|
export 'src/game/camera/camera.dart';
|
||||||
export 'src/game/camera/viewport.dart';
|
export 'src/game/camera/viewport.dart';
|
||||||
export 'src/game/flame_game.dart';
|
export 'src/game/flame_game.dart';
|
||||||
|
export 'src/game/game.dart';
|
||||||
export 'src/game/game_widget/game_widget.dart';
|
export 'src/game/game_widget/game_widget.dart';
|
||||||
export 'src/game/mixins/fps_counter.dart';
|
export 'src/game/mixins/fps_counter.dart';
|
||||||
export 'src/game/mixins/game.dart';
|
|
||||||
export 'src/game/mixins/has_draggables.dart';
|
export 'src/game/mixins/has_draggables.dart';
|
||||||
export 'src/game/mixins/has_hoverables.dart';
|
export 'src/game/mixins/has_hoverables.dart';
|
||||||
export 'src/game/mixins/has_tappables.dart';
|
export 'src/game/mixins/has_tappables.dart';
|
||||||
|
|||||||
@ -7,7 +7,7 @@ import 'package:flame/src/components/component_set.dart';
|
|||||||
import 'package:flame/src/components/mixins/coordinate_transform.dart';
|
import 'package:flame/src/components/mixins/coordinate_transform.dart';
|
||||||
import 'package:flame/src/components/position_type.dart';
|
import 'package:flame/src/components/position_type.dart';
|
||||||
import 'package:flame/src/game/flame_game.dart';
|
import 'package:flame/src/game/flame_game.dart';
|
||||||
import 'package:flame/src/game/mixins/game.dart';
|
import 'package:flame/src/game/game.dart';
|
||||||
import 'package:flame/src/gestures/events.dart';
|
import 'package:flame/src/gestures/events.dart';
|
||||||
import 'package:flame/src/text/text_paint.dart';
|
import 'package:flame/src/text/text_paint.dart';
|
||||||
import 'package:flutter/painting.dart';
|
import 'package:flutter/painting.dart';
|
||||||
|
|||||||
@ -1,5 +1,5 @@
|
|||||||
import 'package:flame/src/events/interfaces/multi_drag_listener.dart';
|
import 'package:flame/src/events/interfaces/multi_drag_listener.dart';
|
||||||
import 'package:flame/src/game/mixins/game.dart';
|
import 'package:flame/src/game/game.dart';
|
||||||
import 'package:flutter/gestures.dart';
|
import 'package:flutter/gestures.dart';
|
||||||
import 'package:meta/meta.dart';
|
import 'package:meta/meta.dart';
|
||||||
|
|
||||||
|
|||||||
@ -1,6 +1,6 @@
|
|||||||
import 'package:flame/src/events/flame_game_mixins/has_draggable_components.dart';
|
import 'package:flame/src/events/flame_game_mixins/has_draggable_components.dart';
|
||||||
import 'package:flame/src/events/interfaces/multi_drag_listener.dart';
|
import 'package:flame/src/events/interfaces/multi_drag_listener.dart';
|
||||||
import 'package:flame/src/game/mixins/game.dart';
|
import 'package:flame/src/game/game.dart';
|
||||||
import 'package:flame/src/gestures/events.dart';
|
import 'package:flame/src/gestures/events.dart';
|
||||||
import 'package:flutter/gestures.dart';
|
import 'package:flutter/gestures.dart';
|
||||||
|
|
||||||
|
|||||||
@ -1,6 +1,6 @@
|
|||||||
import 'package:flame/src/events/flame_game_mixins/has_tappable_components.dart';
|
import 'package:flame/src/events/flame_game_mixins/has_tappable_components.dart';
|
||||||
import 'package:flame/src/events/interfaces/multi_tap_listener.dart';
|
import 'package:flame/src/events/interfaces/multi_tap_listener.dart';
|
||||||
import 'package:flame/src/game/mixins/game.dart';
|
import 'package:flame/src/game/game.dart';
|
||||||
import 'package:flame/src/gestures/events.dart';
|
import 'package:flame/src/gestures/events.dart';
|
||||||
import 'package:flutter/gestures.dart';
|
import 'package:flutter/gestures.dart';
|
||||||
|
|
||||||
|
|||||||
@ -4,7 +4,7 @@ import 'package:flame/src/events/flame_game_mixins/has_tappable_components.dart'
|
|||||||
import 'package:flame/src/events/messages/position_event.dart';
|
import 'package:flame/src/events/messages/position_event.dart';
|
||||||
import 'package:flame/src/events/messages/tap_cancel_event.dart';
|
import 'package:flame/src/events/messages/tap_cancel_event.dart';
|
||||||
import 'package:flame/src/events/messages/tap_up_event.dart';
|
import 'package:flame/src/events/messages/tap_up_event.dart';
|
||||||
import 'package:flame/src/game/mixins/game.dart';
|
import 'package:flame/src/game/game.dart';
|
||||||
import 'package:flame/src/gestures/events.dart';
|
import 'package:flame/src/gestures/events.dart';
|
||||||
import 'package:flutter/gestures.dart';
|
import 'package:flutter/gestures.dart';
|
||||||
|
|
||||||
|
|||||||
@ -1,7 +1,7 @@
|
|||||||
import 'package:flame/extensions.dart';
|
import 'package:flame/extensions.dart';
|
||||||
import 'package:flame/src/events/messages/position_event.dart';
|
import 'package:flame/src/events/messages/position_event.dart';
|
||||||
import 'package:flame/src/events/messages/tap_down_event.dart';
|
import 'package:flame/src/events/messages/tap_down_event.dart';
|
||||||
import 'package:flame/src/game/mixins/game.dart';
|
import 'package:flame/src/game/game.dart';
|
||||||
import 'package:flame/src/gestures/events.dart';
|
import 'package:flame/src/gestures/events.dart';
|
||||||
import 'package:flutter/gestures.dart';
|
import 'package:flutter/gestures.dart';
|
||||||
|
|
||||||
|
|||||||
@ -4,7 +4,7 @@ import 'package:flame/src/components/component.dart';
|
|||||||
import 'package:flame/src/extensions/vector2.dart';
|
import 'package:flame/src/extensions/vector2.dart';
|
||||||
import 'package:flame/src/game/camera/camera.dart';
|
import 'package:flame/src/game/camera/camera.dart';
|
||||||
import 'package:flame/src/game/camera/camera_wrapper.dart';
|
import 'package:flame/src/game/camera/camera_wrapper.dart';
|
||||||
import 'package:flame/src/game/mixins/game.dart';
|
import 'package:flame/src/game/game.dart';
|
||||||
import 'package:flame/src/game/projector.dart';
|
import 'package:flame/src/game/projector.dart';
|
||||||
import 'package:meta/meta.dart';
|
import 'package:meta/meta.dart';
|
||||||
|
|
||||||
|
|||||||
@ -10,11 +10,11 @@ import 'package:meta/meta.dart';
|
|||||||
/// This gives access to a low-level game API, to not build everything from a
|
/// This gives access to a low-level game API, to not build everything from a
|
||||||
/// low level `FlameGame` should be used.
|
/// low level `FlameGame` should be used.
|
||||||
///
|
///
|
||||||
/// Add this mixin to your game class and implement the [update] and [render]
|
/// You can either extend this class, or add it as a mixin.
|
||||||
/// methods to use it in a `GameWidget`.
|
///
|
||||||
/// Flame will deal with calling these methods properly when the game's widget
|
/// Methods [update] and [render] need to be implemented in order to connect
|
||||||
/// is rendered.
|
/// your class with the internal game loop.
|
||||||
mixin Game {
|
abstract class Game {
|
||||||
final images = Images();
|
final images = Images();
|
||||||
final assets = AssetsCache();
|
final assets = AssetsCache();
|
||||||
|
|
||||||
@ -1,5 +1,5 @@
|
|||||||
|
import 'package:flame/src/game/game.dart';
|
||||||
import 'package:flame/src/game/game_loop.dart';
|
import 'package:flame/src/game/game_loop.dart';
|
||||||
import 'package:flame/src/game/mixins/game.dart';
|
|
||||||
import 'package:flutter/rendering.dart';
|
import 'package:flutter/rendering.dart';
|
||||||
import 'package:flutter/widgets.dart' hide WidgetBuilder;
|
import 'package:flutter/widgets.dart' hide WidgetBuilder;
|
||||||
//ignore_for_file: unnecessary_non_null_assertion
|
//ignore_for_file: unnecessary_non_null_assertion
|
||||||
|
|||||||
@ -2,9 +2,9 @@ import 'dart:async';
|
|||||||
|
|
||||||
import 'package:flame/extensions.dart';
|
import 'package:flame/extensions.dart';
|
||||||
import 'package:flame/input.dart';
|
import 'package:flame/input.dart';
|
||||||
|
import 'package:flame/src/game/game.dart';
|
||||||
import 'package:flame/src/game/game_render_box.dart';
|
import 'package:flame/src/game/game_render_box.dart';
|
||||||
import 'package:flame/src/game/game_widget/gestures.dart';
|
import 'package:flame/src/game/game_widget/gestures.dart';
|
||||||
import 'package:flame/src/game/mixins/game.dart';
|
|
||||||
import 'package:flutter/services.dart';
|
import 'package:flutter/services.dart';
|
||||||
import 'package:flutter/widgets.dart';
|
import 'package:flutter/widgets.dart';
|
||||||
|
|
||||||
|
|||||||
@ -1,6 +1,6 @@
|
|||||||
import 'package:flame/events.dart';
|
import 'package:flame/events.dart';
|
||||||
import 'package:flame/src/events/flame_drag_adapter.dart';
|
import 'package:flame/src/events/flame_drag_adapter.dart';
|
||||||
import 'package:flame/src/game/mixins/game.dart';
|
import 'package:flame/src/game/game.dart';
|
||||||
import 'package:flutter/gestures.dart';
|
import 'package:flutter/gestures.dart';
|
||||||
import 'package:flutter/widgets.dart';
|
import 'package:flutter/widgets.dart';
|
||||||
|
|
||||||
|
|||||||
@ -1,5 +1,5 @@
|
|||||||
import 'package:flame/src/components/component.dart';
|
import 'package:flame/src/components/component.dart';
|
||||||
import 'package:flame/src/game/mixins/game.dart';
|
import 'package:flame/src/game/game.dart';
|
||||||
|
|
||||||
/// Mixin that declares a [Game] class as a singleton.
|
/// Mixin that declares a [Game] class as a singleton.
|
||||||
///
|
///
|
||||||
|
|||||||
@ -1,4 +1,4 @@
|
|||||||
import 'package:flame/src/game/mixins/game.dart';
|
import 'package:flame/src/game/game.dart';
|
||||||
import 'package:flame/src/gestures/events.dart';
|
import 'package:flame/src/gestures/events.dart';
|
||||||
import 'package:flutter/gestures.dart';
|
import 'package:flutter/gestures.dart';
|
||||||
|
|
||||||
|
|||||||
@ -1,5 +1,5 @@
|
|||||||
import 'package:flame/extensions.dart';
|
import 'package:flame/extensions.dart';
|
||||||
import 'package:flame/src/game/mixins/game.dart';
|
import 'package:flame/src/game/game.dart';
|
||||||
import 'package:flutter/gestures.dart';
|
import 'package:flutter/gestures.dart';
|
||||||
|
|
||||||
/// [EventPosition] converts position based events to three different coordinate
|
/// [EventPosition] converts position based events to three different coordinate
|
||||||
|
|||||||
@ -10,7 +10,7 @@ import 'package:oxygen/oxygen.dart';
|
|||||||
/// [OxygenGame] should be extended to add your own game logic.
|
/// [OxygenGame] should be extended to add your own game logic.
|
||||||
///
|
///
|
||||||
/// It is based on the Oxygen package.
|
/// It is based on the Oxygen package.
|
||||||
abstract class OxygenGame with Game {
|
abstract class OxygenGame extends Game {
|
||||||
late final FlameWorld world;
|
late final FlameWorld world;
|
||||||
|
|
||||||
OxygenGame() {
|
OxygenGame() {
|
||||||
|
|||||||
Reference in New Issue
Block a user