前语

我们好,我是一名it小菜鸟,寻思着业余时间写写笔记,便利以后的翻看,并养成一个好的习气与君共勉


场景阐明

本文使用Flame引擎来简略的完成一个下雪的小场景。

  • 游戏组件的挂载
  • 简易雪花的制作
  • 浪漫的雪花场景

【用flutter做点啥捏】(一)飘雪

正文

1.增加项目依赖

首要在pubspec.yaml中引入flame包。

dependencies:
  flutter:
    sdk: flutter
  cupertino_icons: ^1.0.2
  flame: ^1.8.1
2.代码结构

目前lib的代码结构如下:

├── lib
│ ├── component
│ │ ├──snow_background.dart //布景组件
│   │   └──snow_sprite.dart  //雪花组件
│   │
│ ├── game
│   │   └──game_snow.dart //游戏主进口
│   │
│ └── main.dart //程序主进口
3.代码进口

main.dart中的runApp办法中传入GameWidget组件,其中game入参对象是自定义的GameSnow,承继自FlameGame,并重写onLoad办法,增加”布景组件”和多个自定义的”雪花组件”SnowSprite

---->[main.dart]<----
void main() {
  runApp(GameWidget(game: GameSnow()));
}
---->[game_snow.dart]<----
class GameSnow extends FlameGame {
  @override
  FutureOr<void> onLoad() async {
    await super.onLoad();
    //第一层:布景组件挂载
    add(SnowBackGround());
    //最外层:雪花组件挂载
    List.generate(500, (index) {
      add(SnowSprite());
    });
  }
}
4.组件解说

在flutter 中一切皆组件,那flame结构也应如此。今天介绍的主角就是这个组件CustomPainterComponent,简略瞄一下源码,对制作熟悉的小伙伴是不是嘴角漏出了浅浅的浅笑呢,仅有一个归于自己的入参painter他的类型是CustomPainter

【用flutter做点啥捏】(一)飘雪

接下来咱们介绍下雪花组件,根据上图可知,其父类是CustomPainterComponent,职责:”用画笔来制作自己图画的”,再往上走就看到了CustomPainterComponent的父类:PositionComponent,简略瞄一眼,它具有巨细位置等特点。ok开搞。

【用flutter做点啥捏】(一)飘雪

首要混入HasGameRef这个mixin,混入他的目的就是为了得到游戏窗口的相关信息,比如游戏窗口的尺度信息。 紧接着重写onLoad办法,对雪花的巨细位置形状、以及下落速度的定义。

  • 巨细:取了巨细5~15的随机数(宽高1:1,雪花假如都相同大,那就不美观了)。
  • 位置:在整个游戏窗口取随机数(不然刚开始的时分,有的地方齐刷刷的空白不太美观)。
  • 速度:取1~2的随机数(参差错落、我们可根据自己的喜好调节)。
  • 形状:一种是制作一个小白点、一种是制作简易的小雪花。
class SnowSprite extends CustomPainterComponent with HasGameRef<GameSnow> {
  double speed = 1;
  @override
  FutureOr<void> onLoad() async {
    super.onLoad();
    size = getRandomSize();
    position = getRandomPosition();
    painter = SnowPainter();
    speed = Random().nextDouble() * 1 + 1;
  }
  ///屏幕内的随机数
  Vector2 getRandomPosition() {
    double x = Random().nextDouble() * gameRef.size.x;
    double y = Random().nextDouble() * gameRef.size.y;
    return Vector2(x, y);
  }
  ///5~15的巨细
  Vector2 getRandomSize() {
    double size = Random().nextDouble() * 10+5;
    return Vector2(size, size);
  }
  @override
  void update(double dt) {
    super.update(dt);
    //下落的过程假如超出屏幕的话,将y坐标从头开始,巨细和横向坐标从头随机
    if (position.y > gameRef.size.y) {
      position.y = 0;
      position.x = Random().nextDouble() * gameRef.size.x;
      size = getRandomSize();
    }
    //update 毎帧都会被执行到,所以下降间隔就等于单位时间内的速度
    position.y += speed;
  }
}

其实形状这块能够简略也能够复杂,看个人寻求,我呢就取简略的了。假如有人觉得自己的制作能力突出,能够制作一些炫酷的雪花来;假如有人觉得自己一点制作都不会,不要忧虑后续文章还会涉及到其他得精灵组件,不用制作也能到达很好的效果。本文讲到了两个简略完成方案:

  • 用小圆点”顶替”雪花: 在_drawSnowCircle办法中,制作一个白色的填充圆即可。

  • 制作简易雪花: 在_drawSnowCustom办法中,首要将画布移到了中心位置,然后制作一条线,线的宽度为雪花的宽度,在间隔中心点左右1/6和2/6位置处分别制作一定长度的竖线,然后经过4次旋转,一个小雪花就被制作出来了。

