Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

docs: Added documentation for GameLoop class #1234

Merged
merged 11 commits into from
Jan 18, 2022
82 changes: 58 additions & 24 deletions packages/flame/lib/src/game/game_loop.dart
Original file line number Diff line number Diff line change
@@ -1,48 +1,82 @@
import 'package:flutter/scheduler.dart';

/// Internal class that drives the game loop by calling the provided [callback]
/// function on every Flutter animation frame.
///
/// After creating a GameLoop, call `start()` in order to make it actually run.
/// When a GameLoop object is no longer needed, it must be `dispose()`d.
///
/// For example:
/// ```dart
/// final gameLoop = GameLoop(onGameLoopTick);
/// gameLoop.start();
/// ...
/// gameLoop.dispose();
/// ```
class GameLoop {
void Function(double dt) callback;
Duration previous = Duration.zero;
late Ticker _ticker;

GameLoop(this.callback) {
_ticker = Ticker(_tick);
}

/// Function to be called on every Flutter rendering frame.
///
/// This function takes a single parameter `dt`, which is the amount of time
/// passed since the previous invocation of this function. The time is
/// measured in seconds, with microsecond precision. The argument will be
/// equal to 0 on first invocation of the callback.
void Function(double dt) callback;

/// Total amount of time passed since the game loop was started.
///
/// This variable is updated on every rendering frame, just before the
/// [callback] is invoked. It will be equal to zero while the game loop is
/// stopped. It is also guaranteed to be equal to zero on the first invocation
/// of the callback.
Duration _previous = Duration.zero;

/// Internal object responsible for periodically calling the [callback]
/// function.
late final Ticker _ticker;

/// This method is periodically invoked by the [_ticker].
void _tick(Duration timestamp) {
final dt = _computeDeltaT(timestamp);
final durationDelta = timestamp - _previous;
final dt = durationDelta.inMicroseconds / Duration.microsecondsPerSecond;
_previous = timestamp;
callback(dt);
}

double _computeDeltaT(Duration now) {
final delta = previous == Duration.zero ? Duration.zero : now - previous;
previous = now;
return delta.inMicroseconds / Duration.microsecondsPerSecond;
}

/// Start running the game loop. The game loop is created in a paused state,
/// so this must be called once in order to make the loop running. Calling
/// this method again when the game loop already runs is a noop.
void start() {
_ticker.start();
if (!_ticker.isActive) {
_ticker.start();
}
}

/// Stop the game loop. While it is stopped, the time "freezes". When the
/// game loop is started again, the [callback] will NOT be made aware that
/// any amount of time has passed.
void stop() {
_ticker.stop();
_previous = Duration.zero;
}

/// Call this before deleting the [GameLoop] object.
///
/// The [GameLoop] will no longer be usable after this method is called. You
/// do not have to stop the game loop before disposing of it.
void dispose() {
_ticker.dispose();
}

void pause() {
_ticker.muted = true;
previous = Duration.zero;
}
@Deprecated('Internal variable')
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why use Deprecated? There are specialized decorators for this like Protected.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Because we don't use that variable anywhere. It was never intended to be public.
So, now that we make it private, the getter is provided in case anyone's code was reading that variable. The getter is marked deprecated, as a way to signal that this variable really shouldn't be used, and that the getter will be removed after a while.

Duration get previous => _previous;

void resume() {
_ticker.muted = false;
// If the game has started paused, we need to start the ticker
// as it would not have been started yet
if (!_ticker.isActive) {
start();
}
}
@Deprecated('Use stop() instead')
st-pasha marked this conversation as resolved.
Show resolved Hide resolved
void pause() => stop();

@Deprecated('Use start() instead')
void resume() => start();
}
5 changes: 2 additions & 3 deletions packages/flame/lib/src/game/game_render_box.dart
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@ import '../extensions/size.dart';
import 'game_loop.dart';
import 'mixins/game.dart';

// ignore: prefer_mixin
class GameRenderBox extends RenderBox with WidgetsBindingObserver {
BuildContext buildContext;
Game game;
Expand All @@ -31,8 +30,8 @@ class GameRenderBox extends RenderBox with WidgetsBindingObserver {

final gameLoop = this.gameLoop = GameLoop(gameLoopCallback);

game.pauseEngineFn = gameLoop.pause;
game.resumeEngineFn = gameLoop.resume;
game.pauseEngineFn = gameLoop.stop;
game.resumeEngineFn = gameLoop.start;

if (!game.paused) {
gameLoop.start();
Expand Down