flutter游戏开发flame(二)状态、登录、主场景

接着教程一,不讲废话,开始创建游戏主体功能,本来想分功能来讲像手柄什么的,想想还是按项目模块来讲,按顺序用到了什么就讲什么:

bloc状态

在main中声明了一个全局状态GlobalCubit globalCubit = GlobalCubit();,让我们看下这个状态应该怎么写:

  1. global_cubit.dart
js 复制代码
class GlobalCubit extends Cubit<GlobalState> {
  GlobalCubit() : super(GlobalInitial(0));

  increment() {
    emit(GlobalInitial(state.count + 1));
  }
}

这里要注意的是emit传一个新的state,直接改state上的状态是不生效的。

  1. global_state.dart
js 复制代码
@immutable
abstract class GlobalState {
  int count = 0;
}

class GlobalInitial extends GlobalState {
  GlobalInitial(int count) {
    this.count = count;
  }
}

这里只是意思下只定义了一个count,登录信息等等都可以放在这。

登录

先上代码:

js 复制代码
class LoginWidget extends StatefulWidget {
  const LoginWidget({super.key});

  @override
  State<StatefulWidget> createState() {
    return LoginWidgetState();
  }
}

class LoginWidgetState extends State<LoginWidget> {
  @override
  Widget build(BuildContext context) {
    return BlocProvider.value(
      value: app.globalCubit,
      child: BlocBuilder<GlobalCubit, GlobalState>(
        builder: (context, state) => Material(
          child: Container(
            width: double.infinity,
            height: double.infinity,
            decoration: const BoxDecoration(
              image: DecorationImage(
                image: AssetImage("assets/images/login_bg.png"),
                fit: BoxFit.cover,
              ),
            ),
            child: Column(
              mainAxisAlignment: MainAxisAlignment.center,
              crossAxisAlignment: CrossAxisAlignment.center,
              children: [
                Text(
                  '游戏名${state.count}',
                  textAlign: TextAlign.center,
                  style: const TextStyle(color: Colors.red, fontSize: 24),
                ),
                Container(
                  margin: const EdgeInsets.only(top: 10),
                  width: 200,
                  color: Colors.white,
                  child: TextField(
                    maxLines: 1,
                    controller: TextEditingController(),
                    decoration: const InputDecoration(
                      hintText: '用户名',
                      contentPadding: EdgeInsets.fromLTRB(10, 0, 10, 0),
                    ),
                  ),
                ),
                Container(
                  width: 200,
                  margin: const EdgeInsets.only(top: 10, bottom: 10),
                  color: Colors.white,
                  child: TextField(
                    maxLines: 1,
                    controller: TextEditingController(),
                    decoration: const InputDecoration(
                      hintText: '密码',
                      contentPadding: EdgeInsets.fromLTRB(10, 0, 10, 0),
                    ),
                  ),
                ),
                ElevatedButton(
                  onPressed: () {
                    // context.read<GlobalCubit>().increment();
                    app.globalCubit.increment();
                    Navigator.pushReplacementNamed(context, "game");
                  },
                  style: const ButtonStyle(
                      // backgroundColor: MaterialStateProperty.all(Colors.red),
                      ),
                  child: const SizedBox(
                    width: 170,
                    child: Text(
                      '登 录',
                      textAlign: TextAlign.center,
                    ),
                  ),
                ),
              ],
            ),
          ),
        ),
      ),
    );
  }
}

内容很简单,就是一个背景、两个输入框、一个登录按钮。这里要格外注意的一点是:因为是全局的原因,必须是BlocProvider.value这种形式,如果是builder形式在跳转后,页面销毁,state会被close掉,后续使用就状态异常了。

主场景

老规矩,先上代码再讲:

js 复制代码
class MyGame extends ContextGame {
  late TiledComponent mapComponent;
  late final CameraComponent cameraComponent;

  MyGame(super.context);

  @override
  Future<void> onLoad() async {
    var joystick = MyJoystickComponent();
    add(joystick);
    var player = PlayerComponent(joystick);
    var skillJoystick = SkillJoystickComponent(
      size: Vector2(200, 200),
      position: Vector2(canvasSize.x - 200, canvasSize.y - 200),
      player: player,
    );
    add(skillJoystick);
    final world = CollisionDetectionWorld();
    final CameraComponent cameraComponent = CameraComponent(world: world);
    // final CameraComponent cameraComponent = CameraComponent.withFixedResolution(
    //   world: world,
    //   width: 16 * 280,
    //   height: 16 * 140,
    // );
    mapComponent = await TiledComponent.load('map1.tmx', Vector2(32.0, 32.0));
    world.add(mapComponent);
    world.add(player);
    // world.add(RectangleCollidable(canvasSize));
    cameraComponent.follow(player);
    //获取硬币的占位,然后动态添加
    final objectGroup = mapComponent.tileMap.getLayer<ObjectGroup>('AnimatedCoins');
    for (final object in objectGroup!.objects) {
      world.add(CoinComponent(position: Vector2(object.x, object.y)));
    }

    addAll([cameraComponent]);
    await add(FlameMultiBlocProvider(
      providers: [FlameBlocProvider<GlobalCubit, GlobalState>.value(value: app.globalCubit)],
      children: [world],
    ));
  }
}

这里我把经常用的的组件基本都上了:

  1. joystick 手柄:这个框架自带了一个基本可以直接使用。
  2. player 游戏主角
  3. skillJoystick:自定义的一个技能盘
  4. world:1.8.2以前没有world这个概念,其实就是在Game增加了个layer,相当于安卓activity中放了个fragment,切换场景功能的增强。
  5. cameraComponent:1.8.2以前Game自带了一个camera对象,1.8.2以后就开始废弃了。cameraComponent.follow(player);让视口跟着主角。
  6. mapComponent:背景地图。这里直接加载tmx资源。地图中有一些比如金币什么的需要动态创建,因为还有吃掉硬币的碰撞检查。
  7. FlameMultiBlocProvider:全局状态绑定

至此app运行后可能是这个样子的:

相关推荐
梧桐ty5 小时前
解耦之道:鸿蒙+Flutter混合工程的微内核架构与模块化实战
flutter·华为·harmonyos
名字被你们想完了8 小时前
Flutter 实现一个容器内部元素可平移、缩放和旋转等功能(六)
flutter
Melrose8 小时前
Flutter - 使用Jaspr来构建SEO友好网站
前端·flutter
wanhengidc10 小时前
巨椰 云手机 云游戏稳定运行
运维·服务器·arm开发·游戏·云计算
梧桐ty10 小时前
鸿蒙+Flutter混合工程化:构建、依赖管理与持续集成实战
flutter·华为·harmonyos
名字被你们想完了10 小时前
Flutter 实现一个容器内部元素可平移、缩放和旋转等功能(五)
flutter
无知的前端12 小时前
Flutter常见问题以及解决方案
前端·flutter·dart
无知的前端12 小时前
一文精通-Mixin特性
flutter·面试·dart
_大学牲13 小时前
Flutter 勇闯2D像素游戏之路(四):与哥布林战斗的滑步魔法师
flutter·游戏·游戏开发
ujainu小13 小时前
Flutter 视频播放全攻略:video_player 2.10.1 插件全平台集成与实战
flutter·音视频