improve mixins & adds HasGameRef & fixes composedComponent

This commit is contained in:
Luan Nico
2019-09-21 12:59:32 -04:00
parent 42397bbcc8
commit e2622cde41
13 changed files with 208 additions and 46 deletions

View File

@ -1,4 +1,7 @@
## [next]
- Improve our mixin structure (breaking change)
- Adds HasGameRef mixin
- Fixes for ComposedComponent
## 0.15.2
- Exposing tile objects on TiledComponent (thanks @renatoferreira656)

View File

@ -1,7 +1,7 @@
import 'package:flutter/material.dart';
import 'package:flame/game.dart';
import 'package:flame/components/component.dart';
import 'package:flame/components/events/gestures.dart';
import 'package:flame/components/mixins/tapeable.dart';
void main() {
final game = MyGame();

View File

@ -67,7 +67,7 @@ abstract class PositionComponent extends Component {
bool debugMode = false;
Color get debugColor => Color(0xFFFF00FF);
Color get debugColor => const Color(0xFFFF00FF);
Paint get _debugPaint => Paint()
..color = debugColor

View File

@ -1,11 +1,16 @@
import 'dart:ui';
import 'package:flame/components/component.dart';
import 'package:flame/components/resizable.dart';
import 'package:flame/components/mixins/has_game_ref.dart';
import 'package:flame/components/mixins/tapeable.dart';
import 'package:flame/game.dart';
import 'package:ordered_set/comparing.dart';
import 'package:ordered_set/ordered_set.dart';
/// A mixin that helps you to make a `Component` wraps other components. It is useful to group visual components through a hierarchy. When implemented, makes every item in its `components` collection field be updated and rendered with the same conditions.
import 'component.dart';
import 'mixins/resizable.dart';
/// A mixin that helps you to make a `Component` wraps other components. It is useful to group visual components through a hierarchy.
/// When implemented, makes every item in its `components` collection field be updated and rendered with the same conditions.
///
/// Example of usage, where visibility of two components are handled by a wrapper:
///
@ -32,7 +37,7 @@ import 'package:ordered_set/ordered_set.dart';
/// }
/// ```
///
mixin ComposedComponent on Component {
mixin ComposedComponent on Component, HasGameRef, Tapeable {
OrderedSet<Component> components =
OrderedSet(Comparing.on((c) => c.priority()));
@ -56,17 +61,28 @@ mixin ComposedComponent on Component {
}
void add(Component c) {
components.add(c);
if (this is Resizable) {
// first time resize
final Resizable thisResizable = this as Resizable;
if (thisResizable.size != null) {
c.resize(thisResizable.size);
}
if (gameRef is BaseGame) {
(gameRef as BaseGame).preAdd(c);
}
components.add(c);
}
List<Resizable> children() =>
components.where((r) => r is Resizable).cast<Resizable>().toList();
// this is an important override for the Tapeable mixin
@override
Iterable<Tapeable> tapeableChildren() => _findT<Tapeable>();
// this is an important override for the Resizable mixin
Iterable<Resizable> resizableChildren() => _findT<Resizable>();
// Finds all children of type T, recursively
Iterable<T> _findT<T>() => components.expand((c) {
final List<T> r = [];
if (c is T) {
r.add(c as T);
}
if (c is ComposedComponent) {
r.addAll(c._findT<T>());
}
return r;
}).cast();
}

View File

@ -1,8 +0,0 @@
import '../component.dart';
import 'package:flutter/gestures.dart';
mixin Tapeable on PositionComponent {
void onTapCancel() {}
void onTapDown(TapDownDetails details) {}
void onTapUp(TapUpDetails details) {}
}

View File

@ -0,0 +1,5 @@
import '../../game.dart';
mixin HasGameRef<T extends Game> {
T gameRef;
}

View File

@ -11,11 +11,11 @@ class Resizable {
/// Implementation provided by this mixin to the resize hook.
void resize(Size size) {
this.size = size;
children().where((e) => e != null).forEach((e) => e.resize(size));
resizableChildren().where((e) => e != null).forEach((e) => e.resize(size));
}
/// Overwrite this to add children to this [Resizable].
///
/// If a [Resizable] has children, its children as resized as well.
List<Resizable> children() => [];
Iterable<Resizable> resizableChildren() => [];
}

View File

@ -0,0 +1,37 @@
import 'dart:ui';
import 'package:flutter/gestures.dart';
mixin Tapeable {
Rect toRect();
void onTapCancel() {}
void onTapDown(TapDownDetails details) {}
void onTapUp(TapUpDetails details) {}
bool checkTapOverlap(Offset o) => toRect().contains(o);
void handleTapDown(TapDownDetails details) {
if (checkTapOverlap(details.globalPosition)) {
onTapDown(details);
}
tapeableChildren().forEach((c) => c.handleTapDown(details));
}
void handleTapUp(TapUpDetails details) {
if (checkTapOverlap(details.globalPosition)) {
onTapUp(details);
}
tapeableChildren().forEach((c) => c.handleTapUp(details));
}
void handleTapCancel() {
onTapCancel();
tapeableChildren().forEach((c) => c.handleTapCancel());
}
/// Overwrite this to add children to this [Tapeable].
///
/// If a [Tapeable] has children, its children be taped as well.
Iterable<Tapeable> tapeableChildren() => [];
}

View File

@ -4,7 +4,7 @@ import 'dart:math' as math;
import 'package:flutter/widgets.dart' as widgets;
import 'component.dart';
import 'resizable.dart';
import 'mixins/resizable.dart';
import '../text_config.dart';
import '../palette.dart';
import '../position.dart';

View File

@ -1,6 +1,7 @@
import 'dart:math' as math;
import 'dart:ui';
import 'package:flame/components/composed_component.dart';
import 'package:flutter/gestures.dart';
import 'package:flutter/rendering.dart';
import 'package:flutter/scheduler.dart';
@ -9,9 +10,10 @@ import 'package:ordered_set/comparing.dart';
import 'package:ordered_set/ordered_set.dart';
import 'components/component.dart';
import 'position.dart';
import 'components/events/gestures.dart';
import 'components/mixins/has_game_ref.dart';
import 'components/mixins/tapeable.dart';
import 'flame.dart';
import 'position.dart';
/// Represents a generic game.
///
@ -109,32 +111,21 @@ abstract class BaseGame extends Game {
/// List of deltas used in debug mode to calculate FPS
final List<double> _dts = [];
bool _checkTapOverlap(Tapeable c, Offset o) => c.toRect().contains(o);
Iterable<Tapeable> get _tapeableComponents =>
components.where((c) => c is Tapeable).cast();
Iterable<Tapeable> get _tapeableComponents => components.where((c) => c is Tapeable).cast();
@override
void onTapCancel() {
_tapeableComponents.forEach((c) => c.onTapCancel());
_tapeableComponents.forEach((c) => c.handleTapCancel());
}
@override
void onTapDown(TapDownDetails details) {
_tapeableComponents.forEach((c) {
if (_checkTapOverlap(c, details.globalPosition)) {
c.onTapDown(details);
}
});
_tapeableComponents.forEach((c) => c.handleTapDown(details));
}
@override
void onTapUp(TapUpDetails details) {
_tapeableComponents.forEach((c) {
if (_checkTapOverlap(c, details.globalPosition)) {
c.onTapUp(details);
}
});
_tapeableComponents.forEach((c) => c.handleTapUp(details));
}
/// This method is called for every component added, both via [add] and [addLater] methods.
@ -151,6 +142,14 @@ abstract class BaseGame extends Game {
if (size != null) {
c.resize(size);
}
if (c is HasGameRef) {
(c as HasGameRef).gameRef = this;
}
if (c is ComposedComponent) {
c.components.forEach(preAdd);
}
}
/// Adds a new component to the components list.

View File

@ -0,0 +1,70 @@
import 'dart:ui';
import 'package:flame/components/composed_component.dart';
import 'package:flame/components/mixins/has_game_ref.dart';
import 'package:flame/components/mixins/resizable.dart';
import 'package:flame/components/mixins/tapeable.dart';
import 'package:flutter/gestures.dart';
import 'package:test/test.dart';
import 'package:flame/game.dart';
import 'package:flame/components/component.dart';
class MyGame extends BaseGame {}
class MyTap extends PositionComponent with Tapeable, Resizable {
bool tapped = false;
@override
void render(Canvas c) {}
@override
void update(double t) {}
@override
void onTapDown(TapDownDetails details) {
tapped = true;
}
@override
bool checkTapOverlap(Offset o) => true;
}
class MyComposed extends Component with HasGameRef, Tapeable, ComposedComponent {
@override
void update(double dt) {}
@override
void render(Canvas c) {}
@override
Rect toRect() => Rect.zero;
}
class PositionComponentNoNeedForRect extends PositionComponent with Tapeable {
@override
void render(Canvas c) {}
@override
void update(double t) {}
}
const Size size = Size(1.0, 1.0);
void main() {
group('composable component test', () {
test('taps and resizes children', () {
final MyGame game = MyGame();
final MyTap child = MyTap();
final MyComposed wrapper = MyComposed()..add(child);
game.size = size;
game.add(wrapper);
game.onTapDown(TapDownDetails(globalPosition: const Offset(0.0, 0.0)));
expect(child.size, size);
expect(child.tapped, true);
});
});
}

View File

@ -0,0 +1,40 @@
import 'dart:ui';
import 'package:flame/components/mixins/has_game_ref.dart';
import 'package:test/test.dart';
import 'package:flame/game.dart';
import 'package:flame/components/component.dart';
class MyGame extends BaseGame {
bool calledFoo = false;
void foo() {
calledFoo = true;
}
}
class MyComponent extends PositionComponent with HasGameRef<MyGame> {
@override
void update(double dt) {
}
@override
void render(Canvas c) {}
void foo() {
gameRef.foo();
}
}
void main() {
group('has game ref test', () {
test('simple test', () {
final MyComponent c = MyComponent();
final MyGame game = MyGame();
game.add(c);
c.foo();
expect(game.calledFoo, true);
});
});
}

View File

@ -4,7 +4,7 @@ import 'package:test/test.dart';
import 'package:flame/game.dart';
import 'package:flame/components/component.dart';
import 'package:flame/components/resizable.dart';
import 'package:flame/components/mixins/resizable.dart';
class MyComponent extends PositionComponent with Resizable {
String name;
@ -13,7 +13,7 @@ class MyComponent extends PositionComponent with Resizable {
MyComponent(this.name, {this.myChildren = const []});
@override
List<Resizable> children() => myChildren;
Iterable<Resizable> resizableChildren() => myChildren;
@override
void update(double dt) {}