Description:
I’m encountering challenges with my Flutter application where I’ve implemented a spinning wheel feature. The goal is to rotate the wheel smoothly and stop it at a predefined position within a specified duration. However, I’m facing difficulties ensuring that the wheel stops precisely at the desired slice within the given time frame.
Here’s a breakdown of the issues and the relevant code snippet:
Smooth Rotation: While the wheel rotates, I aim to maintain a smooth movement throughout the spin. However, achieving consistent angular velocity and smooth deceleration pose challenges.
Precise Stopping: The main issue arises when trying to halt the wheel precisely at a predetermined slice within the specified time frame. Despite setting up conditions for stopping, such as checking if the maximum speed is reached and verifying the angle, the stopping point often exceeds the target or takes extra time.
Below is the excerpt of the code I’m currently using for wheel rotation and stopping logic:
class WheelUpdater extends PositionComponent {
final SpinTheWheel spinTheWheelModel;
final VoidCallback onSpinStop;
late double _angularVelocity;
late bool _spinning;
late bool _isMaxSpeedReached;
late int _sliceCount;
late double _sliceAngle;
late AudioService _audioService;
late double _elapsedTime;
late double stopAngle;
late int totalDuration;
WheelUpdater({
required this.spinTheWheelModel,
required this.onSpinStop,
}) : assert(
spinTheWheelModel.stopPosition <= spinTheWheelModel.slices.length,
'Stop position must be less than or equal to the number of slices'),
assert(spinTheWheelModel.duration >= 0,
'duration must be greater than or equal to 0') {
_sliceCount = spinTheWheelModel.slices.length;
_sliceAngle = 2 * pi / _sliceCount;
_angularVelocity = 0;
_spinning = false;
_isMaxSpeedReached = false;
_audioService = AudioServiceImp();
totalDuration = spinTheWheelModel.duration;
_elapsedTime = 0;
}
@override
FutureOr<void> onLoad() async {
await _audioService.loadAll([Song.tick, Song.winner]);
return super.onLoad();
}
@override
void onRemove() async {
await _audioService.clearAll();
super.onRemove();
}
@override
void update(double dt) {
super.update(dt);
double previousAngle = angle;
if (_spinning) {
_elapsedTime += dt * 1000;
angle += _angularVelocity * dt;
double maxSpeedTime = totalDuration * 0.5;
if (_elapsedTime >= maxSpeedTime && !_isMaxSpeedReached) {
_isMaxSpeedReached = true;
}
if (_isMaxSpeedReached && _angularVelocity >= 1) {
_angularVelocity -= dt;
} else {
_angularVelocity += dt;
}
if (angle >= 2 * pi) {
angle = (2 * pi) - angle;
}
int previousSliceIndex = (previousAngle / _sliceAngle).floor();
int currentSliceIndex = (angle / _sliceAngle).floor();
if (previousSliceIndex != currentSliceIndex) {
_playTickSound();
}
if (isStoppingConditionReached) {
_spinning = false;
angle = stopAngle + 0.3;
_winnerDeclared();
onSpinStop();
}
}
}
void startSpin() {
if (!_spinning) {
_spinning = true;
_angularVelocity = 0;
_isMaxSpeedReached = false;
_elapsedTime = 0;
}
}
bool get isStoppingConditionReached {
if (spinTheWheelModel.stopPosition == 0) {
stopAngle = 0;
} else {
stopAngle = _sliceAngle * (_sliceCount - spinTheWheelModel.stopPosition);
}
double offsetAngle = _sliceAngle * 0.4;
return _isMaxSpeedReached &&
_angularVelocity <= 1 &&
angle >= (stopAngle + offsetAngle) &&
angle <= (stopAngle + _sliceAngle - offsetAngle);
}
/// Plays the tick sound when the wheel rotates to a new slice.
Future<void> _playTickSound() async {
await _audioService.play(Song.tick);
}
Future<void> _winnerDeclared() async {
if (spinTheWheelModel.showHapticFeedback) {
await HapticFeedback.vibrate();
}
/// Plays the winner sound when the spinning stops at the designated position.
await _audioService.play(Song.winner);
}
}
I’ve attempted to adjust parameters such as angular velocity and stopping conditions but haven’t achieved the desired outcome yet.
Could someone assist me in refining the logic or suggest a formula to ensure smooth rotation and precise stopping within the specified time frame? Any insights or guidance would be greatly appreciated. Thank you!