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