diff --git a/CHANGELOG.md b/CHANGELOG.md index 8721dae82..b1ab0a3d7 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,11 @@ # CHANGELOG +## 0.24.0 + - Outsourcing SVG support to an external package + - Adding MemoryCache class + - Fixing games crashes on Web + - Update tiled dependency to 0.6.0 (objects' properties are now double) + ## 0.23.0 - Add Joystick Component - Adding BaseGame#markToRemove diff --git a/doc/README.md b/doc/README.md index 4a59aa775..2eb948485 100644 --- a/doc/README.md +++ b/doc/README.md @@ -18,7 +18,7 @@ Put the pub package as your dependency by dropping the following in your `pubspe ```yaml dependencies: - flame: ^0.23.0 + flame: ^0.24.0 ``` And start using it! diff --git a/doc/examples/debug/assets/android.svg b/doc/examples/debug/assets/android.svg deleted file mode 100644 index 1db6886aa..000000000 --- a/doc/examples/debug/assets/android.svg +++ /dev/null @@ -1,11 +0,0 @@ - - - - - - - - - - - diff --git a/doc/examples/debug/assets/images/android.png b/doc/examples/debug/assets/images/android.png new file mode 100644 index 000000000..54fe8001b Binary files /dev/null and b/doc/examples/debug/assets/images/android.png differ diff --git a/doc/examples/debug/lib/main.dart b/doc/examples/debug/lib/main.dart index 032987a90..5fb0639d5 100644 --- a/doc/examples/debug/lib/main.dart +++ b/doc/examples/debug/lib/main.dart @@ -1,8 +1,7 @@ import 'package:flame/game.dart'; import 'package:flame/flame.dart'; -import 'package:flame/svg.dart'; import 'package:flame/position.dart'; -import 'package:flame/components/component.dart' show SvgComponent; +import 'package:flame/components/component.dart'; import 'package:flame/components/mixins/resizable.dart'; import 'package:flame/text_config.dart'; @@ -16,12 +15,12 @@ void main() async { myGame.start(); } -class AndroidComponent extends SvgComponent with Resizable { +class AndroidComponent extends SpriteComponent with Resizable { static const int SPEED = 150; int xDirection = 1; int yDirection = 1; - AndroidComponent() : super.fromSvg(100, 100, Svg('android.svg')); + AndroidComponent() : super.square(100, 'android.png'); @override void update(double dt) { @@ -49,7 +48,7 @@ class AndroidComponent extends SvgComponent with Resizable { } class MyGame extends BaseGame { - final fpsTextConfig = const TextConfig(color: const Color(0xFFFFFFFF)); + final fpsTextConfig = TextConfig(color: const Color(0xFFFFFFFF)); @override bool debugMode() => true; diff --git a/doc/examples/debug/pubspec.yaml b/doc/examples/debug/pubspec.yaml index abc656df6..6dea24adf 100644 --- a/doc/examples/debug/pubspec.yaml +++ b/doc/examples/debug/pubspec.yaml @@ -18,4 +18,4 @@ dev_dependencies: flutter: assets: - - assets/android.svg + - assets/images/android.png diff --git a/doc/examples/flare/lib/main.dart b/doc/examples/flare/lib/main.dart index 36310713f..af5dda162 100644 --- a/doc/examples/flare/lib/main.dart +++ b/doc/examples/flare/lib/main.dart @@ -16,8 +16,7 @@ void main() { } class MyGame extends BaseGame with TapDetector { - final TextConfig fpsTextConfig = - const TextConfig(color: const Color(0xFFFFFFFF)); + final TextConfig fpsTextConfig = TextConfig(color: const Color(0xFFFFFFFF)); final paint = Paint()..color = const Color(0xFFE5E5E5E5); final List _animations = ["Stand", "Wave", "Jump", "Dance"]; diff --git a/doc/examples/particles/lib/main.dart b/doc/examples/particles/lib/main.dart index aa59a502c..4f5608ab7 100644 --- a/doc/examples/particles/lib/main.dart +++ b/doc/examples/particles/lib/main.dart @@ -43,7 +43,7 @@ class MyGame extends BaseGame { final Random rnd = Random(); final StepTween steppedTween = StepTween(begin: 0, end: 5); final trafficLight = TrafficLightComponent(); - final TextConfig fpsTextConfig = const TextConfig( + final TextConfig fpsTextConfig = TextConfig( color: const Color(0xFFFFFFFF), ); diff --git a/doc/examples/svg/.gitignore b/doc/examples/svg/.gitignore deleted file mode 100644 index 07488ba61..000000000 --- a/doc/examples/svg/.gitignore +++ /dev/null @@ -1,70 +0,0 @@ -# Miscellaneous -*.class -*.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 diff --git a/doc/examples/svg/.metadata b/doc/examples/svg/.metadata deleted file mode 100644 index 2746ae240..000000000 --- a/doc/examples/svg/.metadata +++ /dev/null @@ -1,10 +0,0 @@ -# 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: f91df4abe1427fef8862c9e81b2e5af6fc05a67a - channel: dev - -project_type: app diff --git a/doc/examples/svg/README.md b/doc/examples/svg/README.md deleted file mode 100644 index a3a0a6345..000000000 --- a/doc/examples/svg/README.md +++ /dev/null @@ -1,3 +0,0 @@ -# svg - -A sample Flame game showcasing hot to use Flame's SVG components diff --git a/doc/examples/svg/assets/android.svg b/doc/examples/svg/assets/android.svg deleted file mode 100644 index 1db6886aa..000000000 --- a/doc/examples/svg/assets/android.svg +++ /dev/null @@ -1,11 +0,0 @@ - - - - - - - - - - - diff --git a/doc/examples/svg/lib/main.dart b/doc/examples/svg/lib/main.dart deleted file mode 100644 index b50756c26..000000000 --- a/doc/examples/svg/lib/main.dart +++ /dev/null @@ -1,38 +0,0 @@ -import 'package:flame/game.dart'; -import 'package:flame/svg.dart'; -import 'package:flame/position.dart'; -import 'package:flame/components/component.dart' show SvgComponent; - -import 'package:flutter/material.dart'; - -void main() { - WidgetsFlutterBinding.ensureInitialized(); - - final game = MyGame(); - runApp(game.widget); -} - -class MyGame extends BaseGame { - Svg svgInstance; - SvgComponent android; - - MyGame() { - _start(); - } - - void _start() { - svgInstance = Svg('android.svg'); - android = SvgComponent.fromSvg(100, 100, svgInstance); - android.x = 100; - android.y = 100; - - add(android); - } - - @override - void render(Canvas canvas) { - super.render(canvas); - - svgInstance.renderPosition(canvas, Position(100, 200), 300, 300); - } -} diff --git a/doc/examples/svg/pubspec.yaml b/doc/examples/svg/pubspec.yaml deleted file mode 100644 index 9f7405ac5..000000000 --- a/doc/examples/svg/pubspec.yaml +++ /dev/null @@ -1,21 +0,0 @@ -name: svg -description: Flame sample for using SVG images - -version: 1.0.0+1 - -environment: - sdk: ">=2.1.0 <3.0.0" - -dependencies: - flutter: - sdk: flutter - flame: - path: ../../../ - -dev_dependencies: - flutter_test: - sdk: flutter - -flutter: - assets: - - assets/android.svg diff --git a/doc/examples/text/lib/main.dart b/doc/examples/text/lib/main.dart index c10090dee..037c8df67 100644 --- a/doc/examples/text/lib/main.dart +++ b/doc/examples/text/lib/main.dart @@ -19,8 +19,7 @@ TextConfig tiny = regular.withFontSize(12.0); class MyTextBox extends TextBoxComponent { MyTextBox(String text) - : super(text, - config: tiny, boxConfig: const TextBoxConfig(timePerChar: 0.05)); + : super(text, config: tiny, boxConfig: TextBoxConfig(timePerChar: 0.05)); @override void drawBackground(Canvas c) { diff --git a/doc/examples/timer/lib/main.dart b/doc/examples/timer/lib/main.dart index dcb8e7d25..0e1dad562 100644 --- a/doc/examples/timer/lib/main.dart +++ b/doc/examples/timer/lib/main.dart @@ -33,8 +33,7 @@ class GameWidget extends StatelessWidget { } class RenderedTimeComponent extends TimerComponent { - final TextConfig textConfig = - const TextConfig(color: const Color(0xFFFFFFFF)); + final TextConfig textConfig = TextConfig(color: const Color(0xFFFFFFFF)); RenderedTimeComponent(Timer timer) : super(timer); @@ -58,8 +57,7 @@ class MyBaseGame extends BaseGame with TapDetector, DoubleTapDetector { } class MyGame extends Game with TapDetector { - final TextConfig textConfig = - const TextConfig(color: const Color(0xFFFFFFFF)); + final TextConfig textConfig = TextConfig(color: const Color(0xFFFFFFFF)); Timer countdown; Timer interval; diff --git a/doc/images.md b/doc/images.md index 6ac9e47de..85bd8dc51 100644 --- a/doc/images.md +++ b/doc/images.md @@ -68,7 +68,9 @@ See example [here](/doc/examples/sprite_batch). Flame provides a simple API to render SVG images in your game. -To use it just import the `Svg` class from `'package:flame/svg.dart'`, and use the following snippet to render it on the canvas: +Svg support is provided by the `flame_svg` external package, be sure to put it on your pubspec to use it + +To use it just import the `Svg` class from `'package:flame_svg/flame_svg.dart'`, and use the following snippet to render it on the canvas: ```dart Svg svgInstance = Svg('android.svg'); diff --git a/lib/components/component.dart b/lib/components/component.dart index 18b95712f..35cd27cc5 100644 --- a/lib/components/component.dart +++ b/lib/components/component.dart @@ -4,7 +4,6 @@ import 'dart:ui'; import 'package:flutter/painting.dart'; import 'package:meta/meta.dart'; -import '../svg.dart'; import '../sprite.dart'; import '../position.dart'; import '../anchor.dart'; @@ -205,23 +204,3 @@ class SpriteComponent extends PositionComponent { return sprite != null && sprite.loaded() && x != null && y != null; } } - -class SvgComponent extends PositionComponent { - Svg svg; - - SvgComponent.fromSvg(double width, double height, this.svg) { - this.width = width; - this.height = height; - } - - @override - void render(Canvas canvas) { - prepareCanvas(canvas); - svg.render(canvas, width, height); - } - - @override - bool loaded() { - return svg != null && svg.loaded() && x != null && y != null; - } -} diff --git a/lib/components/text_box_component.dart b/lib/components/text_box_component.dart index c2180f29a..02e9be5e8 100644 --- a/lib/components/text_box_component.dart +++ b/lib/components/text_box_component.dart @@ -15,7 +15,7 @@ class TextBoxConfig { final double timePerChar; final double dismissDelay; - const TextBoxConfig({ + TextBoxConfig({ this.maxWidth = 200.0, this.margin = 8.0, this.timePerChar = 0.0, @@ -46,11 +46,13 @@ class TextBoxComponent extends PositionComponent with Resizable { TextBoxConfig get boxConfig => _boxConfig; - TextBoxComponent(String text, - {TextConfig config = const TextConfig(), - TextBoxConfig boxConfig = const TextBoxConfig()}) { - _boxConfig = boxConfig; - _config = config; + TextBoxComponent( + String text, { + TextConfig config, + TextBoxConfig boxConfig, + }) { + _boxConfig = boxConfig ?? TextBoxConfig(); + _config = config ?? TextConfig(); _text = text; _lines = []; text.split(' ').forEach((word) { diff --git a/lib/components/text_component.dart b/lib/components/text_component.dart index e21b732ad..4e52ca28b 100644 --- a/lib/components/text_component.dart +++ b/lib/components/text_component.dart @@ -27,8 +27,8 @@ class TextComponent extends PositionComponent { _updateBox(); } - TextComponent(this._text, {TextConfig config = const TextConfig()}) { - _config = config; + TextComponent(this._text, {TextConfig config}) { + _config = config ?? TextConfig(); _updateBox(); } diff --git a/lib/memory_cache.dart b/lib/memory_cache.dart new file mode 100644 index 000000000..4c5d46e7a --- /dev/null +++ b/lib/memory_cache.dart @@ -0,0 +1,27 @@ +import 'dart:collection'; + +/// Simple class to cache values on the cache +/// +class MemoryCache { + final LinkedHashMap _cache = LinkedHashMap(); + final int cacheSize; + + MemoryCache({this.cacheSize = 10}); + + void setValue(K key, V value) { + if (!_cache.containsKey(key)) { + _cache[key] = value; + + while (_cache.length > cacheSize) { + final k = _cache.keys.first; + _cache.remove(k); + } + } + } + + V getValue(K key) => _cache[key]; + + bool containsKey(K key) => _cache.containsKey(key); + + int get size => _cache.length; +} diff --git a/lib/svg.dart b/lib/svg.dart deleted file mode 100644 index f41b9f535..000000000 --- a/lib/svg.dart +++ /dev/null @@ -1,51 +0,0 @@ -import 'dart:ui'; -import 'package:flutter_svg/flutter_svg.dart'; - -import 'flame.dart'; -import 'position.dart'; - -class Svg { - DrawableRoot svgRoot; - Size size; - - Svg(String fileName) { - Flame.assets.readFile(fileName).then((svgString) async { - svgRoot = await svg.fromSvgString(svgString, svgString); - }); - } - - /// Renders the svg on the [canvas] using the dimensions provided on [width] and [height] - /// - /// If not loaded, does nothing - void render(Canvas canvas, double width, double height) { - if (!loaded()) { - return; - } - - svgRoot.scaleCanvasToViewBox(canvas, Size(width, height)); - svgRoot.draw(canvas, null); - } - - /// Renders the svg on the [canvas] on the given [position] using the dimensions provided on [width] and [height] - /// - /// If not loaded, does nothing - void renderPosition( - Canvas canvas, - Position position, - double width, - double height, - ) { - if (!loaded()) { - return; - } - - canvas.save(); - canvas.translate(position.x, position.y); - render(canvas, width, height); - canvas.restore(); - } - - bool loaded() { - return svgRoot != null; - } -} diff --git a/lib/text_config.dart b/lib/text_config.dart index 9f9bdcfa8..088b62805 100644 --- a/lib/text_config.dart +++ b/lib/text_config.dart @@ -4,6 +4,7 @@ import 'package:flutter/material.dart' as material; import 'position.dart'; import 'anchor.dart'; +import 'memory_cache.dart'; /// A Text Config contains all typographical information required to render texts; i.e., font size and color, family, etc. /// @@ -52,10 +53,13 @@ class TextConfig { /// For proper fonts of languages like Hebrew or Arabic, replace this with [TextDirection.rtl]. final TextDirection textDirection; + final MemoryCache _textPainterCache = + MemoryCache(); + /// Creates a constant [TextConfig] with sensible defaults. /// /// Every parameter can be specified. - const TextConfig({ + TextConfig({ this.fontSize = 24.0, this.color = const Color(0xFF000000), this.fontFamily = 'Arial', @@ -93,22 +97,26 @@ class TextConfig { /// However, you probably want to use the [render] method witch already renders for you considering the anchor. /// That way, you don't need to perform the math for yourself. material.TextPainter toTextPainter(String text) { - final material.TextStyle style = material.TextStyle( - color: color, - fontSize: fontSize, - fontFamily: fontFamily, - ); - final material.TextSpan span = material.TextSpan( - style: style, - text: text, - ); - final material.TextPainter tp = material.TextPainter( - text: span, - textAlign: textAlign, - textDirection: textDirection, - ); - tp.layout(); - return tp; + if (!_textPainterCache.containsKey(text)) { + final material.TextStyle style = material.TextStyle( + color: color, + fontSize: fontSize, + fontFamily: fontFamily, + ); + final material.TextSpan span = material.TextSpan( + style: style, + text: text, + ); + final material.TextPainter tp = material.TextPainter( + text: span, + textAlign: textAlign, + textDirection: textDirection, + ); + tp.layout(); + + _textPainterCache.setValue(text, tp); + } + return _textPainterCache.getValue(text); } /// Creates a new [TextConfig] changing only the [fontSize]. diff --git a/pubspec.yaml b/pubspec.yaml index 0ebcfbba0..19b46bf25 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -1,6 +1,6 @@ name: flame description: A minimalist Flutter game engine, provides a nice set of somewhat independent modules you can choose from. -version: 0.23.0 +version: 0.24.0 homepage: https://github.com/flame-engine/flame dependencies: @@ -11,9 +11,8 @@ dependencies: path_provider: ^1.6.0 box2d_flame: ^0.4.6 synchronized: ^2.1.0 - tiled: ^0.5.0 + tiled: ^0.6.0 convert: ^2.0.1 - flutter_svg: ^0.18.0 flare_flutter: ^2.0.1 meta: ^1.1.8 diff --git a/test/memory_cache_test.dart b/test/memory_cache_test.dart new file mode 100644 index 000000000..bf4cf6473 --- /dev/null +++ b/test/memory_cache_test.dart @@ -0,0 +1,31 @@ +import 'package:test/test.dart'; + +import 'package:flame/memory_cache.dart'; + +void main() { + group('MemoryCache', () { + test('basic cache addition', () { + final cache = MemoryCache(); + cache.setValue(0, 'bla'); + expect(cache.getValue(0), 'bla'); + }); + + test('contains key', () { + final cache = MemoryCache(); + cache.setValue(0, 'bla'); + expect(cache.containsKey(0), true); + expect(cache.containsKey(1), false); + }); + + test('cache size', () { + final cache = MemoryCache(cacheSize: 1); + cache.setValue(0, 'bla'); + cache.setValue(1, 'ble'); + expect(cache.containsKey(0), false); + expect(cache.containsKey(1), true); + expect(cache.getValue(0), null); + expect(cache.getValue(1), 'ble'); + expect(cache.size, 1); + }); + }); +}