using System.Collections;
|
using System.Collections.Generic;
|
using UnityEngine;
|
|
public class BackgroundManager : MonoBehaviour
|
{
|
[System.Serializable]
|
public class BackgroundLayer
|
{
|
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 BackgroundLayer[] backgroundLayers;
|
[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()
|
{
|
mainCamera = Camera.main;
|
cameraTransform = mainCamera.transform;
|
lastCameraPosition = cameraTransform.position;
|
|
if (spriteRenderer == null)
|
spriteRenderer = GetComponent<SpriteRenderer>();
|
|
if (backgroundLayers != null && backgroundLayers.Length > 0)
|
{
|
Sprite firstSprite = backgroundLayers[0].backgroundSprite;
|
tileSize = new Vector2(firstSprite.bounds.size.x, firstSprite.bounds.size.y);
|
}
|
// 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;
|
|
// Update X position with parallax
|
float parallaxX = (cameraTransform.position.x - startPositionX) * GetCurrentParallaxEffect();
|
newPosition.x = startPositionX + parallaxX;
|
|
// Update Y position to follow camera
|
float parallaxY = (cameraTransform.position.y - startPositionY) * GetCurrentParallaxEffect();
|
newPosition.y = startPositionY + parallaxY;
|
|
transform.position = newPosition;
|
|
// Check for background change
|
float currentY = playerTransform.position.y;
|
int newLayerIndex = currentLayerIndex;
|
|
for (int i = 0; i < backgroundLayers.Length; i++)
|
{
|
if (currentY > backgroundLayers[i].yThreshold)
|
{
|
newLayerIndex = i;
|
break;
|
}
|
}
|
|
if (newLayerIndex != currentLayerIndex && !isTransitioning) // Add transition check
|
{
|
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()
|
{
|
if (backgroundLayers == null || backgroundLayers.Length == 0)
|
return 0.5f;
|
return backgroundLayers[currentLayerIndex].parallaxEffect;
|
}
|
|
private IEnumerator TransitionBackground(BackgroundLayer newLayer)
|
{
|
if (isTransitioning || newLayer.backgroundSprite.name == backgroundLayers[lastLayerIndex].backgroundSprite.name)
|
yield break;
|
|
isTransitioning = true;
|
bool initialMovingUp = isMovingUp;
|
float initialY = playerTransform.position.y;
|
|
// Create transition renderers for all existing tiles
|
foreach (var tile in backgroundTiles)
|
{
|
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 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;
|
}
|
}
|