class SnowPainter extends CustomPainter {
  late final Paint snowPaint;
  SnowPainter() {
    snowPaint = Paint();
    snowPaint.strokeWidth = Random().nextDouble() + 0.5;
    snowPaint.color = Colors.white;
  }
  @override
  void paint(Canvas canvas, Size size) {
    _drawSnowCustom(canvas, size);
    // _drawSnowCircle(canvas, size);
  }
  @override
  bool shouldRepaint(covariant SnowPainter oldDelegate) {
    return true;
  }
  void _drawSnowCircle(Canvas canvas, Size size) {
    canvas.save();
    double r = size.width / 2;
    canvas.translate(r, r);
    canvas.drawCircle(Offset.zero, r, snowPaint);
    canvas.restore();
  }
  void _drawSnowCustom(Canvas canvas, Size size) {
    canvas.save();
    //半径
    double r = size.width / 2;
    //内圈点位
    double dxInner = size.width / 6;
    double dyInner = dxInner / 3;
    //外圈点位
    double dxOuter = dxInner * 2;
    double dyOuter = dxOuter / 3;
    //圆心
    canvas.translate(r, r);
    for (int i = 0; i < 4; i++) {
      //旋转弧度
      canvas.rotate(pi / 180 * 45 * i);
      canvas.drawLine(Offset(-r, 0), Offset(r, 0), snowPaint);
      //内线
      canvas.drawLine(Offset(-dxInner, -dyInner), Offset(-dxInner, dyInner), snowPaint);
      canvas.drawLine(Offset(dxInner, -dyInner), Offset(dxInner, dyInner), snowPaint);
      //外线
      canvas.drawLine(Offset(-dxOuter, -dyOuter), Offset(-dxOuter, dyOuter), snowPaint);
      canvas.drawLine(Offset(dxOuter, -dyOuter), Offset(dxOuter, dyOuter), snowPaint);
    }
    canvas.restore();
  }
}

同理贴一下布景(浅灰和蓝灰相间的图画)制作的代码SnowBackGround:

class SnowBackGround extends CustomPainterComponent with HasGameRef<GameSnow> {
  @override
  FutureOr<void> onLoad() async {
    super.onLoad();
    size = gameRef.size;
    position = Vector2.zero();
    painter = SnowBackGroundPainter();
  }
  //游戏窗口发生变化的监听
  @override
  void onGameResize(Vector2 size) {
    this.size = size;
    super.onGameResize(size);
  }
}
class SnowBackGroundPainter extends CustomPainter {
  late final Paint snowBackgroundPaint;
  final double sizeUnit = 10;
  SnowBackGroundPainter() {
    snowBackgroundPaint = Paint();
    snowBackgroundPaint.strokeWidth = Random().nextDouble() + 0.5;
    snowBackgroundPaint.color = Colors.white;
  }
  @override
  void paint(Canvas canvas, Size size) {
    _drawSnowBackground(canvas, size);
  }
  @override
  bool shouldRepaint(covariant SnowBackGroundPainter oldDelegate) {
    return true;
  }
  ///制作布景
  void _drawSnowBackground(Canvas canvas, Size size) {
    canvas.save();
    //横向格子数量
    int hNum = (size.width / sizeUnit).ceil();
    //纵向格子数量
    int vNum = (size.height / sizeUnit).ceil();
    //制作
    for (int v = 0; v < vNum; v++) {
      for (int h = 0; h < hNum; h++) {
        _chooseColor(h, v);
        canvas.drawRect(
          Offset(h * sizeUnit, v * sizeUnit) & Size.square(sizeUnit),
          snowBackgroundPaint,
        );
      }
    }
    canvas.restore();
  }
  ///h 横向第几格
  ///v 纵向第几格
  void _chooseColor(int h, int v) {
    //处理第一列
    if (v % 2 == 0 && h == 0) {
      snowBackgroundPaint.color = Colors.blueGrey;
      return;
    } else if (h == 0) {
      snowBackgroundPaint.color = Colors.grey.shade500;
      return;
    }
    //剩余反向取色
    if (snowBackgroundPaint.color.value == Colors.blueGrey.value) {
      snowBackgroundPaint.color = Colors.grey.shade500;
    } else {
      snowBackgroundPaint.color = Colors.blueGrey;
    }
  }
}

完 ~