mirror of
https://github.com/flame-engine/flame.git
synced 2025-11-02 03:15:43 +08:00
Improving TimerComponent API (#1085)
* Improving TimerComponent API * pr suggestion * Update packages/flame/lib/src/components/timer_component.dart Co-authored-by: Lukas Klingsbo <lukas.klingsbo@gmail.com> * pr suggestion * docs * pr suggestions * fixing * renaming to onTick * Update packages/flame/lib/src/components/timer_component.dart Co-authored-by: Luan Nico <luanpotter27@gmail.com> * Apply suggestions from code review Co-authored-by: Lukas Klingsbo <lukas.klingsbo@gmail.com> * fixing issues * fixing flmae bloc isse * more suggestions * Update packages/flame/lib/src/timer.dart Co-authored-by: Lukas Klingsbo <lukas.klingsbo@gmail.com> Co-authored-by: Lukas Klingsbo <lukas.klingsbo@gmail.com> Co-authored-by: Luan Nico <luanpotter27@gmail.com>
This commit is contained in:
16
doc/util.md
16
doc/util.md
@ -60,10 +60,6 @@ class MyGame extends Game {
|
||||
final TextConfig textConfig = TextConfig(color: const Color(0xFFFFFFFF));
|
||||
final countdown = Timer(2);
|
||||
|
||||
MyGame() {
|
||||
countdown.start();
|
||||
}
|
||||
|
||||
@override
|
||||
void update(double dt) {
|
||||
countdown.update(dt);
|
||||
@ -103,10 +99,9 @@ class MyGame extends Game {
|
||||
MyGame() {
|
||||
interval = Timer(
|
||||
1,
|
||||
callback: () => elapsedSecs += 1,
|
||||
onTick: () => elapsedSecs += 1,
|
||||
repeat: true,
|
||||
);
|
||||
interval.start();
|
||||
}
|
||||
|
||||
@override
|
||||
@ -135,12 +130,9 @@ class MyFlameGame extends FlameGame {
|
||||
MyFlameGame() {
|
||||
add(
|
||||
TimerComponent(
|
||||
Timer(
|
||||
10,
|
||||
callback: () => print("10 seconds elapsed"),
|
||||
repeat: true,
|
||||
)
|
||||
..start()
|
||||
period: 10,
|
||||
repeat: true,
|
||||
onTick: () => print('10 seconds elapsed'),
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
@ -27,7 +27,7 @@ class MovableSquare extends SquareComponent
|
||||
await super.onLoad();
|
||||
timer = Timer(3.0)
|
||||
..stop()
|
||||
..callback = () {
|
||||
..onTick = () {
|
||||
gameRef.camera.setRelativeOffset(Anchor.center);
|
||||
};
|
||||
}
|
||||
|
||||
@ -13,6 +13,19 @@ changes its parent from its original game to the component that is rotating.
|
||||
After another 5 seconds it changes back to its original parent, and so on.
|
||||
''';
|
||||
|
||||
class GameChangeTimer extends TimerComponent with HasGameRef<GameInGame> {
|
||||
GameChangeTimer() : super(period: 5, repeat: true);
|
||||
|
||||
@override
|
||||
void onTick() {
|
||||
final child = gameRef.draggablesGame.square;
|
||||
final newParent = child.parent == gameRef.draggablesGame
|
||||
? gameRef.composedGame.parentSquare
|
||||
: gameRef.draggablesGame;
|
||||
child.changeParent(newParent);
|
||||
}
|
||||
}
|
||||
|
||||
class GameInGame extends FlameGame with HasDraggableComponents {
|
||||
@override
|
||||
bool debugMode = true;
|
||||
@ -24,20 +37,10 @@ class GameInGame extends FlameGame with HasDraggableComponents {
|
||||
await super.onLoad();
|
||||
composedGame = Composability();
|
||||
draggablesGame = DraggablesGame(zoom: 1.0);
|
||||
|
||||
await add(composedGame);
|
||||
await add(draggablesGame);
|
||||
final child = draggablesGame.square;
|
||||
final timer = Timer(
|
||||
5,
|
||||
callback: () {
|
||||
final newParent = child.parent == draggablesGame
|
||||
? composedGame.parentSquare
|
||||
: draggablesGame;
|
||||
child.changeParent(newParent);
|
||||
},
|
||||
repeat: true,
|
||||
);
|
||||
timer.start();
|
||||
add(TimerComponent(timer));
|
||||
|
||||
add(GameChangeTimer());
|
||||
}
|
||||
}
|
||||
|
||||
@ -18,7 +18,7 @@ class TimerGame extends FlameGame with TapDetector {
|
||||
countdown = Timer(2);
|
||||
interval = Timer(
|
||||
1,
|
||||
callback: () => elapsedSecs += 1,
|
||||
onTick: () => elapsedSecs += 1,
|
||||
repeat: true,
|
||||
);
|
||||
interval.start();
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
import 'package:flame/components.dart';
|
||||
import 'package:flame/game.dart';
|
||||
import 'package:flame/input.dart';
|
||||
import 'package:flame/timer.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
class RenderedTimeComponent extends TimerComponent {
|
||||
@ -10,9 +10,9 @@ class RenderedTimeComponent extends TimerComponent {
|
||||
|
||||
final double yOffset;
|
||||
|
||||
RenderedTimeComponent(Timer timer, {this.yOffset = 150})
|
||||
RenderedTimeComponent(double period, {this.yOffset = 150})
|
||||
: super(
|
||||
timer,
|
||||
period: period,
|
||||
removeOnFinish: true,
|
||||
);
|
||||
|
||||
@ -29,11 +29,11 @@ class RenderedTimeComponent extends TimerComponent {
|
||||
class TimerComponentGame extends FlameGame with TapDetector, DoubleTapDetector {
|
||||
@override
|
||||
void onTap() {
|
||||
add(RenderedTimeComponent(Timer(1)..start()));
|
||||
add(RenderedTimeComponent(1));
|
||||
}
|
||||
|
||||
@override
|
||||
void onDoubleTap() {
|
||||
add(RenderedTimeComponent(Timer(5)..start(), yOffset: 180));
|
||||
add(RenderedTimeComponent(5, yOffset: 180));
|
||||
}
|
||||
}
|
||||
|
||||
@ -27,6 +27,7 @@
|
||||
- `HitboxShape` takes parents ancestors transformations into consideration (not scaling)
|
||||
- Renamed `FlameTester` to `GameTester`
|
||||
- Modified `FlameTester` to be specific for `T extends FlameGame`
|
||||
- Improved `TimerComponent`
|
||||
|
||||
## [1.0.0-releasecandidate.16]
|
||||
- `changePriority` no longer breaks game loop iteration
|
||||
|
||||
@ -24,6 +24,7 @@ export 'src/components/sprite_component.dart';
|
||||
export 'src/components/sprite_group_component.dart';
|
||||
export 'src/components/text_box_component.dart';
|
||||
export 'src/components/text_component.dart';
|
||||
export 'src/components/timer_component.dart';
|
||||
export 'src/extensions/vector2.dart';
|
||||
export 'src/game/mixins/has_collidables.dart';
|
||||
export 'src/text.dart';
|
||||
|
||||
49
packages/flame/lib/src/components/timer_component.dart
Normal file
49
packages/flame/lib/src/components/timer_component.dart
Normal file
@ -0,0 +1,49 @@
|
||||
import 'dart:ui';
|
||||
|
||||
import '../../components.dart';
|
||||
|
||||
/// A component that uses a [Timer] instance which you can react to when it has finished.
|
||||
class TimerComponent extends Component {
|
||||
late final Timer timer;
|
||||
final bool removeOnFinish;
|
||||
final VoidCallback? _onTick;
|
||||
|
||||
/// Creates a [TimerComponent]
|
||||
///
|
||||
/// [period] The period of time in seconds that the tick will be called
|
||||
/// [repeat] When true, this will continue running after [period] is reached
|
||||
/// [autoStart] When true, will start upon instantiation (default is true)
|
||||
/// [onTick] When provided, will be called everytime [period] is reached. This
|
||||
/// overrides the [onTick] method
|
||||
TimerComponent({
|
||||
required double period,
|
||||
bool repeat = false,
|
||||
bool autoStart = true,
|
||||
this.removeOnFinish = false,
|
||||
VoidCallback? onTick,
|
||||
}) : _onTick = onTick {
|
||||
timer = Timer(
|
||||
period,
|
||||
repeat: repeat,
|
||||
onTick: this.onTick,
|
||||
autoStart: autoStart,
|
||||
);
|
||||
}
|
||||
|
||||
/// Called everytime the [timer] reached a tick.
|
||||
/// The default implementation calls the closure received on the
|
||||
/// constructor and can be overriden to add custom logic.
|
||||
void onTick() {
|
||||
_onTick?.call();
|
||||
}
|
||||
|
||||
@override
|
||||
void update(double dt) {
|
||||
super.update(dt);
|
||||
timer.update(dt);
|
||||
|
||||
if (removeOnFinish && timer.finished) {
|
||||
removeFromParent();
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -93,7 +93,7 @@ abstract class Particle {
|
||||
// TODO: Maybe make it into a setter/getter?
|
||||
_lifespan = lifespan;
|
||||
_timer?.stop();
|
||||
_timer = Timer(lifespan, callback: () => _shouldBeRemoved = true)..start();
|
||||
_timer = Timer(lifespan, onTick: () => _shouldBeRemoved = true)..start();
|
||||
}
|
||||
|
||||
/// Wraps this particle with a [TranslatedParticle].
|
||||
|
||||
@ -1,18 +1,23 @@
|
||||
import 'dart:math';
|
||||
import 'dart:ui';
|
||||
|
||||
import 'components/component.dart';
|
||||
|
||||
/// Simple utility class that helps handling time counting and implementing
|
||||
/// interval like events.
|
||||
///
|
||||
/// Timer auto start by default.
|
||||
class Timer {
|
||||
final double limit;
|
||||
VoidCallback? callback;
|
||||
VoidCallback? onTick;
|
||||
bool repeat;
|
||||
double _current = 0;
|
||||
bool _running = false;
|
||||
bool _running;
|
||||
|
||||
Timer(this.limit, {this.callback, this.repeat = false});
|
||||
Timer(
|
||||
this.limit, {
|
||||
this.onTick,
|
||||
this.repeat = false,
|
||||
bool autoStart = true,
|
||||
}) : _running = autoStart;
|
||||
|
||||
/// The current amount of ms that has passed on this iteration
|
||||
double get current => _current;
|
||||
@ -32,15 +37,15 @@ class Timer {
|
||||
if (_current >= limit) {
|
||||
if (!repeat) {
|
||||
_running = false;
|
||||
callback?.call();
|
||||
onTick?.call();
|
||||
return;
|
||||
}
|
||||
// This is used to cover the rare case of _current being more than
|
||||
// two times the value of limit, so that the callback is called the
|
||||
// two times the value of limit, so that the onTick is called the
|
||||
// correct number of times
|
||||
while (_current >= limit) {
|
||||
_current -= limit;
|
||||
callback?.call();
|
||||
onTick?.call();
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -73,21 +78,3 @@ class Timer {
|
||||
_running = true;
|
||||
}
|
||||
}
|
||||
|
||||
/// Simple component which wraps a [Timer] instance allowing it to be easily
|
||||
/// used inside a FlameGame game.
|
||||
class TimerComponent extends Component {
|
||||
Timer timer;
|
||||
final bool removeOnFinish;
|
||||
|
||||
TimerComponent(this.timer, {this.removeOnFinish = false});
|
||||
|
||||
@override
|
||||
void update(double dt) {
|
||||
super.update(dt);
|
||||
timer.update(dt);
|
||||
if (removeOnFinish && timer.finished) {
|
||||
removeFromParent();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
82
packages/flame/test/components/timer_component_test.dart
Normal file
82
packages/flame/test/components/timer_component_test.dart
Normal file
@ -0,0 +1,82 @@
|
||||
import 'package:flame/components.dart';
|
||||
import 'package:flame/game.dart';
|
||||
import 'package:flame_test/flame_test.dart';
|
||||
import 'package:flutter_test/flutter_test.dart';
|
||||
|
||||
class MyTimerComponent extends TimerComponent {
|
||||
int count = 0;
|
||||
|
||||
MyTimerComponent()
|
||||
: super(
|
||||
period: 1,
|
||||
repeat: true,
|
||||
removeOnFinish: false,
|
||||
);
|
||||
|
||||
@override
|
||||
void onTick() {
|
||||
count++;
|
||||
}
|
||||
}
|
||||
|
||||
class NonRepeatingTimerComponent extends TimerComponent {
|
||||
NonRepeatingTimerComponent()
|
||||
: super(
|
||||
period: 1,
|
||||
repeat: false,
|
||||
removeOnFinish: true,
|
||||
);
|
||||
}
|
||||
|
||||
void main() {
|
||||
group('TimerComponent', () {
|
||||
final tester = FlameTester(() => FlameGame());
|
||||
|
||||
tester.test('runs the tick method', (game) {
|
||||
final timer = MyTimerComponent();
|
||||
game.add(timer);
|
||||
game.update(0);
|
||||
|
||||
game.update(1.2);
|
||||
|
||||
game.update(0);
|
||||
expect(timer.count, equals(1));
|
||||
});
|
||||
|
||||
tester.test('never remove from the game when is repeating', (game) {
|
||||
game.add(MyTimerComponent());
|
||||
game.update(0);
|
||||
|
||||
game.update(1.2);
|
||||
|
||||
game.update(0);
|
||||
expect(game.children.length, equals(1));
|
||||
});
|
||||
|
||||
tester.test('is removed from the game when is finished', (game) {
|
||||
game.add(NonRepeatingTimerComponent());
|
||||
game.update(0);
|
||||
|
||||
game.update(1.2);
|
||||
|
||||
game.update(0);
|
||||
expect(game.children.length, equals(0));
|
||||
});
|
||||
|
||||
tester.test('calls onTick when provided', (game) {
|
||||
var called = false;
|
||||
game.add(
|
||||
TimerComponent(
|
||||
period: 1,
|
||||
onTick: () {
|
||||
called = true;
|
||||
},
|
||||
),
|
||||
);
|
||||
game.update(0);
|
||||
game.update(1.2);
|
||||
|
||||
expect(called, isTrue);
|
||||
});
|
||||
});
|
||||
}
|
||||
@ -4,7 +4,7 @@ import 'package:test/test.dart';
|
||||
void main() {
|
||||
group('Timer', () {
|
||||
test('can be started and stopped, discarding progress', () {
|
||||
final timer = Timer(1.0);
|
||||
final timer = Timer(1.0, autoStart: false);
|
||||
expect(timer.isRunning(), false);
|
||||
timer.start();
|
||||
expect(timer.isRunning(), true);
|
||||
@ -15,7 +15,7 @@ void main() {
|
||||
});
|
||||
|
||||
test('can be paused and resumed, retaining progress', () {
|
||||
final timer = Timer(1.0);
|
||||
final timer = Timer(1.0, autoStart: false);
|
||||
expect(timer.isRunning(), false);
|
||||
timer.start();
|
||||
expect(timer.isRunning(), true);
|
||||
@ -29,7 +29,6 @@ void main() {
|
||||
|
||||
test('tracks current delta time', () {
|
||||
final timer = Timer(1.0);
|
||||
timer.start();
|
||||
timer.update(0.5);
|
||||
expect(timer.current, 0.5);
|
||||
timer.update(0.2);
|
||||
@ -38,7 +37,6 @@ void main() {
|
||||
|
||||
test('tracks progress percent capped at 1.0', () {
|
||||
final timer = Timer(2.0);
|
||||
timer.start();
|
||||
timer.update(0.5);
|
||||
expect(timer.progress, 0.25);
|
||||
timer.update(0.5);
|
||||
@ -47,41 +45,37 @@ void main() {
|
||||
expect(timer.progress, 1.0);
|
||||
});
|
||||
|
||||
test('callback fires once if non-repeating', () {
|
||||
var callbackCount = 0;
|
||||
final timer = Timer(1.0, callback: () => callbackCount++);
|
||||
timer.start();
|
||||
test('onTick fires once if non-repeating', () {
|
||||
var onTickCount = 0;
|
||||
final timer = Timer(1.0, onTick: () => onTickCount++);
|
||||
timer.update(0.9);
|
||||
expect(callbackCount, 0);
|
||||
expect(onTickCount, 0);
|
||||
timer.update(0.2);
|
||||
expect(callbackCount, 1);
|
||||
expect(onTickCount, 1);
|
||||
timer.update(1.0);
|
||||
expect(callbackCount, 1);
|
||||
expect(onTickCount, 1);
|
||||
});
|
||||
|
||||
test('finishes when complete if non-repeating', () {
|
||||
final timer = Timer(1.0);
|
||||
timer.start();
|
||||
expect(timer.finished, false);
|
||||
timer.update(1.1);
|
||||
expect(timer.finished, true);
|
||||
});
|
||||
|
||||
test('callback fires repeatedly if repeating', () {
|
||||
var callbackCount = 0;
|
||||
final timer = Timer(1.0, repeat: true, callback: () => callbackCount++);
|
||||
timer.start();
|
||||
test('onTick fires repeatedly if repeating', () {
|
||||
var onTickCount = 0;
|
||||
final timer = Timer(1.0, repeat: true, onTick: () => onTickCount++);
|
||||
timer.update(0.9);
|
||||
expect(callbackCount, 0);
|
||||
expect(onTickCount, 0);
|
||||
timer.update(0.2);
|
||||
expect(callbackCount, 1);
|
||||
expect(onTickCount, 1);
|
||||
timer.update(1.0);
|
||||
expect(callbackCount, 2);
|
||||
expect(onTickCount, 2);
|
||||
});
|
||||
|
||||
test('does not finish past limit if repeating', () {
|
||||
final timer = Timer(1.0, repeat: true);
|
||||
timer.start();
|
||||
expect(timer.finished, false);
|
||||
timer.update(1.1);
|
||||
expect(timer.finished, false);
|
||||
|
||||
@ -1,35 +1,22 @@
|
||||
import 'dart:math';
|
||||
|
||||
import 'package:flame/components.dart';
|
||||
import 'package:flame/timer.dart';
|
||||
|
||||
import './enemy.dart';
|
||||
import '../game.dart';
|
||||
|
||||
class EnemyCreator extends Component with HasGameRef<SpaceShooterGame> {
|
||||
late Timer enemyCreator;
|
||||
|
||||
class EnemyCreator extends TimerComponent with HasGameRef<SpaceShooterGame> {
|
||||
Random random = Random();
|
||||
|
||||
EnemyCreator() {
|
||||
enemyCreator = Timer(
|
||||
1,
|
||||
repeat: true,
|
||||
callback: () {
|
||||
gameRef.add(
|
||||
EnemyComponent(
|
||||
(gameRef.size.x - 25) * random.nextDouble(),
|
||||
0,
|
||||
),
|
||||
);
|
||||
},
|
||||
);
|
||||
enemyCreator.start();
|
||||
}
|
||||
EnemyCreator() : super(period: 1, repeat: true);
|
||||
|
||||
@override
|
||||
void update(double dt) {
|
||||
super.update(dt);
|
||||
enemyCreator.update(dt);
|
||||
void onTick() {
|
||||
gameRef.add(
|
||||
EnemyComponent(
|
||||
(gameRef.size.x - 25) * random.nextDouble(),
|
||||
0,
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@ -45,7 +45,7 @@ class PlayerComponent extends SpriteAnimationComponent
|
||||
|
||||
PlayerComponent()
|
||||
: super(size: Vector2(50, 75), position: Vector2(100, 500)) {
|
||||
bulletCreator = Timer(0.5, repeat: true, callback: _createBullet);
|
||||
bulletCreator = Timer(0.5, repeat: true, onTick: _createBullet);
|
||||
|
||||
addHitbox(HitboxRectangle());
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user