flutter游戏开发flame(一)游戏创建

276 阅读2分钟

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我没声明,但是也能运行成功,奇了怪了。

项目结构

image.png

assets

  1. images:图片资源。
  2. tiles:tmx资源,从官网文档看现在只支持tmx,其他的地图编辑器没看到。
  3. 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 { ... }

我为什么要这么干呢,原因有以下几点:

  1. 可以直接使用flutter系统的路由,也就是说能直接使用开发app的所有组件
  2. 使用简单,不像flame还需要创建Route对象等,很复杂
  3. 它这个是在游戏场景下做的路由,从层级结构上来讲,个人感觉不是很好。

缺点也是有的:本来我想引入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两个场景中加点东西,不考虑状态的话已经可以运行起来了,恭喜你的第一个游戏运行成功。