Game Loop 遊戲迴圈
遊戲迴圈模組是對遊戲迴圈概念的簡單抽象。基本上大部分的遊戲都建立在兩個方法之上:
- render渲染方法,拿著canvas準備繪製遊戲的當前狀態
- update更新方法,在1s接收自從上次更新的delta時間,並且允許你移動到下一狀態。
Game類可以是子類,並且可以為你提供實現的方法。作為回撥,它可以為你提供widget屬性,它返回遊戲的widget,可以在你的app中被渲染。
你可以在你的runApp中直接渲染它,或者你有一個更大的專案結構,在你的遊戲中擁有路由、其他頁面和選單。
作為開始,只需要直接新增你的game widget到runApp即可,如下所示:
main(){
Game game = MyGameImpl();
runApp(game.widget);
}
複製程式碼
你應該可能使用更加功能的BaseGame類,來替代使用低階別的Game類。
BaseGame基於Game實現了Component;基本上它擁有Component的List,並且在合適的時機重新傳遞給update和render方法。你仍然可以繼承這些方法以便新增自定義的行為,同時你也可以獲得免費的其他屬性,比如重傳resize方法(每次螢幕重新resized的資訊會被傳遞給所有components的resize方法),並且也是一個基礎的camera屬性。BaseGame.camera控制的是螢幕左上角的座標系統(預設是[0,0],和普通的Canvas一樣).
一個非常簡單的BaseGame實現例子如下所示:
class MyCrate extends SpriteComponent{
MyCrate() : super.fromSprite(16.0,16.0,new Sprite('crate.png'));
@override
void resize(Size size){
this.x = (size.width - this.width)/2;
this.y = (size.height - this.height)/2;
}
}
class MyGame extends BaseGame{
MyGame() {
add(new MyCrate());
}
}
複製程式碼
Flutter Widgets and Game instances Flutter元件和遊戲例項
因為Flame game本身是一個widget,它非常容易將Flutter widgets 和Flame Game組合起來。為了使用更簡單,Flame提供了mixin,叫做HasWidgetsOverlay,允許Flutter widget可以顯示在你的game 例項之上,這樣使得比如暫停選單,或者檢視倉庫的螢幕非常容易建立。
為了使用這個mixin,只需要簡單的將HasWidgetsOverlay mixin新增到你的game類,通過這樣,你可以擁有兩個可用方法addWidgetOverlay和removeWidgetOverlay,正如名稱所示,他們勇於新增和移除在你的game之上的 overlay widgets(覆蓋的小元件),他們的用法如下:
addWidgetOverlay(
"PauseMenu", // 你的overlay id
Center(child:
Container(
width: 100,
height:100,
color: const Color(0xFFFF0000),
child: const Center(child: const Text("Paused")),
),
);
removeWidgetOverlay("PauseMenu"); // 通過overlay id去移除overlay
複製程式碼
在底層,Flame使用Stack widget來展示overlay,所以需要注意的是overlay新增的順序有關係。最後一個新增的overlay,會展示在之前新增的overlay之上。
現在你可以檢視這個屬性的例子。具體程式碼如下: main.dart
import 'package:flutter/material.dart';
import './example_game.dart';
void main(){
runApp(ExampleGame().widget);
}
複製程式碼
example_game.dart
import 'package:flame/game.dart';
import 'package:flame/gestures.dart';
import 'package:flame:/palette.dart';
import 'package:flutter/material.dart';
class ExampleGame extends Game with HasWidgetsOverlay, TapDetector {
bool isPaused = false;
@override
void update(double dt){}
@override
void render(Canvas canvas){
canvas.drawRect(const Rect.fromLTWH(100, 100, 100, 100),
Paint()..color = BasicPalette.white.color);
}
@override
void onTap(){
if(isPaused){
remvoeWidgetOverlay('PauseMenu');
isPaused = false;
}else{
addWidgetOverlay(
'PauseMenu',
Center(
child: Container(
width: 100,
height: 100,
color: const Color(0xFFFF0000),
child: const Center(child: const Text('Paused')),
),
));
isPaused = true;
}
}
}
複製程式碼
main_dynamic_game.dart:
import 'package:flutter/material.dart';
import './example_game.dart';
void main(){
runAPp(MyApp());
}
class MyApp extends StatelessWidget{
@override
Widget build(BuildContext context){
return MaterialApp(
home: MyHomePage(),
);
}
}
class MyHomePage extends StatefulWidget{
MyHomePage({Key key}) : super(key: key);
@override
_MyHomePageState createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage>{
ExampleGame _myGame;
@override
Widget build(BuildContext context){
return Scaffold(
appBar: AppBar(
title: const Text('Testing addingOverlay'),
),
body: _myGame == null ? const Text('Wait') :_myGame.widget,
floatingActionButton: FloatingActionButton(
onPressed: ()=> newGame(),
child: Icon(Icons.add),
),
);
}
void newGame(){
print('New game created');
_myGame = ExampleGame();
setState((){});
}
}
複製程式碼
BaseGame的除錯模式
Flame的BaseGame類提供了一個方法叫做debugMode,預設返回false。但是在game的component中,它可以被重寫以用於除錯。請注意這個方法返回的狀態,只有在他們被新增到game時,才被傳遞到component。所以你如果在執行時改變debugMode,它可能不會影響已經新增的components。
檢視關於Flame的更多debugMode,請檢視Debug Docs.