mirror of
https://github.com/flame-engine/flame.git
synced 2025-11-01 01:18:38 +08:00
feat: Children as argument to FlameGame (#1680)
Since we have already added children as an argument to Component, this adds it to the FlameGame.
This commit is contained in:
@ -7,14 +7,30 @@ This diagram might look intimidating, but don't worry, it is not as complex as i
|
||||
|
||||
## Component
|
||||
|
||||
All components inherit from the abstract class `Component`.
|
||||
All components inherit from the abstract class `Component` and all components can have other
|
||||
`Component`s as children. This is the base of what we call the Flame Component System, or FCS for
|
||||
short.
|
||||
|
||||
If you want to skip reading about abstract classes you can jump directly to
|
||||
[](#positioncomponent).
|
||||
Children can be added either with the `add(Component c)` method or directly in the constructor.
|
||||
|
||||
Example:
|
||||
|
||||
```dart
|
||||
void main() {
|
||||
final component1 = Component(children: [Component(), Component()]);
|
||||
final component2 = Component();
|
||||
component2.add(Component());
|
||||
component2.addAll([Component(), Component()]);
|
||||
}
|
||||
```
|
||||
|
||||
The `Component()` here could of course be any subclass of `Component`.
|
||||
|
||||
Every `Component` has a few methods that you can optionally implement, which are used by the
|
||||
`FlameGame` class. If you are not using `FlameGame`, you can use these methods on your own game loop
|
||||
if you wish.
|
||||
`FlameGame` class.
|
||||
|
||||
|
||||
### Component lifecycle
|
||||
|
||||

|
||||
|
||||
@ -37,6 +53,7 @@ If the parent is not mounted yet, then this method will wait in a queue (this wi
|
||||
on the rest of the game engine).
|
||||
|
||||
A component lifecycle state can be checked by a series of getters:
|
||||
|
||||
- `isLoaded`: Returns a bool with the current loaded state
|
||||
- `loaded`: Returns a future that will complete once the component has finished loading
|
||||
- `isMounted`: Returns a bool with the current mounted state
|
||||
|
||||
@ -1,39 +1,27 @@
|
||||
# FlameGame
|
||||
|
||||
`FlameGame` is the most basic and most commonly used `Game` class in Flame.
|
||||
`FlameGame` is the most most commonly used `Game` class in Flame.
|
||||
|
||||
The `FlameGame` class implements a `Component` based `Game`. Basically it has a list of `Component`s
|
||||
and passes the `update` and `render` calls to all `Component`s that have been added to the game.
|
||||
The `FlameGame` class implements a `Component` based `Game`. Basically it has a tree of `Component`s
|
||||
and calls the `update` and `render` methods of all `Component`s that have been added to the game.
|
||||
|
||||
We refer to this component based system as the Flame Component System, FCS for short.
|
||||
|
||||
Every time the game needs to be resized, for example when the orientation is changed,
|
||||
`FlameGame` will call all of the `Component`s `resize` methods and it will also pass this information
|
||||
to the camera and viewport.
|
||||
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.
|
||||
|
||||
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`).
|
||||
|
||||
A `FlameGame` implementation example can be seen below:
|
||||
A simple `FlameGame` implementation which adds two components, one in `onLoad` and one directly in
|
||||
the constructor can look like this:
|
||||
|
||||
```dart
|
||||
/// A component that renders the crate sprite, with a 16 x 16 size.
|
||||
class MyCrate extends SpriteComponent {
|
||||
// creates a component that renders the crate.png sprite, with size 16 x 16
|
||||
MyCrate() : super(size: Vector2.all(16), anchor: Anchor.center);
|
||||
MyCrate() : super(size: Vector2.all(16));
|
||||
|
||||
@override
|
||||
Future<void> onLoad() async {
|
||||
sprite = await Sprite.load('crate.png');
|
||||
}
|
||||
|
||||
@override
|
||||
void onGameResize(Vector2 gameSize) {
|
||||
super.onGameResize(gameSize);
|
||||
// We don't need to set the position in the constructor, we can set it
|
||||
// directly here since it will be called once before the first time it
|
||||
// is rendered.
|
||||
position = gameSize / 2;
|
||||
}
|
||||
}
|
||||
|
||||
class MyGame extends FlameGame {
|
||||
@ -44,7 +32,7 @@ class MyGame extends FlameGame {
|
||||
}
|
||||
|
||||
main() {
|
||||
final myGame = MyGame();
|
||||
final myGame = MyGame(children: [MyCrate]);
|
||||
runApp(
|
||||
GameWidget(
|
||||
game: myGame,
|
||||
@ -62,8 +50,15 @@ To remove components from the list on a `FlameGame` the `remove` or `removeAll`
|
||||
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.
|
||||
|
||||
Any component on which the `remove()` method has been called will also be removed. You can do this
|
||||
simply by doing `yourComponent.remove();`.
|
||||
|
||||
## Resizing
|
||||
|
||||
Every time the game needs to be resized, for example when the orientation is changed,
|
||||
`FlameGame` 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`).
|
||||
|
||||
|
||||
## Lifecycle
|
||||
|
||||
@ -9,16 +9,15 @@ class PriorityExample extends FlameGame with HasTappables {
|
||||
the priority.
|
||||
''';
|
||||
|
||||
@override
|
||||
Future<void> onLoad() async {
|
||||
final squares = [
|
||||
PriorityExample()
|
||||
: super(
|
||||
children: [
|
||||
Square(Vector2(100, 100)),
|
||||
Square(Vector2(160, 100)),
|
||||
Square(Vector2(170, 150)),
|
||||
Square(Vector2(110, 150)),
|
||||
];
|
||||
addAll(squares);
|
||||
}
|
||||
],
|
||||
);
|
||||
}
|
||||
|
||||
class Square extends RectangleComponent
|
||||
|
||||
@ -16,13 +16,16 @@ 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 Component with Game {
|
||||
FlameGame({Camera? camera}) {
|
||||
FlameGame({
|
||||
Iterable<Component>? children,
|
||||
Camera? camera,
|
||||
}) : super(children: children) {
|
||||
assert(
|
||||
Component.staticGameInstance == null,
|
||||
'$this instantiated, while another game ${Component.staticGameInstance} '
|
||||
'declares itself to be a singleton',
|
||||
);
|
||||
_cameraWrapper = CameraWrapper(camera ?? Camera(), children);
|
||||
_cameraWrapper = CameraWrapper(camera ?? Camera(), this.children);
|
||||
}
|
||||
|
||||
late final CameraWrapper _cameraWrapper;
|
||||
|
||||
@ -1,3 +1,4 @@
|
||||
import 'package:collection/collection.dart';
|
||||
import 'package:flame/components.dart';
|
||||
import 'package:flame/game.dart';
|
||||
import 'package:flame/src/game/game_render_box.dart';
|
||||
@ -610,10 +611,76 @@ void main() {
|
||||
expect(game.projector.unscaleVector(Vector2(8, 16)), Vector2(1, 2));
|
||||
},
|
||||
);
|
||||
|
||||
testWithGame<FlameGame>(
|
||||
'children in the constructor',
|
||||
() {
|
||||
return FlameGame(
|
||||
children: [_IndexedComponent(1), _IndexedComponent(2)],
|
||||
);
|
||||
},
|
||||
(game) async {
|
||||
game.add(_IndexedComponent(3));
|
||||
game.add(_IndexedComponent(4));
|
||||
await game.ready();
|
||||
|
||||
expect(game.children.length, 4);
|
||||
expect(
|
||||
game.children
|
||||
.whereType<_IndexedComponent>()
|
||||
.map((c) => c.index)
|
||||
.isSorted((a, b) => a.compareTo(b)),
|
||||
isTrue,
|
||||
);
|
||||
},
|
||||
);
|
||||
|
||||
testWithGame<FlameGame>(
|
||||
'children in the constructor and onLoad',
|
||||
() {
|
||||
return _ConstructorChildrenGame(
|
||||
constructorChildren: [_IndexedComponent(1), _IndexedComponent(2)],
|
||||
onLoadChildren: [_IndexedComponent(3), _IndexedComponent(4)],
|
||||
);
|
||||
},
|
||||
(game) async {
|
||||
game.add(_IndexedComponent(5));
|
||||
game.add(_IndexedComponent(6));
|
||||
await game.ready();
|
||||
|
||||
expect(game.children.length, 6);
|
||||
expect(
|
||||
game.children
|
||||
.whereType<_IndexedComponent>()
|
||||
.map((c) => c.index)
|
||||
.isSorted((a, b) => a.compareTo(b)),
|
||||
isTrue,
|
||||
);
|
||||
},
|
||||
);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
class _IndexedComponent extends Component {
|
||||
final int index;
|
||||
_IndexedComponent(this.index);
|
||||
}
|
||||
|
||||
class _ConstructorChildrenGame extends FlameGame {
|
||||
final Iterable<_IndexedComponent> onLoadChildren;
|
||||
|
||||
_ConstructorChildrenGame({
|
||||
required Iterable<_IndexedComponent> constructorChildren,
|
||||
required this.onLoadChildren,
|
||||
}) : super(children: constructorChildren);
|
||||
|
||||
@override
|
||||
Future<void> onLoad() async {
|
||||
addAll(onLoadChildren);
|
||||
}
|
||||
}
|
||||
|
||||
class _GameWithTappables extends FlameGame with HasTappables {}
|
||||
|
||||
class _MyTappableComponent extends _MyComponent with Tappable {
|
||||
|
||||
Reference in New Issue
Block a user