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

前言

大家好,我是一名it小菜鸟,寻思着业余时间写写笔记,方便以后的翻看,并养成一个好的习惯👉🏻与君共勉


场景说明

本文使用 Flame引擎来简单的实现一个下雪的小场景。

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

正文

1.添加项目依赖

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

yaml 复制代码
dependencies:
  flutter:
    sdk: flutter

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

目前lib 的代码结构如下:

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

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

dart 复制代码
---->[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

接下来咱们介绍下雪花组件,根据上图可知,其父类是CustomPainterComponent,职责:"用画笔来绘制自己图案的",再往上走就看到了CustomPainterComponent的父类:PositionComponent,简单瞄一眼,它具有大小位置等属性。ok开搞。

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

  • 大小:取了大小5~15的随机数(宽高1:1,雪花如果都一样大,那就不好看了)。
  • 位置:在整个游戏窗口取随机数(不然刚开始的时候,有的地方齐刷刷的空白不太好看)。
  • 速度:取1~2的随机数(参差错落、大家可根据自己的喜好调节)。
  • 形状:一种是绘制一个小白点、一种是绘制简易的小雪花。
dart 复制代码
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次旋转,一个小雪花就被绘制出来了。

dart 复制代码
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:

dart 复制代码
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;
    }
  }
}

完 ~

相关推荐
JarvanMo9 小时前
关于Flutter架构的小小探讨
前端·flutter
顾林海9 小时前
Flutter 图标和按钮组件
android·开发语言·前端·flutter·面试
yzwdzkn10 小时前
解决Flutter 2.10.5在升级Xcode 16后的各种报错
flutter·macos·xcode
亚洲小炫风13 小时前
flutter json解析增强
flutter·json·json兼容格式
SY.ZHOU13 小时前
Flutter 与原生通信
android·flutter·ios
恋猫de小郭16 小时前
IntelliJ IDEA 2025.1 发布 ,默认 K2 模式 | Android Studio 也将跟进
android·前端·flutter
梦想不只是梦与想17 小时前
鸿蒙系统开发状态更新字段区别对比
android·java·flutter·web·鸿蒙
RichardLai8817 小时前
[Flutter学习之Dart基础] - 集合(List, Set,Map)
android·flutter
bst@微胖子17 小时前
Flutter项目之设置页
android·javascript·flutter
亚洲小炫风18 小时前
flutter 桌面应用之窗口自定义
flutter·桌面端程序