using Core; using Minigames.Airplane.Data; using UnityEngine; namespace Minigames.Airplane.Interactive { /// /// Parallax element that adjusts position based on camera movement. /// Creates depth illusion by moving at different speeds for different layers. /// Configuration is provided by ParallaxBackgroundSpawner at initialization. /// IMPORTANT: Always calculates position relative to ORIGINAL spawn position and camera, /// ensuring deterministic behavior during camera blends and switches. /// public class ParallaxElement : MonoBehaviour { // Runtime state (set by spawner via Initialize) private ParallaxLayer _layer; private float _layerSpeed; private float _globalStrength; // Current camera being tracked private Transform _cameraTransform; // ORIGINAL spawn state - NEVER reset after initialization // This ensures objects return to spawn position when camera returns to spawn position private Vector3 _originalSpawnPosition; private float _originalCameraX; private bool _isInitialized; private void Update() { if (!_isInitialized || _cameraTransform == null) return; ApplyParallax(); } /// /// Initialize the parallax element with spawner-provided settings. /// Called by ParallaxBackgroundSpawner when spawning. /// Sets the ORIGINAL spawn position and camera position - these are never reset. /// IMPORTANT: Uses Camera.main for tracking to ensure smooth updates during Cinemachine blends. /// public void Initialize(ParallaxLayer layer, float layerSpeed, float globalStrength, Transform cameraTransform) { _layer = layer; _layerSpeed = layerSpeed; _globalStrength = globalStrength; // ALWAYS use Camera.main for tracking - this ensures smooth updates during Cinemachine blends // Virtual cameras don't move during blends, but Camera.main does _cameraTransform = Camera.main != null ? Camera.main.transform : cameraTransform; // Store ORIGINAL spawn position - never reset _originalSpawnPosition = transform.position; if (_cameraTransform != null) { // Store ORIGINAL camera X - never reset _originalCameraX = _cameraTransform.position.x; _isInitialized = true; } } /// /// Update the camera transform when camera changes. /// Called by ParallaxBackgroundSpawner when camera switches or blends. /// ONLY updates the camera reference - does NOT reset start position or camera X. /// This ensures parallax remains deterministic across camera switches. /// IMPORTANT: Always uses Camera.main to ensure smooth updates during blends. /// public void UpdateCamera(Transform newCameraTransform) { // ALWAYS use Camera.main for tracking - this ensures smooth updates during Cinemachine blends // We ignore the passed transform and always use Camera.main Transform mainCamera = Camera.main != null ? Camera.main.transform : newCameraTransform; if (mainCamera == null) return; // Simply update the camera reference - do NOT reset positions // The parallax will continue to calculate relative to original spawn state _cameraTransform = mainCamera; _isInitialized = true; } private void ApplyParallax() { // Calculate camera displacement from ORIGINAL camera position // This ensures objects return to spawn position when camera returns to spawn position float cameraDisplacement = _cameraTransform.position.x - _originalCameraX; // Calculate parallax offset - negative to move opposite direction // Lower speed = appears further away (moves less) // Camera moves right (+) → background moves left (-) at reduced speed float parallaxOffset = -cameraDisplacement * _layerSpeed * _globalStrength; // Apply offset to ORIGINAL spawn position Vector3 newPosition = _originalSpawnPosition; newPosition.x += parallaxOffset; transform.position = newPosition; } } }