Flutter游戏手柄支持插件gamepads的使用

发布于 1周前 作者 wuwangju 来自 Flutter

Flutter游戏手柄支持插件gamepads的使用

gamepads 是一个Flutter插件,用于处理多个平台上的游戏手柄(或摇杆)输入。它支持同时连接多个游戏手柄,并会自动检测和监听新的连接。

快速开始

列出所有已连接的游戏手柄

使用 list 方法可以列出当前所有已连接的游戏手柄:

final gamepads = await Gamepads.list();

该方法返回一个包含 GamepadController 对象的列表,每个对象都有一个唯一的 id 和用户可见的 name

监听所有游戏手柄的输入事件

通过监听 events 流,可以接收到所有游戏手柄的输入事件:

Gamepads.events.listen((event) {
  // 处理事件
});

如果你想监听特定游戏手柄的事件,可以使用 eventsByGamepad 方法。

输入事件的数据结构

输入事件由 GamepadEvent 类描述,包含以下字段:

  • gamepadId: 触发事件的游戏手柄的ID。
  • timestamp: 事件触发的时间戳,以自纪元以来的毫秒数表示。
  • type: 触发的按键类型,由 KeyType 枚举定义。
  • key: 平台相关的按键标识符。
  • value: 按键的当前值(例如,对于模拟输入,可能是0到1之间的浮点数)。

Android集成

在Android平台上,需要将输入事件转发给插件。可以在 MainActivity 中实现如下代码:

package [YOUR_PACKAGE_NAME]

import android.hardware.input.InputManager
import android.os.Handler
import android.view.InputDevice
import android.view.KeyEvent
import android.view.MotionEvent
import io.flutter.embedding.android.FlutterActivity
import org.flame_engine.gamepads_android.GamepadsCompatibleActivity

class MainActivity: FlutterActivity(), GamepadsCompatibleActivity {
    var keyListener: ((KeyEvent) -> Boolean)? = null
    var motionListener: ((MotionEvent) -> Boolean)? = null

    override fun dispatchGenericMotionEvent(motionEvent: MotionEvent): Boolean {
        return motionListener?.invoke(motionEvent) ?: false
    }
    
    override fun dispatchKeyEvent(keyEvent: KeyEvent): Boolean {
        return keyListener?.invoke(keyEvent) ?: false
    }

    override fun registerInputDeviceListener(
      listener: InputManager.InputDeviceListener, handler: Handler?) {
        val inputManager = getSystemService(INPUT_SERVICE) as InputManager
        inputManager.registerInputDeviceListener(listener, null)
    }

    override fun registerKeyEventHandler(handler: (KeyEvent) -> Boolean) {
        keyListener = handler
    }

    override fun registerMotionEventHandler(handler: (MotionEvent) -> Boolean) {
        motionListener = handler
    }
}

示例代码

下面是一个完整的示例应用程序,展示了如何使用 gamepads 插件:

import 'dart:async';
import 'package:flutter/material.dart';
import 'package:gamepads/gamepads.dart';

void main() {
  runApp(const MyApp());
}

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

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Gamepads Example',
      theme: ThemeData(
        primarySwatch: Colors.blue,
      ),
      home: const MyHomePage(),
    );
  }
}

class MyHomePage extends StatefulWidget {
  const MyHomePage({super.key});

  @override
  State<MyHomePage> createState() => _MyHomePageState();
}

class _MyHomePageState extends State<MyHomePage> {
  StreamSubscription<GamepadEvent>? _subscription;

  List<GamepadController> _gamepads = [];
  List<GamepadEvent> _lastEvents = [];
  bool loading = false;

  Future<void> _getValue() async {
    setState(() => loading = true);
    final response = await Gamepads.list();
    setState(() {
      _gamepads = response;
      loading = false;
    });
  }

  void _clear() {
    setState(() => _lastEvents = []);
  }

  @override
  void initState() {
    super.initState();
    _subscription = Gamepads.events.listen((event) {
      setState(() {
        final newEvents = [
          event,
          ..._lastEvents,
        ];
        if (newEvents.length > 3) {
          newEvents.removeRange(3, newEvents.length);
        }
        _lastEvents = newEvents;
      });
    });
  }

