docs: Jenny example (#3086)

Replace this text.

---------

Co-authored-by: xjyribro <limcheekee.63@gmail.com>
Co-authored-by: Lukas Klingsbo <lukas.klingsbo@gmail.com>
This commit is contained in:
Lim Chee Keen
2024-03-23 20:21:09 +08:00
committed by GitHub
parent cc035fb4a3
commit 0028b5cb15
17 changed files with 467 additions and 0 deletions

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 675 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 670 B

View File

@ -0,0 +1,21 @@
<<character Jenny>>
<<declare $winnings = 0>>
title: gamble
---
Jenny: Hello {$playerName}. This is a game of chance.
Jenny: You can win or lose up to 10 coins. Do you want to play?
-> No
Jenny: No bids made.
-> Yes
<<set $winnings = dice(21) - 11>> // returns a random value from -10 to 10
<<if $winnings == 0 >>
Jenny: Too bad, you did not win anything.
<<elseif $winnings < 0 >>
Jenny: Bad luck. You lost {$winnings} coins.
Jenny: Play again to change your fortunes.
<<else>>
Jenny: Congratulations! You won {$winnings} coins.
Jenny: Play again to win even more.
<<endif>>
<<updateCoins {$winnings}>>
===

View File

@ -0,0 +1,6 @@
<<character Jenny>>
title: hello_world
---
Jenny: Hello world. My name is Jenny.
Jenny: Thanks for using Flame!
===

View File

@ -16,6 +16,7 @@ import 'package:examples/stories/bridge_libraries/flame_forge2d/joints/revolute_
import 'package:examples/stories/bridge_libraries/flame_forge2d/joints/rope_joint.dart';
import 'package:examples/stories/bridge_libraries/flame_forge2d/joints/weld_joint.dart';
import 'package:examples/stories/bridge_libraries/flame_isolate/isolate.dart';
import 'package:examples/stories/bridge_libraries/flame_jenny/jenny.dart';
import 'package:examples/stories/bridge_libraries/flame_lottie/lottie.dart';
import 'package:examples/stories/bridge_libraries/flame_spine/flame_spine.dart';
import 'package:examples/stories/camera_and_viewport/camera_and_viewport.dart';
@ -98,6 +99,7 @@ void runAsDashbook() {
// Bridge package examples
addForge2DStories(dashbook);
addFlameIsolateExample(dashbook);
addFlameJennyExample(dashbook);
addFlameLottieExample(dashbook);
addFlameSpineExamples(dashbook);

View File

@ -0,0 +1,8 @@
String baseLink(String path) {
const basePath =
'https://github.com/flame-engine/flame/blob/main/packages/flame_jenny/';
return '$basePath$path';
}
const double fontSize = 24;

View File

@ -0,0 +1,71 @@
import 'package:examples/stories/bridge_libraries/flame_jenny/components/dialogue_button.dart';
import 'package:flame/components.dart';
import 'package:jenny/jenny.dart';
class ButtonRow extends PositionComponent {
ButtonRow({required super.size}) : super(position: Vector2(0, 96));
void removeButtons() {
final buttonList = children.query<DialogueButton>();
if (buttonList.isNotEmpty) {
for (final dialogueButton in buttonList) {
if (dialogueButton.parent != null) {
dialogueButton.removeFromParent();
}
}
}
}
void showNextButton(Function() onNextButtonPressed) {
removeButtons();
final nextButton = DialogueButton(
assetPath: 'green_button_sqr.png',
text: 'Next',
position: Vector2(size.x / 2, 0),
onPressed: () {
onNextButtonPressed();
removeButtons();
},
);
add(nextButton);
}
void showOptionButtons({
required Function(int optionNumber) onChoice,
required DialogueOption option1,
required DialogueOption option2,
}) {
removeButtons();
final optionButtons = <DialogueButton>[
DialogueButton(
assetPath: 'green_button_sqr.png',
text: option1.text,
position: Vector2(size.x / 4, 0),
onPressed: () {
onChoice(0);
removeButtons();
},
),
DialogueButton(
assetPath: 'red_button_sqr.png',
text: option2.text,
position: Vector2(size.x * 3 / 4, 0),
onPressed: () {
onChoice(1);
removeButtons();
},
),
];
addAll(optionButtons);
}
void showCloseButton(Function() onClose) {
final closeButton = DialogueButton(
assetPath: 'green_button_sqr.png',
text: 'Close',
onPressed: () => onClose(),
position: Vector2(size.x / 2, 0),
);
add(closeButton);
}
}

View File

@ -0,0 +1,43 @@
import 'package:examples/stories/bridge_libraries/flame_jenny/components/button_row.dart';
import 'package:examples/stories/bridge_libraries/flame_jenny/components/dialogue_text_box.dart';
import 'package:flame/components.dart';
import 'package:jenny/jenny.dart';
class DialogueBoxComponent extends SpriteComponent with HasGameReference {
DialogueTextBox textBox = DialogueTextBox(text: '');
final Vector2 spriteSize = Vector2(736, 128);
late final ButtonRow buttonRow = ButtonRow(size: spriteSize);
@override
Future<void> onLoad() async {
position = Vector2(game.size.x / 2, 96);
anchor = Anchor.center;
sprite = await Sprite.load(
'dialogue_box.png',
srcSize: spriteSize,
);
await addAll([buttonRow, textBox]);
return super.onLoad();
}
void changeText(String newText, Function() goNextLine) {
textBox.text = newText;
buttonRow.showNextButton(goNextLine);
}
void showOptions({
required Function(int optionNumber) onChoice,
required DialogueOption option1,
required DialogueOption option2,
}) {
buttonRow.showOptionButtons(
onChoice: onChoice,
option1: option1,
option2: option2,
);
}
void showCloseButton(Function() onClose) {
buttonRow.showCloseButton(onClose);
}
}

View File

@ -0,0 +1,36 @@
import 'package:examples/stories/bridge_libraries/flame_jenny/commons/commons.dart';
import 'package:flame/components.dart';
import 'package:flame/input.dart';
import 'package:flutter/material.dart';
class DialogueButton extends SpriteButtonComponent {
DialogueButton({
required super.position,
required this.assetPath,
required this.text,
required super.onPressed,
super.anchor = Anchor.center,
});
final String text;
final String assetPath;
@override
Future<void> onLoad() async {
button = await Sprite.load(assetPath);
add(
TextComponent(
text: text,
position: Vector2(48, 16),
anchor: Anchor.center,
size: Vector2(88, 28),
textRenderer: TextPaint(
style: const TextStyle(
fontSize: fontSize,
color: Colors.white70,
),
),
),
);
}
}

View File

@ -0,0 +1,85 @@
import 'dart:async';
import 'package:examples/stories/bridge_libraries/flame_jenny/components/dialogue_box.dart';
import 'package:flame/components.dart' hide Timer;
import 'package:jenny/jenny.dart';
class DialogueControllerComponent extends Component
with DialogueView, HasGameReference {
Completer<void> _forwardCompleter = Completer();
Completer<int> _choiceCompleter = Completer<int>();
Completer<void> _closeCompleter = Completer();
late final DialogueBoxComponent _dialogueBoxComponent =
DialogueBoxComponent();
@override
Future<void> onNodeStart(Node node) async {
_closeCompleter = Completer();
_addDialogueBox();
}
void _addDialogueBox() {
game.camera.viewport.add(_dialogueBoxComponent);
}
@override
Future<void> onNodeFinish(Node node) async {
_dialogueBoxComponent.showCloseButton(_onClose);
return _closeCompleter.future;
}
void _onClose() {
if (!_closeCompleter.isCompleted) {
_closeCompleter.complete();
}
final list = game.camera.viewport.children.query<DialogueBoxComponent>();
if (list.isNotEmpty) {
game.camera.viewport.removeAll(list);
}
}
Future<void> _advance() async {
return _forwardCompleter.future;
}
@override
FutureOr<bool> onLineStart(DialogueLine line) async {
_forwardCompleter = Completer();
_changeTextAndShowNextButton(line);
await _advance();
return super.onLineStart(line);
}
void _changeTextAndShowNextButton(DialogueLine line) {
final characterName = line.character?.name ?? '';
final dialogueLineText = '$characterName: ${line.text}';
_dialogueBoxComponent.changeText(dialogueLineText, _goNextLine);
}
void _goNextLine() {
if (!_forwardCompleter.isCompleted) {
_forwardCompleter.complete();
}
}
@override
FutureOr<int?> onChoiceStart(DialogueChoice choice) async {
_forwardCompleter = Completer();
_choiceCompleter = Completer<int>();
_dialogueBoxComponent.showOptions(
onChoice: _onChoice,
option1: choice.options[0],
option2: choice.options[1],
);
await _advance();
return _choiceCompleter.future;
}
void _onChoice(int optionNumber) {
if (!_forwardCompleter.isCompleted) {
_forwardCompleter.complete();
}
if (!_choiceCompleter.isCompleted) {
_choiceCompleter.complete(optionNumber);
}
}
}

View File

@ -0,0 +1,17 @@
import 'package:examples/stories/bridge_libraries/flame_jenny/commons/commons.dart';
import 'package:flame/components.dart';
import 'package:flutter/material.dart';
class DialogueTextBox extends TextBoxComponent {
DialogueTextBox({required super.text})
: super(
position: Vector2(16, 16),
size: Vector2(704, 96),
textRenderer: TextPaint(
style: const TextStyle(
fontSize: fontSize,
color: Colors.black,
),
),
);
}

View File

@ -0,0 +1,35 @@
import 'package:flame/components.dart';
import 'package:flame/input.dart';
import 'package:flame/palette.dart';
import 'package:flame/text.dart';
import 'package:flutter/material.dart';
class MenuButton extends ButtonComponent {
MenuButton({
required super.position,
required super.onPressed,
required this.text,
}) : super(size: Vector2(128, 42));
late String text;
final Paint white = BasicPalette.white.paint();
final TextPaint topTextPaint = TextPaint(
style: TextStyle(color: BasicPalette.black.color),
);
@override
Future<void> onLoad() async {
button = RectangleComponent(paint: white, size: size);
anchor = Anchor.center;
add(
TextComponent(
text: text,
textRenderer: topTextPaint,
position: size / 2,
anchor: Anchor.center,
priority: 1,
),
);
}
}

View File

@ -0,0 +1,29 @@
import 'package:dashbook/dashbook.dart';
import 'package:examples/stories/bridge_libraries/flame_jenny/commons/commons.dart';
import 'package:examples/stories/bridge_libraries/flame_jenny/jenny_advanced_example.dart';
import 'package:examples/stories/bridge_libraries/flame_jenny/jenny_simple_example.dart';
import 'package:flame/game.dart';
void addFlameJennyExample(Dashbook dashbook) {
dashbook.storiesOf('FlameJenny')
..add(
'Simple Jenny example',
(_) => GameWidget(
game: JennySimpleExample(),
),
codeLink: baseLink(
'bridge_libraries/flame_jenny/jenny_simple_example.dart',
),
info: JennySimpleExample.description,
)
..add(
'Advanced Jenny example',
(_) => GameWidget(
game: JennyAdvancedExample(),
),
codeLink: baseLink(
'bridge_libraries/flame_jenny/jenny_advanced_example.dart',
),
info: JennyAdvancedExample.description,
);
}

View File

@ -0,0 +1,76 @@
import 'dart:ui';
import 'package:examples/stories/bridge_libraries/flame_jenny/components/dialogue_controller_component.dart';
import 'package:examples/stories/bridge_libraries/flame_jenny/components/menu_button.dart';
import 'package:flame/components.dart';
import 'package:flame/game.dart';
import 'package:flame/palette.dart';
import 'package:flame/text.dart';
import 'package:flutter/services.dart';
import 'package:jenny/jenny.dart';
class JennyAdvancedExample extends FlameGame {
static const String description = '''
This is an advanced example of how to use the Jenny Package.
It includes implementing dialogue choices, setting custom variables,
using commands and implementing User-Defined Commands, .
''';
int coins = 0;
final Paint white = BasicPalette.white.paint();
final TextPaint mainTextPaint = TextPaint(
style: TextStyle(color: BasicPalette.white.color),
);
final TextPaint buttonTextPaint = TextPaint(
style: TextStyle(color: BasicPalette.black.color),
);
final startButtonSize = Vector2(128, 56);
late final TextComponent header = TextComponent(
text: 'Select player name.',
position: Vector2(size.x / 2, 56),
size: startButtonSize,
anchor: Anchor.center,
textRenderer: mainTextPaint,
);
Future<void> startDialogue(String playerName) async {
final dialogueControllerComponent = DialogueControllerComponent();
add(dialogueControllerComponent);
final yarnProject = YarnProject();
yarnProject
..commands.addCommand1('updateCoins', updateCoins)
..variables.setVariable(r'$playerName', playerName)
..parse(await rootBundle.loadString('assets/yarn/advanced.yarn'));
final dialogueRunner = DialogueRunner(
yarnProject: yarnProject,
dialogueViews: [dialogueControllerComponent],
);
dialogueRunner.startDialogue('gamble');
}
void updateCoins(int amountChange) {
coins += amountChange;
header.text = 'Select player name. Current coins: $coins';
}
@override
Future<void> onLoad() async {
addAll([
header,
MenuButton(
position: Vector2(size.x / 4, 128),
onPressed: () => startDialogue('Jessie'),
text: 'Jessie',
),
MenuButton(
position: Vector2(size.x * 3 / 4, 128),
onPressed: () => startDialogue('James'),
text: 'James',
),
]);
}
}

View File

@ -0,0 +1,36 @@
import 'package:examples/stories/bridge_libraries/flame_jenny/components/dialogue_controller_component.dart';
import 'package:examples/stories/bridge_libraries/flame_jenny/components/menu_button.dart';
import 'package:flame/game.dart';
import 'package:flutter/services.dart';
import 'package:jenny/jenny.dart';
class JennySimpleExample extends FlameGame {
static const String description = '''
This is a simple example of how to use the Jenny Package.
It includes instantiating YarnProject and parsing a .yarn script.
''';
Future<void> startDialogue() async {
final dialogueControllerComponent = DialogueControllerComponent();
add(dialogueControllerComponent);
final yarnProject = YarnProject();
yarnProject.parse(await rootBundle.loadString('assets/yarn/simple.yarn'));
final dialogueRunner = DialogueRunner(
yarnProject: yarnProject,
dialogueViews: [dialogueControllerComponent],
);
dialogueRunner.startDialogue('hello_world');
}
@override
Future<void> onLoad() async {
addAll([
MenuButton(
position: Vector2(size.x / 2, 96),
onPressed: startDialogue,
text: 'Start conversation',
),
]);
}
}

View File

@ -23,6 +23,7 @@ dependencies:
flutter:
sdk: flutter
google_fonts: ^4.0.4
jenny: ^1.3.0
meta: ^1.9.1
padracing: ^1.0.0
provider: ^6.0.5
@ -49,3 +50,4 @@ flutter:
- assets/tiles/
- assets/audio/music/
- assets/audio/sfx/
- assets/yarn/