[Notas de desarrollo para Aircraft Wars basado en Flutter&Flame] Panel de visualización y menú de reinicio

prefacio

El anterior estado global basadobloc en la gestión ha tomado forma. Este artículo usará estos datos para crear un panel de visualización para el juego basado en los controles de Flutter , así como la lógica del menú para reiniciar el juego .

El autor ha incluido esta serie de artículos en las siguientes columnas, y los estudiantes interesados ​​pueden leer:

Notas de desarrollo para Aircraft Wars basadas en Flutter&Flame

panel de visualización

¿Recuerdas el empaque de antes GameView? Aquí está toda la lógica, GameWidgety hay una capa encima Stackpara mostrar los diferentes paneles. Cabe señalar que GameViewes父WidgetMultiBlocProvider para que se 子Widgetpueda obtener 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()),
                    ],
                  ),
                )],
        ))
      ],
    );
  }
}

panel de salud

Hablar solo con el panel de salud es en realidad un modo regular blocAl BlocBuilderescuchar GameStatusState, el método se activará builderpara actualizar la interfaz de usuario después de la actualización. Este es Flutterel punto de conocimiento en el nivel original.

Cabe señalar que aquí se utiliza Offstagepara ocultar y mostrarGameStatus la vista, la condición es el estado de ejecución del juego mencionado en el artículo anterior .

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(),
        ),
      );
    });
  }
}

Efecto

Echemos un vistazo al efecto de la pantalla del panel.

Captura de pantalla_2022-07-15-15-23-31-22_13914082904e1b7ce2b619733dc8fcfe.jpg
  • Hay tres aviones pequeños en la esquina inferior derecha del panel de salud, el monitoreo es GameStatusState.lives.
  • Hay dos más, uno para puntales de misiles y otro para puntuar. Estos dos son similares a los anteriores y no se discutirán aquí.
    • 导弹道具面板,在左下角,顺带一提之前没有提及这个功能,它是在游戏中通过道具获得,和子弹补给同理。监听的是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, reanudandogameRef.resumeEngine() el tiempo de ejecución del juego y eliminando el menú en este momento. Por cierto, hay partes que deben eliminarse aquí , por ejemplo . Uno necesita ser agregado nuevamente porque el anterior ha sido eliminado .Component敌机Component、补给Component、子弹Component战机Component

Menú GameOver

GameWidgetProporcione una overlayBuilderMappropiedad, puede pasar una key-value. value es el buildermétodo de esta vista.

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

Cuando necesite mostrar y ocultar, como arriba, llame al add/removemétodo.

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

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

La clase de menú ResetMenu, dado que es Flutterla operación básica de la interfaz de usuario nativa, no se expandirá aquí. Solo mira el efecto

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

Al final

Este artículo documenta la visualización del panel y el menú de reinicio de Aircraft Wars. En este punto, todo el juego es bastante completo. Para conocer la lógica relacionada, consulte el ejemplo oficial de Flame: flame/packages/flame_bloc .

Supongo que te gusta

Origin juejin.im/post/7120516724151025672
Recomendado
Clasificación