說明
小編也是初學者,為了瞭解flutter動畫的使用與效果, 決定親自定手用flutte寫一款小遊戲出來. 並將過程中的跳過的坑記錄下來.
開發準備
具體參考flutter環境搭建, 筆者環境資訊
- Android Studio 3.2
- macOS 10.14
- flutter v1.1.10-pre.136
- dart 2.0
New Flutter Project
我們大概目錄為:
- assets
- images
- lib
- main.dart
- src
- enter.dart
main.dart是我們程式碼的主入口. lib.src用來存放我們整個遊戲的邏輯程式碼檔案. assets則是用來存放我們的圖片資原始檔.
專案入口 main.dart
在這個檔案中, 我們可以製作一個選單, 先保留著. 我們只留一下按鈕, 點選按鈕後. 將我們的遊戲介面, 入棧到Router中, 開始我們的遊戲.部份程式碼如下:
RaisedButton(
onPressed: () {
Navigator.push(context, MaterialPageRoute(builder: (_) {
return GameEnter();
}));
},
child: Text("開始遊戲")
)
複製程式碼
遊戲入口enter.dart
enter.dart是我們整個遊戲的主入口. 在這個入口中, 我們載入資源, 進行整體的繪圖操作. 我們在enter.dart中定義我們的主畫板, 關於CustomPaint的說明參考: 官方DOC, MainPainter 繼承自 CustomPainter, 按官方的說明我們繼承並實現他的二個方法 paint 和 shouldRepaint, 在當前狀態下. 整個介面是空白的, 什麼都沒有. 接下來我們定義我們的遊戲背景.
// 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 中繪製我們的背景動畫.
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入口繪製背景
接下來我們要將我們的遊戲背景真正的繪製在我們的手機上. 我們在 Enter 的初始化函式 initeState 中 初始化 Background 例項, 並進行資源初始化. 然後在 MainPainter 的繪圖介面上, 增加我們的繪圖邏輯
void paint(Canvas canvas, Size size) {
background.paint(canvas, size);
}
複製程式碼
執行效果如下
接下來我們需要將遊戲的背景圖繪製到背景中, 這裡我們呼叫的是
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在有幾個控制動畫的方法
- forward() 向前運動
- stop() 停止
- reverse() 向後運動 (這個概念, 我也沒懂. 暫時擱這)
我們去監聽 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傳送門