feat: read gyroscope as InputDirection and buttonChange to pause/resume
This commit is contained in:
		
							parent
							
								
									f589cf8e92
								
							
						
					
					
						commit
						3682fa551a
					
				| 
						 | 
				
			
			@ -4,17 +4,27 @@ import 'dart:io';
 | 
			
		|||
import 'package:esense_flutter/esense.dart';
 | 
			
		||||
import 'package:flutter/material.dart';
 | 
			
		||||
import 'package:permission_handler/permission_handler.dart';
 | 
			
		||||
import 'package:sense_the_rhythm/arrows.dart';
 | 
			
		||||
import 'package:sense_the_rhythm/level.dart';
 | 
			
		||||
 | 
			
		||||
class ESenseInput {
 | 
			
		||||
  static final instance = ESenseInput._();
 | 
			
		||||
 | 
			
		||||
  ESenseManager eSenseManager = ESenseManager('unknown');
 | 
			
		||||
  ValueNotifier<String> deviceStatus = ValueNotifier('');
 | 
			
		||||
  StreamSubscription? subscription;
 | 
			
		||||
 | 
			
		||||
  String eSenseDeviceName = '';
 | 
			
		||||
  bool connected = false;
 | 
			
		||||
  bool sampling = false;
 | 
			
		||||
 | 
			
		||||
  int sampleRate = 20;
 | 
			
		||||
 | 
			
		||||
  InputDirection inputDirection = InputDirection();
 | 
			
		||||
  int x = 0;
 | 
			
		||||
  int y = 0;
 | 
			
		||||
  int z = 0;
 | 
			
		||||
 | 
			
		||||
  ESenseInput._() {
 | 
			
		||||
    _listenToESense();
 | 
			
		||||
  }
 | 
			
