style: refactored arrows in own file

This commit is contained in:
Orangerot 2024-12-29 16:25:32 +01:00
parent e9ba842e21
commit 5a763a5de2
2 changed files with 192 additions and 103 deletions

57
lib/arrows.dart Normal file
View file

@ -0,0 +1,57 @@
import 'package:flutter/material.dart';
enum ArrowDirection {
left(Icons.arrow_back),
down(Icons.arrow_downward),
up(Icons.arrow_upward),
right(Icons.arrow_forward);
const ArrowDirection(this.icon);
final IconData icon;
}
class Note {
final double time;
final ArrowDirection direction;
double position = 0;
bool wasHit = false;
Note({required this.time, required this.direction});
}
class Arrows extends StatelessWidget {
final List<Note> notes;
final double position;
const Arrows({super.key, required this.notes, required this.position});
@override
Widget build(BuildContext context) {
return Stack(
children: notes.map((note) {
double position = note.position * 10000; // * 20 * MediaQuery.of(context).size.height;
return Arrow(
position: position,
direction: note.direction,
);
}).toList());
}
}
class Arrow extends StatelessWidget {
final double position;
final ArrowDirection direction;
const Arrow({super.key, required this.position, required this.direction});
@override
Widget build(BuildContext context) {
return Positioned(
left: MediaQuery.of(context).size.width / 2 - 50, // Center the arrow
bottom: position + 50,
child: Icon(size: 100, color: Colors.redAccent.shade400, direction.icon),
);
}
}

View file

