2

I am trying to make a flame game using material controls for game settings and control. It works fine with overlays, but I would like the overlay to slide on screen like drawer widget. I have made an overlay with widgets that animate, but I do not know how to play animation controller forward when layout is added to the game, and how to play it backwards before the layout is removed from game.

My idea was to reference the layout and call some method for animation from the class implementing the layout. However, I was not able to find references to overlays in FlameGame class. All I found is final Map<String, OverlayWidgetBuilder<T>>? overlayBuilderMap; in class GameWidget<T extends Game> extends StatefulWidget, but I don't know how to access it from a game component, which should open the menu on tap. The sprite component to open the menu is declared as

class MenuButtonComponent extends SpriteComponent with Tappable, HasGameRef<MainGame>

It shows the overlay with the following method

@override
bool onTapDown(TapDownInfo info) {
  gameRef.menuButtonClicked = true;
  if (gameRef.overlays.isActive('MainMenu')) {
    // This will be changed as another button (from overlay) will hide the overlay.
    gameRef.overlays.remove('MainMenu');
  }
  else {
    gameRef.overlays.add('MainMenu');
    // I should call method to animate from overlay widget
  }
  return super.onTapDown(info);
}

Can you give me some idea how to access overlay or how to achieve this.

The game is started from MainApp that extends StatelesWidget like this

Future<void> main() async {
  //initPackageInfo();
  //loadUIPreferences();

  WidgetsFlutterBinding.ensureInitialized();
  await Flame.device.fullScreen();
  await Flame.device.setPortrait();

  runApp(MainApp());
}

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

  final game = MainGame();

  @override
  Widget build(BuildContext context) {
    globals.darkNotifier = ValueNotifier<bool>(MediaQuery.platformBrightnessOf(context) == Brightness.dark);

    return ValueListenableBuilder<bool>(
      valueListenable: globals.darkNotifier,
      builder: (BuildContext context, bool isDark, Widget? child) {
        return MaterialApp(
          localizationsDelegates: const [
            S.delegate,
            GlobalMaterialLocalizations.delegate,
            GlobalWidgetsLocalizations.delegate,
            GlobalCupertinoLocalizations.delegate,
          ],
          supportedLocales: S.delegate.supportedLocales,
          theme: ThemeData(
            brightness: Brightness.light,
            primarySwatch: Colors.deepPurple,
          ),
          darkTheme: ThemeData(
            brightness: Brightness.dark,
            primarySwatch: Colors.deepPurple,
          ),
          themeMode: isDark ? ThemeMode.dark : ThemeMode.light,
          home: GameWidget(
            game: game,
            overlayBuilderMap: {
              'MainMenu': (context, MainGame game) => MainMenu(game: game),
            },
          ),
        );
      },
    );
  }
}
nobody
  • 64
  • 5
  • 15
  • Can you share some expected visuals? – Diwyansh Jan 05 '22 at 09:33
  • @Diwyansh I had two questions, because I split the problem into two. In the other question I have published a minimum working example, of what is currently working and explained what I want to achieve. Take a look here: https://stackoverflow.com/questions/70532686/flame-overlay-with-transparrent-elements – nobody Jan 05 '22 at 11:31

1 Answers1

0

The answer I found is published in Flame overlay with transparrent elements.

Here is the source:

import 'dart:async';
import 'package:flame/input.dart';
import 'package:flutter/material.dart';
import 'package:flame/flame.dart';
import 'package:flame/game.dart';

Future<void> main() async {
  WidgetsFlutterBinding.ensureInitialized();
  await Flame.device.fullScreen();
  await Flame.device.setPortrait();

  runApp(MainApp());
}

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

  final game = MainGame();

  @override
  Widget build(BuildContext context) {
    final MainMenu _mainMenu = MainMenu(game: game);
    game.mainMenu = _mainMenu;
    return MaterialApp(
      darkTheme: ThemeData(
        brightness: Brightness.light,
        primarySwatch: Colors.deepPurple,
      ),
      home: GameWidget(
        game: game,
        overlayBuilderMap: {
          'MainMenu': (context, MainGame game) => _mainMenu,
        },
      ),
    );
  }
}

class MainGame extends FlameGame with MultiTouchTapDetector {
  late MainMenu mainMenu;
  @override
  void render(Canvas canvas) {
    super.render(canvas);
    Paint paint = Paint()
     ..color = Colors.white
     ..style = PaintingStyle.fill;
    canvas.drawRect(Rect.fromPoints(const Offset(10.0, 10.0), const Offset(30.0, 30.0)), paint);
  }

  @override
  void onTapDown(int pointerId, TapDownInfo info) {
    if (!overlays.isActive('MainMenu')) {
      overlays.add('MainMenu');
    }
    else {
      mainMenu.hide();
    }
    super.onTapDown(pointerId, info);
  }
}

class MainMenu extends StatefulWidget {
  MainMenu({Key? key, required this.game}) : super(key: key);

  final MainGame game;

  void hide() {
    _mainMenuState.hide();
  }

  late _MainMenuState _mainMenuState;
  @override
  _MainMenuState createState() {
    _mainMenuState = _MainMenuState();
    return _mainMenuState;
  }
}

class _MainMenuState extends State<MainMenu> with TickerProviderStateMixin {
  static Duration duration = const Duration(milliseconds: 250);
  late AnimationController _controller;

  @override
  void initState() {
    super.initState();
    _controller = AnimationController(vsync: this, duration: duration)
    ..addStatusListener((AnimationStatus status) {
      if (status == AnimationStatus.dismissed) {
        widget.game.overlays.remove('MainMenu');
      }
    });
    _controller.forward();
  }

  @override
  void dispose() {
    _controller.dispose();
    super.dispose();
  }

  void hide() {
    _controller.reverse();
  }

  @override
  Widget build(BuildContext context) {
    return AnimatedBuilder(
      animation: _controller,
      builder: (BuildContext context, _) {
        double animationVal = _controller.value;
        double translateVal = (animationVal - 1.0) * 320.0;
        return Transform.translate(
          offset: Offset(translateVal, 0.0),
          child: Drawer(
            child: ListView(
              children: <Widget>[
                DrawerHeader(
                  decoration: const BoxDecoration(
                    color: Colors.deepPurple,
                  ),
                  child: Column(
                    children: const <Widget>[
                      Text('MyApp Menu'),
                    ],
                  ),
                ),
                ListTile(
                  title: const Text('Item 1'),
                  onTap: () => hide(),
                ),
                ListTile(
                  title: const Text('Item 2'),
                  onTap: () => hide(),
                ),
              ]
            ),
          ),
        );
      },
    );
  }
}
nobody
  • 64
  • 5
  • 15