mirror of
https://github.com/flame-engine/flame.git
synced 2025-11-02 11:43:19 +08:00
improve mixins & adds HasGameRef & fixes composedComponent
This commit is contained in:
@ -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)
|
||||
|
||||
@ -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();
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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) {
|
||||
if (gameRef is BaseGame) {
|
||||
(gameRef as BaseGame).preAdd(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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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();
|
||||
}
|
||||
|
||||
@ -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) {}
|
||||
}
|
||||
5
lib/components/mixins/has_game_ref.dart
Normal file
5
lib/components/mixins/has_game_ref.dart
Normal file
@ -0,0 +1,5 @@
|
||||
import '../../game.dart';
|
||||
|
||||
mixin HasGameRef<T extends Game> {
|
||||
T gameRef;
|
||||
}
|
||||
@ -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() => [];
|
||||
}
|
||||
37
lib/components/mixins/tapeable.dart
Normal file
37
lib/components/mixins/tapeable.dart
Normal 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() => [];
|
||||
}
|
||||
@ -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';
|
||||
|
||||
@ -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.
|
||||
|
||||
70
test/composed_component_test.dart
Normal file
70
test/composed_component_test.dart
Normal 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);
|
||||
});
|
||||
});
|
||||
}
|
||||
40
test/has_game_ref_test.dart
Normal file
40
test/has_game_ref_test.dart
Normal 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);
|
||||
});
|
||||
});
|
||||
}
|
||||
@ -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) {}
|
||||
|
||||
Reference in New Issue
Block a user