@ -3,6 +3,8 @@ import 'dart:io';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:audioplayers/audioplayers.dart'; import 'package:audioplayers/audioplayers.dart';
import 'package:flutter/services.dart';
import 'package:sense_the_rhythm/arrows.dart';
import 'package:sense_the_rhythm/simfile.dart'; import 'package:sense_the_rhythm/simfile.dart';
class Level extends StatefulWidget { class Level extends StatefulWidget {
@ -13,22 +15,11 @@ class Level extends StatefulWidget {
State<Level> createState() => _LevelState(); State<Level> createState() => _LevelState();
} }
enum ArrowDirection { class InputDirection {
left(Icons.arrow_back), bool up = false;
down(Icons.arrow_downward), bool down = false;
up(Icons.arrow_upward), bool left = false;
right(Icons.arrow_forward); bool right = false;
const ArrowDirection(this.icon);
final IconData icon;
}
class Note {
final double time;
final ArrowDirection direction;
const Note({required this.time, required this.direction});
} }
class _LevelState extends State<Level> { class _LevelState extends State<Level> {
@ -41,6 +32,9 @@ class _LevelState extends State<Level> {
StreamSubscription? _durationSubscription; StreamSubscription? _durationSubscription;
StreamSubscription? _positionSubscription; StreamSubscription? _positionSubscription;
final FocusNode _focusNode = FocusNode();
InputDirection inputDirection = InputDirection();
List<Note> notes = []; List<Note> notes = [];
@override @override
@ -82,6 +76,38 @@ class _LevelState extends State<Level> {
player.onPositionChanged.listen((Duration p) { player.onPositionChanged.listen((Duration p) {
// print('Current position: $p'); // print('Current position: $p');
setState(() => _position = p); setState(() => _position = p);
for (final note in notes) {
note.position = note.time - p.inMilliseconds / 60000.0;
if (!note.wasHit && note.position.abs() < 0.5 * 1.0 / 60.0) {
bool keypressCorrect = false;
switch (note.direction) {
case ArrowDirection.up:
keypressCorrect = inputDirection.up;
break;
case ArrowDirection.down:
keypressCorrect = inputDirection.down;
break;
case ArrowDirection.right:
keypressCorrect = inputDirection.right;
break;
case ArrowDirection.left:
keypressCorrect = inputDirection.left;
break;
}
if (keypressCorrect) {
print("you hit!");
note.wasHit = true;
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(
content: Text('This is a toast message'),
duration: Duration(seconds: 2),
),
);
}
}
}
}); });
String simfilePath = Directory(widget.stepmaniaFolderPath) String simfilePath = Directory(widget.stepmaniaFolderPath)
@ -108,12 +134,12 @@ class _LevelState extends State<Level> {
if (arrowIndex < 0 || arrowIndex > 3) { if (arrowIndex < 0 || arrowIndex > 3) {
continue; continue;
} }
double beat =
measureIndex * 4.0 + (noteIndex.toDouble() / measure.length) * 4.0;
double minutesPerBeat = 1.0 / bpm;
double offsetMinutes = simfile!.offset / 60.0;
notes.add(Note( notes.add(Note(
time: (measureIndex * 4.0 + time: beat * minutesPerBeat + offsetMinutes,
(noteIndex.toDouble() / measure.length) * 4.0) *
1.0 /
bpm +
simfile!.offset / 60.0,
direction: ArrowDirection.values[arrowIndex])); direction: ArrowDirection.values[arrowIndex]));
} }
} }
@ -125,75 +151,97 @@ class _LevelState extends State<Level> {
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
return Scaffold( return KeyboardListener(
appBar: AppBar( focusNode: _focusNode,
leading: IconButton( autofocus: true,
icon: Icon(_isPlaying ? Icons.pause : Icons.play_arrow), onKeyEvent: (event) {
onPressed: () { bool isDown = false;
if (_isPlaying) { if (event is KeyDownEvent) {
player.pause(); isDown = true;
setState(() { } else if (event is KeyUpEvent) {
_isPlaying = false; isDown = false;
}); } else {
} else { return;
player.resume(); }
setState(() { switch (event.logicalKey) {
_isPlaying = true; case LogicalKeyboardKey.arrowUp:
}); inputDirection.up = isDown;
} break;
}, case LogicalKeyboardKey.arrowDown:
), inputDirection.down = isDown;
title: Text(widget.stepmaniaFolderPath.split('/').last), break;
actions: [ case LogicalKeyboardKey.arrowLeft:
IconButton( inputDirection.left = isDown;
icon: Icon(Icons.close), break;
onPressed: () => Navigator.pop(context)) case LogicalKeyboardKey.arrowRight:
], inputDirection.right = isDown;
bottom: PreferredSize( break;
preferredSize: Size(double.infinity, 1.0), }
child: LinearProgressIndicator( },
value: (_duration != null && child: Scaffold(
_position != null && appBar: AppBar(
_position!.inMilliseconds > 0 && leading: IconButton(
_position!.inMilliseconds < _duration!.inMilliseconds) icon: Icon(_isPlaying ? Icons.pause : Icons.play_arrow),
? _position!.inMilliseconds / _duration!.inMilliseconds onPressed: () {
: 0.0, if (_isPlaying) {
)), player.pause();
), setState(() {
body: Stack(children: [ _isPlaying = false;
...notes.map((note) { });
return Arrow( } else {
position: _position != null player.resume();
? (note.time - _position!.inMilliseconds / 60000.0) * setState(() {
20 * _isPlaying = true;
MediaQuery.of(context).size.height });
: 0.0, }
direction: note.direction, },
);
}),
Positioned(
top: 50,
width: MediaQuery.of(context).size.width,
left: 0,
child: Text(
"Great!",
textScaler: TextScaler.linear(4),
textAlign: TextAlign.center,
), ),
title: Text(widget.stepmaniaFolderPath.split('/').last),
actions: [
IconButton(
icon: Icon(Icons.close),
onPressed: () => Navigator.pop(context))
],
bottom: PreferredSize(
preferredSize: Size(double.infinity, 1.0),
child: LinearProgressIndicator(
value: (_duration != null &&
_position != null &&
_position!.inMilliseconds > 0 &&
_position!.inMilliseconds < _duration!.inMilliseconds)
? _position!.inMilliseconds / _duration!.inMilliseconds
: 0.0,
)),
), ),
Positioned( body: Stack(children: [
left: MediaQuery.of(context).size.width / 2 - 50, Arrows(
bottom: 50, notes: notes,
child: Container( position: _position != null
width: 100, ? _position!.inMilliseconds.toDouble()
height: 100, : 0.0),
decoration: BoxDecoration( Positioned(
shape: BoxShape.circle, top: 50,
// color: Colors.blue, width: MediaQuery.of(context).size.width,
border: Border.all(color: Colors.black, width: 10)), left: 0,
child: Text(
"Great!",
textScaler: TextScaler.linear(4),
textAlign: TextAlign.center,
),
), ),
), Positioned(
])); left: MediaQuery.of(context).size.width / 2 - 50,
bottom: 50,
child: Container(
width: 100,
height: 100,
decoration: BoxDecoration(
shape: BoxShape.circle,
border: Border.all(color: Colors.black, width: 10)),
),
),
])),
);
} }
@override @override
@ -204,19 +252,3 @@ class _LevelState extends State<Level> {
super.dispose(); super.dispose();
} }
} }
class Arrow extends StatelessWidget {
final double position;
final ArrowDirection direction;
const Arrow({super.key, required this.position, required this.direction});
@override
Widget build(BuildContext context) {
return Positioned(
left: MediaQuery.of(context).size.width / 2 - 50, // Center the arrow
bottom: position + 50,
child: Icon(size: 100, color: Colors.redAccent.shade400, direction.icon),
);
}
}