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