diff --git a/Assets/Scenes/Game.unity b/Assets/Scenes/Game.unity index 007086c..612e0e7 100644 --- a/Assets/Scenes/Game.unity +++ b/Assets/Scenes/Game.unity @@ -1147,6 +1147,7 @@ MonoBehaviour: m_EditorClassIdentifier: Assembly-CSharp::CakeSpawner cakePrefab: {fileID: 2844589349164124371, guid: 5d4e6d6eee86e410bbf4d9c5ed9a6cdb, type: 3} gameBoardSpriteRenderer: {fileID: 297740151} + worldFiller: {fileID: 2071167548} --- !u!1 &240489635 GameObject: m_ObjectHideFlags: 0 @@ -3399,6 +3400,7 @@ MonoBehaviour: ballSpawner: {fileID: 240489636} percentCovered: 0 multiplier: 1 + worldFiller: {fileID: 2071167548} --- !u!50 &1002905794 Rigidbody2D: serializedVersion: 5 @@ -3503,6 +3505,7 @@ MonoBehaviour: m_EditorClassIdentifier: Assembly-CSharp::MultiplierSpawner multiplierPrefab: {fileID: 972968151998544523, guid: e0793a7c04bca0662982bfa7f5f60935, type: 3} gameBoardSpriteRenderer: {fileID: 297740151} + worldFiller: {fileID: 2071167548} --- !u!4 &1009138784 Transform: m_ObjectHideFlags: 0 diff --git a/Assets/Scripts/Game/LevelEnder.cs b/Assets/Scripts/Game/LevelEnder.cs index b879027..e05dd51 100644 --- a/Assets/Scripts/Game/LevelEnder.cs +++ b/Assets/Scripts/Game/LevelEnder.cs @@ -278,6 +278,7 @@ public class LevelEnder : MonoBehaviour sharkSpawner.StartSpawning(); cakeSpawner.StartSpawning(); ballSpawner.StartSpawning(); + multiplierSpawner.Reset(); // Reset state. levelEnded = false; diff --git a/Assets/Scripts/Game/Player/Player.cs b/Assets/Scripts/Game/Player/Player.cs index 8aa67d2..e6ea867 100644 --- a/Assets/Scripts/Game/Player/Player.cs +++ b/Assets/Scripts/Game/Player/Player.cs @@ -76,6 +76,8 @@ public class Player : MonoBehaviour public float multiplier = 1.0f; + public WorldFiller worldFiller; + private bool flipped = false; private bool gameOver = false; @@ -247,7 +249,12 @@ public class Player : MonoBehaviour } // Mouse input handling for firing lasers, magnets, or normal barriers. - if (Mouse.current.leftButton.wasPressedThisFrame && canFire && !exploding) + if ( + Mouse.current.leftButton.wasPressedThisFrame && + worldFiller.IsPositionInFreeArea(transform.position) && + canFire && + !exploding + ) { if (laserActive) { diff --git a/Assets/Scripts/Game/Spawners/MultiplierSpawner.cs b/Assets/Scripts/Game/Spawners/MultiplierSpawner.cs index 978359b..7e8be62 100644 --- a/Assets/Scripts/Game/Spawners/MultiplierSpawner.cs +++ b/Assets/Scripts/Game/Spawners/MultiplierSpawner.cs @@ -6,6 +6,8 @@ public class MultiplierSpawner : Spawner public SpriteRenderer gameBoardSpriteRenderer; + public WorldFiller worldFiller; + private Bounds multiplierBounds; private Bounds gameBoardBounds; @@ -36,6 +38,7 @@ public class MultiplierSpawner : Spawner spawnTimer = 0f; if (Random.value < spawnProbability) { + spawnPosition = worldFiller.GetRandomPositionInFreeAreas(multiplierBounds); GameObject multiplier = Instantiate(multiplierPrefab, spawnPosition, Quaternion.identity); isActive = false; } @@ -46,15 +49,5 @@ public class MultiplierSpawner : Spawner { isActive = true; spawnTimer = 0f; - spawnPosition = new Vector2( - Random.Range( - gameBoardBounds.min.x + multiplierBounds.extents.x, - gameBoardBounds.max.x - multiplierBounds.extents.x - ), - Random.Range( - gameBoardBounds.min.y + multiplierBounds.extents.y, - gameBoardBounds.max.y - multiplierBounds.extents.y - ) - ); } } diff --git a/Assets/Scripts/Game/Spawners/Spawner.cs b/Assets/Scripts/Game/Spawners/Spawner.cs index 6999900..a334c2d 100644 --- a/Assets/Scripts/Game/Spawners/Spawner.cs +++ b/Assets/Scripts/Game/Spawners/Spawner.cs @@ -13,4 +13,20 @@ public abstract class Spawner : MonoBehaviour { isSpawning = true; } + + protected Vector2 GetRandomSpawnPosition(Bounds bounds) + { + var spawnPosition = new Vector2( + Random.Range( + bounds.min.x + bounds.extents.x, + bounds.max.x - bounds.extents.x + ), + Random.Range( + bounds.min.y + bounds.extents.y, + bounds.max.y - bounds.extents.y + ) + ); + + return spawnPosition; + } } diff --git a/Assets/Scripts/Game/WorldFiller.cs b/Assets/Scripts/Game/WorldFiller.cs index 8e2855a..689edb1 100644 --- a/Assets/Scripts/Game/WorldFiller.cs +++ b/Assets/Scripts/Game/WorldFiller.cs @@ -59,6 +59,7 @@ public class WorldFiller : MonoBehaviour } spawnedWalls.Clear(); + InitializeFreeAreas(); } public void SetWallPosition(Vector2 position) @@ -280,6 +281,80 @@ public class WorldFiller : MonoBehaviour boardArea = boardRect.width * boardRect.height; } + public bool IsPositionInFreeArea(Vector2 position) + { + if (freeAreas.Count == 0 && gameBoardSpriteRenderer != null) + { + InitializeFreeAreas(); + } + + for (int i = 0; i < freeAreas.Count; i++) + { + if (RectContainsWithTolerance(freeAreas[i], position)) + { + return true; + } + } + + return false; + } + + public Vector2 GetRandomPositionInFreeAreas(Bounds? clearingArea = null) + { + if (freeAreas.Count == 0 && gameBoardSpriteRenderer != null) + { + InitializeFreeAreas(); + } + + List candidateAreas = new List(freeAreas); + + if (clearingArea.HasValue) + { + Bounds bounds = clearingArea.Value; + Rect clearingRect = Rect.MinMaxRect(bounds.min.x, bounds.min.y, bounds.max.x, bounds.max.y); + candidateAreas = GetAreasExcludingRect(candidateAreas, clearingRect); + } + + float totalArea = 0.0f; + + for (int i = 0; i < candidateAreas.Count; i++) + { + totalArea += candidateAreas[i].width * candidateAreas[i].height; + } + + if (totalArea <= geometryEpsilon) + { + Debug.LogWarning("No free area available to generate a random position."); + + if (gameBoardSpriteRenderer != null) + { + return gameBoardSpriteRenderer.bounds.center; + } + + return Vector2.zero; + } + + float targetArea = Random.Range(0.0f, totalArea); + float accumulatedArea = 0.0f; + + for (int i = 0; i < candidateAreas.Count; i++) + { + Rect area = candidateAreas[i]; + accumulatedArea += area.width * area.height; + + if (targetArea <= accumulatedArea) + { + return new Vector2( + Random.Range(area.xMin, area.xMax), + Random.Range(area.yMin, area.yMax) + ); + } + } + + Rect fallbackArea = candidateAreas[candidateAreas.Count - 1]; + return fallbackArea.center; + } + private float GetCurrentBarThickness(bool isVerticalLine) { var barSegments = GameObject.FindGameObjectsWithTag("Bar"); @@ -486,6 +561,30 @@ public class WorldFiller : MonoBehaviour freeAreas = updatedAreas; } + private List GetAreasExcludingRect(List sourceAreas, Rect excludedArea) + { + List remainingAreas = new List(); + + for (int i = 0; i < sourceAreas.Count; i++) + { + Rect sourceArea = sourceAreas[i]; + + if (!TryGetRectIntersection(sourceArea, excludedArea, out Rect overlap)) + { + AddValidRect(remainingAreas, sourceArea); + continue; + } + + // Split the source area around the overlap and keep only non-overlapping parts. + AddValidRect(remainingAreas, Rect.MinMaxRect(sourceArea.xMin, sourceArea.yMin, overlap.xMin, sourceArea.yMax)); + AddValidRect(remainingAreas, Rect.MinMaxRect(overlap.xMax, sourceArea.yMin, sourceArea.xMax, sourceArea.yMax)); + AddValidRect(remainingAreas, Rect.MinMaxRect(overlap.xMin, sourceArea.yMin, overlap.xMax, overlap.yMin)); + AddValidRect(remainingAreas, Rect.MinMaxRect(overlap.xMin, overlap.yMax, overlap.xMax, sourceArea.yMax)); + } + + return remainingAreas; + } + private void AddValidRect(List target, Rect candidate) { if (candidate.width <= geometryEpsilon || candidate.height <= geometryEpsilon) diff --git a/Assets/Scripts/Game/Yummies/CakeSpawner.cs b/Assets/Scripts/Game/Yummies/CakeSpawner.cs index d13098d..4cec1d7 100644 --- a/Assets/Scripts/Game/Yummies/CakeSpawner.cs +++ b/Assets/Scripts/Game/Yummies/CakeSpawner.cs @@ -6,6 +6,8 @@ public class CakeSpawner : Spawner public SpriteRenderer gameBoardSpriteRenderer; + public WorldFiller worldFiller; + private Bounds cakeBounds; private Bounds gameBoardBounds; @@ -66,15 +68,6 @@ public class CakeSpawner : Spawner { spawned = false; spawnTimer = 0f; - spawnPosition = new Vector2( - Random.Range( - gameBoardBounds.min.x + cakeBounds.extents.x, - gameBoardBounds.max.x - cakeBounds.extents.x - ), - Random.Range( - gameBoardBounds.min.y + cakeBounds.extents.y, - gameBoardBounds.max.y - cakeBounds.extents.y - ) - ); + spawnPosition = worldFiller.GetRandomPositionInFreeAreas(cakeBounds); } }