update method
Called once per frame while the component is mounted, enabled, and
loaded. deltaSeconds is the elapsed time since the previous tick.
A traversal visits each component at most once. Removing this component
or an earlier sibling is safe. A component inserted before the current
traversal position starts on the next frame. Reordering component or
child lists during traversal is unsupported.
Implementation
@override
void update(double deltaSeconds) {
if (!_initialized) onMount();
if (deltaSeconds <= 0.0) return;
final rawPivot = _currentRawPivotWorld;
// 1. Position lag smoothing (exponential)
if (enablePositionLag && positionLagSpeed > 0.0) {
final t = 1.0 - math.exp(-positionLagSpeed * deltaSeconds);
_smoothedPivotWorld =
_smoothedPivotWorld + (rawPivot - _smoothedPivotWorld) * t;
} else {
_smoothedPivotWorld = rawPivot;
}
final currentYaw = effectiveYaw;
final currentPitch = effectivePitch.clamp(-1.4, 1.4);
// 2. Occlusion query from pivot along boom direction toward desired eye
final unoccludedEye = _computeEyePosition(
_smoothedPivotWorld,
currentYaw,
currentPitch,
targetLength,
);
final rayDir = (unoccludedEye - _smoothedPivotWorld).normalized();
final ray = vm.Ray.originDirection(_smoothedPivotWorld, rayDir);
var desiredLength = targetLength;
final hit = raycastNode(
_rootNode,
ray,
maxDistance: targetLength,
layerMask: layerMask,
where: (n) => !_isExcluded(n),
);
if (hit != null && hit.distance < targetLength) {
desiredLength = math.max(minLength, hit.distance - probeRadius);
}
// Smoothly compress or expand current length
if (desiredLength < currentLength) {
currentLength = desiredLength;
} else {
final t = (1.0 - math.exp(-12.0 * deltaSeconds)).clamp(0.0, 1.0);
currentLength = currentLength + (desiredLength - currentLength) * t;
}
// 3. Compute final camera socket transform
_updateSocketTransform();
// 4. Update child camera node converting world socket to local space
if (cameraNode != null) {
final camParent = cameraNode!.parent;
if (camParent != null) {
final invParent = camParent.globalTransform.clone()..invert();
cameraNode!.localTransform = invParent * _cachedSocketTransform;
} else {
cameraNode!.localTransform = _cachedSocketTransform;
}
}
}