前语
我们好,我是一名it
小菜鸟,寻思着业余时间写写笔记,便利以后的翻看,并养成一个好的习气与君共勉
。
场景阐明
本文使用Flame
引擎来简略的完成一个下雪的小场景。
- 游戏组件的挂载
- 简易雪花的制作
- 浪漫的雪花场景
正文
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
接下来咱们介绍下雪花组件
,根据上图可知,其父类是CustomPainterComponent
,职责:”用画笔来制作自己图画的”,再往上走就看到了CustomPainterComponent的父类:PositionComponent
,简略瞄一眼,它具有巨细
、位置
等特点。ok开搞。
首要混入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;
}
}
}
完 ~