mirror of
https://github.com/flame-engine/flame.git
synced 2025-11-02 20:13:50 +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
|
## 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
|
Children can be added either with the `add(Component c)` method or directly in the constructor.
|
||||||
[](#positioncomponent).
|
|
||||||
|
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
|
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
|
`FlameGame` class.
|
||||||
if you wish.
|
|
||||||
|
|
||||||
|
### 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).
|
on the rest of the game engine).
|
||||||
|
|
||||||
A component lifecycle state can be checked by a series of getters:
|
A component lifecycle state can be checked by a series of getters:
|
||||||
|
|
||||||
- `isLoaded`: Returns a bool with the current loaded state
|
- `isLoaded`: Returns a bool with the current loaded state
|
||||||
- `loaded`: Returns a future that will complete once the component has finished loading
|
- `loaded`: Returns a future that will complete once the component has finished loading
|
||||||
- `isMounted`: Returns a bool with the current mounted state
|
- `isMounted`: Returns a bool with the current mounted state
|
||||||
|
|||||||
@ -1,39 +1,27 @@
|
|||||||
# FlameGame
|
# 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
|
The `FlameGame` class implements a `Component` based `Game`. Basically it has a tree of `Component`s
|
||||||
and passes the `update` and `render` calls to all `Component`s that have been added to the game.
|
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.
|
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,
|
Components can be added to the `FlameGame` directly in the constructor with the named `children`
|
||||||
`FlameGame` will call all of the `Component`s `resize` methods and it will also pass this information
|
argument, or from anywhere else with the `add`/`addAll` methods.
|
||||||
to the camera and viewport.
|
|
||||||
|
|
||||||
The `FlameGame.camera` controls which point in the coordinate space should be the top-left of the
|
A simple `FlameGame` implementation which adds two components, one in `onLoad` and one directly in
|
||||||
screen (it defaults to [0,0] like a regular `Canvas`).
|
the constructor can look like this:
|
||||||
|
|
||||||
A `FlameGame` implementation example can be seen below:
|
|
||||||
|
|
||||||
```dart
|
```dart
|
||||||
|
/// A component that renders the crate sprite, with a 16 x 16 size.
|
||||||
class MyCrate extends SpriteComponent {
|
class MyCrate extends SpriteComponent {
|
||||||
// creates a component that renders the crate.png sprite, with size 16 x 16
|
MyCrate() : super(size: Vector2.all(16));
|
||||||
MyCrate() : super(size: Vector2.all(16), anchor: Anchor.center);
|
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Future<void> onLoad() async {
|
Future<void> onLoad() async {
|
||||||
sprite = await Sprite.load('crate.png');
|
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 {
|
class MyGame extends FlameGame {
|
||||||
@ -44,7 +32,7 @@ class MyGame extends FlameGame {
|
|||||||
}
|
}
|
||||||
|
|
||||||
main() {
|
main() {
|
||||||
final myGame = MyGame();
|
final myGame = MyGame(children: [MyCrate]);
|
||||||
runApp(
|
runApp(
|
||||||
GameWidget(
|
GameWidget(
|
||||||
game: myGame,
|
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
|
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.
|
||||||
|
|
||||||
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
|
## Lifecycle
|
||||||
|
|||||||
@ -9,16 +9,15 @@ class PriorityExample extends FlameGame with HasTappables {
|
|||||||
the priority.
|
the priority.
|
||||||
''';
|
''';
|
||||||
|
|
||||||
@override
|
PriorityExample()
|
||||||
Future<void> onLoad() async {
|
: super(
|
||||||
final squares = [
|
children: [
|
||||||
Square(Vector2(100, 100)),
|
Square(Vector2(100, 100)),
|
||||||
Square(Vector2(160, 100)),
|
Square(Vector2(160, 100)),
|
||||||
Square(Vector2(170, 150)),
|
Square(Vector2(170, 150)),
|
||||||
Square(Vector2(110, 150)),
|
Square(Vector2(110, 150)),
|
||||||
];
|
],
|
||||||
addAll(squares);
|
);
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
class Square extends RectangleComponent
|
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.
|
/// 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).
|
/// It is based on the Flame Component System (also known as FCS).
|
||||||
class FlameGame extends Component with Game {
|
class FlameGame extends Component with Game {
|
||||||
FlameGame({Camera? camera}) {
|
FlameGame({
|
||||||
|
Iterable<Component>? children,
|
||||||
|
Camera? camera,
|
||||||
|
}) : super(children: children) {
|
||||||
assert(
|
assert(
|
||||||
Component.staticGameInstance == null,
|
Component.staticGameInstance == null,
|
||||||
'$this instantiated, while another game ${Component.staticGameInstance} '
|
'$this instantiated, while another game ${Component.staticGameInstance} '
|
||||||
'declares itself to be a singleton',
|
'declares itself to be a singleton',
|
||||||
);
|
);
|
||||||
_cameraWrapper = CameraWrapper(camera ?? Camera(), children);
|
_cameraWrapper = CameraWrapper(camera ?? Camera(), this.children);
|
||||||
}
|
}
|
||||||
|
|
||||||
late final CameraWrapper _cameraWrapper;
|
late final CameraWrapper _cameraWrapper;
|
||||||
|
|||||||
@ -1,3 +1,4 @@
|
|||||||
|
import 'package:collection/collection.dart';
|
||||||
import 'package:flame/components.dart';
|
import 'package:flame/components.dart';
|
||||||
import 'package:flame/game.dart';
|
import 'package:flame/game.dart';
|
||||||
import 'package:flame/src/game/game_render_box.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));
|
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 _GameWithTappables extends FlameGame with HasTappables {}
|
||||||
|
|
||||||
class _MyTappableComponent extends _MyComponent with Tappable {
|
class _MyTappableComponent extends _MyComponent with Tappable {
|
||||||
|
|||||||
Reference in New Issue
Block a user