[Development Notes for Aircraft Wars Based on Flutter&Flame] Display Panel and Restart Menu

foreword

The previous management- based blocglobal state has taken shape. This article will use this data to build a display panel for the game based on Flutter's controls , as well as the menu logic for restarting the game .

The author has included this series of articles in the following columns, and interested students are welcome to read:

Development Notes for Aircraft Wars Based on Flutter&Flame

panel display

Remember the packaging from earlier GameView? Here is all the logic, GameWidgetand there is a layer above Stackit for the display of different panels. It should be noted that it GameViewis父WidgetMultiBlocProvider so that it 子Widgetcan be obtained GameStatusBloc.

class GameView extends StatelessWidget {
  const GameView({Key? key}) : super(key: key);

  @override
  Widget build(BuildContext context) {
    return Stack(
      children: [
        Positioned.fill(
            child: GameWidget(
          game: SpaceGame(gameStatusBloc: context.read<GameStatusBloc>()),
          overlayBuilderMap: {
            'menu_reset': (_, game) {
              return ResetMenu(game: game as SpaceGame);
            }
          },
        )),
        SafeArea(
            child: Stack(children: [
                const Positioned(top: 4, right: 4, child: ScorePanel()),
                Positioned(
                  bottom: 4,
                  right: 4,
                  left: 4,
                  child: Row(
                    children: const [
                      Expanded(child: BombPanel()),
                      Expanded(child: LivePanel()),
                    ],
                  ),
                )],
        ))
      ],
    );
  }
}

health panel

Talking with the health panel alone is actually a regular blocmode. By BlocBuilderlistening GameStatusState, the method will be triggered builderto refresh the UI after the update. This is Flutterthe knowledge point at the original level.

It should be noted that here is used Offstageto hide and displayGameStatus the view, the condition is the running state of the game mentioned in the previous article .

class LivePanel extends StatelessWidget {
  const LivePanel({super.key});

  @override
  Widget build(BuildContext context) {
    return BlocBuilder<GameStatusBloc, GameStatusState>(
        builder: (context, state) {
      int live = state.lives;
      return Offstage(
        offstage: state.status != GameStatus.playing,
        child: Wrap(
          spacing: 2,
          runSpacing: 5,
          alignment: WrapAlignment.end,
          children: List.generate(
              live,
              (index) => Container(
                    constraints:
                        const BoxConstraints(maxWidth: 35, maxHeight: 35),
                    child: const Image(
                        image: AssetImage('assets/images/player/life.png')),
                  )).toList(),
        ),
      );
    });
  }
}

Effect

Let's take a look at the effect of the panel display

Screenshot_2022-07-15-15-23-31-22_13914082904e1b7ce2b619733dc8fcfe.jpg
  • There are three small planes in the lower right corner of the health panel, monitoring is GameStatusState.lives.
  • There are two more, one for missile props and one for scoring. These two are similar to the above , and will not be discussed here.
    • 导弹道具面板,在左下角,顺带一提之前没有提及这个功能,它是在游戏中通过道具获得,和子弹补给同理。监听的是GameStatusState.bombSupplyNumber
    • 计分面板,在右上角,击落一艘敌机Component就会有相应的计分。监听的是GameStatusState.score

GameOver与Replay

GameStatusController

战机Component生命值到0时,使GameStatusState.status == GameStatus.gameOver。来看一下GameStatusBloc的处理逻辑。

// class GameStatusBloc
on<PlayerLoss>((event, emit) {
  if (state.lives > 1) {
    emit(state.copyWith(lives: state.lives - 1));
  } else {
    emit(state.copyWith(lives: 0, status: GameStatus.gameOver));
  }
});

在上文中,我们Component树中塞多了一个叫GameStatusController的东西。答案在这里揭晓了,它是专门用于响应当游戏运行状态变化时界面变化的。

  • GameStatusState.status == GameStatus.gameOver时,需要先暂停游戏的运行时(还记得Flame有一个update方法回调吗?他是依赖运行时响应的)。然后展示GameOver菜单。
  • GameOver菜单会展示分数和一个Replay按钮。
  • Replay按钮点击后,会重新将GameStatusState.status == GameStatus.initial,此时恢复游戏的运行时,与之前的游戏开始逻辑形成闭环
class GameStatusController extends Component with HasGameRef<SpaceGame> {
  @override
  Future<void> onLoad() async {
    add(FlameBlocListener<GameStatusBloc, GameStatusState>(
        listenWhen: (pState, nState) {
      return pState.status != nState.status;
    }, onNewState: (state) {
      if (state.status == GameStatus.initial) {
        gameRef.resumeEngine();
        gameRef.overlays.remove('menu_reset');

        if (parent == null) return;
        parent!.removeAll(parent!.children.where((element) {
          return element is Enemy || element is Supply || element is Bullet;
        }));
        parent!.add(gameRef.player = Player(
            initPosition:
                Vector2((gameRef.size.x - 75) / 2, gameRef.size.y + 100),
            size: Vector2(75, 100)));
      } else if (state.status == GameStatus.gameOver) {
        Future.delayed(const Duration(milliseconds: 600)).then((value) {
          gameRef.pauseEngine();
          gameRef.overlays.add('menu_reset');
        });
      }
    }));
  }
}
  • 还是利用FlameBlocListener监听GameStatusState的变化。
  • GameStatus.gameOver时,通过gameRef.pauseEngine()暂停游戏的运行时。这里的gameRef.overlays.add('menu_reset')会在视图最上层添加一个菜单。下面会讲到。
  • GameStatus.initial, by resuminggameRef.resumeEngine() the game's runtime and removing the menu just now. By the way, there are parts to be removed here , eg . One needs to be added again because the previous one has been removed .Component敌机Component、补给Component、子弹Component战机Component

GameOver Menu

GameWidgetProvide a overlayBuilderMapproperty, you can pass one key-value. value is the buildermethod of this view.

GameWidget(
  game: SpaceGame(gameStatusBloc: context.read<GameStatusBloc>()),
  overlayBuilderMap: {
    'menu_reset': (_, game) {
      return ResetMenu(game: game as SpaceGame);
    }
  },
)

When you need to show and hide, just like above, call the add/removemethod.

// 显示
gameRef.overlays.add('menu_reset');

// 隐藏
gameRef.overlays.remove('menu_reset');

The menu class ResetMenu, since it is Flutterthe basic operation of the native UI, will not be expanded here. Just see the effect

Record_2022-07-15-16-20-36_13914082904e1b7ce2b619733dc8fcfe_.gif

at last

This article documents the panel display and restart menu for Aircraft Wars. At this point, the entire game is quite complete. For related logic, refer to the official example of Flame: flame/packages/flame_bloc .

Guess you like

Origin juejin.im/post/7120516724151025672