mirror of
https://github.com/flame-engine/flame.git
synced 2025-11-03 20:36:31 +08:00
interop
This commit is contained in:
171
lib/game.dart
171
lib/game.dart
@ -1,55 +1,151 @@
|
|||||||
import 'dart:typed_data';
|
import 'dart:typed_data';
|
||||||
import 'dart:ui';
|
import 'dart:ui';
|
||||||
|
import 'dart:ui' as ui;
|
||||||
|
|
||||||
import 'package:flame/component.dart';
|
import 'package:flame/components/component.dart';
|
||||||
import 'package:flutter/gestures.dart';
|
import 'package:flutter/gestures.dart';
|
||||||
import 'package:flutter/rendering.dart';
|
import 'package:flutter/rendering.dart';
|
||||||
import 'package:flutter/scheduler.dart';
|
import 'package:flutter/scheduler.dart';
|
||||||
import 'package:flutter/widgets.dart';
|
import 'package:flutter/widgets.dart';
|
||||||
|
import 'package:meta/meta.dart';
|
||||||
|
|
||||||
abstract class Game {
|
abstract class Game {
|
||||||
|
// these are all the previously held callbacks from the Flutter engine
|
||||||
|
// this is probably a poor solution to injecting our own rendering stuff
|
||||||
|
ui.FrameCallback _previousOnBeginFrame;
|
||||||
|
VoidCallback _previousOnDrawFrame;
|
||||||
|
PointerDataPacketCallback _previousOnPointerDataPacket;
|
||||||
|
|
||||||
|
// the previous time (game clock)
|
||||||
|
Duration _previous = Duration.ZERO;
|
||||||
|
|
||||||
|
// determines if the game loop should revert back to
|
||||||
|
// the previous Flutter callbacks on the next frame
|
||||||
|
bool _stop = false;
|
||||||
|
|
||||||
|
/// Called when the game is updating.
|
||||||
|
/// [t] is the time since the previous update.
|
||||||
void update(double t);
|
void update(double t);
|
||||||
|
|
||||||
|
/// Called when the game is rendering.
|
||||||
|
/// [canvas] is what you will render your components on.
|
||||||
void render(Canvas canvas);
|
void render(Canvas canvas);
|
||||||
|
|
||||||
start() {
|
/// Starts the game by injecting render callbacks into the Flutter engine.
|
||||||
var previous = Duration.ZERO;
|
void start({@required PointerDataPacketCallback onInput}) {
|
||||||
|
_previousOnBeginFrame = window.onBeginFrame;
|
||||||
|
_previousOnDrawFrame = window.onDrawFrame;
|
||||||
|
_previousOnPointerDataPacket = window.onPointerDataPacket;
|
||||||
|
|
||||||
window.onBeginFrame = (now) {
|
window.onBeginFrame = _onBeginFrame;
|
||||||
var recorder = new PictureRecorder();
|
window.onDrawFrame = _onDrawFrame;
|
||||||
var canvas = new Canvas(
|
window.onPointerDataPacket = onInput;
|
||||||
recorder,
|
|
||||||
new Rect.fromLTWH(
|
|
||||||
0.0, 0.0, window.physicalSize.width, window.physicalSize.height));
|
|
||||||
|
|
||||||
Duration delta = now - previous;
|
|
||||||
if (previous == Duration.ZERO) {
|
|
||||||
delta = Duration.ZERO;
|
|
||||||
}
|
|
||||||
previous = now;
|
|
||||||
|
|
||||||
var t = delta.inMicroseconds / Duration.MICROSECONDS_PER_SECOND;
|
|
||||||
|
|
||||||
update(t);
|
|
||||||
render(canvas);
|
|
||||||
|
|
||||||
var deviceTransform = new Float64List(16)
|
|
||||||
..[0] = window.devicePixelRatio
|
|
||||||
..[5] = window.devicePixelRatio
|
|
||||||
..[10] = 1.0
|
|
||||||
..[15] = 1.0;
|
|
||||||
|
|
||||||
var builder = new SceneBuilder()
|
|
||||||
..pushTransform(deviceTransform)
|
|
||||||
..addPicture(Offset.zero, recorder.endRecording())
|
|
||||||
..pop();
|
|
||||||
|
|
||||||
window.render(builder.build());
|
|
||||||
window.scheduleFrame();
|
|
||||||
};
|
|
||||||
|
|
||||||
window.scheduleFrame();
|
window.scheduleFrame();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Stops the game by replacing the injected render callbacks with
|
||||||
|
/// the original Flutter callbacks.
|
||||||
|
void stop() => _stop = true;
|
||||||
|
|
||||||
|
void _onBeginFrame(Duration now) {
|
||||||
|
var recorder = new PictureRecorder();
|
||||||
|
var canvas = new Canvas(
|
||||||
|
recorder,
|
||||||
|
new Rect.fromLTWH(0.0, 0.0, window.physicalSize.width, window.physicalSize.height),
|
||||||
|
);
|
||||||
|
|
||||||
|
Duration delta = now - _previous;
|
||||||
|
if (_previous == Duration.ZERO) {
|
||||||
|
delta = Duration.ZERO;
|
||||||
|
}
|
||||||
|
_previous = now;
|
||||||
|
|
||||||
|
var t = delta.inMicroseconds / Duration.MICROSECONDS_PER_SECOND;
|
||||||
|
|
||||||
|
update(t);
|
||||||
|
render(canvas);
|
||||||
|
|
||||||
|
var deviceTransform = new Float64List(16)
|
||||||
|
..[0] = 1.0 // window.devicePixelRatio
|
||||||
|
..[5] = 1.0 // window.devicePixelRatio
|
||||||
|
..[10] = 1.0
|
||||||
|
..[15] = 1.0;
|
||||||
|
|
||||||
|
var builder = new SceneBuilder()
|
||||||
|
..pushTransform(deviceTransform)
|
||||||
|
..addPicture(Offset.zero, recorder.endRecording())
|
||||||
|
..pop();
|
||||||
|
|
||||||
|
window.render(builder.build());
|
||||||
|
}
|
||||||
|
|
||||||
|
void _onDrawFrame() {
|
||||||
|
if (_stop) {
|
||||||
|
window.onBeginFrame = _previousOnBeginFrame;
|
||||||
|
window.onDrawFrame = _previousOnDrawFrame;
|
||||||
|
// todo if using touch input to "stop", then an exception is thrown
|
||||||
|
// todo where the input is "state.down" and never goes to "state.up"
|
||||||
|
window.onPointerDataPacket = _previousOnPointerDataPacket;
|
||||||
|
_stop = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
window.scheduleFrame();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class BaseGame extends Game {
|
||||||
|
|
||||||
|
List<Component> components = new List();
|
||||||
|
|
||||||
|
@override
|
||||||
|
void render(Canvas canvas) {
|
||||||
|
canvas.save();
|
||||||
|
components.forEach((comp) {
|
||||||
|
comp.render(canvas);
|
||||||
|
canvas.restore();
|
||||||
|
canvas.save();
|
||||||
|
});
|
||||||
|
canvas.restore();
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
void update(double t) {
|
||||||
|
components.forEach((c) => c.update(t));
|
||||||
|
components.removeWhere((c) => c.destroy());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
abstract class BaseGameWidget extends GameWidget {
|
||||||
|
|
||||||
|
final List<Component> components = new List();
|
||||||
|
|
||||||
|
@override
|
||||||
|
bool destroy() {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
bool loaded() {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
void render(Canvas canvas) {
|
||||||
|
canvas.save();
|
||||||
|
components.forEach((comp) {
|
||||||
|
comp.render(canvas);
|
||||||
|
canvas.restore();
|
||||||
|
canvas.save();
|
||||||
|
});
|
||||||
|
canvas.restore();
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
void update(double t) {
|
||||||
|
components.forEach((c) => c.update(t));
|
||||||
|
components.removeWhere((c) => c.destroy());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
abstract class GameWidget extends StatelessWidget implements Component {
|
abstract class GameWidget extends StatelessWidget implements Component {
|
||||||
@ -67,13 +163,12 @@ abstract class GameWidget extends StatelessWidget implements Component {
|
|||||||
}
|
}
|
||||||
|
|
||||||
class GameRenderObjectWidget extends SingleChildRenderObjectWidget {
|
class GameRenderObjectWidget extends SingleChildRenderObjectWidget {
|
||||||
Component component;
|
final Component component;
|
||||||
|
|
||||||
GameRenderObjectWidget(this.component);
|
GameRenderObjectWidget(this.component);
|
||||||
|
|
||||||
@override
|
@override
|
||||||
RenderObject createRenderObject(BuildContext context) =>
|
RenderObject createRenderObject(BuildContext ctx) => new GameRenderBox(ctx, this.component);
|
||||||
new GameRenderBox(context, this.component);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
class GameRenderBox extends RenderBox {
|
class GameRenderBox extends RenderBox {
|
||||||
|
|||||||
Reference in New Issue
Block a user