mirror of
https://github.com/flame-engine/flame.git
synced 2025-11-01 19:12:31 +08:00
Adding SimpleGame, EmbeddedGameWidget and animationAsWidget to allow for easy integration of flame with non-game flutter apps, plus docs improved, minor fixes
This commit is contained in:
71
doc/examples/animation_widget/.gitignore
vendored
Normal file
71
doc/examples/animation_widget/.gitignore
vendored
Normal file
@ -0,0 +1,71 @@
|
||||
# Miscellaneous
|
||||
*.class
|
||||
*.lock
|
||||
*.log
|
||||
*.pyc
|
||||
*.swp
|
||||
.DS_Store
|
||||
.atom/
|
||||
.buildlog/
|
||||
.history
|
||||
.svn/
|
||||
|
||||
# IntelliJ related
|
||||
*.iml
|
||||
*.ipr
|
||||
*.iws
|
||||
.idea/
|
||||
|
||||
# Visual Studio Code related
|
||||
.vscode/
|
||||
|
||||
# Flutter/Dart/Pub related
|
||||
**/doc/api/
|
||||
.dart_tool/
|
||||
.flutter-plugins
|
||||
.packages
|
||||
.pub-cache/
|
||||
.pub/
|
||||
build/
|
||||
|
||||
# Android related
|
||||
**/android/**/gradle-wrapper.jar
|
||||
**/android/.gradle
|
||||
**/android/captures/
|
||||
**/android/gradlew
|
||||
**/android/gradlew.bat
|
||||
**/android/local.properties
|
||||
**/android/**/GeneratedPluginRegistrant.java
|
||||
|
||||
# iOS/XCode related
|
||||
**/ios/**/*.mode1v3
|
||||
**/ios/**/*.mode2v3
|
||||
**/ios/**/*.moved-aside
|
||||
**/ios/**/*.pbxuser
|
||||
**/ios/**/*.perspectivev3
|
||||
**/ios/**/*sync/
|
||||
**/ios/**/.sconsign.dblite
|
||||
**/ios/**/.tags*
|
||||
**/ios/**/.vagrant/
|
||||
**/ios/**/DerivedData/
|
||||
**/ios/**/Icon?
|
||||
**/ios/**/Pods/
|
||||
**/ios/**/.symlinks/
|
||||
**/ios/**/profile
|
||||
**/ios/**/xcuserdata
|
||||
**/ios/.generated/
|
||||
**/ios/Flutter/App.framework
|
||||
**/ios/Flutter/Flutter.framework
|
||||
**/ios/Flutter/Generated.xcconfig
|
||||
**/ios/Flutter/app.flx
|
||||
**/ios/Flutter/app.zip
|
||||
**/ios/Flutter/flutter_assets/
|
||||
**/ios/ServiceDefinitions.json
|
||||
**/ios/Runner/GeneratedPluginRegistrant.*
|
||||
|
||||
# Exceptions to above rules.
|
||||
!**/ios/**/default.mode1v3
|
||||
!**/ios/**/default.mode2v3
|
||||
!**/ios/**/default.pbxuser
|
||||
!**/ios/**/default.perspectivev3
|
||||
!/packages/flutter_tools/test/data/dart_dependencies_test/**/.packages
|
||||
10
doc/examples/animation_widget/.metadata
Normal file
10
doc/examples/animation_widget/.metadata
Normal file
@ -0,0 +1,10 @@
|
||||
# This file tracks properties of this Flutter project.
|
||||
# Used by Flutter tool to assess capabilities and perform upgrades etc.
|
||||
#
|
||||
# This file should be version controlled and should not be manually edited.
|
||||
|
||||
version:
|
||||
revision: 5391447fae6209bb21a89e6a5a6583cac1af9b4b
|
||||
channel: beta
|
||||
|
||||
project_type: app
|
||||
3
doc/examples/animation_widget/README.md
Normal file
3
doc/examples/animation_widget/README.md
Normal file
@ -0,0 +1,3 @@
|
||||
# animation_widget
|
||||
|
||||
A sample Flame project to showcase the animationAsWidget method to render easy sprite sheet animations on
|
||||
BIN
doc/examples/animation_widget/assets/images/minotaur.png
Normal file
BIN
doc/examples/animation_widget/assets/images/minotaur.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 4.0 KiB |
62
doc/examples/animation_widget/lib/main.dart
Normal file
62
doc/examples/animation_widget/lib/main.dart
Normal file
@ -0,0 +1,62 @@
|
||||
import 'package:flame/animation.dart' as animation;
|
||||
import 'package:flame/flame.dart';
|
||||
import 'package:flame/position.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
void main() => runApp(MyApp());
|
||||
|
||||
class MyApp extends StatelessWidget {
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return MaterialApp(
|
||||
title: 'Animation as a Widget Demo',
|
||||
theme: ThemeData(primarySwatch: Colors.blue),
|
||||
home: MyHomePage(),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class MyHomePage extends StatefulWidget {
|
||||
@override
|
||||
_MyHomePageState createState() => _MyHomePageState();
|
||||
}
|
||||
|
||||
class _MyHomePageState extends State<MyHomePage> {
|
||||
void _clickFab(GlobalKey<ScaffoldState> key) {
|
||||
key.currentState.showSnackBar(new SnackBar(
|
||||
content: new Text('You clicked the FAB!'),
|
||||
));
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final key = new GlobalKey<ScaffoldState>();
|
||||
return Scaffold(
|
||||
key: key,
|
||||
appBar: AppBar(
|
||||
title: Text('Animation as a Widget Demo'),
|
||||
),
|
||||
body: Center(
|
||||
child: Column(
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
children: <Widget>[
|
||||
Text('Hi there! This is a regular Flutter app,'),
|
||||
Text('with a complex widget tree and also'),
|
||||
Text('some pretty sprite sheet animations :)'),
|
||||
Flame.util.animationAsWidget(
|
||||
Position(256.0, 256.0),
|
||||
animation.Animation.sequenced('minotaur.png', 19,
|
||||
textureWidth: 96.0)),
|
||||
Text('Neat, hum?'),
|
||||
Text('Sprites from Elthen\'s amazing work on itch.io:'),
|
||||
Text('https://elthen.itch.io/2d-pixel-art-minotaur-sprites'),
|
||||
],
|
||||
),
|
||||
),
|
||||
floatingActionButton: FloatingActionButton(
|
||||
onPressed: () => _clickFab(key),
|
||||
child: Icon(Icons.add),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
22
doc/examples/animation_widget/pubspec.yaml
Normal file
22
doc/examples/animation_widget/pubspec.yaml
Normal file
@ -0,0 +1,22 @@
|
||||
name: animation_widget
|
||||
description: A sample Flame project to showcase the animationAsWidget method.
|
||||
|
||||
version: 0.1.0
|
||||
|
||||
environment:
|
||||
sdk: ">=2.0.0-dev.68.0 <3.0.0"
|
||||
|
||||
dependencies:
|
||||
flutter:
|
||||
sdk: flutter
|
||||
flame:
|
||||
path: ../../../
|
||||
|
||||
dev_dependencies:
|
||||
flutter_test:
|
||||
sdk: flutter
|
||||
|
||||
flutter:
|
||||
uses-material-design: true
|
||||
assets:
|
||||
- assets/images/minotaur.png # thanks https://elthen.itch.io/2d-pixel-art-minotaur-sprites
|
||||
@ -15,7 +15,8 @@ TextConfig regular = TextConfig(color: BasicPalette.white.color);
|
||||
TextConfig tiny = regular.withFontSize(12.0);
|
||||
|
||||
class MyTextBox extends TextBoxComponent {
|
||||
MyTextBox(String text) : super(text, config: tiny, boxConfig: TextBoxConfig(timePerChar: 0.05));
|
||||
MyTextBox(String text)
|
||||
: super(text, config: tiny, boxConfig: TextBoxConfig(timePerChar: 0.05));
|
||||
|
||||
@override
|
||||
void drawBackground(Canvas c) {
|
||||
@ -52,7 +53,8 @@ class MyGame extends BaseGame {
|
||||
..x = size.width
|
||||
..y = size.height);
|
||||
|
||||
add(MyTextBox('Lorem ipsum dolor sit amet, consectetur adipiscing elit. Etiam eget ligula eu lectus lobortis condimentum.')
|
||||
add(MyTextBox(
|
||||
'Lorem ipsum dolor sit amet, consectetur adipiscing elit. Etiam eget ligula eu lectus lobortis condimentum.')
|
||||
..anchor = Anchor.bottomLeft
|
||||
..y = size.height);
|
||||
}
|
||||
|
||||
@ -10,7 +10,7 @@ dependencies:
|
||||
flutter:
|
||||
sdk: flutter
|
||||
flame:
|
||||
path: ../../
|
||||
path: ../../../
|
||||
|
||||
dev_dependencies:
|
||||
flutter_test:
|
||||
|
||||
@ -10,7 +10,7 @@ dependencies:
|
||||
flutter:
|
||||
sdk: flutter
|
||||
flame:
|
||||
path: ../..
|
||||
path: ../../../
|
||||
|
||||
dev_dependencies:
|
||||
flutter_test:
|
||||
|
||||
@ -18,6 +18,7 @@ class Anchor {
|
||||
const Anchor(this.relativePosition);
|
||||
|
||||
Position translate(Position p, Position size) {
|
||||
return p.clone().minus(new Position(size.x * relativePosition.dx, size.y * relativePosition.dy));
|
||||
return p.clone().minus(new Position(
|
||||
size.x * relativePosition.dx, size.y * relativePosition.dy));
|
||||
}
|
||||
}
|
||||
@ -95,8 +95,8 @@ abstract class PositionComponent extends Component {
|
||||
canvas.translate(x, y);
|
||||
|
||||
canvas.rotate(angle);
|
||||
double dx = - anchor.relativePosition.dx * width;
|
||||
double dy = - anchor.relativePosition.dy * height;
|
||||
double dx = -anchor.relativePosition.dx * width;
|
||||
double dy = -anchor.relativePosition.dy * height;
|
||||
canvas.translate(dx, dy);
|
||||
}
|
||||
}
|
||||
@ -109,9 +109,11 @@ class SpriteComponent extends PositionComponent {
|
||||
|
||||
SpriteComponent();
|
||||
|
||||
SpriteComponent.square(double size, String imagePath) : this.rectangle(size, size, imagePath);
|
||||
SpriteComponent.square(double size, String imagePath)
|
||||
: this.rectangle(size, size, imagePath);
|
||||
|
||||
SpriteComponent.rectangle(double width, double height, String imagePath) : this.fromSprite(width, height, new Sprite(imagePath));
|
||||
SpriteComponent.rectangle(double width, double height, String imagePath)
|
||||
: this.fromSprite(width, height, new Sprite(imagePath));
|
||||
|
||||
SpriteComponent.fromSprite(double width, double height, this.sprite) {
|
||||
this.width = width;
|
||||
|
||||
@ -43,7 +43,9 @@ class TextBoxComponent extends PositionComponent with Resizable {
|
||||
|
||||
TextBoxConfig get boxConfig => _boxConfig;
|
||||
|
||||
TextBoxComponent(String text, {TextConfig config = const TextConfig(), TextBoxConfig boxConfig = const TextBoxConfig()}) {
|
||||
TextBoxComponent(String text,
|
||||
{TextConfig config = const TextConfig(),
|
||||
TextBoxConfig boxConfig = const TextBoxConfig()}) {
|
||||
_boxConfig = boxConfig;
|
||||
_config = config;
|
||||
_text = text;
|
||||
@ -76,7 +78,9 @@ class TextBoxComponent extends PositionComponent with Resizable {
|
||||
|
||||
bool get finished => _lifeTime > totalCharTime + _boxConfig.dismissDelay;
|
||||
|
||||
int get currentChar => _boxConfig.timePerChar == 0.0 ? _text.length - 1 : math.min(_lifeTime ~/ _boxConfig.timePerChar, _text.length - 1);
|
||||
int get currentChar => _boxConfig.timePerChar == 0.0
|
||||
? _text.length - 1
|
||||
: math.min(_lifeTime ~/ _boxConfig.timePerChar, _text.length - 1);
|
||||
|
||||
int get currentLine {
|
||||
int totalCharCount = 0;
|
||||
@ -103,7 +107,9 @@ class TextBoxComponent extends PositionComponent with Resizable {
|
||||
double get totalHeight => _withMargins(_lineHeight * _lines.length);
|
||||
|
||||
double getLineWidth(String line, int charCount) {
|
||||
return _withMargins(_config.toTextPainter(line.substring(0, math.min(charCount, line.length))).width);
|
||||
return _withMargins(_config
|
||||
.toTextPainter(line.substring(0, math.min(charCount, line.length)))
|
||||
.width);
|
||||
}
|
||||
|
||||
double get currentWidth {
|
||||
@ -112,7 +118,8 @@ class TextBoxComponent extends PositionComponent with Resizable {
|
||||
int _currentChar = currentChar;
|
||||
int _currentLine = currentLine;
|
||||
return _lines.sublist(0, _currentLine + 1).map((line) {
|
||||
int charCount = (i < _currentLine) ? line.length : (_currentChar - totalCharCount);
|
||||
int charCount =
|
||||
(i < _currentLine) ? line.length : (_currentChar - totalCharCount);
|
||||
totalCharCount += line.length;
|
||||
i++;
|
||||
return getLineWidth(line, charCount);
|
||||
@ -128,7 +135,8 @@ class TextBoxComponent extends PositionComponent with Resizable {
|
||||
|
||||
Image _redrawCache() {
|
||||
PictureRecorder recorder = new PictureRecorder();
|
||||
Canvas c = new Canvas(recorder, new Rect.fromLTWH(0.0, 0.0, width.toDouble(), height.toDouble()));
|
||||
Canvas c = new Canvas(recorder,
|
||||
new Rect.fromLTWH(0.0, 0.0, width.toDouble(), height.toDouble()));
|
||||
_fullRender(c);
|
||||
return recorder.endRecording().toImage(width.toInt(), height.toInt());
|
||||
}
|
||||
@ -143,11 +151,15 @@ class TextBoxComponent extends PositionComponent with Resizable {
|
||||
double dy = _boxConfig.margin;
|
||||
for (int line = 0; line < _currentLine; line++) {
|
||||
charCount += _lines[line].length;
|
||||
_config.toTextPainter(_lines[line]).paint(c, new Offset(_boxConfig.margin, dy));
|
||||
_config
|
||||
.toTextPainter(_lines[line])
|
||||
.paint(c, new Offset(_boxConfig.margin, dy));
|
||||
dy += _lineHeight;
|
||||
}
|
||||
int max = math.min(currentChar - charCount, _lines[_currentLine].length);
|
||||
_config.toTextPainter(_lines[_currentLine].substring(0, max)).paint(c, new Offset(_boxConfig.margin, dy));
|
||||
_config
|
||||
.toTextPainter(_lines[_currentLine].substring(0, max))
|
||||
.paint(c, new Offset(_boxConfig.margin, dy));
|
||||
}
|
||||
|
||||
void update(double dt) {
|
||||
|
||||
@ -24,7 +24,7 @@ class TextComponent extends PositionComponent {
|
||||
_updateBox();
|
||||
}
|
||||
|
||||
TextComponent(this._text, { TextConfig config = const TextConfig() }) {
|
||||
TextComponent(this._text, {TextConfig config = const TextConfig()}) {
|
||||
this._config = config;
|
||||
_updateBox();
|
||||
}
|
||||
|
||||
@ -38,6 +38,7 @@ abstract class Game {
|
||||
|
||||
void _recordDt(double dt) {}
|
||||
|
||||
Offset _offset = Offset.zero;
|
||||
Widget _widget;
|
||||
|
||||
/// Returns the game widget. Put this in your structure to start rendering and updating the game.
|
||||
@ -135,7 +136,10 @@ class _GameRenderBox extends RenderBox with WidgetsBindingObserver {
|
||||
|
||||
@override
|
||||
void paint(PaintingContext context, Offset offset) {
|
||||
context.canvas.save();
|
||||
context.canvas.translate(game._offset.dx, game._offset.dy);
|
||||
game.render(context.canvas);
|
||||
context.canvas.restore();
|
||||
}
|
||||
|
||||
void _bindLifecycleListener() {
|
||||
@ -177,7 +181,8 @@ abstract class BaseGame extends Game {
|
||||
/// This method is called for every component added, both via [add] and [addLater] methods.
|
||||
///
|
||||
/// You can use this to setup your mixins, pre-calculate stuff on every component, or anything you desire.
|
||||
/// By default this calls the first time resize for every component, so don't forget to call super.preAdd when overriding.
|
||||
/// By default, this calls the first time resize for every component, so don't forget to call super.preAdd when overriding.
|
||||
@mustCallSuper
|
||||
void preAdd(Component c) {
|
||||
// first time resize
|
||||
if (size != null) {
|
||||
@ -187,7 +192,7 @@ abstract class BaseGame extends Game {
|
||||
|
||||
/// Adds a new component to the components list.
|
||||
///
|
||||
/// Also calls [preAdd], witch in turn sets the current size on the component (because the resize hook won't be called).
|
||||
/// Also calls [preAdd], witch in turn sets the current size on the component (because the resize hook won't be called until a new resize happens).
|
||||
void add(Component c) {
|
||||
this.preAdd(c);
|
||||
this.components.add(c);
|
||||
@ -196,7 +201,7 @@ abstract class BaseGame extends Game {
|
||||
/// Registers a component to be added on the components on the next tick.
|
||||
///
|
||||
/// Use this to add components in places where a concurrent issue with the update method might happen.
|
||||
/// Also calls [preAdd] for the component added.
|
||||
/// Also calls [preAdd] for the component added, immediately.
|
||||
void addLater(Component c) {
|
||||
this.preAdd(c);
|
||||
this._addLater.add(c);
|
||||
@ -205,6 +210,7 @@ abstract class BaseGame extends Game {
|
||||
/// This implementation of render basically calls [renderComponent] for every component, making sure the canvas is reset for each one.
|
||||
///
|
||||
/// You can override it further to add more custom behaviour.
|
||||
/// Beware of however you are rendering components if not using this; you must be careful to save and restore the canvas to avoid components messing up with each other.
|
||||
@override
|
||||
void render(Canvas canvas) {
|
||||
canvas.save();
|
||||
@ -231,7 +237,7 @@ abstract class BaseGame extends Game {
|
||||
/// This implementation of update updates every component in the list.
|
||||
///
|
||||
/// It also actually adds the components that were added by the [addLater] method, and remove those that are marked for destruction via the [Component.destroy] method.
|
||||
/// You can override it futher to add more custom behaviour.
|
||||
/// You can override it further to add more custom behaviour.
|
||||
@override
|
||||
void update(double t) {
|
||||
components.addAll(_addLater);
|
||||
@ -241,10 +247,12 @@ abstract class BaseGame extends Game {
|
||||
components.removeWhere((c) => c.destroy());
|
||||
}
|
||||
|
||||
/// This implementation of resize repasses the resize call to every component in the list, enabling each one to make their decisions as how to handle the resize.
|
||||
/// This implementation of resize passes the resize call along to every component in the list, enabling each one to make their decisions as how to handle the resize.
|
||||
///
|
||||
/// You can override it futher to add more custom behaviour.
|
||||
/// It also updates the [size] field of the class to be used by later added components and other methods.
|
||||
/// You can override it further to add more custom behaviour, but you should seriously consider calling the super implementation as well.
|
||||
@override
|
||||
@mustCallSuper
|
||||
void resize(Size size) {
|
||||
this.size = size;
|
||||
components.forEach((c) => c.resize(size));
|
||||
@ -255,7 +263,7 @@ abstract class BaseGame extends Game {
|
||||
/// Returns `false` by default. Override to use the debug mode.
|
||||
/// In debug mode, the [_recordDt] method actually records every `dt` for statistics.
|
||||
/// Then, you can use the [fps] method to check the game FPS.
|
||||
/// You can also use this value to enable other debug behaviors for your game.
|
||||
/// You can also use this value to enable other debug behaviors for your game, like bounding box rendering, for instance.
|
||||
bool debugMode() => false;
|
||||
|
||||
/// This is a hook that comes from the RenderBox to allow recording of render times and statistics.
|
||||
@ -290,3 +298,69 @@ abstract class BaseGame extends Game {
|
||||
Duration.microsecondsPerSecond;
|
||||
}
|
||||
}
|
||||
|
||||
/// This is a helper implementation of a [BaseGame] designed to allow to easily create a game with a single component.
|
||||
///
|
||||
/// This is useful to add sprites, animations and other Flame components "directly" to your non-game Flutter widget tree, when combined with [EmbeddedGameWidget].
|
||||
class SimpleGame extends BaseGame {
|
||||
SimpleGame(Component c) {
|
||||
add(c);
|
||||
}
|
||||
}
|
||||
|
||||
/// This a widget to embed a game inside the Widget tree. You can use it in pair with [SimpleGame] or any other more complex [Game], as desired.
|
||||
///
|
||||
/// It handles for you positioning, size constraints and other factors that arise when your game is embedded within the component tree.
|
||||
/// Provided it with a [Game] instance for your game and the optional size of the widget.
|
||||
/// Creating this without a fixed size might mess up how other components are rendered with relation to this one in the tree.
|
||||
/// You can bind Gesture Recognizers immediately around this to add controls to your widgets, with easy coordinate conversions.
|
||||
class EmbeddedGameWidget extends StatefulWidget {
|
||||
final Game game;
|
||||
final Position size;
|
||||
|
||||
EmbeddedGameWidget(this.game, {this.size});
|
||||
|
||||
@override
|
||||
State<StatefulWidget> createState() {
|
||||
return new _EmbeddedGameWidgetState(game, size: size);
|
||||
}
|
||||
}
|
||||
|
||||
class _EmbeddedGameWidgetState extends State<EmbeddedGameWidget> {
|
||||
final Game game;
|
||||
final Position size;
|
||||
|
||||
_EmbeddedGameWidgetState(this.game, {this.size});
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
WidgetsBinding.instance.addPostFrameCallback(_afterLayout);
|
||||
}
|
||||
|
||||
@override
|
||||
void didUpdateWidget(EmbeddedGameWidget oldWidget) {
|
||||
super.didUpdateWidget(oldWidget);
|
||||
WidgetsBinding.instance.addPostFrameCallback(_afterLayout);
|
||||
}
|
||||
|
||||
void _afterLayout(_) {
|
||||
RenderBox box = context.findRenderObject();
|
||||
game._offset = box.localToGlobal(Offset.zero);
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
if (size == null) {
|
||||
return game.widget;
|
||||
}
|
||||
return Container(
|
||||
child: game.widget,
|
||||
constraints: BoxConstraints(
|
||||
minWidth: size.x,
|
||||
maxWidth: size.x,
|
||||
minHeight: size.y,
|
||||
maxHeight: size.y),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@ -126,6 +126,7 @@ class Sprite {
|
||||
return;
|
||||
}
|
||||
size ??= this.size;
|
||||
renderRect(canvas, new Rect.fromLTWH(p.x - size.x / 2, p.y - size.y / 2, size.x, size.y));
|
||||
renderRect(canvas,
|
||||
new Rect.fromLTWH(p.x - size.x / 2, p.y - size.y / 2, size.x, size.y));
|
||||
}
|
||||
}
|
||||
|
||||
@ -9,7 +9,6 @@ import 'anchor.dart';
|
||||
/// It does not hold information regarding the position of the text to be render neither the text itself (the string).
|
||||
/// To hold all those information, use the Text component.
|
||||
class TextConfig {
|
||||
|
||||
/// The font size to be used, in points.
|
||||
final double fontSize;
|
||||
|
||||
@ -70,9 +69,11 @@ class TextConfig {
|
||||
///
|
||||
/// const TextConfig config = TextConfig(fontSize: 48.0, fontFamily: 'Awesome Font', anchor: Anchor.rightBottom);
|
||||
/// config.render(c, Offset(size.width - 10, size.height - 10);
|
||||
void render(Canvas canvas, String text, Position p, { Anchor anchor: Anchor.topLeft }) {
|
||||
void render(Canvas canvas, String text, Position p,
|
||||
{Anchor anchor: Anchor.topLeft}) {
|
||||
material.TextPainter tp = toTextPainter(text);
|
||||
Position translatedPosition = anchor.translate(p, Position.fromSize(tp.size));
|
||||
Position translatedPosition =
|
||||
anchor.translate(p, Position.fromSize(tp.size));
|
||||
tp.paint(canvas, translatedPosition.toOffset());
|
||||
}
|
||||
|
||||
@ -149,7 +150,7 @@ class TextConfig {
|
||||
/// Creates a new [TextConfig] changing only the [textAlign].
|
||||
///
|
||||
/// This does not change the original (as it's immutable).
|
||||
TextConfig withTextAlign (TextAlign textAlign) {
|
||||
TextConfig withTextAlign(TextAlign textAlign) {
|
||||
return TextConfig(
|
||||
fontSize: fontSize,
|
||||
color: color,
|
||||
|
||||
@ -3,7 +3,11 @@ import 'dart:ui';
|
||||
|
||||
import 'package:flutter/gestures.dart';
|
||||
import 'package:flutter/services.dart';
|
||||
import 'package:flutter/widgets.dart' as widgets;
|
||||
|
||||
import 'animation.dart';
|
||||
import 'components/animation_component.dart';
|
||||
import 'game.dart';
|
||||
import 'position.dart';
|
||||
|
||||
/// Some utilities that did not fit anywhere else.
|
||||
@ -17,7 +21,7 @@ class Util {
|
||||
return SystemChrome.setEnabledSystemUIOverlays([]);
|
||||
}
|
||||
|
||||
/// Sets the preferred orietation (landscape or protrait for the app).
|
||||
/// Sets the preferred orientation (landscape or portrait for the app).
|
||||
///
|
||||
/// When it opens, it will automatically change orientation to the preferred one (if possible).
|
||||
Future<void> setOrientation(DeviceOrientation orientation) {
|
||||
@ -68,4 +72,15 @@ class Util {
|
||||
fn(c);
|
||||
c.translate(-p.x, -p.y);
|
||||
}
|
||||
|
||||
/// Returns a regular Flutter widget representing this animation, rendered with the specified size.
|
||||
///
|
||||
/// This actually creates an [EmbeddedGameWidget] with a [SimpleGame] whose only content is an [AnimationComponent] created from the provided [animation].
|
||||
/// You can use this implementation as base to easily create your own widgets based on more complex games.
|
||||
/// This is intended to be used by non-game apps that want to add a sprite sheet animation.
|
||||
widgets.Widget animationAsWidget(Position size, Animation animation) {
|
||||
return EmbeddedGameWidget(
|
||||
SimpleGame(AnimationComponent(size.x, size.y, animation)),
|
||||
size: size);
|
||||
}
|
||||
}
|
||||
|
||||
@ -17,8 +17,7 @@ void main() {
|
||||
|
||||
class TestAssetBundle extends CachingAssetBundle {
|
||||
@override
|
||||
Future<ByteData> load(String key) async =>
|
||||
new File('assets/map-level1.png')
|
||||
Future<ByteData> load(String key) async => new File('assets/map-level1.png')
|
||||
.readAsBytes()
|
||||
.then((bytes) => ByteData.view(Uint8List.fromList(bytes).buffer));
|
||||
|
||||
|
||||
Reference in New Issue
Block a user