flutter的app教程很多,但是使用flutter开发游戏的很少,在写这个教程之前其实还挺纠结的,第一、自己也是边查资料边学习的,会有很多盲点,第二、网上其实还是有几篇flame的,只是讲的都是知识点,项目方面的也比较少。然后很多功能实现其实不只一种,我也就只做一种我认为更适合我的。
依赖版本
sdk: ">=3.0.0"
flutter: ">=3.0.0"
flame: 1.8.2
flame_forge2d:
flame_tiled:
flame_bloc:
dio: ^5.3.2
这里贴出flame版本,不同版本代码是有很多不同的,不得不说flame更新还是很频繁的。然后记住app开发中万物是widget,而flame中万物是component
flutter_bloc我没声明,但是也能运行成功,奇了怪了。
项目结构
assets
- images:图片资源。
- tiles:tmx资源,从官网文档看现在只支持tmx,其他的地图编辑器没看到。
- audio:音频资源,我没用到
在pubspec.yaml注册资源
assets:
- assets/images/
- assets/tiles/
我试过直接 - assets/
注册所有,不行,应该是不会递归查找。
main.dart
//全局状态
GlobalCubit globalCubit = GlobalCubit();
void main() {
WidgetsFlutterBinding.ensureInitialized(); //不加这个强制横/竖屏会报错
SystemChrome.setPreferredOrientations([DeviceOrientation.landscapeLeft, DeviceOrientation.landscapeRight]).then(
(value) {
var app = MaterialApp(
routes: {
"login": (context) => const LoginWidget(),
"game": (context) => game.GameWidget(game: MyGame(context)),
},
initialRoute: "login",
);
runApp(app);
},
);
}
主程序入口,我是这样干的,flame官网示例是这样的
void main() {
runApp(
GameWidget(game: MyGame()),
);
}
class MyGame extends FlameGame {
late final RouterComponent router;
@override
void onLoad() {
add(
router = RouterComponent(
routes: {
'home': Route(HomePage.new),
'level-selector': Route(LevelSelectorPage.new),
'settings': Route(SettingsPage.new, transparent: true),
'pause': PauseRoute(),
'confirm-dialog': OverlayRoute.existing(),
},
initialRoute: 'home',
),
);
}
}
class PauseRoute extends Route { ... }
我为什么要这么干呢,原因有以下几点:
- 可以直接使用flutter系统的路由,也就是说能直接使用开发app的所有组件
- 使用简单,不像flame还需要创建Route对象等,很复杂
- 它这个是在游戏场景下做的路由,从层级结构上来讲,个人感觉不是很好。
缺点也是有的:本来我想引入bloc,但由于我是app(widget)和游戏(component)是混用的这两个体系的bloc实现不一样,所以flutter_bloc和flame_bloc两种写法都要会。
这里还有一个小问题,就是widget中跳转到Navigator.pushReplacementNamed(context, "game")
很容易,但是想要从游戏跳转到app是不是就有问题了,FlameGame是没有context对象的,只有game对象。
对,你没想错,"game": (context) => game.GameWidget(game: MyGame(context))
,在路由声明的时候我把context传了过去。 稍微封装了下:
/// 界面跳转 Navigator.pushNamed((game as ContextGame).context, 'login');
class ContextGame extends FlameGame {
BuildContext context;
ContextGame(this.context);
}
class MyGame extends ContextGame with HasDraggablesBridge {
MyGame(super.context);
...
}
scene
最后你随便在LoginWidget和MyGame两个场景中加点东西,不考虑状态的话已经可以运行起来了,恭喜你的第一个游戏运行成功。