mirror of
https://github.com/flame-engine/flame.git
synced 2025-11-01 01:18:38 +08:00
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:
BIN
examples/assets/images/dialogue_box.png
Normal file
BIN
examples/assets/images/dialogue_box.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 1.4 KiB |
BIN
examples/assets/images/green_button_sqr.png
Normal file
BIN
examples/assets/images/green_button_sqr.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 675 B |
BIN
examples/assets/images/red_button_sqr.png
Normal file
BIN
examples/assets/images/red_button_sqr.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 670 B |
21
examples/assets/yarn/advanced.yarn
Normal file
21
examples/assets/yarn/advanced.yarn
Normal 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}>>
|
||||
===
|
||||
6
examples/assets/yarn/simple.yarn
Normal file
6
examples/assets/yarn/simple.yarn
Normal file
@ -0,0 +1,6 @@
|
||||
<<character Jenny>>
|
||||
title: hello_world
|
||||
---
|
||||
Jenny: Hello world. My name is Jenny.
|
||||
Jenny: Thanks for using Flame!
|
||||
===
|
||||
@ -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);
|
||||
|
||||
|
||||
@ -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;
|
||||
@ -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);
|
||||
}
|
||||
}
|
||||
@ -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);
|
||||
}
|
||||
}
|
||||
@ -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,
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -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,
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
@ -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,
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
29
examples/lib/stories/bridge_libraries/flame_jenny/jenny.dart
Normal file
29
examples/lib/stories/bridge_libraries/flame_jenny/jenny.dart
Normal 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,
|
||||
);
|
||||
}
|
||||
@ -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',
|
||||
),
|
||||
]);
|
||||
}
|
||||
}
|
||||
@ -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',
|
||||
),
|
||||
]);
|
||||
}
|
||||
}
|
||||
@ -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/
|
||||
|
||||
Reference in New Issue
Block a user