mirror of
				https://github.com/flame-engine/flame.git
				synced 2025-10-31 08:56:01 +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
	 Lukas Klingsbo
					Lukas Klingsbo