  @override
  void dispose() {
    _subscription?.cancel();
    super.dispose();
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: const Text('Gamepads Example'),
      ),
      body: Center(
        child: Column(
          mainAxisAlignment: MainAxisAlignment.center,
          children: [
            const Text('Last Events:'),
            ..._lastEvents.map((e) => Text(e.toString())),
            TextButton(
              onPressed: _clear,
              child: const Text('clear events'),
            ),
            const SizedBox(height: 16),
            TextButton(
              onPressed: _getValue,
              child: const Text('listGamepads()'),
            ),
            const Text('Gamepads:'),
            if (loading)
              const CircularProgressIndicator()
            else ...[
              for (final gamepad in _gamepads) ...[
                Text('${gamepad.id} - ${gamepad.name}'),
                Text('  Analog inputs: ${gamepad.state.analogInputs}'),
                Text('  Button inputs: ${gamepad.state.buttonInputs}'),
              ],
            ],
          ],
        ),
      ),
    );
  }
}

这个示例应用程序展示了如何列出所有已连接的游戏手柄,并实时显示最近的输入事件。你可以根据需要扩展和修改这个示例来满足你的具体需求。


更多关于Flutter游戏手柄支持插件gamepads的使用的实战系列教程也可以访问 https://www.itying.com/category-92-b0.html

1 回复

更多关于Flutter游戏手柄支持插件gamepads的使用的实战系列教程也可以访问 https://www.itying.com/category-92-b0.html


当然,下面是一个关于如何在Flutter应用中使用gamepads插件来支持游戏手柄的示例代码。我们将使用gamepad包,该包允许你检测并读取连接到设备上的游戏手柄的输入。

首先,确保你的Flutter项目已经创建好了。如果还没有,请使用以下命令创建一个新的Flutter项目:

flutter create game_controller_app
cd game_controller_app

接下来,在你的pubspec.yaml文件中添加gamepad依赖:

dependencies:
  flutter:
    sdk: flutter
  gamepad: ^x.y.z  # 请替换为最新版本号

然后运行flutter pub get来安装依赖。

现在,你可以在你的Flutter应用中使用gamepad包来读取游戏手柄的输入。以下是一个简单的示例代码,展示如何在主屏幕上显示连接的游戏手柄的状态:

import 'package:flutter/material.dart';
import 'package:gamepad/gamepad.dart';

void main() {
  runApp(MyApp());
}

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Game Controller App',
      theme: ThemeData(
        primarySwatch: Colors.blue,
      ),
      home: GameControllerScreen(),
    );
  }
}

class GameControllerScreen extends StatefulWidget {
  @override
  _GameControllerScreenState createState() => _GameControllerScreenState();
}

class _GameControllerScreenState extends State<GameControllerScreen> {
  List<Gamepad> _gamepads = [];

  @override
  void initState() {
    super.initState();
    _listenForGamepads();
  }

  @override
  void dispose() {
    GamepadManager.instance.stopListening();
    super.dispose();
  }

  void _listenForGamepads() {
    GamepadManager.instance.startListening((gamepads) {
      setState(() {
        _gamepads = gamepads;
      });
    });
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text('Game Controller Support'),
      ),
      body: Padding(
        padding: const EdgeInsets.all(16.0),
        child: Column(
          mainAxisAlignment: MainAxisAlignment.center,
          children: <Widget>[
            Text('Connected Gamepads:', style: TextStyle(fontSize: 20)),
            SizedBox(height: 16),
            Expanded(
              child: ListView.builder(
                itemCount: _gamepads.length,
                itemBuilder: (context, index) {
                  Gamepad gamepad = _gamepads[index];
                  return Card(
                    child: Padding(
                      padding: const EdgeInsets.all(8.0),
                      child: Column(
                        crossAxisAlignment: CrossAxisAlignment.start,
                        children: <Widget>[
                          Text('Gamepad ${index + 1}', style: TextStyle(fontSize: 18)),
                          SizedBox(height: 8),
                          Text('Buttons:'),
                          ...(gamepad.buttons.map((button) => Text('Button ${button.index}: ${button.pressed ? 'Pressed' : 'Not Pressed'}'))),
                          SizedBox(height: 16),
                          Text('Axes:'),
                          ...(gamepad.axes.map((axis) => Text('Axis ${axis.index}: ${axis.value.toStringAsFixed(2)}'))),
                        ],
                      ),
                    ),
                  );
                },
              ),
            ),
          ],
        ),
      ),
    );
  }
}

在这个示例中,我们创建了一个Flutter应用,该应用使用gamepad包来监听连接的游戏手柄。每当有游戏手柄连接或断开时,_listenForGamepads方法会更新状态,并在UI中显示当前连接的游戏手柄的状态,包括按钮是否被按下以及轴的值。

请确保你的设备支持并连接了游戏手柄,然后运行这个Flutter应用来查看效果。

注意:由于gamepad包的具体API和版本可能会随时间变化,请参考该包的官方文档和示例代码以确保兼容性。

回到顶部