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/Sprites/BG/Sky_tiling.png.meta | 127 +++++++++++++++ Assets/Sprites/BG/Ground_tiling.png.meta | 127 +++++++++++++++ Assets/Scenes/GameplayScene.unity | 12 Assets/BackgroundManager.cs | 203 ++++++++++++++++++++---- Assets/Sprites/BG/Ground_tiling.png | 0 Assets/Sprites/BG/Sky_tiling.png | 0 6 files changed, 426 insertions(+), 43 deletions(-) diff --git a/Assets/BackgroundManager.cs b/Assets/BackgroundManager.cs index 2722ba7..306cac4 100644 --- a/Assets/BackgroundManager.cs +++ b/Assets/BackgroundManager.cs @@ -14,27 +14,40 @@ } [SerializeField] private SpriteRenderer spriteRenderer; - [SerializeField] private SpriteRenderer transitionRenderer; // Add this field [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() { - cameraTransform = Camera.main.transform; + 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) { @@ -53,39 +66,77 @@ spriteRenderer.sprite = backgroundLayers[initialLayerIndex].backgroundSprite; currentLayerIndex = initialLayerIndex; + lastLayerIndex = currentLayerIndex; } + InitializeInitialTiles(); - if (transitionRenderer == null) + 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++) { - 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 + for (int y = -verticalTiles; y <= verticalTiles; y++) + { + CreateTile(new Vector2Int(x, y)); + } } - transitionRenderer.color = new Color(1, 1, 1, 0); // Start fully transparent + } + + 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 - 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++) @@ -120,6 +171,57 @@ } } + 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() { @@ -130,51 +232,78 @@ private IEnumerator TransitionBackground(BackgroundLayer newLayer) { - // Check if the transition is already in progress or if the new sprite is the same as the current one - if (isTransitioning || newLayer.backgroundSprite.name == spriteRenderer.sprite.name) yield break; + if (isTransitioning || newLayer.backgroundSprite.name == backgroundLayers[lastLayerIndex].backgroundSprite.name) + yield break; + isTransitioning = true; - - // Setup transition renderer - transitionRenderer.sprite = newLayer.backgroundSprite; - transitionRenderer.transform.localPosition = Vector3.zero; - - bool initialMovingUp = isMovingUp; // Store the initial moving direction + bool initialMovingUp = isMovingUp; float initialY = playerTransform.position.y; - //bool movingUp = playerTransform.position.y > initialY; - // Fade in new sprite over old one + // 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 the direction has changed - if (initialMovingUp != isMovingUp && - ((initialMovingUp ? initialY > currentY : initialY < currentY) - )) + // Check if direction changed + if (initialMovingUp != isMovingUp && + ((initialMovingUp ? initialY > currentY : initialY < currentY))) { - transitionRenderer.color = new Color(1, 1, 1, 0); + foreach (var transTile in transitionTiles.Values) + { + Destroy(transTile.gameObject); + } + transitionTiles.Clear(); isTransitioning = false; yield break; } - // Calculate the alpha based on the distance moved + // Calculate alpha float alpha = Mathf.Abs(currentY - initialY) / newLayer.transitionDistance; - transitionRenderer.color = new Color(1, 1, 1, Mathf.Clamp01(alpha)); + Color newColor = new Color(1, 1, 1, Mathf.Clamp01(alpha)); - // Check if transition is complete + // Update all transition tiles + foreach (var transTile in transitionTiles.Values) + { + transTile.color = newColor; + } + if (alpha >= 1f) break; yield return null; } - // Ensure the final alpha value is set correctly - transitionRenderer.color = new Color(1, 1, 1, 1); + // Switch sprites on all tiles + foreach (var tile in backgroundTiles.Values) + { + tile.sprite = newLayer.backgroundSprite; + } - // Switch the sprites - spriteRenderer.sprite = newLayer.backgroundSprite; - transitionRenderer.color = new Color(1, 1, 1, 0); + // Cleanup transition tiles + foreach (var transTile in transitionTiles.Values) + { + Destroy(transTile.gameObject); + } + transitionTiles.Clear(); isTransitioning = false; + lastLayerIndex = currentLayerIndex; } } diff --git a/Assets/Scenes/GameplayScene.unity b/Assets/Scenes/GameplayScene.unity index 1d75531..a0b7bb5 100644 --- a/Assets/Scenes/GameplayScene.unity +++ b/Assets/Scenes/GameplayScene.unity @@ -5244,7 +5244,7 @@ serializedVersion: 2 m_LocalRotation: {x: 0, y: 0, z: 0, w: 1} m_LocalPosition: {x: 0, y: 0, z: -2} - m_LocalScale: {x: 5, y: 5, z: 1} + m_LocalScale: {x: 1, y: 1, z: 1} m_ConstrainProportionsScale: 0 m_Children: [] m_Father: {fileID: 796676099} @@ -5314,17 +5314,17 @@ m_Name: m_EditorClassIdentifier: spriteRenderer: {fileID: 2136420134} - transitionRenderer: {fileID: 0} backgroundLayers: - yThreshold: -5 - backgroundSprite: {fileID: 21300000, guid: 7d5be99b0261348468ab492c35422f1b, type: 3} + backgroundSprite: {fileID: 21300000, guid: 7e7e3a72941b2294c8323b1d6db71aaa, type: 3} transitionDistance: 4 - parallaxEffect: 0.439 + parallaxEffect: 0.5 - yThreshold: -300 - backgroundSprite: {fileID: 21300000, guid: 58a6eeb45a0e2674ab35114433129f28, type: 3} + backgroundSprite: {fileID: 21300000, guid: 3597d973a54f81842a7c183a2bc8b55a, type: 3} transitionDistance: 5 - parallaxEffect: 0.482 + parallaxEffect: 0.5 playerTransform: {fileID: 254538002} + tileBuffer: 0.5 --- !u!114 &18669457987763773 MonoBehaviour: m_ObjectHideFlags: 0 diff --git a/Assets/Sprites/BG/Ground_tiling.png b/Assets/Sprites/BG/Ground_tiling.png new file mode 100644 index 0000000..6ee272a --- /dev/null +++ b/Assets/Sprites/BG/Ground_tiling.png Binary files differ diff --git a/Assets/Sprites/BG/Ground_tiling.png.meta b/Assets/Sprites/BG/Ground_tiling.png.meta new file mode 100644 index 0000000..8c6a6bf --- /dev/null +++ b/Assets/Sprites/BG/Ground_tiling.png.meta @@ -0,0 +1,127 @@ +fileFormatVersion: 2 +guid: 3597d973a54f81842a7c183a2bc8b55a +TextureImporter: + internalIDToNameTable: [] + externalObjects: {} + serializedVersion: 12 + mipmaps: + mipMapMode: 0 + enableMipMap: 0 + sRGBTexture: 1 + linearTexture: 0 + fadeOut: 0 + borderMipMap: 0 + mipMapsPreserveCoverage: 0 + alphaTestReferenceValue: 0.5 + mipMapFadeDistanceStart: 1 + mipMapFadeDistanceEnd: 3 + bumpmap: + convertToNormalMap: 0 + externalNormalMap: 0 + heightScale: 0.25 + normalMapFilter: 0 + flipGreenChannel: 0 + isReadable: 0 + streamingMipmaps: 0 + streamingMipmapsPriority: 0 + vTOnly: 0 + ignoreMipmapLimit: 0 + grayScaleToAlpha: 0 + generateCubemap: 6 + cubemapConvolution: 0 + seamlessCubemap: 0 + textureFormat: 1 + maxTextureSize: 2048 + textureSettings: + serializedVersion: 2 + filterMode: 0 + aniso: 1 + mipBias: 0 + wrapU: 1 + wrapV: 1 + wrapW: 1 + nPOTScale: 0 + lightmap: 0 + compressionQuality: 50 + spriteMode: 1 + spriteExtrude: 1 + spriteMeshType: 1 + alignment: 0 + spritePivot: {x: 0.5, y: 0.5} + spritePixelsToUnits: 16 + spriteBorder: {x: 0, y: 0, z: 0, w: 0} + spriteGenerateFallbackPhysicsShape: 1 + alphaUsage: 1 + alphaIsTransparency: 1 + spriteTessellationDetail: -1 + textureType: 8 + textureShape: 1 + singleChannelComponent: 0 + flipbookRows: 1 + flipbookColumns: 1 + maxTextureSizeSet: 0 + compressionQualitySet: 0 + textureFormatSet: 0 + ignorePngGamma: 0 + applyGammaDecoding: 0 + swizzle: 50462976 + cookieLightType: 0 + platformSettings: + - serializedVersion: 3 + buildTarget: DefaultTexturePlatform + maxTextureSize: 4096 + resizeAlgorithm: 0 + textureFormat: -1 + textureCompression: 0 + compressionQuality: 50 + crunchedCompression: 0 + allowsAlphaSplitting: 0 + overridden: 0 + ignorePlatformSupport: 0 + androidETC2FallbackOverride: 0 + forceMaximumCompressionQuality_BC6H_BC7: 0 + - serializedVersion: 3 + buildTarget: Standalone + maxTextureSize: 2048 + resizeAlgorithm: 0 + textureFormat: -1 + textureCompression: 1 + compressionQuality: 50 + crunchedCompression: 0 + allowsAlphaSplitting: 0 + overridden: 0 + ignorePlatformSupport: 0 + androidETC2FallbackOverride: 0 + forceMaximumCompressionQuality_BC6H_BC7: 0 + - serializedVersion: 3 + buildTarget: Server + maxTextureSize: 2048 + resizeAlgorithm: 0 + textureFormat: -1 + textureCompression: 1 + compressionQuality: 50 + crunchedCompression: 0 + allowsAlphaSplitting: 0 + overridden: 0 + ignorePlatformSupport: 0 + androidETC2FallbackOverride: 0 + forceMaximumCompressionQuality_BC6H_BC7: 0 + spriteSheet: + serializedVersion: 2 + sprites: [] + outline: [] + physicsShape: [] + bones: [] + spriteID: 5e97eb03825dee720800000000000000 + internalID: 0 + vertices: [] + indices: + edges: [] + weights: [] + secondaryTextures: [] + nameFileIdTable: {} + mipmapLimitGroupName: + pSDRemoveMatte: 0 + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Sprites/BG/Sky_tiling.png b/Assets/Sprites/BG/Sky_tiling.png new file mode 100644 index 0000000..ecfc0a2 --- /dev/null +++ b/Assets/Sprites/BG/Sky_tiling.png Binary files differ diff --git a/Assets/Sprites/BG/Sky_tiling.png.meta b/Assets/Sprites/BG/Sky_tiling.png.meta new file mode 100644 index 0000000..aef5fda --- /dev/null +++ b/Assets/Sprites/BG/Sky_tiling.png.meta @@ -0,0 +1,127 @@ +fileFormatVersion: 2 +guid: 7e7e3a72941b2294c8323b1d6db71aaa +TextureImporter: + internalIDToNameTable: [] + externalObjects: {} + serializedVersion: 12 + mipmaps: + mipMapMode: 0 + enableMipMap: 0 + sRGBTexture: 1 + linearTexture: 0 + fadeOut: 0 + borderMipMap: 0 + mipMapsPreserveCoverage: 0 + alphaTestReferenceValue: 0.5 + mipMapFadeDistanceStart: 1 + mipMapFadeDistanceEnd: 3 + bumpmap: + convertToNormalMap: 0 + externalNormalMap: 0 + heightScale: 0.25 + normalMapFilter: 0 + flipGreenChannel: 0 + isReadable: 0 + streamingMipmaps: 0 + streamingMipmapsPriority: 0 + vTOnly: 0 + ignoreMipmapLimit: 0 + grayScaleToAlpha: 0 + generateCubemap: 6 + cubemapConvolution: 0 + seamlessCubemap: 0 + textureFormat: 1 + maxTextureSize: 2048 + textureSettings: + serializedVersion: 2 + filterMode: 0 + aniso: 1 + mipBias: 0 + wrapU: 1 + wrapV: 1 + wrapW: 1 + nPOTScale: 0 + lightmap: 0 + compressionQuality: 50 + spriteMode: 1 + spriteExtrude: 1 + spriteMeshType: 1 + alignment: 0 + spritePivot: {x: 0.5, y: 0.5} + spritePixelsToUnits: 16 + spriteBorder: {x: 0, y: 0, z: 0, w: 0} + spriteGenerateFallbackPhysicsShape: 1 + alphaUsage: 1 + alphaIsTransparency: 1 + spriteTessellationDetail: -1 + textureType: 8 + textureShape: 1 + singleChannelComponent: 0 + flipbookRows: 1 + flipbookColumns: 1 + maxTextureSizeSet: 0 + compressionQualitySet: 0 + textureFormatSet: 0 + ignorePngGamma: 0 + applyGammaDecoding: 0 + swizzle: 50462976 + cookieLightType: 0 + platformSettings: + - serializedVersion: 3 + buildTarget: DefaultTexturePlatform + maxTextureSize: 4096 + resizeAlgorithm: 0 + textureFormat: -1 + textureCompression: 0 + compressionQuality: 50 + crunchedCompression: 0 + allowsAlphaSplitting: 0 + overridden: 0 + ignorePlatformSupport: 0 + androidETC2FallbackOverride: 0 + forceMaximumCompressionQuality_BC6H_BC7: 0 + - serializedVersion: 3 + buildTarget: Standalone + maxTextureSize: 2048 + resizeAlgorithm: 0 + textureFormat: -1 + textureCompression: 1 + compressionQuality: 50 + crunchedCompression: 0 + allowsAlphaSplitting: 0 + overridden: 0 + ignorePlatformSupport: 0 + androidETC2FallbackOverride: 0 + forceMaximumCompressionQuality_BC6H_BC7: 0 + - serializedVersion: 3 + buildTarget: Server + maxTextureSize: 2048 + resizeAlgorithm: 0 + textureFormat: -1 + textureCompression: 1 + compressionQuality: 50 + crunchedCompression: 0 + allowsAlphaSplitting: 0 + overridden: 0 + ignorePlatformSupport: 0 + androidETC2FallbackOverride: 0 + forceMaximumCompressionQuality_BC6H_BC7: 0 + spriteSheet: + serializedVersion: 2 + sprites: [] + outline: [] + physicsShape: [] + bones: [] + spriteID: 5e97eb03825dee720800000000000000 + internalID: 0 + vertices: [] + indices: + edges: [] + weights: [] + secondaryTextures: [] + nameFileIdTable: {} + mipmapLimitGroupName: + pSDRemoveMatte: 0 + userData: + assetBundleName: + assetBundleVariant: -- Gitblit v1.9.3