Files
Barrack-Unity/Assets/Scripts/Game/Spawners/BallSpawner.cs
T

320 lines
9.1 KiB
C#
Raw Normal View History

2026-07-26 00:59:17 -04:00
using UnityEngine;
using System.Collections;
using System.Collections.Generic;
2026-08-25 22:24:07 -04:00
public class BallSpawner : Spawner
2026-07-26 00:59:17 -04:00
{
public GameObject basicBallPrefab;
public GameObject nuclearBallPrefab;
public GameObject glassBallPrefab;
public GameObject eyeBallPrefab;
public GameObject oozeBallPrefab;
public GameObject fruitBallPrefab;
public GameObject gameBoard;
2026-08-27 16:12:47 -04:00
public Player player;
2026-08-28 17:44:31 -04:00
public WorldFiller worldFiller;
2026-07-26 00:59:17 -04:00
private static WaitForSeconds _waitForSeconds0_5 = new WaitForSeconds(0.5f);
private Queue<System.Action> ballQueue = new Queue<System.Action>();
2026-08-03 01:47:14 -04:00
private Queue<GameObject> balls = new Queue<GameObject>();
2026-08-27 16:12:47 -04:00
private int basicBallCount = 0;
2026-07-26 00:59:17 -04:00
// Start is called once before the first execution of Update after the MonoBehaviour is created
void Start()
{
int currentLevel = PlayerPrefs.GetInt("CurrentLevel");
2026-08-05 04:52:06 -04:00
ballQueue.Enqueue(() => SpawnBall(basicBallPrefab));
ballQueue.Enqueue(() => SpawnBall(basicBallPrefab));
2026-08-28 17:44:31 -04:00
ballQueue.Enqueue(() => SpawnBall(nuclearBallPrefab));
2026-08-27 16:12:47 -04:00
basicBallCount = 2;
2026-07-26 00:59:17 -04:00
StartCoroutine(SpawnBalls());
}
2026-08-03 02:37:41 -04:00
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;
}
}
2026-08-27 16:12:47 -04:00
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());
}
2026-08-03 02:37:41 -04:00
private void SpawnBall(GameObject ballPrefab, Vector2? position = null)
2026-07-26 00:59:17 -04:00
{
2026-08-03 00:16:32 -04:00
if (ballPrefab == null || gameBoard == null)
2026-07-26 00:59:17 -04:00
{
2026-08-03 00:16:32 -04:00
Debug.LogWarning("BallSpawner: Missing ballPrefab or gameBoard reference.");
2026-07-26 00:59:17 -04:00
return;
}
2026-08-03 02:37:41 -04:00
Vector3 spawnPosition = position.HasValue ?
new Vector3(position.Value.x, position.Value.y, gameBoard.transform.position.z) :
GetRandomPositionInsideGameBoard();
2026-08-03 01:47:14 -04:00
var newBall = Instantiate(ballPrefab, spawnPosition, Quaternion.identity);
2026-08-03 02:37:41 -04:00
if (ballPrefab == oozeBallPrefab)
{
var oozeBallScript = newBall.GetComponent<OozeBall>();
if (oozeBallScript != null)
{
oozeBallScript.spawner = this;
}
}
2026-08-28 17:44:31 -04:00
else if (ballPrefab == nuclearBallPrefab)
{
var nuclearBallScript = newBall.GetComponent<NukeBall>();
if (nuclearBallScript != null)
{
nuclearBallScript.worldFiller = worldFiller;
}
}
2026-08-03 02:37:41 -04:00
2026-08-03 01:47:14 -04:00
balls.Enqueue(newBall);
2026-08-04 01:17:03 -04:00
FixCollisions();
}
2026-07-26 00:59:17 -04:00
private Vector3 GetRandomPositionInsideGameBoard()
{
Bounds bounds;
Collider2D collider2D = gameBoard.GetComponent<Collider2D>();
if (collider2D != null)
{
bounds = collider2D.bounds;
}
else
{
Renderer boardRenderer = gameBoard.GetComponent<Renderer>();
if (boardRenderer == null)
{
Debug.LogWarning("BallSpawner: gameBoard needs a Collider2D or Renderer to read bounds.");
return gameBoard.transform.position;
}
bounds = boardRenderer.bounds;
}
2026-08-04 21:23:39 -04:00
const float edgeInset = 0.1f;
2026-08-03 01:47:14 -04:00
float randomX = Random.Range(bounds.min.x + edgeInset, bounds.max.x - edgeInset);
float randomY = Random.Range(bounds.min.y + edgeInset, bounds.max.y - edgeInset);
2026-07-26 00:59:17 -04:00
return new Vector3(randomX, randomY, gameBoard.transform.position.z);
}
2026-08-03 02:37:41 -04:00
public void FixCollisions() {
2026-08-03 01:47:14 -04:00
// 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;
2026-08-03 01:47:14 -04:00
foreach (var otherBall in balls)
{
if (otherBall == null) continue;
2026-08-03 01:47:14 -04:00
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<Collider2D>(),
otherBall.GetComponent<Collider2D>(),
true
);
}
}
}
2026-07-26 00:59:17 -04:00
}
2026-08-03 02:37:41 -04:00
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<BallBase>();
if (ballScript != null)
{
ballScript.MagnetActive(flipped);
}
}
}
public void MagnetInactive()
{
foreach (var ball in balls)
{
if (ball == null) continue;
var ballScript = ball.GetComponent<BallBase>();
if (ballScript != null)
{
ballScript.MagnetInactive();
}
}
}
2026-08-12 00:52:57 -04:00
public Queue<GameObject> GetBalls()
{
return balls;
}
2026-07-26 00:59:17 -04:00
}