		||||
| 
						 | 
				
			
			@ -22,7 +32,8 @@ class ESenseInput {
 | 
			
		|||
  Future<void> _askForPermissions() async {
 | 
			
		||||
    if (!Platform.isAndroid && !Platform.isIOS) return;
 | 
			
		||||
    if (!(await Permission.bluetoothScan.request().isGranted &&
 | 
			
		||||
        await Permission.bluetoothConnect.request().isGranted)) {
 | 
			
		||||
        await Permission.bluetoothConnect.request().isGranted &&
 | 
			
		||||
        await Permission.bluetooth.request().isGranted)) {
 | 
			
		||||
      print(
 | 
			
		||||
          'WARNING - no permission to use Bluetooth granted. Cannot access eSense device.');
 | 
			
		||||
    }
 | 
			
		||||
| 
						 | 
				
			
			@ -35,10 +46,10 @@ class ESenseInput {
 | 
			
		|||
    }
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  StreamSubscription _listenToESense() {
 | 
			
		||||
  void _listenToESense() {
 | 
			
		||||
    // if you want to get the connection events when connecting,
 | 
			
		||||
    // set up the listener BEFORE connecting...
 | 
			
		||||
    return eSenseManager.connectionEvents.listen((event) {
 | 
			
		||||
    eSenseManager.connectionEvents.listen((event) {
 | 
			
		||||
      print('CONNECTION event: $event');
 | 
			
		||||
 | 
			
		||||
      // when we're connected to the eSense device, we can start listening to events from it
 | 
			
		||||
| 
						 | 
				
			
			@ -49,6 +60,7 @@ class ESenseInput {
 | 
			
		|||
        case ConnectionType.connected:
 | 
			
		||||
          deviceStatus.value = 'connected';
 | 
			
		||||
          connected = true;
 | 
			
		||||
          _startListenToSensorEvents();
 | 
			
		||||
          break;
 | 
			
		||||
        case ConnectionType.unknown:
 | 
			
		||||
          deviceStatus.value = 'unknown';
 | 
			
		||||
| 
						 | 
				
			
			@ -56,6 +68,7 @@ class ESenseInput {
 | 
			
		|||
        case ConnectionType.disconnected:
 | 
			
		||||
          deviceStatus.value = 'disconnected';
 | 
			
		||||
          sampling = false;
 | 
			
		||||
          _pauseListenToSensorEvents();
 | 
			
		||||
          break;
 | 
			
		||||
        case ConnectionType.device_found:
 | 
			
		||||
          deviceStatus.value = 'device_found';
 | 
			
		||||
| 
						 | 
				
			
			@ -67,6 +80,75 @@ class ESenseInput {
 | 
			
		|||
    });
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  Stream<ButtonEventChanged> buttonEvents() {
 | 
			
		||||
    return eSenseManager.eSenseEvents
 | 
			
		||||
        .where((event) => event.runtimeType == ButtonEventChanged)
 | 
			
		||||
        .cast();
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  void _startListenToSensorEvents() async {
 | 
			
		||||
    // // any changes to the sampling frequency must be done BEFORE listening to sensor events
 | 
			
		||||
    print('setting sampling frequency...');
 | 
			
		||||
    bool successs = await eSenseManager.setSamplingRate(sampleRate);
 | 
			
		||||
    if (successs) {
 | 
			
		||||
      print('setSamplingRate success');
 | 
			
		||||
    } else {
 | 
			
		||||
      print('setSamplingRate fail');
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    // subscribe to sensor event from the eSense device
 | 
			
		||||
    subscription = eSenseManager.sensorEvents.listen((event) {
 | 
			
		||||
      // print('SENSOR event: $event');
 | 
			
		||||
      if (event.gyro != null) {
 | 
			
		||||
        _parseGyroData(event.gyro!);
 | 
			
		||||
      }
 | 
			
		||||
    });
 | 
			
		||||
    sampling = true;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  void _pauseListenToSensorEvents() async {
 | 
			
		||||
    subscription?.cancel();
 | 
			
		||||
    sampling = false;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  void _parseGyroData(List<int> data) {
 | 
			
		||||
    // Float value in deg/s = Gyro value / Gyro scale factor
 | 
			
		||||
    // The default configuration is +- 500deg/s for the gyroscope.
 | 
			
		||||
    x = (x + (15 * data[0] ~/ (500 * sampleRate))) % 360;
 | 
			
		||||
    y = (y + (15 * data[1] ~/ (500 * sampleRate))) % 360;
 | 
			
		||||
    z = (z + (15 * data[2] ~/ (500 * sampleRate))) % 360;
 | 
			
		||||
    print('$x, $y, $z');
 | 
			
		||||
    // print('${(z.toDouble() / 500.0 * (1.0 / sampleRate.toDouble())) * 7.5}');
 | 
			
		||||
    // print('${z.toDouble() / 500.0 * (1.0 / 10.0)}');
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  void resetAngles() {
 | 
			
		||||
    inputDirection.reset();
 | 
			
		||||
    x = 0;
 | 
			
		||||
    y = 0;
 | 
			
		||||
    z = 0;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  InputDirection getInputDirection(ArrowDirection expect) {
 | 
			
		||||
    inputDirection.up = z > 270 && z < 340;
 | 
			
		||||
    inputDirection.down = z > 40 && z < 180;
 | 
			
		||||
    inputDirection.left = y > 40 && y < 180;
 | 
			
		||||
    inputDirection.right = y > 270 && y < 340;
 | 
			
		||||
 | 
			
		||||
    if (expect == ArrowDirection.up && inputDirection.up ||
 | 
			
		||||
        expect == ArrowDirection.down && inputDirection.down) {
 | 
			
		||||
      y = 0;
 | 
			
		||||
      print("ehit");
 | 
			
		||||
    }
 | 
			
		||||
    if (expect == ArrowDirection.left && inputDirection.left ||
 | 
			
		||||
        expect == ArrowDirection.right && inputDirection.right) {
 | 
			
		||||
      z = 0;
 | 
			
		||||
      print("ehit");
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    return inputDirection;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  Future<void> connectToESense(String deviceName) async {
 | 
			
		||||
    if (!connected) {
 | 
			
		||||
      await _askForPermissions();
 | 
			
		||||
| 
						 | 
				
			
			@ -74,7 +156,8 @@ class ESenseInput {
 | 
			
		|||
      eSenseDeviceName = deviceName;
 | 
			
		||||
      eSenseManager.deviceName = deviceName;
 | 
			
		||||
      connected = await eSenseManager.connect();
 | 
			
		||||
      print('Trying to connect to eSense device namend \'${eSenseManager.deviceName}\'');
 | 
			
		||||
      print(
 | 
			
		||||
          'Trying to connect to eSense device namend \'${eSenseManager.deviceName}\'');
 | 
			
		||||
 | 
			
		||||
      deviceStatus.value = connected ? 'connecting...' : 'connection failed';
 | 
			
		||||
      print(deviceStatus.value);
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -4,6 +4,7 @@ import 'package:flutter/material.dart';
 | 
			
		|||
import 'package:audioplayers/audioplayers.dart';
 | 
			
		||||
import 'package:flutter/services.dart';
 | 
			
		||||
import 'package:sense_the_rhythm/arrows.dart';
 | 
			
		||||
import 'package:sense_the_rhythm/esense_input.dart';
 | 
			
		||||
import 'package:sense_the_rhythm/game_over_stats.dart';
 | 
			
		||||
import 'package:sense_the_rhythm/simfile.dart';
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -20,6 +21,13 @@ class InputDirection {
 | 
			
		|||
  bool down = false;
 | 
			
		||||
  bool left = false;
 | 
			
		||||
  bool right = false;
 | 
			
		||||
 | 
			
		||||
  void reset() {
 | 
			
		||||
    up = false;
 | 
			
		||||
    down = false;
 | 
			
		||||
    left = false;
 | 
			
		||||
    right = false;
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
class _LevelState extends State<Level> with SingleTickerProviderStateMixin {
 | 
			
		||||
| 
						 | 
				
			
			@ -30,6 +38,7 @@ class _LevelState extends State<Level> with SingleTickerProviderStateMixin {
 | 
			
		|||
 | 
			
		||||
  StreamSubscription? _durationSubscription;
 | 
			
		||||
  StreamSubscription? _positionSubscription;
 | 
			
		||||
  StreamSubscription? _buttonSubscription;
 | 
			
		||||
 | 
			
		||||
  final FocusNode _focusNode = FocusNode();
 | 
			
		||||
  InputDirection inputDirection = InputDirection();
 | 
			
		||||
| 
						 | 
				
			
			@ -53,6 +62,7 @@ class _LevelState extends State<Level> with SingleTickerProviderStateMixin {
 | 
			
		|||
  @override
 | 
			
		||||
  void initState() {
 | 
			
		||||
    super.initState();
 | 
			
		||||
    ESenseInput.instance.resetAngles();
 | 
			
		||||
 | 
			
		||||
    _animationController = AnimationController(
 | 
			
		||||
      vsync: this,
 | 
			
		||||
| 
						 | 
				
			
			@ -77,6 +87,22 @@ class _LevelState extends State<Level> with SingleTickerProviderStateMixin {
 | 
			
		|||
      setState(() => _duration = duration);
 | 
			
		||||
    });
 | 
			
		||||
 | 
			
		||||
    _buttonSubscription = ESenseInput.instance.buttonEvents().listen((event) {
 | 
			
		||||
      if (!event.pressed) {
 | 
			
		||||
        if (_isPlaying) {
 | 
			
		||||
          player.pause();
 | 
			
		||||
          setState(() {
 | 
			
		||||
            _isPlaying = false;
 | 
			
		||||
          });
 | 
			
		||||
        } else {
 | 
			
		||||
          player.resume();
 | 
			
		||||
          setState(() {
 | 
			
		||||
            _isPlaying = true;
 | 
			
		||||
          });
 | 
			
		||||
        }
 | 
			
		||||
      }
 | 
			
		||||
    });
 | 
			
		||||
 | 
			
		||||
    _positionSubscription = player.onPositionChanged.listen(
 | 
			
		||||
      (p) => setState(() => _position = p),
 | 
			
		||||
    );
 | 
			
		||||
| 
						 | 
				
			
			@ -104,6 +130,12 @@ class _LevelState extends State<Level> with SingleTickerProviderStateMixin {
 | 
			
		|||
          continue;
 | 
			
		||||
        }
 | 
			
		||||
        if (note.position.abs() < 0.5 * 1.0 / 60.0) {
 | 
			
		||||
          InputDirection esenseDirection =
 | 
			
		||||
              ESenseInput.instance.getInputDirection(note.direction);
 | 
			
		||||
          inputDirection.up |= esenseDirection.up;
 | 
			
		||||
          inputDirection.down |= esenseDirection.down;
 | 
			
		||||
          inputDirection.left |= esenseDirection.left;
 | 
			
		||||
          inputDirection.right |= esenseDirection.right;
 | 
			
		||||
          bool keypressCorrect = false;
 | 
			
		||||
          switch (note.direction) {
 | 
			
		||||
            case ArrowDirection.up:
 | 
			
		||||
| 
						 | 
				
			
			@ -124,6 +156,7 @@ class _LevelState extends State<Level> with SingleTickerProviderStateMixin {
 | 
			
		|||
            note.wasHit = true;
 | 
			
		||||
            _animationController.reset();
 | 
			
		||||
            _animationController.forward();
 | 
			
		||||
            inputDirection.reset();
 | 
			
		||||
            setState(() {
 | 
			
		||||
              hitOrMissMessage = 'Great!';
 | 
			
		||||
            });
 | 
			
		||||
| 
						 | 
				
			
			@ -133,6 +166,7 @@ class _LevelState extends State<Level> with SingleTickerProviderStateMixin {
 | 
			
		|||
          note.wasHit = false;
 | 
			
		||||
          _animationController.reset();
 | 
			
		||||
          _animationController.forward();
 | 
			
		||||
          inputDirection.reset();
 | 
			
		||||
          setState(() {
 | 
			
		||||
            hitOrMissMessage = 'Missed';
 | 
			
		||||
          });
 | 
			
		||||
| 
						 | 
				
			
			@ -254,6 +288,7 @@ class _LevelState extends State<Level> with SingleTickerProviderStateMixin {
 | 
			
		|||
    _animationController.dispose();
 | 
			
		||||
    _durationSubscription?.cancel();
 | 
			
		||||
    _positionSubscription?.cancel();
 | 
			
		||||
    _buttonSubscription?.cancel();
 | 
			
		||||
    player.dispose();
 | 
			
		||||
    super.dispose();
 | 
			
		||||
  }
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
		Loading…
	
		Reference in a new issue