From ab5a24f4c0cffbc8e8f76e40293f2201027b7c50 Mon Sep 17 00:00:00 2001 From: miepzerino <o.skotnik@gmail.com> Date: Mon, 07 Apr 2025 22:59:16 +0000 Subject: [PATCH] #49 added parallax and tiling to background --- Assets/BackgroundManager.cs | 260 ++++++++++++++++++++++++++++++++++++++++++++------- 1 files changed, 224 insertions(+), 36 deletions(-) diff --git a/Assets/BackgroundManager.cs b/Assets/BackgroundManager.cs index 7679293..306cac4 100644 --- a/Assets/BackgroundManager.cs +++ b/Assets/BackgroundManager.cs @@ -9,48 +9,118 @@ { public float yThreshold; public Sprite backgroundSprite; + public float transitionDistance = 3f; // Distance over which the transition occurs [Range(0f, 1f)] public float parallaxEffect = 0.5f; } [SerializeField] private SpriteRenderer spriteRenderer; - [SerializeField] private SpriteRenderer transitionRenderer; // Add this field [SerializeField] private BackgroundLayer[] backgroundLayers; - [SerializeField] private float transitionDuration = 1f; + [SerializeField] private Transform playerTransform; // Add this field + [SerializeField] private float tileBuffer = 1f; // Extra tiles beyond camera view private bool isTransitioning = false; // Add this field private Transform cameraTransform; private Vector3 lastCameraPosition; private int currentLayerIndex = 0; + private int lastLayerIndex = 0; private float startPositionX; + private float startPositionY; + + private float lastUpdatePlayerY = 0f; // Track the last Y position of the player + private bool isMovingUp = false; // Track if the player is moving up + + private Dictionary<Vector2Int, SpriteRenderer> backgroundTiles = new Dictionary<Vector2Int, SpriteRenderer>(); + private Dictionary<Vector2Int, SpriteRenderer> transitionTiles = new Dictionary<Vector2Int, SpriteRenderer>(); + private Vector2 tileSize; + private Camera mainCamera; private void Start() { - cameraTransform = Camera.main.transform; + mainCamera = Camera.main; + cameraTransform = mainCamera.transform; lastCameraPosition = cameraTransform.position; - startPositionX = transform.position.x; - + if (spriteRenderer == null) spriteRenderer = GetComponent<SpriteRenderer>(); - // Set initial background if (backgroundLayers != null && backgroundLayers.Length > 0) - spriteRenderer.sprite = backgroundLayers[0].backgroundSprite; - - if (transitionRenderer == null) { - var transitionObj = new GameObject("TransitionLayer"); - transitionObj.transform.parent = transform; - transitionObj.transform.localPosition = new Vector3(0, 0, -2); // Set Z to -2 - transitionObj.transform.localScale = Vector3.one; // Match scale - transitionRenderer = transitionObj.AddComponent<SpriteRenderer>(); - transitionRenderer.sortingOrder = spriteRenderer.sortingOrder + 1; - transitionRenderer.sortingLayerName = spriteRenderer.sortingLayerName; // Match background layer + Sprite firstSprite = backgroundLayers[0].backgroundSprite; + tileSize = new Vector2(firstSprite.bounds.size.x, firstSprite.bounds.size.y); } - transitionRenderer.color = new Color(1, 1, 1, 0); // Start fully transparent + // Set initial background based on player position + if (backgroundLayers != null && backgroundLayers.Length > 0) + { + float playerY = playerTransform.position.y; + int initialLayerIndex = 0; + + // Find the correct background layer for the player's position + for (int i = 0; i < backgroundLayers.Length; i++) + { + if (playerY > backgroundLayers[i].yThreshold) + { + initialLayerIndex = i; + break; + } + } + + spriteRenderer.sprite = backgroundLayers[initialLayerIndex].backgroundSprite; + currentLayerIndex = initialLayerIndex; + lastLayerIndex = currentLayerIndex; + } + InitializeInitialTiles(); + + startPositionX = playerTransform.position.x; + startPositionY = playerTransform.position.y; + } + private void InitializeInitialTiles() + { + // Calculate visible area + float cameraHeight = 2f * mainCamera.orthographicSize; + float cameraWidth = cameraHeight * mainCamera.aspect; + + // Calculate number of tiles needed + int horizontalTiles = Mathf.CeilToInt(cameraWidth / tileSize.x) + 2; + int verticalTiles = Mathf.CeilToInt(cameraHeight / tileSize.y) + 2; + + // Create initial grid of tiles + for (int x = -horizontalTiles; x <= horizontalTiles; x++) + { + for (int y = -verticalTiles; y <= verticalTiles; y++) + { + CreateTile(new Vector2Int(x, y)); + } + } + } + + private void CreateTile(Vector2Int gridPosition) + { + if (backgroundTiles.ContainsKey(gridPosition)) + return; + + GameObject tileObj = new GameObject($"Tile_{gridPosition.x}_{gridPosition.y}"); + tileObj.transform.parent = transform; + + SpriteRenderer tileSR = tileObj.AddComponent<SpriteRenderer>(); + tileSR.sprite = backgroundLayers[currentLayerIndex].backgroundSprite; + tileSR.sortingOrder = spriteRenderer.sortingOrder; + tileSR.sortingLayerName = spriteRenderer.sortingLayerName; + + // Position the tile + Vector3 position = new Vector3( + gridPosition.x * tileSize.x, + gridPosition.y * tileSize.y, + transform.position.z + ); + tileObj.transform.localPosition = position; + tileObj.transform.localScale = Vector3.one; // Match scale + + backgroundTiles.Add(gridPosition, tileSR); } private void Update() { + UpdateTiles(); // Calculate parallax movement Vector3 cameraDelta = cameraTransform.position - lastCameraPosition; Vector3 newPosition = transform.position; @@ -60,12 +130,13 @@ newPosition.x = startPositionX + parallaxX; // Update Y position to follow camera - newPosition.y = cameraTransform.position.y; + float parallaxY = (cameraTransform.position.y - startPositionY) * GetCurrentParallaxEffect(); + newPosition.y = startPositionY + parallaxY; transform.position = newPosition; // Check for background change - float currentY = cameraTransform.position.y; + float currentY = playerTransform.position.y; int newLayerIndex = currentLayerIndex; for (int i = 0; i < backgroundLayers.Length; i++) @@ -79,11 +150,77 @@ if (newLayerIndex != currentLayerIndex && !isTransitioning) // Add transition check { - StartCoroutine(TransitionBackground(backgroundLayers[newLayerIndex].backgroundSprite)); + StartCoroutine(TransitionBackground(backgroundLayers[newLayerIndex])); currentLayerIndex = newLayerIndex; } lastCameraPosition = cameraTransform.position; + float lastUpdatePlayerYRounded = Mathf.Round(lastUpdatePlayerY * 10) / 10; + float playerYRounded = Mathf.Round(playerTransform.position.y * 10) / 10; + if (lastUpdatePlayerYRounded != playerYRounded) // Check if player has moved + { + if (lastUpdatePlayerYRounded > playerYRounded) // Check if player is moving down + { + isMovingUp = false; + } + else if (lastUpdatePlayerYRounded < playerYRounded) // Check if player is moving up + { + isMovingUp = true; + } + lastUpdatePlayerY = playerYRounded; // Update the last Y position + } + + } + private void UpdateTiles() + { + // Calculate visible area with buffer + float cameraHeight = 2f * mainCamera.orthographicSize * (1f + tileBuffer); + float cameraWidth = cameraHeight * mainCamera.aspect * (1f + tileBuffer); + + // Calculate parallax offset + float parallaxX = (cameraTransform.position.x - startPositionX) * GetCurrentParallaxEffect(); + float parallaxY = (cameraTransform.position.y - startPositionY) * GetCurrentParallaxEffect(); + + // Calculate grid positions that should be visible + Vector2 cameraPos = mainCamera.transform.position; + + // Adjust the buffer based on parallax effect + float parallaxBuffer = 1f; // Base buffer size + float effectiveBuffer = parallaxBuffer * (1f + GetCurrentParallaxEffect()); + + int minX = Mathf.FloorToInt((parallaxX - (cameraWidth / 2 * effectiveBuffer)) / tileSize.x); + int maxX = Mathf.CeilToInt((parallaxX + (cameraWidth / 2 * effectiveBuffer)) / tileSize.x); + int minY = Mathf.FloorToInt((parallaxY - (cameraHeight / 2 * effectiveBuffer)) / tileSize.y); + int maxY = Mathf.CeilToInt((parallaxY + (cameraHeight / 2 * effectiveBuffer)) / tileSize.y); + + // Create new tiles + for (int x = minX; x <= maxX; x++) + { + for (int y = minY; y <= maxY; y++) + { + CreateTile(new Vector2Int(x, y)); + } + } + + // Remove out-of-view tiles + List<Vector2Int> tilesToRemove = new List<Vector2Int>(); + foreach (var tile in backgroundTiles) + { + if (tile.Key.x < minX - 1 || tile.Key.x > maxX + 1 || + tile.Key.y < minY - 1 || tile.Key.y > maxY + 1) + { + tilesToRemove.Add(tile.Key); + } + } + + foreach (var tilePos in tilesToRemove) + { + if (!transitionTiles.ContainsKey(tilePos)) + { + Destroy(backgroundTiles[tilePos].gameObject); + backgroundTiles.Remove(tilePos); + } + } } private float GetCurrentParallaxEffect() @@ -93,29 +230,80 @@ return backgroundLayers[currentLayerIndex].parallaxEffect; } - private IEnumerator TransitionBackground(Sprite newSprite) + private IEnumerator TransitionBackground(BackgroundLayer newLayer) { - if (isTransitioning) yield break; + if (isTransitioning || newLayer.backgroundSprite.name == backgroundLayers[lastLayerIndex].backgroundSprite.name) + yield break; + isTransitioning = true; + bool initialMovingUp = isMovingUp; + float initialY = playerTransform.position.y; - // Setup transition renderer - transitionRenderer.sprite = newSprite; - transitionRenderer.transform.localPosition = Vector3.zero; - - // Fade in new sprite over old one - float elapsedTime = 0; - while (elapsedTime < transitionDuration) + // Create transition renderers for all existing tiles + foreach (var tile in backgroundTiles) { - elapsedTime += Time.deltaTime; - float alpha = elapsedTime / transitionDuration; - transitionRenderer.color = new Color(1, 1, 1, alpha); + GameObject transObj = new GameObject($"TransitionTile_{tile.Key.x}_{tile.Key.y}"); + transObj.transform.parent = transform; + transObj.transform.localPosition = tile.Value.transform.localPosition + new Vector3(0, 0, -0.1f); + transObj.transform.localScale = Vector3.one; + + SpriteRenderer transSR = transObj.AddComponent<SpriteRenderer>(); + transSR.sprite = newLayer.backgroundSprite; + transSR.sortingOrder = tile.Value.sortingOrder + 1; + transSR.sortingLayerName = tile.Value.sortingLayerName; + transSR.color = new Color(1, 1, 1, 0); + + transitionTiles.Add(tile.Key, transSR); + } + + // Fade in new sprites over old ones + while (true) + { + float currentY = playerTransform.position.y; + + // Check if direction changed + if (initialMovingUp != isMovingUp && + ((initialMovingUp ? initialY > currentY : initialY < currentY))) + { + foreach (var transTile in transitionTiles.Values) + { + Destroy(transTile.gameObject); + } + transitionTiles.Clear(); + isTransitioning = false; + yield break; + } + + // Calculate alpha + float alpha = Mathf.Abs(currentY - initialY) / newLayer.transitionDistance; + Color newColor = new Color(1, 1, 1, Mathf.Clamp01(alpha)); + + // Update all transition tiles + foreach (var transTile in transitionTiles.Values) + { + transTile.color = newColor; + } + + if (alpha >= 1f) + break; + yield return null; } - // Switch the sprites - spriteRenderer.sprite = newSprite; - transitionRenderer.color = new Color(1, 1, 1, 0); + // Switch sprites on all tiles + foreach (var tile in backgroundTiles.Values) + { + tile.sprite = newLayer.backgroundSprite; + } + + // Cleanup transition tiles + foreach (var transTile in transitionTiles.Values) + { + Destroy(transTile.gameObject); + } + transitionTiles.Clear(); isTransitioning = false; + lastLayerIndex = currentLayerIndex; } -} \ No newline at end of file +} -- Gitblit v1.9.3