//////////////////////////////////////////////////////////////// public void OnPlayerLeftAnchorZone(CameraAnchorZone anchorZone) { //Debug.Log("Exit Anchor Zone"); AssetDataCamera assetDataCamera = AssetManager.GetAssetData <AssetDataCamera>(); m_PreviousScaleFactor = m_ZoomFactor; m_DesiredScaleFactor = 1.0f; m_ZoomTimeRemaining = assetDataCamera.ZoomTime; if (m_CurrentAnchorZone != anchorZone) { // Valid Case: If two zones are close by, it can happen that we first enter the new zone, then leave the old zone. Unity Events I guess. return; } //////////////////////////////////////////////////////////////// m_CurrentAnchorZone?.SetCameraCollidersActive(false); m_CurrentAnchorZone = null; if (m_CameraMode != CameraModes.LockOnPlayer) { SetCameraMode(CameraModes.TransitionToFocusPlayer); } }
//////////////////////////////////////////////////////////////// public void TickTrauma() { if (m_CurrentTrauma == 0.0f) { return; } //////////////////////////////////////////////////////////////// AssetDataCamera assetDataCamera = AssetManager.GetAssetData <AssetDataCamera>(); m_CurrentTrauma -= assetDataCamera.ReduceTraumaPerTickBy; if (m_CurrentTrauma <= 0.0f) { // End Trauma! ResetTrauma(); m_Camera.transform.localPosition = Vector3.zero; return; } m_CurrentSmoothedTrauma = Mathf.Lerp(m_CurrentSmoothedTrauma, m_CurrentTrauma, 0.2f); float currentTraumaSQ = m_CurrentSmoothedTrauma * m_CurrentSmoothedTrauma; float noise1 = Mathf.PerlinNoise(Time.time * assetDataCamera.ScreenShakeSpeed, 0.0f) * assetDataCamera.ScreenShakeMaxAmountX - assetDataCamera.ScreenShakeMaxAmountX / 2.0f; float noise2 = Mathf.PerlinNoise(0.0f, Time.time * assetDataCamera.ScreenShakeSpeed) * assetDataCamera.ScreenShakeMaxAmountY - assetDataCamera.ScreenShakeMaxAmountY / 2.0f; Vector2 screenShakePositionOffset = new Vector2(currentTraumaSQ * noise1, currentTraumaSQ * noise2); m_Camera.transform.localPosition = screenShakePositionOffset.To3D_Z(); }
//////////////////////////////////////////////////////////////// void SetupParallaxLayers() { AssetDataCamera assetDataCamera = AssetManager.GetAssetData <AssetDataCamera>(); SceneContext context = SceneContext.MetaContext(); m_ParallaxCoreObject = new GameObject("~ ParallaxRenderer"); m_ParallaxCoreObject.transform.parent = GameCore.Get().transform; for (int i = 0; i < assetDataCamera._ParallaxMaxLayers; i++) { GameObject parallaxObject = new GameObject("Layer " + i); parallaxObject.transform.parent = m_ParallaxCoreObject.transform; SpriteRenderer spriteRenderer = parallaxObject.AddComponent <SpriteRenderer>(); spriteRenderer.sprite = null; spriteRenderer.sortingLayerName = "Background"; spriteRenderer.sortingOrder = i; spriteRenderer.drawMode = SpriteDrawMode.Tiled; spriteRenderer.tileMode = SpriteTileMode.Continuous; ParallaxLayer layer = new ParallaxLayer() { Renderer = spriteRenderer, DimensionsWS = Vector2.one }; m_ParallaxLayers.Add(layer); } context.Unset(); }
//////////////////////////////////////////////////////////////// void TickCameraScale() { AssetDataCamera assetDataCamera = AssetManager.GetAssetData <AssetDataCamera>(); if (m_ZoomTimeRemaining > 0.0f) { m_ZoomTimeRemaining -= Time.fixedDeltaTime; float newScaleFactor = Mathf.LerpAngle(m_PreviousScaleFactor, m_DesiredScaleFactor, 1.0f - (m_ZoomTimeRemaining / assetDataCamera.ZoomTime)); SetCameraZoom(newScaleFactor); } }
//////////////////////////////////////////////////////////////// public void OnPlayerEnteredAnchorZone(CameraAnchorZone anchorZone) { //Debug.Log("Enter Anchor Zone"); m_CurrentAnchorZone?.SetCameraCollidersActive(false); m_CurrentAnchorZone = anchorZone; SetCameraMode(CameraModes.TransitionToAnchorZone); AssetDataCamera assetDataCamera = AssetManager.GetAssetData <AssetDataCamera>(); m_PreviousScaleFactor = m_ZoomFactor; m_DesiredScaleFactor = anchorZone.CameraZoom; m_ZoomTimeRemaining = assetDataCamera.ZoomTime; anchorZone.SetCameraCollidersActive(true); }
//////////////////////////////////////////////////////////////// public override void Initialize() { JSDK.Events.EventManager.Get().AddListener <PlayerResetEvent>(OnPlayerReset); JSDK.Events.EventManager.Get().AddListener <PlayerDeathEvent>(OnPlayerDeath); m_CameraMode = CameraModes.FocusPlayer; m_CurrentAnchorZone = null; AssetDataCamera assetDataCamera = AssetManager.GetAssetData <AssetDataCamera>(); { SceneContext context = SceneContext.MetaContext(); m_Camera = Camera.main; m_CameraHolder = m_Camera.transform.parent.gameObject; m_BaseSizeY = assetDataCamera.SizeY / 2.0f; m_BaseSizeX = m_BaseSizeY * Screen.width / (float)Screen.height; SetCameraZoom(1.0f); m_ScreenEffectRenderer = m_CameraHolder.GetComponentInChildren <MeshRenderer>(); m_ScreenEffectRenderer.transform.position = m_ScreenEffectRenderer.transform.position.xy().To3D_Z(); context.Unset(); } }
//////////////////////////////////////////////////////////////// void TickCameraMovement() { float SIGNIFICANT_MOVE = 0.01f; AssetDataCamera assetDataCamera = AssetManager.GetAssetData <AssetDataCamera>(); //////////////////////////////////////////////////////////////// Vector2 unclampedBaseMoveVector = m_FocusPosition - m_CameraHolder.transform.position.xy(); float unclampedBaseMoveMagnitude = unclampedBaseMoveVector.magnitude; // Only for non locked modes float movementSimilarity = Vector2.Dot(m_LastLerpedCameraMove, (m_CameraHolder.transform.position.xy() - m_FocusPosition).normalized); float movementSimilarityFactor = MathExtensions.Remap(movementSimilarity, -1.0f, 1.0f, 0.1f, 1.0f); if (m_CameraMode != CameraModes.LockOnPlayer) { float lerpFactor = assetDataCamera.Smoothness2 * Time.deltaTime; unclampedBaseMoveVector = m_FocusPosition - Vector2.Lerp(m_CameraHolder.transform.position.xy(), m_FocusPosition, assetDataCamera.Smoothness2 * Time.deltaTime * movementSimilarityFactor); // m_FocusPosition - m_Camera.transform.position.xy(); unclampedBaseMoveMagnitude = unclampedBaseMoveVector.magnitude; } m_CollidedXLastTick = false; m_CollidedYLastTick = false; if (m_DoDebugRendering) { DebugDrawingInterface.DrawPersistentLine(m_CameraHolder.transform.position.xy(), m_CameraHolder.transform.position.xy() + unclampedBaseMoveVector, new Color(0.0f, 0.0f, 1.0f, 0.8f), m_TickEvery - 1); DebugDrawingInterface.DrawPersistentLine(m_FocusPosition, m_CameraHolder.transform.position.xy(), new Color(1.0f, 1.0f, 1.0f, 0.3f), m_TickEvery - 1); DebugDrawingInterface.DrawPersistentLine(m_CameraHolder.transform.position.xy(), m_CameraHolder.transform.position.xy() + m_LastLerpedCameraMove, new Color(1.0f, 0.0f, 0.0f, 0.8f), m_TickEvery - 1); DebugDrawingInterface.DrawPersistentSSText("Uncl Lerped. Delta: " + unclampedBaseMoveVector.x + ", Y " + unclampedBaseMoveVector.y, new Vector2(5, 105), Color.white, m_TickEvery - 1); DebugDrawingInterface.DrawPersistentSSText("Camera Mode: " + m_CameraMode.ToString(), new Vector2(5, 135), Color.white, m_TickEvery - 1); DebugDrawingInterface.DrawPersistentSSText("CollidedLastTick X: " + m_CollidedXLastTick.ToString() + ", Y: " + m_CollidedYLastTick.ToString(), new Vector2(5, 165), Color.white, m_TickEvery - 1); } if (Mathf.Abs(unclampedBaseMoveVector.x) < SIGNIFICANT_MOVE && Mathf.Abs(unclampedBaseMoveVector.y) < SIGNIFICANT_MOVE) { return; } //Vector2 clampedDelta = new Vector2(Mathf.Clamp(deltaX, -assetDataCamera.MaximumSpeedFreeCam, assetDataCamera.MaximumSpeedFreeCam), // Mathf.Clamp(deltaY, -assetDataCamera.MaximumSpeedFreeCam, assetDataCamera.MaximumSpeedFreeCam)); // Get colliders we collide with //Vector2 cameraCollider = GetCameraColliderSize(); //float checkDistance = assetDataCamera.MaximumSpeedFreeCam * Mathf.Sqrt(2); //m_RaycastHitCount = Physics2D.BoxCastNonAlloc(m_Camera.transform.position, cameraCollider, 0.0f, unclampedMoveVector, m_RaycastHits, checkDistance, (int) CommonLayerMasks.Camera); //int? colliderIndex = GetFirstNonTriggerCollider(m_RaycastHits, m_RaycastHitCount, ignoreAnchorCollider); //// Early Out: We do not collide with anything, so we can just move //if (!colliderIndex.HasValue) //{ // Vector2 moveVector = unclampedMoveVector; // moveVector.x = Mathf.Clamp(moveVector.x, -assetDataCamera.MaximumSpeedFreeCam, assetDataCamera.MaximumSpeedFreeCam); // moveVector.y = Mathf.Clamp(moveVector.y, -assetDataCamera.MaximumSpeedFreeCam, assetDataCamera.MaximumSpeedFreeCam); // m_Camera.transform.position += moveVector.To3D_Z(); // return; //} //////////////////////////////////////////////////////////////// // 2) Perform movement along these axes bool ignoreAnchorColliders = m_CameraMode == CameraModes.TransitionToAnchorZone || m_CameraMode == CameraModes.TransitionToFocusPlayer || m_CameraMode == CameraModes.LockOnPlayer; Vector2 moveAxisMain = Vector2.right; Vector2 moveAxisSecondary = Vector2.up; Vector2 moveMain = moveAxisMain * Vector2.Dot(unclampedBaseMoveVector, moveAxisMain); Vector2 moveSecondary = moveAxisSecondary * Vector2.Dot(unclampedBaseMoveVector, moveAxisSecondary); Vector2 newPosition = m_CameraHolder.transform.position.xy(); // Movement Main (X only at the moment) bool performMovementMain = Mathf.Abs(moveMain.x) > SIGNIFICANT_MOVE; if (performMovementMain) { Vector2 validPositionAfterMove = newPosition + moveMain; // Move the full move ? float paddingX = m_BaseSizeX * 1.0f / m_ZoomFactor; m_RaycastHitCount = Physics2D.RaycastNonAlloc(newPosition, moveMain, m_RaycastHits, paddingX + moveMain.magnitude, (int)CommonLayerMasks.Camera); int?colliderIndex = GetFirstNonTriggerCollider(m_RaycastHits, m_RaycastHitCount, ignoreAnchorColliders); if (colliderIndex.HasValue) { Debug.Assert(!m_RaycastHits[colliderIndex.Value].collider.isTrigger); //////////////////////////////////////////////////////////////// // No, we hit something: Only move until the collision! validPositionAfterMove.x = m_RaycastHits[colliderIndex.Value].point.x - (moveMain.normalized.x * paddingX); m_CollidedXLastTick = true; } newPosition = validPositionAfterMove; } // Movement Secondary (Y only at the moment) bool performMovementSecondary = Mathf.Abs(moveSecondary.y) > SIGNIFICANT_MOVE; if (performMovementSecondary) { Vector2 validPositionAfterMove = newPosition + moveSecondary; // Move the full move ? float paddingY = m_BaseSizeY * 1.0f / m_ZoomFactor; m_RaycastHitCount = Physics2D.RaycastNonAlloc(newPosition, moveSecondary, m_RaycastHits, paddingY + moveSecondary.magnitude, (int)CommonLayerMasks.Camera); int?colliderIndex = GetFirstNonTriggerCollider(m_RaycastHits, m_RaycastHitCount, ignoreAnchorColliders); if (colliderIndex.HasValue) { Debug.Assert(!m_RaycastHits[colliderIndex.Value].collider.isTrigger); //////////////////////////////////////////////////////////////// // No, we hit something: Only move until the collision! validPositionAfterMove.y = m_RaycastHits[colliderIndex.Value].point.y - (moveSecondary.normalized.y * paddingY); m_CollidedYLastTick = true; } newPosition = validPositionAfterMove; } float startSlowMoveAtDistance = 2.0f; float minMoveFactor = 0.1f; float maxMove = Mathf.Lerp(assetDataCamera.MaximumSpeedFreeCam * minMoveFactor, assetDataCamera.MaximumSpeedFreeCam, unclampedBaseMoveMagnitude / startSlowMoveAtDistance); if (m_CameraMode == CameraModes.LockOnPlayer) { maxMove *= 2.5f; } //DebugDrawingInterface.DrawPersistentSSText("MaxMove " + maxMove.ToString() + " (MoveMagnitude: " + unclampedBaseMoveMagnitude + ")", new Vector2(5, 195), Color.blue, m_TickEvery); Vector2 moveVector = newPosition - m_CameraHolder.transform.position.xy(); moveVector.x = Mathf.Clamp(moveVector.x, -maxMove, maxMove); moveVector.y = Mathf.Clamp(moveVector.y, -maxMove, maxMove); if (m_CameraMode != CameraModes.LockOnPlayer) { m_LastLerpedCameraMove = Vector2.Lerp(m_LastLerpedCameraMove.normalized, moveVector.normalized, movementSimilarityFactor); } else { m_LastLerpedCameraMove = moveVector.normalized; } m_CameraHolder.transform.position += moveVector.To3D_Z(); }
//////////////////////////////////////////////////////////////// void TickCameraModes(PlayerController player) { AssetDataCamera assetDataCamera = AssetManager.GetAssetData <AssetDataCamera>(); // 1) Find Target Position Vector2 cameraColliderSize = GetCameraColliderSize(); Vector3 desiredFocusPosition = Vector3.zero; switch (m_CameraMode) { case CameraModes.FocusPlayer: case CameraModes.TransitionToFocusPlayer: case CameraModes.LockOnPlayer: Vector2 movementPrediction = player.SmoothMovingDirection * assetDataCamera.MovementDirectionOffsetPercentages * cameraColliderSize; desiredFocusPosition = player.transform.position + movementPrediction.To3D_Z(); break; case CameraModes.TransitionToAnchorZone: desiredFocusPosition = m_CurrentAnchorZone.GetTargetCameraPosition(cameraColliderSize, PlayerManager.Get().GetPlayerController().transform.position); break; } //////////////////////////////////////////////////////////////// // 2) Find Reachable Target Position float outSideThreshholdFactorX = 1.0f; float outSideThreshholdFactorY = 1.0f; Vector2 reachableDesiredFocusPositionWS = desiredFocusPosition; { Vector2 desiredFocusDelta = desiredFocusPosition.xy() - m_CameraHolder.transform.position.xy(); Vector2 cameraExtendsWS = m_Camera.orthographicSize * new Vector2(1.0f, 1.0f * 9.0f / 16.0f); Vector2 cameraThreshholdWS = cameraExtendsWS * new Vector2(1.0f - assetDataCamera.MoveThreshholdPercentageX, 1.0f - assetDataCamera.MoveThreshholdPercentageY); if (m_DoDebugRendering) { DebugDrawingInterface.DrawPersistentPointBox(m_CameraHolder.transform.position.xy(), 0.2f, Color.gray, m_TickEvery - 1); DebugDrawingInterface.DrawPersistentPointBox(m_FocusPosition, 0.2f, Color.white, m_TickEvery - 1); DebugDrawingInterface.DrawPersistentPointBox(reachableDesiredFocusPositionWS, 0.2f, Color.black, m_TickEvery - 1); Vector2 cameraThreshholdBottomRight = m_CameraHolder.transform.position.xy() - cameraThreshholdWS; DebugDrawingInterface.DrawPersistentLines(new List <Vector3>() { cameraThreshholdBottomRight, cameraThreshholdBottomRight + new Vector2(2 * cameraThreshholdWS.x, 0), cameraThreshholdBottomRight + new Vector2(2 * cameraThreshholdWS.x, 0), cameraThreshholdBottomRight + 2 * cameraThreshholdWS, cameraThreshholdBottomRight + 2 * cameraThreshholdWS, cameraThreshholdBottomRight + new Vector2(0, 2 * cameraThreshholdWS.y), cameraThreshholdBottomRight + new Vector2(0, 2 * cameraThreshholdWS.y), cameraThreshholdBottomRight }, Color.white, m_TickEvery - 1); DebugDrawingInterface.DrawPersistentSSText("Anchor Zone: " + m_CurrentAnchorZone?.ToString(), new Vector2(5, 85), Color.white, m_TickEvery - 1); } if (m_CameraMode == CameraModes.FocusPlayer || m_CameraMode == CameraModes.TransitionToFocusPlayer) { // Modify Target Position if we are focusing the player. float absFocusDeltaX = Mathf.Abs(desiredFocusDelta.x); float absFocusDeltaY = Mathf.Abs(desiredFocusDelta.y); bool isPlayerOutsideOfCameraThreshholdX = absFocusDeltaX > cameraThreshholdWS.x; bool isPlayerOutsideOfCameraThreshholdY = absFocusDeltaY > cameraThreshholdWS.y; outSideThreshholdFactorX = MathExtensions.Remap(absFocusDeltaX, cameraThreshholdWS.x, cameraExtendsWS.x, 1.0f, 1.5f); outSideThreshholdFactorY = MathExtensions.Remap(absFocusDeltaY, cameraThreshholdWS.y, cameraExtendsWS.y, 1.0f, 1.5f); //////////////////////////////////////////////////////////////// reachableDesiredFocusPositionWS = m_CameraHolder.transform.position; if (isPlayerOutsideOfCameraThreshholdX) { if (desiredFocusDelta.x < 0) { reachableDesiredFocusPositionWS.x -= (-cameraThreshholdWS.x - desiredFocusDelta.x); } else { reachableDesiredFocusPositionWS.x -= (cameraThreshholdWS.x - desiredFocusDelta.x); } } if (isPlayerOutsideOfCameraThreshholdY) { if (desiredFocusDelta.y < 0) { reachableDesiredFocusPositionWS.y -= (-cameraThreshholdWS.y - desiredFocusDelta.y); } else { reachableDesiredFocusPositionWS.y -= (cameraThreshholdWS.y - desiredFocusDelta.y); } } } } //////////////////////////////////////////////////////////////// // 3) Find focus position if (m_CameraMode != CameraModes.LockOnPlayer) { m_FocusPosition = new Vector2(Mathf.Lerp(m_FocusPosition.x, reachableDesiredFocusPositionWS.x, assetDataCamera.Smoothness * Time.deltaTime * outSideThreshholdFactorX), Mathf.Lerp(m_FocusPosition.y, reachableDesiredFocusPositionWS.y, assetDataCamera.Smoothness * Time.deltaTime * outSideThreshholdFactorY)); } else { m_FocusPosition = reachableDesiredFocusPositionWS; } Vector2 focusDelta = m_FocusPosition - m_CameraHolder.transform.position.xy(); //////////////////////////////////////////////////////////////// // 4) Check for reached target bool reachedTarget = (Mathf.Abs(focusDelta.x) < 0.1f) && (Mathf.Abs(focusDelta.y) < 0.1f); if (reachedTarget) { switch (m_CameraMode) { case CameraModes.LockOnPlayer: case CameraModes.FocusPlayer: break; case CameraModes.TransitionToFocusPlayer: SetCameraMode(CameraModes.FocusPlayer); break; case CameraModes.TransitionToAnchorZone: SetCameraMode(CameraModes.FocusPlayer); break; } } }
//////////////////////////////////////////////////////////////// void OnPlayerDeath(PlayerDeathEvent e) { AssetDataCamera assetDataCamera = AssetManager.GetAssetData <AssetDataCamera>(); AddTrauma(assetDataCamera.TraumaOnPlayerDeath); }
//////////////////////////////////////////////////////////////// public void PopBlob(BlobComponent blob) { int blobHiveIndex = -1; for (int i = 0; i < m_BlobHives.Count; i++) { if (!m_BlobHives[i]._Hive.Equals(blob.ParentBlobHive)) { continue; } blobHiveIndex = i; break; } if (blobHiveIndex == -1) { Debug.LogError("Could not find parent blob hive in playerManager for blob-to-be-plopped " + blob.gameObject.name); return; } BlobHiveData data = m_BlobHives[blobHiveIndex]; //////////////////////////////////////////////////////////////// AssetDataEntities assetDataBlobs = AssetManager.GetAssetData <AssetDataEntities>(); data._PloppedTimers[blob.ParentBlobInHiveID] = 10000; //< We dont want plops to regrow. // assetDataBlobs.PloppedFor; blob.TransitionToState(BlobState.Destroyed, false); EventManager.Get().FireEvent(new PlaySoundEvent(Sounds.FX_SmallBlop, blob.gameObject)); AssetDataCamera assetDataCamera = AssetManager.GetAssetData <AssetDataCamera>(); CameraManager.Get().AddTrauma(assetDataCamera.TraumaOnBlobPop); m_PlayerController.RegainDash(PlayerController.RegainDashContext.TemporaryReset); //////////////////////////////////////////////////////////////// // Check if the hive is now deactivated if (data._Hive.Deactivatebale) { bool deactiveBlobHive = true; for (int i = 0; i < data._PloppedTimers.Length; i++) { if (data._PloppedTimers[i] == 0) { deactiveBlobHive = false; break; } } if (deactiveBlobHive) { data._Active = false; data._Hive.SetRelatedObjectsActive(false); } } //////////////////////////////////////////////////////////////// m_BlobHives[blobHiveIndex] = data; }
//////////////////////////////////////////////////////////////// public void SwitchParallaxLayersToArea(AreaID newAreaID, LevelID rootLevel) { AssetDataCamera assetDataCamera = AssetManager.GetAssetData <AssetDataCamera>(); bool success = assetDataCamera._ParallaxLayerDictionary.TryGetValue(newAreaID, out AssetDataCamera.ParallaxAreaData parallaxAreaData); if (!success || parallaxAreaData._LayerDatas.Count == 0) { Debug.LogError("Could not find parallaxAreaData dictionary entry for area " + newAreaID.ToString()); return; } //////////////////////////////////////////////////////////////// Debug.Assert(parallaxAreaData._LayerDatas.Count <= assetDataCamera._ParallaxMaxLayers, "Area " + newAreaID.ToString() + " defines more parallax layers than _ParallaxMaxLayers allows. Consider changing that!"); Vector2 cameraDimensionsWS = new Vector2(16.0f / 9.0f * assetDataCamera.SizeY, assetDataCamera.SizeY); { SceneContext metaContext = SceneContext.MetaContext(); m_CurrentAreaRootWS = parallaxAreaData.generated_AreaBoundsForParallax.position; m_CurrentAreaDimensionsWS = parallaxAreaData.generated_AreaBoundsForParallax.size; for (int i = 0; i < assetDataCamera._ParallaxMaxLayers; i++) { if (parallaxAreaData._LayerDatas.Count <= i) { m_ParallaxLayers[i].Renderer.sprite = null; m_ParallaxLayers[i].Renderer.size = Vector2.one; m_ParallaxLayers[i].DimensionsWS = Vector2.one; } else { /* * Parallax Images come in a 4k by 4k format * They always make up 100% of their parallax layers height * They tile in x-Direction * * Parallax Layers are sized by multiplying the area size with a factor * Then, a parallax renderer is scaled up to make sure that the one sprite it renders fills out the full layer. * This scales influence is only for the renderer, all other logic stays the same. */ AssetDataCamera.ParallaxLayerData layerData = parallaxAreaData._LayerDatas[i]; m_ParallaxLayers[i].Renderer.sprite = layerData._Sprite; if (layerData._Sprite == null) { continue; } Vector2 coveredDimensionsWS = Vector2.one; switch (layerData._OrientLayerSizeAt) { case AssetDataCamera.ParallaxLayerData.OrientSizeAt.Area: coveredDimensionsWS = m_CurrentAreaDimensionsWS * layerData._LayerToAreaSizeRatio; break; case AssetDataCamera.ParallaxLayerData.OrientSizeAt.Screen: coveredDimensionsWS = cameraDimensionsWS * layerData._LayerToScreenSizeRatio; break; default: Debug.LogError("Did you miss a new enum entry here?"); break; } float rendererScaleFactor = coveredDimensionsWS.y / cameraDimensionsWS.y; m_ParallaxLayers[i].Renderer.transform.localScale = Vector3.one * rendererScaleFactor; // > Make it so that the sprite renderer renders the sprite exactly once in y axis // > Then, we can just set the width to how wide (/scaleFactor) we want to be and tiling will do the rest for us. Vector2 rendererSize = new Vector2(coveredDimensionsWS.x / rendererScaleFactor, layerData._Sprite.texture.height / layerData._Sprite.pixelsPerUnit); m_ParallaxLayers[i].Renderer.size = rendererSize; m_ParallaxLayers[i].DimensionsWS = rendererSize * rendererScaleFactor; } } metaContext.Unset(); } m_CurrentAreaID = newAreaID; UpdateParallaxLayerPositions(); }