Canvas 每帧调用一次颤振回调
我正在学习定制绘画,我知道我可以告诉弗利特何时重新绘制画布。但是如果我正在开发一个游戏,我想如果我被调用每一帧,那么我可以更新游戏板上的任何状态,然后进行绘制,事情会变得更顺利。这可能吗 我在想我总是可以从shouldRepaint()返回true。然后,是否会对每个帧调用我的绘制方法(即每秒60次) 我也知道有一个飞镖计时器,但我不认为它会与帧同步,所以除非我想要一个更慢的更新,否则这似乎不是一个好办法Canvas 每帧调用一次颤振回调,canvas,flutter,custom-painting,Canvas,Flutter,Custom Painting,我正在学习定制绘画,我知道我可以告诉弗利特何时重新绘制画布。但是如果我正在开发一个游戏,我想如果我被调用每一帧,那么我可以更新游戏板上的任何状态,然后进行绘制,事情会变得更顺利。这可能吗 我在想我总是可以从shouldRepaint()返回true。然后,是否会对每个帧调用我的绘制方法(即每秒60次) 我也知道有一个飞镖计时器,但我不认为它会与帧同步,所以除非我想要一个更慢的更新,否则这似乎不是一个好办法 我知道有很多游戏引擎——我的目标是更好地理解flutter,所以我尝试自己做一些事情来更好
我知道有很多游戏引擎——我的目标是更好地理解flutter,所以我尝试自己做一些事情来更好地理解它。如果你正在编写一个游戏,我强烈建议你使用一个框架,因为它可以处理像这样的各种事情 然而,我很感激你试图理解这将如何实现——这是一个令人钦佩的目标,所以我将尽我所能解释 基本上,您需要连接到Flatter的渲染并更改其执行方式,因为Flatter优化了许多不必为游戏优化的内容 从
shouldRepaint
返回true是一个很好的第一步,但实际上您还需要做更多的工作。在普通应用程序中,CustomPainter对象应该是一个常量对象,每次发生更改时都会创建该对象。通常,您会使用AnimationBuilder之类的工具,并使用每次更新的值创建一个新的custompainter。然后在custompainter的shouldRepaint中,您可以将旧custompainter的值与新值进行比较,并仅在它们不同时重新绘制
对于一个非常简单的游戏,您可能可以使用一些动画,以及颤振的拖放和按钮等。但是,任何复杂的事情都可以在更定制、更适合游戏的环境中完成
为了理解如何做到这一点,我建议阅读Flame的源代码。Flame是一个轻量级的游戏引擎,在Flitter上运行。它实际上是从《颤栗》中抽象出绘画,取而代之的是一堆游戏“组件”,每个组件都有自己的绘画。看看火焰的逻辑所在
继承RenderBox的GameRenderBox中的一些相关代码:
void _scheduleTick() {
_frameCallbackId = SchedulerBinding.instance.scheduleFrameCallback(_tick);
}
void _unscheduleTick() {
SchedulerBinding.instance.cancelFrameCallbackWithId(_frameCallbackId);
}
void _tick(Duration timestamp) {
if (!attached) {
return;
}
_scheduleTick();
_update(timestamp);
markNeedsPaint();
}
_在每一帧中都会调用tick,然后在下一帧中对其自身进行重新调度
将覆盖RenderBox的渲染函数:
@override
void paint(PaintingContext context, Offset offset) {
context.canvas.save();
context.canvas.translate(
game.builder.offset.dx + offset.dx, game.builder.offset.dy + offset.dy);
game.render(context.canvas);
context.canvas.restore();
}
这将调用游戏的渲染函数,该函数依次通知其每个组件进行渲染:
void render(Canvas canvas) {
canvas.save();
components.forEach((comp) => renderComponent(canvas, comp));
canvas.restore();
}
这更符合游戏通常的运作方式。如果你在编写游戏,我强烈建议你使用一个框架,因为它可以处理所有类似的事情 然而,我很感激你试图理解这将如何实现——这是一个令人钦佩的目标,所以我将尽我所能解释 基本上,您需要连接到Flatter的渲染并更改其执行方式,因为Flatter优化了许多不必为游戏优化的内容 从
shouldRepaint
返回true是一个很好的第一步,但实际上您还需要做更多的工作。在普通应用程序中,CustomPainter对象应该是一个常量对象,每次发生更改时都会创建该对象。通常,您会使用AnimationBuilder之类的工具,并使用每次更新的值创建一个新的custompainter。然后在custompainter的shouldRepaint中,您可以将旧custompainter的值与新值进行比较,并仅在它们不同时重新绘制
对于一个非常简单的游戏,您可能可以使用一些动画,以及颤振的拖放和按钮等。但是,任何复杂的事情都可以在更定制、更适合游戏的环境中完成
为了理解如何做到这一点,我建议阅读Flame的源代码。Flame是一个轻量级的游戏引擎,在Flitter上运行。它实际上是从《颤栗》中抽象出绘画,取而代之的是一堆游戏“组件”,每个组件都有自己的绘画。看看火焰的逻辑所在
继承RenderBox的GameRenderBox中的一些相关代码:
void _scheduleTick() {
_frameCallbackId = SchedulerBinding.instance.scheduleFrameCallback(_tick);
}
void _unscheduleTick() {
SchedulerBinding.instance.cancelFrameCallbackWithId(_frameCallbackId);
}
void _tick(Duration timestamp) {
if (!attached) {
return;
}
_scheduleTick();
_update(timestamp);
markNeedsPaint();
}
_在每一帧中都会调用tick,然后在下一帧中对其自身进行重新调度
将覆盖RenderBox的渲染函数:
@override
void paint(PaintingContext context, Offset offset) {
context.canvas.save();
context.canvas.translate(
game.builder.offset.dx + offset.dx, game.builder.offset.dy + offset.dy);
game.render(context.canvas);
context.canvas.restore();
}
这将调用游戏的渲染函数,该函数依次通知其每个组件进行渲染:
void render(Canvas canvas) {
canvas.save();
components.forEach((comp) => renderComponent(canvas, comp));
canvas.restore();
}
这与游戏通常的工作方式更为一致。有关调度各种类型的回调,请参见检查
debugPrintBeginFrameBanner
&debugPrintEndFrameBanner
顶级属性,以确保回调在每个帧运行有关调度各种类型的回调,请参见检查debugPrintBeginFrameBanner
&debugPrintEndFrameBanner
顶级属性,以确保回调在每一帧都运行