小編也是初學者,爲了瞭解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傳送門