Canvas 每帧调用一次颤振回调

Canvas 每帧调用一次颤振回调,canvas,flutter,custom-painting,Canvas,Flutter,Custom Painting,我正在学习定制绘画,我知道我可以告诉弗利特何时重新绘制画布。但是如果我正在开发一个游戏,我想如果我被调用每一帧,那么我可以更新游戏板上的任何状态,然后进行绘制,事情会变得更顺利。这可能吗 我在想我总是可以从shouldRepaint()返回true。然后,是否会对每个帧调用我的绘制方法(即每秒60次) 我也知道有一个飞镖计时器,但我不认为它会与帧同步,所以除非我想要一个更慢的更新,否则这似乎不是一个好办法 我知道有很多游戏引擎——我的目标是更好地理解flutter,所以我尝试自己做一些事情来更好

我正在学习定制绘画,我知道我可以告诉弗利特何时重新绘制画布。但是如果我正在开发一个游戏,我想如果我被调用每一帧,那么我可以更新游戏板上的任何状态,然后进行绘制,事情会变得更顺利。这可能吗

我在想我总是可以从shouldRepaint()返回true。然后,是否会对每个帧调用我的绘制方法(即每秒60次)

我也知道有一个飞镖计时器,但我不认为它会与帧同步,所以除非我想要一个更慢的更新,否则这似乎不是一个好办法


我知道有很多游戏引擎——我的目标是更好地理解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
顶级属性,以确保回调在每一帧都运行