mirror of
https://github.com/flame-engine/flame.git
synced 2025-11-02 20:13:50 +08:00
150 lines
3.0 KiB
Dart
150 lines
3.0 KiB
Dart
import 'dart:ui';
|
|
|
|
import 'package:flutter/gestures.dart';
|
|
import 'package:flutter/rendering.dart';
|
|
import 'package:flutter/scheduler.dart';
|
|
import 'package:flutter/widgets.dart';
|
|
|
|
import 'components/component.dart';
|
|
import 'position.dart';
|
|
|
|
abstract class Game {
|
|
void update(double t);
|
|
|
|
void render(Canvas canvas);
|
|
|
|
void resize(Size size);
|
|
|
|
Widget _widget;
|
|
|
|
Widget get widget {
|
|
if (_widget == null) {
|
|
_widget = new Center(
|
|
child: new Directionality(
|
|
textDirection: TextDirection.ltr,
|
|
child: new GameRenderObjectWidget(this)));
|
|
}
|
|
return _widget;
|
|
}
|
|
}
|
|
|
|
class GameRenderObjectWidget extends SingleChildRenderObjectWidget {
|
|
final Game game;
|
|
|
|
GameRenderObjectWidget(this.game);
|
|
|
|
@override
|
|
RenderObject createRenderObject(BuildContext context) =>
|
|
new GameRenderBox(context, this.game);
|
|
}
|
|
|
|
class GameRenderBox extends RenderBox {
|
|
BuildContext context;
|
|
|
|
Game game;
|
|
|
|
int _frameCallbackId;
|
|
|
|
Duration previous = Duration.zero;
|
|
|
|
GameRenderBox(this.context, this.game);
|
|
|
|
@override
|
|
bool get sizedByParent => true;
|
|
|
|
@override
|
|
void performResize() {
|
|
super.performResize();
|
|
game.resize(constraints.biggest);
|
|
}
|
|
|
|
@override
|
|
void attach(PipelineOwner owner) {
|
|
super.attach(owner);
|
|
_scheduleTick();
|
|
}
|
|
|
|
@override
|
|
void detach() {
|
|
super.detach();
|
|
_unscheduleTick();
|
|
}
|
|
|
|
void _scheduleTick() {
|
|
_frameCallbackId = SchedulerBinding.instance.scheduleFrameCallback(_tick);
|
|
}
|
|
|
|
void _unscheduleTick() {
|
|
SchedulerBinding.instance.cancelFrameCallbackWithId(_frameCallbackId);
|
|
}
|
|
|
|
void _tick(Duration timestamp) {
|
|
if (!attached) return;
|
|
_scheduleTick();
|
|
_update(timestamp);
|
|
markNeedsPaint();
|
|
}
|
|
|
|
void _update(Duration now) {
|
|
game.update(_computeDeltaT(now));
|
|
}
|
|
|
|
double _computeDeltaT(Duration now) {
|
|
Duration delta = now - previous;
|
|
if (previous == Duration.zero) {
|
|
delta = Duration.zero;
|
|
}
|
|
previous = now;
|
|
return delta.inMicroseconds / Duration.microsecondsPerSecond;
|
|
}
|
|
|
|
@override
|
|
void paint(PaintingContext context, Offset offset) {
|
|
game.render(context.canvas);
|
|
}
|
|
}
|
|
|
|
abstract class BaseGame extends Game {
|
|
|
|
List<Component> components = new List();
|
|
Size size;
|
|
Position camera = new Position.empty();
|
|
|
|
void add(Component c) {
|
|
this.components.add(c);
|
|
|
|
// first time resize
|
|
if (size != null) {
|
|
c.resize(size);
|
|
}
|
|
}
|
|
|
|
@override
|
|
void render(Canvas canvas) {
|
|
canvas.save();
|
|
components.forEach((comp) => renderComponent(canvas, comp));
|
|
canvas.restore();
|
|
}
|
|
|
|
void renderComponent(Canvas canvas, Component c) {
|
|
if (!c.isHud()) {
|
|
canvas.translate(-camera.x, -camera.y);
|
|
}
|
|
c.render(canvas);
|
|
canvas.restore();
|
|
canvas.save();
|
|
}
|
|
|
|
@override
|
|
void update(double t) {
|
|
components.forEach((c) => c.update(t));
|
|
components.removeWhere((c) => c.destroy());
|
|
}
|
|
|
|
@override
|
|
void resize(Size size) {
|
|
this.size = size;
|
|
components.forEach((c) => c.resize(size));
|
|
}
|
|
}
|