start method
void
start()
Starts tracking. Keep the device still during the one-second calibration.
Implementation
void start() {
stop();
if (!canStart) return;
_running = true;
final session = _session;
_offsetX = 0;
_offsetY = 0;
_fusion.reset(clearGravity: true);
_lastGravityEvent = null;
_silentIntervals = 0;
_gravitySilentIntervals = 0;
_smoothPitch = 0;
calibrate();
// One timer, not a newly allocated timer per 60–120 Hz sample.
_healthTimer = Timer.periodic(const Duration(milliseconds: 200), (_) {
if (!_running || session != _session) return;
if (++_silentIntervals >= 4 && isGyroscopeActive) {
_loseGyroscope();
}
if (++_gravitySilentIntervals >= 4 && _lastGravityEvent != null) {
_loseGravity();
}
});
if (useIsolate) _startWorker(session);
// An injected stream may work on desktop even though the native backend
// does not. In that case never fill a missing channel with a plugin call.
final platformSensors = VrSensorCapabilities.supportsDeviceMotion;
final accelerationEvents =
accelerometerStreamOverride ??
(platformSensors
? accelerometerEventStream(
samplingPeriod: SensorInterval.fastestInterval,
)
: null);
_accelSubscription = accelerationEvents?.listen(
(event) {
if (!_running || session != _session) return;
if (!_fusion.updateGravity(event.x, event.y, event.z)) return;
_lastGravityEvent = event;
_gravitySilentIntervals = 0;
_isolateSendPort?.send([1, event.x, event.y, event.z]);
if (!isGyroscopeActive) {
_smoothPitch = _smoothPitch * 0.85 + _fusion.gravityPitch! * 0.15;
target.setPitch(_smoothPitch * pitchGain);
}
},
onError: (Object error) {
if (_running && session == _session) _loseGravity();
},
onDone: () {
if (_running && session == _session) _loseGravity();
},
);
final gyroscopeEvents =
gyroscopeStreamOverride ??
(platformSensors
? gyroscopeEventStream(
samplingPeriod: SensorInterval.fastestInterval,
)
: null);
_subscription = gyroscopeEvents?.listen(
(event) {
if (!_running ||
session != _session ||
!event.x.isFinite ||
!event.y.isFinite ||
!event.z.isFinite) {
return;
}
_silentIntervals = 0;
if (!isGyroscopeActive) _resetFusion();
isGyroscopeActive = true;
if (_calibrating) {
_calibrationSumX += event.x;
_calibrationSumY += event.y;
_calibrationSamples++;
return;
}
final timestampUs = event.timestamp.microsecondsSinceEpoch;
if (_isolateSendPort != null) {
_sendConfiguration();
_isolateSendPort.send([
2,
event.x,
event.y,
timestampUs,
_workerRevision,
]);
} else {
_configureFusion();
if (_fusion.addGyroscope(event.x, event.y, timestampUs)) {
_applyFusion(_fusion.dYaw, _fusion.dPitch, _fusion.absolutePitch);
}
}
},
onError: (Object error) {
if (_running && session == _session) _loseGyroscope();
},
onDone: () {
if (_running && session == _session) _loseGyroscope();
},
);
}