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:esense_flutter/esense.dart'; | ||||||
| import 'package:flutter/material.dart'; | import 'package:flutter/material.dart'; | ||||||
| import 'package:permission_handler/permission_handler.dart'; | import 'package:permission_handler/permission_handler.dart'; | ||||||
|  | import 'package:sense_the_rhythm/arrows.dart'; | ||||||
|  | import 'package:sense_the_rhythm/level.dart'; | ||||||
| 
 | 
 | ||||||
| class ESenseInput { | class ESenseInput { | ||||||
|   static final instance = ESenseInput._(); |   static final instance = ESenseInput._(); | ||||||
| 
 | 
 | ||||||
|   ESenseManager eSenseManager = ESenseManager('unknown'); |   ESenseManager eSenseManager = ESenseManager('unknown'); | ||||||
|   ValueNotifier<String> deviceStatus = ValueNotifier(''); |   ValueNotifier<String> deviceStatus = ValueNotifier(''); | ||||||
|  |   StreamSubscription? subscription; | ||||||
| 
 | 
 | ||||||
|   String eSenseDeviceName = ''; |   String eSenseDeviceName = ''; | ||||||
|   bool connected = false; |   bool connected = false; | ||||||
|   bool sampling = false; |   bool sampling = false; | ||||||
| 
 | 
 | ||||||
|  |   int sampleRate = 20; | ||||||
|  | 
 | ||||||
|  |   InputDirection inputDirection = InputDirection(); | ||||||
|  |   int x = 0; | ||||||
|  |   int y = 0; | ||||||
|  |   int z = 0; | ||||||
|  | 
 | ||||||
|   ESenseInput._() { |   ESenseInput._() { | ||||||
|     _listenToESense(); |     _listenToESense(); | ||||||
|   } |   } | ||||||
|  | @ -22,7 +32,8 @@ class ESenseInput { | ||||||
|   Future<void> _askForPermissions() async { |   Future<void> _askForPermissions() async { | ||||||
|     if (!Platform.isAndroid && !Platform.isIOS) return; |     if (!Platform.isAndroid && !Platform.isIOS) return; | ||||||
|     if (!(await Permission.bluetoothScan.request().isGranted && |     if (!(await Permission.bluetoothScan.request().isGranted && | ||||||
|         await Permission.bluetoothConnect.request().isGranted)) { |         await Permission.bluetoothConnect.request().isGranted && | ||||||
|  |         await Permission.bluetooth.request().isGranted)) { | ||||||
|       print( |       print( | ||||||
|           'WARNING - no permission to use Bluetooth granted. Cannot access eSense device.'); |           '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, |     // if you want to get the connection events when connecting, | ||||||
|     // set up the listener BEFORE connecting... |     // set up the listener BEFORE connecting... | ||||||
|     return eSenseManager.connectionEvents.listen((event) { |     eSenseManager.connectionEvents.listen((event) { | ||||||
|       print('CONNECTION event: $event'); |       print('CONNECTION event: $event'); | ||||||
| 
 | 
 | ||||||
|       // when we're connected to the eSense device, we can start listening to events from it |       // 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: |         case ConnectionType.connected: | ||||||
|           deviceStatus.value = 'connected'; |           deviceStatus.value = 'connected'; | ||||||
|           connected = true; |           connected = true; | ||||||
|  |           _startListenToSensorEvents(); | ||||||
|           break; |           break; | ||||||
|         case ConnectionType.unknown: |         case ConnectionType.unknown: | ||||||
|           deviceStatus.value = 'unknown'; |           deviceStatus.value = 'unknown'; | ||||||
|  | @ -56,6 +68,7 @@ class ESenseInput { | ||||||
|         case ConnectionType.disconnected: |         case ConnectionType.disconnected: | ||||||
|           deviceStatus.value = 'disconnected'; |           deviceStatus.value = 'disconnected'; | ||||||
|           sampling = false; |           sampling = false; | ||||||
|  |           _pauseListenToSensorEvents(); | ||||||
|           break; |           break; | ||||||
|         case ConnectionType.device_found: |         case ConnectionType.device_found: | ||||||
|           deviceStatus.value = '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 { |   Future<void> connectToESense(String deviceName) async { | ||||||
|     if (!connected) { |     if (!connected) { | ||||||
|       await _askForPermissions(); |       await _askForPermissions(); | ||||||
|  | @ -74,7 +156,8 @@ class ESenseInput { | ||||||
|       eSenseDeviceName = deviceName; |       eSenseDeviceName = deviceName; | ||||||
|       eSenseManager.deviceName = deviceName; |       eSenseManager.deviceName = deviceName; | ||||||
|       connected = await eSenseManager.connect(); |       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'; |       deviceStatus.value = connected ? 'connecting...' : 'connection failed'; | ||||||
|       print(deviceStatus.value); |       print(deviceStatus.value); | ||||||
|  |  | ||||||
|  | @ -4,6 +4,7 @@ import 'package:flutter/material.dart'; | ||||||
| import 'package:audioplayers/audioplayers.dart'; | import 'package:audioplayers/audioplayers.dart'; | ||||||
| import 'package:flutter/services.dart'; | import 'package:flutter/services.dart'; | ||||||
| import 'package:sense_the_rhythm/arrows.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/game_over_stats.dart'; | ||||||
| import 'package:sense_the_rhythm/simfile.dart'; | import 'package:sense_the_rhythm/simfile.dart'; | ||||||
| 
 | 
 | ||||||
|  | @ -20,6 +21,13 @@ class InputDirection { | ||||||
|   bool down = false; |   bool down = false; | ||||||
|   bool left = false; |   bool left = false; | ||||||
|   bool right = false; |   bool right = false; | ||||||
|  | 
 | ||||||
|  |   void reset() { | ||||||
|  |     up = false; | ||||||
|  |     down = false; | ||||||
|  |     left = false; | ||||||
|  |     right = false; | ||||||
|  |   } | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| class _LevelState extends State<Level> with SingleTickerProviderStateMixin { | class _LevelState extends State<Level> with SingleTickerProviderStateMixin { | ||||||
|  | @ -30,6 +38,7 @@ class _LevelState extends State<Level> with SingleTickerProviderStateMixin { | ||||||
| 
 | 
 | ||||||
|   StreamSubscription? _durationSubscription; |   StreamSubscription? _durationSubscription; | ||||||
|   StreamSubscription? _positionSubscription; |   StreamSubscription? _positionSubscription; | ||||||
|  |   StreamSubscription? _buttonSubscription; | ||||||
| 
 | 
 | ||||||
|   final FocusNode _focusNode = FocusNode(); |   final FocusNode _focusNode = FocusNode(); | ||||||
|   InputDirection inputDirection = InputDirection(); |   InputDirection inputDirection = InputDirection(); | ||||||
|  | @ -53,6 +62,7 @@ class _LevelState extends State<Level> with SingleTickerProviderStateMixin { | ||||||
|   @override |   @override | ||||||
|   void initState() { |   void initState() { | ||||||
|     super.initState(); |     super.initState(); | ||||||
|  |     ESenseInput.instance.resetAngles(); | ||||||
| 
 | 
 | ||||||
|     _animationController = AnimationController( |     _animationController = AnimationController( | ||||||
|       vsync: this, |       vsync: this, | ||||||
|  | @ -77,6 +87,22 @@ class _LevelState extends State<Level> with SingleTickerProviderStateMixin { | ||||||
|       setState(() => _duration = duration); |       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( |     _positionSubscription = player.onPositionChanged.listen( | ||||||
|       (p) => setState(() => _position = p), |       (p) => setState(() => _position = p), | ||||||
|     ); |     ); | ||||||
|  | @ -104,6 +130,12 @@ class _LevelState extends State<Level> with SingleTickerProviderStateMixin { | ||||||
|           continue; |           continue; | ||||||
|         } |         } | ||||||
|         if (note.position.abs() < 0.5 * 1.0 / 60.0) { |         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; |           bool keypressCorrect = false; | ||||||
|           switch (note.direction) { |           switch (note.direction) { | ||||||
|             case ArrowDirection.up: |             case ArrowDirection.up: | ||||||
|  | @ -124,6 +156,7 @@ class _LevelState extends State<Level> with SingleTickerProviderStateMixin { | ||||||
|             note.wasHit = true; |             note.wasHit = true; | ||||||
|             _animationController.reset(); |             _animationController.reset(); | ||||||
|             _animationController.forward(); |             _animationController.forward(); | ||||||
|  |             inputDirection.reset(); | ||||||
|             setState(() { |             setState(() { | ||||||
|               hitOrMissMessage = 'Great!'; |               hitOrMissMessage = 'Great!'; | ||||||
|             }); |             }); | ||||||
|  | @ -133,6 +166,7 @@ class _LevelState extends State<Level> with SingleTickerProviderStateMixin { | ||||||
|           note.wasHit = false; |           note.wasHit = false; | ||||||
|           _animationController.reset(); |           _animationController.reset(); | ||||||
|           _animationController.forward(); |           _animationController.forward(); | ||||||
|  |           inputDirection.reset(); | ||||||
|           setState(() { |           setState(() { | ||||||
|             hitOrMissMessage = 'Missed'; |             hitOrMissMessage = 'Missed'; | ||||||
|           }); |           }); | ||||||
|  | @ -254,6 +288,7 @@ class _LevelState extends State<Level> with SingleTickerProviderStateMixin { | ||||||
|     _animationController.dispose(); |     _animationController.dispose(); | ||||||
|     _durationSubscription?.cancel(); |     _durationSubscription?.cancel(); | ||||||
|     _positionSubscription?.cancel(); |     _positionSubscription?.cancel(); | ||||||
|  |     _buttonSubscription?.cancel(); | ||||||
|     player.dispose(); |     player.dispose(); | ||||||
|     super.dispose(); |     super.dispose(); | ||||||
|   } |   } | ||||||
|  |  | ||||||
		Loading…
	
		Reference in a new issue