using UnityEngine; using System.Collections; using System.Collections.Generic; public class BallSpawner : Spawner { public GameObject basicBallPrefab; public GameObject nuclearBallPrefab; public GameObject glassBallPrefab; public GameObject eyeBallPrefab; public GameObject oozeBallPrefab; public GameObject fruitBallPrefab; public GameObject gameBoard; public Player player; private static WaitForSeconds _waitForSeconds0_5 = new WaitForSeconds(0.5f); private Queue ballQueue = new Queue(); private Queue balls = new Queue(); private int basicBallCount = 0; // Start is called once before the first execution of Update after the MonoBehaviour is created void Start() { int currentLevel = PlayerPrefs.GetInt("CurrentLevel"); ballQueue.Enqueue(() => SpawnBall(basicBallPrefab)); ballQueue.Enqueue(() => SpawnBall(basicBallPrefab)); basicBallCount = 2; StartCoroutine(SpawnBalls()); } public void SpawnBallOfType(string ballType, Vector2? position = null) { switch (ballType) { case "BasicBall": SpawnBall(basicBallPrefab, position); break; case "NuclearBall": SpawnBall(nuclearBallPrefab, position); break; case "GlassBall": SpawnBall(glassBallPrefab, position); break; case "EyeBall": SpawnBall(eyeBallPrefab, position); break; case "OozeBall": SpawnBall(oozeBallPrefab, position); break; case "FruitBall": SpawnBall(fruitBallPrefab, position); break; default: Debug.LogWarning($"Unknown ball type: {ballType}"); break; } } public void ResetBalls() { // Clear all balls. int nukeBallCount = 0; int glassBallCount = 0; int eyeBallCount = 0; int oozeBallCount = 0; int fruitBallCount = 0; foreach (var ball in balls) { if (ball != null) { if (ball.CompareTag("NuclearBall")) { nukeBallCount++; } else if (ball.CompareTag("GlassBall")) { glassBallCount++; } else if (ball.CompareTag("EyeBall")) { eyeBallCount++; } else if (ball.CompareTag("OozeBall")) { oozeBallCount++; } else if (ball.CompareTag("FruitBall")) { fruitBallCount++; } Destroy(ball); } } balls.Clear(); // Always spawn one basic ball more than in the previous level. basicBallCount++; // At special levels force spawn certain types of balls. if (player.level % 5 == 0) { // One nuclear ball each five levels. nukeBallCount++; if (player.level >= 10) { // Glass balls after level 10. glassBallCount += Random.Range(1, 3); } if (player.level >= 15) { // An extra nuke ball after level 15. nukeBallCount++; // Eye balls after level 15. eyeBallCount++; } if (player.level >= 20) { // Ooze balls after level 20. oozeBallCount++; } // No extra basic ball when a special ball spawns for the first time. basicBallCount--; } // Spawn balls. for (int i = 0; i < basicBallCount; i++) { ballQueue.Enqueue(() => SpawnBall(basicBallPrefab)); } for (int i = 0; i < nukeBallCount; i++) { ballQueue.Enqueue(() => SpawnBall(nuclearBallPrefab)); } for (int i = 0; i < glassBallCount; i++) { ballQueue.Enqueue(() => SpawnBall(glassBallPrefab)); } for (int i = 0; i < eyeBallCount; i++) { ballQueue.Enqueue(() => SpawnBall(eyeBallPrefab)); } for (int i = 0; i < oozeBallCount; i++) { ballQueue.Enqueue(() => SpawnBall(oozeBallPrefab)); } for (int i = 0; i < fruitBallCount; i++) { ballQueue.Enqueue(() => SpawnBall(fruitBallPrefab)); } StartCoroutine(SpawnBalls()); } private void SpawnBall(GameObject ballPrefab, Vector2? position = null) { if (ballPrefab == null || gameBoard == null) { Debug.LogWarning("BallSpawner: Missing ballPrefab or gameBoard reference."); return; } Vector3 spawnPosition = position.HasValue ? new Vector3(position.Value.x, position.Value.y, gameBoard.transform.position.z) : GetRandomPositionInsideGameBoard(); var newBall = Instantiate(ballPrefab, spawnPosition, Quaternion.identity); if (ballPrefab == oozeBallPrefab) { var oozeBallScript = newBall.GetComponent(); if (oozeBallScript != null) { oozeBallScript.spawner = this; } } balls.Enqueue(newBall); FixCollisions(); } private Vector3 GetRandomPositionInsideGameBoard() { Bounds bounds; Collider2D collider2D = gameBoard.GetComponent(); if (collider2D != null) { bounds = collider2D.bounds; } else { Renderer boardRenderer = gameBoard.GetComponent(); if (boardRenderer == null) { Debug.LogWarning("BallSpawner: gameBoard needs a Collider2D or Renderer to read bounds."); return gameBoard.transform.position; } bounds = boardRenderer.bounds; } const float edgeInset = 0.1f; float randomX = Random.Range(bounds.min.x + edgeInset, bounds.max.x - edgeInset); float randomY = Random.Range(bounds.min.y + edgeInset, bounds.max.y - edgeInset); return new Vector3(randomX, randomY, gameBoard.transform.position.z); } public void FixCollisions() { // Disable collision between existing balls of certain types. // Only basic balls and fruit balls will collide with each other, while ooze, // nuclear, glass, and eye balls will not collide with any other balls. // As an exception, glass balls will collide with other glass balls. foreach (var ball in balls) { if (ball == null) continue; foreach (var otherBall in balls) { if (otherBall == null) continue; if (ball == otherBall) continue; bool ballIsBasicOrFruit = ball.CompareTag("BasicBall") || ball.CompareTag("FruitBall"); bool otherIsBasicOrFruit = otherBall.CompareTag("BasicBall") || otherBall.CompareTag("FruitBall"); bool bothAreGlass = ball.CompareTag("GlassBall") && otherBall.CompareTag("GlassBall"); bool shouldCollide = (ballIsBasicOrFruit && otherIsBasicOrFruit) || bothAreGlass; if (!shouldCollide) { Physics2D.IgnoreCollision( ball.GetComponent(), otherBall.GetComponent(), true ); } } } } private IEnumerator SpawnBalls() { while (ballQueue.Count > 0) { System.Action spawnAction = ballQueue.Dequeue(); spawnAction.Invoke(); yield return _waitForSeconds0_5; } FixCollisions(); } public void MagnetActive(bool flipped) { foreach (var ball in balls) { if (ball == null) continue; var ballScript = ball.GetComponent(); if (ballScript != null) { ballScript.MagnetActive(flipped); } } } public void MagnetInactive() { foreach (var ball in balls) { if (ball == null) continue; var ballScript = ball.GetComponent(); if (ballScript != null) { ballScript.MagnetInactive(); } } } public Queue GetBalls() { return balls; } }