mirror of
https://github.com/flame-engine/flame.git
synced 2025-10-31 00:48:47 +08:00
Possibility to initialize all PositionComponents from onLoad (#1113)
* Fix ParallaxComponent constructor * Fix sizing bug parallax_component * Unify TextComponent and TextBoxComponent * Fix tests * Update PositionComponent docs * Add changelog entry * Apply suggestions from code review Co-authored-by: Erick <erickzanardoo@gmail.com> * Fix analyze issue * Apply suggestions from code review Co-authored-by: Luan Nico <luanpotter27@gmail.com> * Fix line length in components.md Co-authored-by: Erick <erickzanardoo@gmail.com> Co-authored-by: Luan Nico <luanpotter27@gmail.com>
This commit is contained in:
@ -8,7 +8,7 @@ This diagram might look intimidating, but don't worry, it is not as complex as i
|
||||
All components inherit from the abstract class `Component`.
|
||||
|
||||
If you want to skip reading about abstract classes you can jump directly to
|
||||
[PositionComponent](#positioncomponent).
|
||||
[](#positioncomponent).
|
||||
|
||||
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
|
||||
@ -90,15 +90,67 @@ class GameOverPanel extends PositionComponent with HasGameRef<MyGame> {
|
||||
|
||||
## PositionComponent
|
||||
|
||||
This class represent a single object on the screen, being a floating rectangle or a rotating sprite.
|
||||
This class represent a positioned object on the screen, being a floating rectangle or a rotating
|
||||
sprite. It can also represent a group of positioned components if children are added to it.
|
||||
|
||||
A `PositionComponent` has a `position`, `size` and `angle`, as well as some useful methods like
|
||||
`distance` and `angleBetween`.
|
||||
The base of the `PositionComponent` is that it has a `position`, `size`, `scale`, `angle` and
|
||||
`anchor` which transforms how the component is rendered.
|
||||
|
||||
### Position
|
||||
|
||||
The `position` is just a `Vector2` which represents the position of the component's anchor in
|
||||
relation to its parent; if the parent is a `FlameGame`, it is in relation to the viewport.
|
||||
|
||||
### Size
|
||||
|
||||
The `size` of the component when the zoom level of the camera is 1.0 (no zoom, default).
|
||||
The `size` is *not* in relation to the parent of the component.
|
||||
|
||||
### Scale
|
||||
|
||||
The `scale` is how much the component and its children should be scaled. Since it is represented
|
||||
by a `Vector2`, you can scale in a uniform way by changing `x` and `y` with the same amount, or in a
|
||||
non-uniform way, by change `x` or `y` by different amounts.
|
||||
|
||||
### Angle
|
||||
|
||||
The `angle` is the rotation angle around the anchor, represented as a double in radians. It is
|
||||
relative to the parent's angle.
|
||||
|
||||
### Anchor
|
||||
|
||||
The `anchor` is where on the component that the position and rotation should be defined from (the
|
||||
default is `Anchor.topLeft`). So if you have the anchor set as `Anchor.center` the component's
|
||||
position on the screen will be in the center of the component and if an `angle` is applied, it is
|
||||
rotated around the anchor, so in this case around the center of the component. You can think of it
|
||||
as the point within the component by which Flame "grabs" it.
|
||||
|
||||
### PositionComponent children
|
||||
|
||||
All children of the `PositionComponent` will be transformed in relation to the parent, which means
|
||||
that the `position`, `angle` and `scale` will be relative to the parents state.
|
||||
So if you, for example, wanted to position a child 50 logical pixels above the center of the parent
|
||||
you would do this:
|
||||
|
||||
```
|
||||
final parent = PositionComponent(
|
||||
position: Vector2(100, 100),
|
||||
size: Vector2(100, 100),
|
||||
anchor: Anchor.center,
|
||||
);
|
||||
final child = PositionComponent(position: Vector2(0, -50));
|
||||
parent.add(child);
|
||||
```
|
||||
|
||||
Remember that most components that are rendered on the screen are `PositionComponent`s, so
|
||||
this pattern can be used in for example [](#spritecomponent) and [](#spriteanimationcomponent) too.
|
||||
|
||||
### Render PositionComponent
|
||||
|
||||
When implementing the `render` method for a component that extends `PositionComponent` remember to
|
||||
render from the top left corner (0.0). Your render method should not handle where on the screen your
|
||||
component should be rendered. To handle where and how your component should be rendered use the
|
||||
`position`, `angle` and `anchor` properties and flame will automatically handle the rest for you.
|
||||
`position`, `angle` and `anchor` properties and Flame will automatically handle the rest for you.
|
||||
|
||||
If you want to know where on the screen the bounding box of the component is you can use the
|
||||
`toRect` method.
|
||||
@ -307,9 +359,9 @@ For a working example, check the example in the
|
||||
|
||||
## ParallaxComponent
|
||||
|
||||
This `Component` can be used to render backgrounds with a depth feeling by drawing several transparent
|
||||
images on top of each other, where each image or animation (`ParallaxRenderer`) is moving with a
|
||||
different velocity.
|
||||
This `Component` can be used to render backgrounds with a depth feeling by drawing several
|
||||
transparent images on top of each other, where each image or animation (`ParallaxRenderer`) is
|
||||
moving with a different velocity.
|
||||
|
||||
The rationale is that when you look at the horizon and moving, closer objects seem to move faster
|
||||
than distant ones.
|
||||
@ -378,8 +430,8 @@ parallax.velocityMultiplierDelta = Vector2(2.0, 1.0);
|
||||
By default, the images are aligned to the bottom left, repeated along the X-axis and scaled
|
||||
proportionally so that the image covers the height of the screen. If you want to change this
|
||||
behavior, for example if you are not making a side-scrolling game, you can set the `repeat`,
|
||||
`alignment` and `fill` parameters for each `ParallaxRenderer` and add them to `ParallaxLayer`s that you
|
||||
then pass in to the `ParallaxComponent`'s constructor.
|
||||
`alignment` and `fill` parameters for each `ParallaxRenderer` and add them to `ParallaxLayer`s that
|
||||
you then pass in to the `ParallaxComponent`'s constructor.
|
||||
|
||||
Advanced example:
|
||||
```dart
|
||||
@ -409,13 +461,16 @@ component (`game.add(parallaxComponent`).
|
||||
Also, don't forget to add you images to the `pubspec.yaml` file as assets or they wont be found.
|
||||
|
||||
The `Parallax` file contains an extension of the game which adds `loadParallax`, `loadParallaxLayer`
|
||||
, `loadParallaxImage` and `loadParallaxAnimation` so that it automatically uses your game's image cache instead of the global
|
||||
one. The same goes for the `ParallaxComponent` file, but that provides `loadParallaxComponent`.
|
||||
, `loadParallaxImage` and `loadParallaxAnimation` so that it automatically uses your game's image
|
||||
cache instead of the global one. The same goes for the `ParallaxComponent` file, but that provides
|
||||
`loadParallaxComponent`.
|
||||
|
||||
If you want a fullscreen `ParallaxComponent` simply omit the `size` argument and it will take the
|
||||
size of the game, it will also resize to fullscreen when the game changes size or orientation.
|
||||
|
||||
Flame provides two kinds of `ParallaxRenderer`: `ParallaxImage` and `ParallaxAnimation`, `ParallaxImage` is a static image renderer and `ParallaxAnimation` is, as it's name implies, an animation and frame based renderer.
|
||||
Flame provides two kinds of `ParallaxRenderer`: `ParallaxImage` and `ParallaxAnimation`,
|
||||
`ParallaxImage` is a static image renderer and `ParallaxAnimation` is, as it's name implies, an
|
||||
animation and frame based renderer.
|
||||
It is also possible to create custom renderers by extending the `ParallaxRenderer` class.
|
||||
|
||||
Three example implementations can be found in the
|
||||
@ -431,8 +486,8 @@ the shape of the specific component, it also takes all the arguments that can be
|
||||
There are three implementations of `ShapeComponent`, which are the following:
|
||||
|
||||
### CircleComponent
|
||||
A `CircleComponent` can be created only by defining its `radius`, but you most likely want to pass it
|
||||
a `position` and maybe `paint` (the default is white) too.
|
||||
A `CircleComponent` can be created only by defining its `radius`, but you most likely want to pass
|
||||
it a `position` and maybe `paint` (the default is white) too.
|
||||
|
||||
Example:
|
||||
```dart
|
||||
|
||||
@ -105,11 +105,11 @@ class JoystickAdvancedGame extends FlameGame with HasDraggables, HasTappables {
|
||||
style: TextStyle(color: BasicPalette.white.color),
|
||||
);
|
||||
speedText = TextComponent(
|
||||
'Speed: 0',
|
||||
text: 'Speed: 0',
|
||||
textRenderer: _regular,
|
||||
)..isHud = true;
|
||||
directionText = TextComponent(
|
||||
'Direction: idle',
|
||||
text: 'Direction: idle',
|
||||
textRenderer: _regular,
|
||||
)..isHud = true;
|
||||
|
||||
|
||||
@ -20,8 +20,8 @@ class AdvancedParallaxGame extends FlameGame {
|
||||
velocityMultiplier: Vector2(e.value, 1.0),
|
||||
),
|
||||
);
|
||||
final parallax = ParallaxComponent.fromParallax(
|
||||
Parallax(
|
||||
final parallax = ParallaxComponent(
|
||||
parallax: Parallax(
|
||||
await Future.wait(layers),
|
||||
baseVelocity: Vector2(20, 0),
|
||||
),
|
||||
|
||||
@ -39,7 +39,7 @@ class AnimationParallaxGame extends FlameGame {
|
||||
baseVelocity: Vector2(20, 0),
|
||||
);
|
||||
|
||||
final parallaxComponent = ParallaxComponent.fromParallax(parallax);
|
||||
final parallaxComponent = ParallaxComponent(parallax: parallax);
|
||||
add(parallaxComponent);
|
||||
}
|
||||
}
|
||||
|
||||
@ -65,6 +65,6 @@ class SandBoxLayerParallaxGame extends FlameGame {
|
||||
baseVelocity: Vector2(20, 0),
|
||||
);
|
||||
|
||||
add(ParallaxComponent.fromParallax(parallax));
|
||||
add(ParallaxComponent(parallax: parallax));
|
||||
}
|
||||
}
|
||||
|
||||
@ -30,7 +30,7 @@ final _shaded = TextPaint(
|
||||
class MyTextBox extends TextBoxComponent {
|
||||
MyTextBox(String text)
|
||||
: super(
|
||||
text,
|
||||
text: text,
|
||||
textRenderer: _box,
|
||||
boxConfig: TextBoxConfig(
|
||||
maxWidth: 400,
|
||||
@ -52,26 +52,26 @@ class TextGame extends FlameGame {
|
||||
Future<void> onLoad() async {
|
||||
await super.onLoad();
|
||||
add(
|
||||
TextComponent('Hello, Flame', textRenderer: _regular)
|
||||
TextComponent(text: 'Hello, Flame', textRenderer: _regular)
|
||||
..anchor = Anchor.topCenter
|
||||
..x = size.x / 2
|
||||
..y = 32.0,
|
||||
);
|
||||
|
||||
add(
|
||||
TextComponent('Text with shade', textRenderer: _shaded)
|
||||
TextComponent(text: 'Text with shade', textRenderer: _shaded)
|
||||
..anchor = Anchor.topRight
|
||||
..position = size - Vector2.all(100),
|
||||
);
|
||||
|
||||
add(
|
||||
TextComponent('center', textRenderer: _tiny)
|
||||
TextComponent(text: 'center', textRenderer: _tiny)
|
||||
..anchor = Anchor.center
|
||||
..position.setFrom(size / 2),
|
||||
);
|
||||
|
||||
add(
|
||||
TextComponent('bottomRight', textRenderer: _tiny)
|
||||
TextComponent(text: 'bottomRight', textRenderer: _tiny)
|
||||
..anchor = Anchor.bottomRight
|
||||
..position.setFrom(size),
|
||||
);
|
||||
|
||||
@ -39,8 +39,8 @@ class SpritebatchGame extends FlameGame {
|
||||
}
|
||||
|
||||
add(
|
||||
SpriteBatchComponent.fromSpriteBatch(
|
||||
spriteBatch,
|
||||
SpriteBatchComponent(
|
||||
spriteBatch: spriteBatch,
|
||||
blendMode: BlendMode.srcOver,
|
||||
),
|
||||
);
|
||||
|
||||
@ -33,6 +33,7 @@
|
||||
- Removed methods `preRender()` and `postRender()` in `Component`
|
||||
- Use `FlameTester` everywhere where it makes sense in the tests
|
||||
- Improved `IsometricTileMap`
|
||||
- Initialization of all `PositionComponent`s can be done from `onLoad` instead of the constructor
|
||||
- Rename `HasTappableComponents` to `HasTappables`
|
||||
- Rename `HasDraggableComponents` to `HasDraggables`
|
||||
- Rename `HasHoverableComponents` to `HasHoverableis`
|
||||
|
||||
@ -11,13 +11,13 @@ export '../nine_tile_box.dart';
|
||||
|
||||
/// This class is a thin wrapper on top of [NineTileBox] as a component.
|
||||
class NineTileBoxComponent extends PositionComponent {
|
||||
NineTileBox nineTileBox;
|
||||
NineTileBox? nineTileBox;
|
||||
|
||||
/// Takes the [NineTileBox] instance used to render this box.
|
||||
///
|
||||
/// It uses the x, y, width and height coordinates from the [PositionComponent] to render.
|
||||
NineTileBoxComponent(
|
||||
this.nineTileBox, {
|
||||
NineTileBoxComponent({
|
||||
this.nineTileBox,
|
||||
Vector2? position,
|
||||
Vector2? size,
|
||||
Vector2? scale,
|
||||
@ -33,9 +33,18 @@ class NineTileBoxComponent extends PositionComponent {
|
||||
priority: priority,
|
||||
);
|
||||
|
||||
@override
|
||||
@mustCallSuper
|
||||
void onMount() {
|
||||
assert(
|
||||
nineTileBox != null,
|
||||
'The nineTileBox should be set either in the constructor or in onLoad',
|
||||
);
|
||||
}
|
||||
|
||||
@mustCallSuper
|
||||
@override
|
||||
void render(Canvas c) {
|
||||
nineTileBox.drawRect(c, size.toRect());
|
||||
nineTileBox?.drawRect(c, size.toRect());
|
||||
}
|
||||
}
|
||||
|
||||
@ -13,7 +13,7 @@ import 'position_component.dart';
|
||||
|
||||
extension ParallaxComponentExtension on FlameGame {
|
||||
Future<ParallaxComponent> loadParallaxComponent(
|
||||
List<ParallaxData> dataList, {
|
||||
Iterable<ParallaxData> dataList, {
|
||||
Vector2? baseVelocity,
|
||||
Vector2? velocityMultiplierDelta,
|
||||
ImageRepeat repeat = ImageRepeat.repeatX,
|
||||
@ -59,42 +59,25 @@ class ParallaxComponent<T extends FlameGame> extends PositionComponent
|
||||
|
||||
/// Creates a component with an empty parallax which can be set later.
|
||||
ParallaxComponent({
|
||||
Parallax? parallax,
|
||||
Vector2? position,
|
||||
Vector2? size,
|
||||
Vector2? scale,
|
||||
double? angle,
|
||||
Anchor? anchor,
|
||||
int? priority,
|
||||
}) : isFullscreen = size == null ? true : false,
|
||||
}) : _parallax = parallax,
|
||||
isFullscreen =
|
||||
size == null && !(parallax?.isSized ?? false) ? true : false,
|
||||
super(
|
||||
position: position,
|
||||
size: size,
|
||||
size: size ?? ((parallax?.isSized ?? false) ? parallax?.size : null),
|
||||
scale: scale,
|
||||
angle: angle,
|
||||
anchor: anchor,
|
||||
priority: priority,
|
||||
);
|
||||
|
||||
/// Creates a component from a [Parallax] object.
|
||||
factory ParallaxComponent.fromParallax(
|
||||
Parallax parallax, {
|
||||
Vector2? position,
|
||||
Vector2? size,
|
||||
Vector2? scale,
|
||||
double? angle,
|
||||
Anchor? anchor,
|
||||
int? priority,
|
||||
}) {
|
||||
return ParallaxComponent(
|
||||
position: position,
|
||||
size: size ?? (parallax.isSized ? parallax.size : null),
|
||||
scale: scale,
|
||||
angle: angle,
|
||||
anchor: anchor,
|
||||
priority: priority,
|
||||
)..parallax = parallax;
|
||||
}
|
||||
|
||||
@mustCallSuper
|
||||
@override
|
||||
void onGameResize(Vector2 size) {
|
||||
@ -107,6 +90,14 @@ class ParallaxComponent<T extends FlameGame> extends PositionComponent
|
||||
parallax?.resize(newSize);
|
||||
}
|
||||
|
||||
@override
|
||||
void onMount() {
|
||||
assert(
|
||||
parallax != null,
|
||||
'The parallax needs to be set in either the constructor or in onLoad',
|
||||
);
|
||||
}
|
||||
|
||||
@override
|
||||
void update(double dt) {
|
||||
super.update(dt);
|
||||
@ -139,7 +130,7 @@ class ParallaxComponent<T extends FlameGame> extends PositionComponent
|
||||
///
|
||||
/// If no image cache is set, the global flame cache is used.
|
||||
static Future<ParallaxComponent> load(
|
||||
List<ParallaxData> dataList, {
|
||||
Iterable<ParallaxData> dataList, {
|
||||
Vector2? baseVelocity,
|
||||
Vector2? velocityMultiplierDelta,
|
||||
ImageRepeat repeat = ImageRepeat.repeatX,
|
||||
@ -153,8 +144,8 @@ class ParallaxComponent<T extends FlameGame> extends PositionComponent
|
||||
Anchor? anchor,
|
||||
int? priority,
|
||||
}) async {
|
||||
return ParallaxComponent.fromParallax(
|
||||
await Parallax.load(
|
||||
return ParallaxComponent(
|
||||
parallax: await Parallax.load(
|
||||
dataList,
|
||||
size: size,
|
||||
baseVelocity: baseVelocity,
|
||||
|
||||
@ -1,5 +1,7 @@
|
||||
import 'dart:ui';
|
||||
|
||||
import 'package:meta/meta.dart';
|
||||
|
||||
import '../sprite_batch.dart';
|
||||
import 'component.dart';
|
||||
|
||||
@ -10,15 +12,22 @@ class SpriteBatchComponent extends Component {
|
||||
Paint? paint;
|
||||
|
||||
/// Creates a component with an empty sprite batch which can be set later
|
||||
SpriteBatchComponent();
|
||||
|
||||
SpriteBatchComponent.fromSpriteBatch(
|
||||
this.spriteBatch, {
|
||||
SpriteBatchComponent({
|
||||
this.spriteBatch,
|
||||
this.blendMode,
|
||||
this.cullRect,
|
||||
this.paint,
|
||||
});
|
||||
|
||||
@override
|
||||
@mustCallSuper
|
||||
void onMount() {
|
||||
assert(
|
||||
spriteBatch != null,
|
||||
'You have to set spriteBatch in either the constructor or in onLoad',
|
||||
);
|
||||
}
|
||||
|
||||
@override
|
||||
void render(Canvas canvas) {
|
||||
spriteBatch?.render(
|
||||
|
||||
@ -68,6 +68,15 @@ class SpriteComponent extends PositionComponent with HasPaint {
|
||||
priority: priority,
|
||||
);
|
||||
|
||||
@override
|
||||
@mustCallSuper
|
||||
void onMount() {
|
||||
assert(
|
||||
sprite != null,
|
||||
'You have to set the sprite in either the constructor or in onLoad',
|
||||
);
|
||||
}
|
||||
|
||||
@mustCallSuper
|
||||
@override
|
||||
void render(Canvas canvas) {
|
||||
|
||||
@ -45,6 +45,19 @@ class SpriteGroupComponent<T> extends PositionComponent with HasPaint {
|
||||
|
||||
Sprite? get sprite => sprites?[current];
|
||||
|
||||
@override
|
||||
@mustCallSuper
|
||||
void onMount() {
|
||||
assert(
|
||||
sprites != null,
|
||||
'You have to set the sprites in either the constructor or in onLoad',
|
||||
);
|
||||
assert(
|
||||
current != null,
|
||||
'You have to set current in either the constructor or in onLoad',
|
||||
);
|
||||
}
|
||||
|
||||
@mustCallSuper
|
||||
@override
|
||||
void render(Canvas canvas) {
|
||||
|
||||
@ -3,6 +3,7 @@ import 'dart:math' as math;
|
||||
import 'dart:ui';
|
||||
|
||||
import 'package:flutter/widgets.dart' hide Image;
|
||||
import 'package:meta/meta.dart';
|
||||
|
||||
import '../../components.dart';
|
||||
import '../extensions/vector2.dart';
|
||||
@ -45,16 +46,13 @@ class TextBoxConfig {
|
||||
});
|
||||
}
|
||||
|
||||
class TextBoxComponent<T extends TextRenderer> extends PositionComponent {
|
||||
class TextBoxComponent<T extends TextRenderer> extends TextComponent {
|
||||
static final Paint _imagePaint = BasicPalette.white.paint()
|
||||
..filterQuality = FilterQuality.high;
|
||||
|
||||
final String _text;
|
||||
final T _textRenderer;
|
||||
final TextBoxConfig _boxConfig;
|
||||
final double pixelRatio;
|
||||
|
||||
late List<String> _lines;
|
||||
final List<String> _lines = [];
|
||||
double _maxLineWidth = 0.0;
|
||||
late double _lineHeight;
|
||||
late int _totalLines;
|
||||
@ -63,14 +61,10 @@ class TextBoxComponent<T extends TextRenderer> extends PositionComponent {
|
||||
Image? _cache;
|
||||
int? _previousChar;
|
||||
|
||||
String get text => _text;
|
||||
|
||||
TextRenderer get renderer => _textRenderer;
|
||||
|
||||
TextBoxConfig get boxConfig => _boxConfig;
|
||||
|
||||
TextBoxComponent(
|
||||
String text, {
|
||||
TextBoxComponent({
|
||||
String? text,
|
||||
T? textRenderer,
|
||||
TextBoxConfig? boxConfig,
|
||||
double? pixelRatio,
|
||||
@ -79,24 +73,35 @@ class TextBoxComponent<T extends TextRenderer> extends PositionComponent {
|
||||
double? angle,
|
||||
Anchor? anchor,
|
||||
int? priority,
|
||||
}) : _text = text,
|
||||
_boxConfig = boxConfig ?? TextBoxConfig(),
|
||||
_textRenderer = textRenderer ?? TextRenderer.createDefault<T>(),
|
||||
}) : _boxConfig = boxConfig ?? TextBoxConfig(),
|
||||
pixelRatio = pixelRatio ?? window.devicePixelRatio,
|
||||
super(
|
||||
text: text,
|
||||
textRenderer: textRenderer,
|
||||
position: position,
|
||||
scale: scale,
|
||||
angle: angle,
|
||||
anchor: anchor,
|
||||
priority: priority,
|
||||
) {
|
||||
_lines = [];
|
||||
);
|
||||
|
||||
@override
|
||||
@mustCallSuper
|
||||
Future<void> onLoad() async {
|
||||
await super.onLoad();
|
||||
await redraw();
|
||||
}
|
||||
|
||||
@override
|
||||
@internal
|
||||
void updateBounds() {
|
||||
_lines.clear();
|
||||
double? lineHeight;
|
||||
text.split(' ').forEach((word) {
|
||||
final possibleLine = _lines.isEmpty ? word : '${_lines.last} $word';
|
||||
lineHeight ??= _textRenderer.measureTextHeight(possibleLine);
|
||||
lineHeight ??= textRenderer.measureTextHeight(possibleLine);
|
||||
|
||||
final textWidth = _textRenderer.measureTextWidth(possibleLine);
|
||||
final textWidth = textRenderer.measureTextWidth(possibleLine);
|
||||
if (textWidth <= _boxConfig.maxWidth - _boxConfig.margins.horizontal) {
|
||||
if (_lines.isNotEmpty) {
|
||||
_lines.last = possibleLine;
|
||||
@ -114,19 +119,13 @@ class TextBoxComponent<T extends TextRenderer> extends PositionComponent {
|
||||
size = _recomputeSize();
|
||||
}
|
||||
|
||||
@override
|
||||
Future<void> onLoad() async {
|
||||
await super.onLoad();
|
||||
await redraw();
|
||||
}
|
||||
|
||||
void _updateMaxWidth(double w) {
|
||||
if (w > _maxLineWidth) {
|
||||
_maxLineWidth = w;
|
||||
}
|
||||
}
|
||||
|
||||
double get totalCharTime => _text.length * _boxConfig.timePerChar;
|
||||
double get totalCharTime => text.length * _boxConfig.timePerChar;
|
||||
|
||||
bool get finished => _lifeTime > totalCharTime + _boxConfig.dismissDelay;
|
||||
|
||||
@ -151,7 +150,7 @@ class TextBoxComponent<T extends TextRenderer> extends PositionComponent {
|
||||
}
|
||||
|
||||
double getLineWidth(String line, int charCount) {
|
||||
return _textRenderer.measureTextWidth(
|
||||
return textRenderer.measureTextWidth(
|
||||
line.substring(0, math.min(charCount, line.length)),
|
||||
);
|
||||
}
|
||||
@ -222,7 +221,7 @@ class TextBoxComponent<T extends TextRenderer> extends PositionComponent {
|
||||
}
|
||||
|
||||
void _drawLine(Canvas c, String line, double dy) {
|
||||
_textRenderer.render(c, line, Vector2(_boxConfig.margins.left, dy));
|
||||
textRenderer.render(c, line, Vector2(_boxConfig.margins.left, dy));
|
||||
}
|
||||
|
||||
Future<void> redraw() async {
|
||||
|
||||
@ -17,7 +17,7 @@ class TextComponent<T extends TextRenderer> extends PositionComponent {
|
||||
set text(String text) {
|
||||
if (_text != text) {
|
||||
_text = text;
|
||||
_updateBox();
|
||||
updateBounds();
|
||||
}
|
||||
}
|
||||
|
||||
@ -25,11 +25,11 @@ class TextComponent<T extends TextRenderer> extends PositionComponent {
|
||||
|
||||
set textRenderer(T textRenderer) {
|
||||
_textRenderer = textRenderer;
|
||||
_updateBox();
|
||||
updateBounds();
|
||||
}
|
||||
|
||||
TextComponent(
|
||||
this._text, {
|
||||
TextComponent({
|
||||
String? text,
|
||||
T? textRenderer,
|
||||
Vector2? position,
|
||||
Vector2? size,
|
||||
@ -37,7 +37,8 @@ class TextComponent<T extends TextRenderer> extends PositionComponent {
|
||||
double? angle,
|
||||
Anchor? anchor,
|
||||
int? priority,
|
||||
}) : _textRenderer = textRenderer ?? TextRenderer.createDefault<T>(),
|
||||
}) : _text = text ?? '',
|
||||
_textRenderer = textRenderer ?? TextRenderer.createDefault<T>(),
|
||||
super(
|
||||
position: position,
|
||||
size: size,
|
||||
@ -46,15 +47,15 @@ class TextComponent<T extends TextRenderer> extends PositionComponent {
|
||||
anchor: anchor,
|
||||
priority: priority,
|
||||
) {
|
||||
_updateBox();
|
||||
updateBounds();
|
||||
}
|
||||
|
||||
void _updateBox() {
|
||||
@internal
|
||||
void updateBounds() {
|
||||
final expectedSize = textRenderer.measureText(_text);
|
||||
size.setValues(expectedSize.x, expectedSize.y);
|
||||
}
|
||||
|
||||
@mustCallSuper
|
||||
@override
|
||||
void render(Canvas canvas) {
|
||||
_textRenderer.render(canvas, text, Vector2.zero());
|
||||
|
||||
@ -472,7 +472,7 @@ class Parallax {
|
||||
/// used can also be passed in.
|
||||
/// If no image cache is set, the global flame cache is used.
|
||||
static Future<Parallax> load(
|
||||
List<ParallaxData> dataList, {
|
||||
Iterable<ParallaxData> dataList, {
|
||||
Vector2? size,
|
||||
Vector2? baseVelocity,
|
||||
Vector2? velocityMultiplierDelta,
|
||||
|
||||
@ -10,7 +10,7 @@ void main() {
|
||||
group('TextBoxComponent', () {
|
||||
test('size is properly computed', () async {
|
||||
final c = TextBoxComponent(
|
||||
'The quick brown fox jumps over the lazy dog.',
|
||||
text: 'The quick brown fox jumps over the lazy dog.',
|
||||
boxConfig: TextBoxConfig(
|
||||
maxWidth: 100.0,
|
||||
),
|
||||
@ -21,7 +21,7 @@ void main() {
|
||||
});
|
||||
|
||||
flameGame.test('onLoad waits for cache to be done', (game) async {
|
||||
final c = TextBoxComponent('foo bar');
|
||||
final c = TextBoxComponent(text: 'foo bar');
|
||||
|
||||
await game.ensureAdd(c);
|
||||
|
||||
|
||||
@ -157,27 +157,14 @@ void main() {
|
||||
});
|
||||
});
|
||||
|
||||
flameGame.test('remove depend SpriteComponent.shouldRemove', (game) async {
|
||||
// addLater here
|
||||
await game.ensureAdd(SpriteComponent()..shouldRemove = true);
|
||||
flameGame.test('removes PositionComponent when shouldRemove is true',
|
||||
(game) async {
|
||||
await game.ensureAdd(PositionComponent()..shouldRemove = true);
|
||||
expect(game.children.length, equals(1));
|
||||
|
||||
// remove effected here
|
||||
game.update(0);
|
||||
expect(game.children.isEmpty, equals(true));
|
||||
});
|
||||
|
||||
flameGame.test(
|
||||
'remove depend SpriteAnimationComponent.shouldRemove',
|
||||
(game) async {
|
||||
await game.ensureAdd(SpriteAnimationComponent()..shouldRemove = true);
|
||||
expect(game.children.length, equals(1));
|
||||
|
||||
game.update(0);
|
||||
expect(game.children.isEmpty, equals(true));
|
||||
},
|
||||
);
|
||||
|
||||
flameGame.test('clear removes all components', (game) async {
|
||||
final components = List.generate(3, (index) => Component());
|
||||
await game.ensureAddAll(components);
|
||||
|
||||
@ -45,7 +45,7 @@ void main() {
|
||||
});
|
||||
|
||||
test('change parameters of text component', () {
|
||||
final tc = TextComponent<TextPaint>('foo');
|
||||
final tc = TextComponent<TextPaint>(text: 'foo');
|
||||
tc.textRenderer = tc.textRenderer.copyWith(
|
||||
(c) => c.copyWith(fontSize: 200),
|
||||
);
|
||||
@ -55,12 +55,12 @@ void main() {
|
||||
test('custom renderer', () {
|
||||
TextRenderer.defaultRenderersRegistry[_CustomTextRenderer] =
|
||||
() => _CustomTextRenderer();
|
||||
final tc = TextComponent<_CustomTextRenderer>('foo');
|
||||
final tc = TextComponent<_CustomTextRenderer>(text: 'foo');
|
||||
expect(tc.textRenderer, isA<_CustomTextRenderer>());
|
||||
});
|
||||
|
||||
test('text component size is set', () {
|
||||
final t = TextComponent('foobar');
|
||||
final t = TextComponent(text: 'foobar');
|
||||
expect(t.size, isNot(equals(Vector2.zero())));
|
||||
});
|
||||
});
|
||||
|
||||
@ -42,7 +42,10 @@ class TapableBall extends Ball with Tappable {
|
||||
Future<void> onLoad() async {
|
||||
super.onLoad();
|
||||
_textPaint = TextPaint(style: _textStyle);
|
||||
textComponent = TextComponent(counter.toString(), textRenderer: _textPaint);
|
||||
textComponent = TextComponent(
|
||||
text: counter.toString(),
|
||||
textRenderer: _textPaint,
|
||||
);
|
||||
add(textComponent);
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user