Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: Scrollable TextBoxComponent #2901

Merged
merged 11 commits into from
Dec 8, 2023
37 changes: 34 additions & 3 deletions doc/flame/rendering/text_rendering.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,10 +10,15 @@ components:

- `TextComponent` for rendering a single line of text
- `TextBoxComponent` for bounding multi-line text within a sized box, including the possibility of a
typing effect
typing effect
- `ScrollTextBoxComponent` enhances the functionality of `TextBoxComponent` by adding scrolling
capability when the text exceeds the boundaries of the enclosing box.

Both components are showcased in [this
example](https://github.com/flame-engine/flame/blob/main/examples/lib/stories/rendering/text_example.dart).
Use the `onFinished` callback to get notified when the text is completely printed.


All components are showcased in
[this example](https://github.com/flame-engine/flame/blob/main/examples/lib/stories/rendering/text_example.dart).


### TextComponent
Expand Down Expand Up @@ -109,10 +114,36 @@ class MyTextBox extends TextBoxComponent {
}
```


You can find all the options under [TextBoxComponent's
API](https://pub.dev/documentation/flame/latest/components/TextBoxComponent-class.html).


### ScrollTextBoxComponent

The `ScrollTextBoxComponent` is an advanced version of the `TextBoxComponent`,
designed for displaying scrollable text within a defined area.
This component is particularly useful for creating interfaces where large amounts of text
need to be presented in a constrained space, such as dialogues or information panels.

Note that the `align` property of `TextBoxComponent` is not available.


Example usage:


```dart
class MyScrollableText extends ScrollTextBoxComponent {
MyScrollableText(Vector2 frameSize, String text) : super(
size: frameSize,
text: text,
textRenderer: regular,
boxConfig: TextBoxConfig(timePerChar: 0.05),
);
}
```


### TextElementComponent

If you want to render an arbitrary TextElement, ranging from a single InlineTextElement to a
Expand Down
166 changes: 121 additions & 45 deletions examples/lib/stories/rendering/text_example.dart
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
import 'dart:async';

import 'package:flame/components.dart';
import 'package:flame/game.dart';
import 'package:flame/palette.dart';
Expand All @@ -11,52 +13,87 @@ class TextExample extends FlameGame {

@override
Future<void> onLoad() async {
addAll([
TextComponent(text: 'Hello, Flame', textRenderer: _regular)
..anchor = Anchor.topCenter
..x = size.x / 2
..y = 32.0,
TextComponent(text: 'Text with shade', textRenderer: _shaded)
..anchor = Anchor.topRight
..position = size - Vector2.all(100),
TextComponent(text: 'center', textRenderer: _tiny)
..anchor = Anchor.center
..position.setFrom(size / 2),
TextComponent(text: 'bottomRight', textRenderer: _tiny)
..anchor = Anchor.bottomRight
..position.setFrom(size),
MyTextBox(
'"This is our world now. The world of the electron and the switch; '
'the beauty of the baud. We exist without nationality, skin color, '
'or religious bias. You wage wars, murder, cheat, lie to us and try '
"to make us believe it's for our own good, yet we're the "
'criminals. Yes, I am a criminal. My crime is that of curiosity."',
)
..anchor = Anchor.bottomLeft
..y = size.y,
MyTextBox(
'Let A be a finitely generated torsion-free abelian group. Then '
'A is free.',
align: Anchor.center,
size: Vector2(300, 200),
timePerChar: 0,
margins: 10,
)..position = Vector2(10, 50),
MyTextBox(
'Let A be a torsion abelian group. Then A is the direct sum of its '
'subgroups A(p) for all primes p such that A(p) ≠ 0.',
align: Anchor.bottomRight,
size: Vector2(300, 200),
timePerChar: 0,
margins: 10,
)..position = Vector2(10, 260),
]);
addAll(
[
TextComponent(text: 'Hello, Flame', textRenderer: _regular)
..anchor = Anchor.topCenter
..x = size.x / 2
..y = 32.0,
TextComponent(text: 'Text with shade', textRenderer: _shaded)
..anchor = Anchor.topRight
..position = size - Vector2.all(100),
TextComponent(text: 'center', textRenderer: _tiny)
..anchor = Anchor.center
..position.setFrom(size / 2),
TextComponent(text: 'bottomRight', textRenderer: _tiny)
..anchor = Anchor.bottomRight
..position.setFrom(size),
MyTextBox(
'"This is our world now. The world of the electron and the switch; '
'the beauty of the baud. We exist without nationality, skin color, '
'or religious bias. You wage wars, murder, cheat, lie to us and try '
"to make us believe it's for our own good, yet we're the "
'criminals. Yes, I am a criminal. My crime is that of curiosity."',
)
..anchor = Anchor.bottomLeft
..y = size.y,
MyTextBox(
'Let A be a finitely generated torsion-free abelian group. Then '
'A is free.',
align: Anchor.center,
size: Vector2(300, 200),
timePerChar: 0,
margins: 10,
)..position = Vector2(10, 50),
MyTextBox(
'Let A be a torsion abelian group. Then A is the direct sum of its '
'subgroups A(p) for all primes p such that A(p) ≠ 0.',
align: Anchor.bottomRight,
size: Vector2(300, 200),
timePerChar: 0,
margins: 10,
)..position = Vector2(10, 260),
TextComponent(
text: 'Scroll me when finished:',
position: Vector2(size.x / 2, size.y / 2 + 100),
anchor: Anchor.bottomCenter,
),
MyScrollTextBox(
'In a bustling city, a small team of developers set out to create '
'a mobile game using the Flame engine for Flutter. Their goal was '
'simple: to create an engaging, easy-to-play game that could reach '
'a wide audience on both iOS and Android platforms. '
'After weeks of brainstorming, they decided on a concept: '
'a fast-paced, endless runner game set in a whimsical, '
'ever-changing world. They named it "Swift Dash." '
"Using Flutter's versatility and the Flame engine's "
'capabilities, the team crafted a game with vibrant graphics, '
'smooth animations, and responsive controls. '
'The game featured a character dashing through various landscapes, '
'dodging obstacles, and collecting points. '
'As they launched "Swift Dash," the team was anxious but hopeful. '
'To their delight, the game was well-received. Players loved its '
'simplicity and charm, and the game quickly gained popularity.',
size: Vector2(200, 150),
position: Vector2(size.x / 2, size.y / 2 + 100),
anchor: Anchor.topCenter,
boxConfig: TextBoxConfig(
timePerChar: 0.005,
margins: const EdgeInsets.fromLTRB(10, 10, 10, 10),
),
),
],
);
}
}

final _regularTextStyle =
TextStyle(fontSize: 18, color: BasicPalette.white.color);
final _regular = TextPaint(style: _regularTextStyle);
final _regularTextStyle = TextStyle(
fontSize: 18,
color: BasicPalette.white.color,
);
final _regular = TextPaint(
style: _regularTextStyle,
);
final _tiny = TextPaint(style: _regularTextStyle.copyWith(fontSize: 14.0));
final _box = _regular.copyWith(
(style) => style.copyWith(
Expand All @@ -77,6 +114,9 @@ final _shaded = TextPaint(
);

class MyTextBox extends TextBoxComponent {
late Paint paint;
late Rect bgRect;

MyTextBox(
String text, {
super.align,
Expand All @@ -94,10 +134,46 @@ class MyTextBox extends TextBoxComponent {
),
);

@override
Future<void> onLoad() {
paint = Paint();
bgRect = Rect.fromLTWH(0, 0, width, height);

paint.color = Colors.white10;
return super.onLoad();
}

@override
void render(Canvas canvas) {
canvas.drawRect(bgRect, paint);
super.render(canvas);
}
}

class MyScrollTextBox extends ScrollTextBoxComponent {
late Paint paint;
late Rect bgRect;

MyScrollTextBox(
String text, {
required super.size,
super.boxConfig,
super.position,
super.anchor,
}) : super(text: text, textRenderer: _box);

@override
FutureOr<void> onLoad() {
paint = Paint();
bgRect = Rect.fromLTWH(0, 0, width, height);

paint.color = Colors.white10;
return super.onLoad();
}

@override
void render(Canvas canvas) {
final rect = Rect.fromLTWH(0, 0, width, height);
canvas.drawRect(rect, Paint()..color = Colors.white10);
canvas.drawRect(bgRect, paint);
super.render(canvas);
}
}
1 change: 1 addition & 0 deletions packages/flame/lib/components.dart
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@ export 'src/components/nine_tile_box_component.dart';
export 'src/components/parallax_component.dart';
export 'src/components/particle_system_component.dart';
export 'src/components/position_component.dart';
export 'src/components/scroll_text_box_component.dart';
export 'src/components/spawn_component.dart';
export 'src/components/sprite_animation_component.dart';
export 'src/components/sprite_animation_group_component.dart';
Expand Down
Loading