小编也是初学者,为了了解flutter动画的使用与效果, 决定亲自定手用flutte写一款小游戏出来. 并将过程当中的跳过的坑记录下来.html
具体参考flutter环境搭建, 笔者环境信息git
咱们大概目录为:github
main.dart是咱们代码的主入口. lib.src用来存放咱们整个游戏的逻辑代码文件. assets则是用来存放咱们的图片资源文件.canvas
在这个文件中, 咱们能够制做一个菜单, 先保留着. 咱们只留一下按钮, 点击按钮后. 将咱们的游戏界面, 入栈到Router中, 开始咱们的游戏.部份代码以下:bash
RaisedButton(
onPressed: () {
Navigator.push(context, MaterialPageRoute(builder: (_) {
return GameEnter();
}));
},
child: Text("开始游戏")
)
复制代码
enter.dart是咱们整个游戏的主入口. 在这个入口中, 咱们加载资源, 进行总体的绘图操做. 咱们在enter.dart中定义咱们的主画板, 关于CustomPaint的说明参考: 官方DOC, MainPainter 继承自 CustomPainter, 按官方的说明咱们继承并实现他的二个方法 paint 和 shouldRepaint, 在当前状态下. 整个界面是空白的, 什么都没有. 接下来咱们定义咱们的游戏背景.async
// CustomPaint.painter
class MainPainter extends CustomPainter {
@override
void paint(Canvas canvas, Size size) {
// TODO: implement paint
}
@override
bool shouldRepaint(CustomPainter oldDelegate) {
// TODO: implement shouldRepaint
return oldDelegate != this;
}
}
// GameEnter.build
Widget build(BuildContext context) {
return CustomPaint(
painter: MainPainter(),
);
}
复制代码
咱们在 src 下, 新建一个叫作bg.dart的文件, 并新建一个 Background的类, init函数, 是用来在Enter 中加载咱们游戏所须要的资源文件, paint 函数是用来在 MainPainter 中绘制咱们的背景动画.ide
class Background {
// 初始背景的偏移量
double offsetY = -100.0;
// 屏幕的宽度
double screenWidth;
// 屏幕的高度
double screenHeight;
// 画布滚动的速度
double speed = 10;
// 加载的背景图片
ui.Image image;
// 二张背景图的纵坐标点
double y1 = 100.0;
double y2 = 0.0;
// 构造函数
Background();
// 初始化, 各类资源
Future<VoidCallback> init() async {
return null;
}
// 绘图函数
paint(Canvas canvas, Size size) async {
Rect screenWrap = Offset(0.0, 0.0) & Size(screenWidth, screenHeight);
Paint screenWrapPainter = new Paint();
screenWrapPainter.color = Colors.red;
screenWrapPainter.style = PaintingStyle.fill;
canvas.drawRect(screenWrap, screenWrapPainter);
}
}
复制代码
接下来咱们要将咱们的游戏背景真正的绘制在咱们的手机上. 咱们在 Enter 的初始化函数 initeState 中 初始化 Background 实例, 并进行资源初始化. 而后在 MainPainter 的绘图接口上, 增长咱们的绘图逻辑函数
void paint(Canvas canvas, Size size) {
background.paint(canvas, size);
}
复制代码
运行效果以下 动画
接下来咱们须要将游戏的背景图绘制到背景中, 这里咱们调用的是ui
Canvas.drawImage(Image image, Offset offset, Painter paint) API
在 Background.paint 函数中咱们增长如下代码, 而后执行
Paint paint = new Paint();
canvas.drawImage(image, Offset(0, 0), paint);
复制代码
效果以下:
在本次探动画探究中, 我使用 AnimationController 与 CurvedAnimation 完成咱们的效果. 有关这二个类的具体文档参考AnimationController 与 CurvedAnimation. 咱们先在 Enter 的 initeState 中声明二个实例,
animation = CurvedAnimation(
parent: controller,
curve: Curves.linear,
);
animation.addListener(() {
setState(() {});
});
animation.addStatusListener((status) {
if (status == AnimationStatus.completed) {
controller.repeat();
}
});
复制代码
controller在有几个控制动画的方法
咱们去监听 animation 每次动画值的改变, 增长监听函数, 经过 setState 去触发当前视图的刷新.
咱们去监听 animation 动画状态, 判断是否动画结束,从而调用 repeat 方法, 使动画一直循环下去. 经过以上代码. 运行后发现, 每一帧都会触发 Background的重绘, 经过这点咱们每次更改背景图起绘点的坐标, 就能够达到动画的效果.
paint(Canvas canvas, Size size) async {
...
y1 += 10;
}
复制代码
在正常的2D飞机类游戏中, 游戏的背景是循环滚动的 ,常见的处理方法是, 二张背景图,头尾相连循环绘制, 当其中某个背景图, 滚出屏幕视野, 将其从新定位到上一张背景图的正上方, 来回往复, 从而达到背景循环滚动的效果. 在这里咱们为二张图景图, 起绘点坐标增长如下的逻辑,
y1 = y1 + 1 * speed;
y2 = y2 + 1 * speed;
if (y2 > image.height) {
y2 = y1 - image.height;
}
if (y1 > image.height) {
y1 = y2 - image.height;
}
复制代码
在此次项目中, 因为我找到的背景图比较小, 没有办法撑满整个屏幕, 因此我在绘制的时候, 将Canvas进行了缩放操做.
canvas.scale(1, screenHeight / image.height);
复制代码
最后让咱们看一下效果:
第一部份, 大工告成. 在接下来几天. 我会把其余的元素的相关逻辑加上. git传送门