Files
AppleHillsProduction/Assets/Scripts/Minigames/DivingForPictures/TrenchTileSpawner.cs

161 lines
5.3 KiB
C#

using System.Collections.Generic;
using UnityEngine;
using UnityEngine.Events;
using UnityEngine.Serialization;
namespace Minigames.DivingForPictures
{
/// <summary>
/// Spawns and manages trench wall tiles for the endless descender minigame.
/// </summary>
public class TrenchTileSpawner : MonoBehaviour
{
[Header("Tile Prefabs")]
[Tooltip("List of possible trench tile prefabs.")]
public List<GameObject> tilePrefabs;
[Header("Tile Settings")]
private const float TileHeight = 5f; // Set to match prefab height
public int initialTileCount = 3;
public float tileSpawnBuffer = 1f;
[Header("Movement Settings")]
public float moveSpeed = 3f;
public float speedUpFactor = 0.2f;
public float speedUpInterval = 10f;
[FormerlySerializedAs("OnTileSpawned")] [Header("Events")]
public UnityEvent<GameObject> onTileSpawned;
[FormerlySerializedAs("OnTileDestroyed")]
public UnityEvent<GameObject> onTileDestroyed;
private readonly List<GameObject> _activeTiles = new List<GameObject>();
private readonly Dictionary<int, int> _tileLastUsed = new Dictionary<int, int>();
private int _spawnCounter;
private float _speedUpTimer;
private Camera _mainCamera;
private float _screenBottom;
private float _screenTop;
void Start()
{
_mainCamera = Camera.main;
CalculateScreenBounds();
for (int i = 0; i < initialTileCount; i++)
{
SpawnTileAtY(_screenBottom + i * TileHeight);
}
}
void Update()
{
MoveTiles();
HandleTileDestruction();
HandleTileSpawning();
HandleSpeedRamping();
}
private void CalculateScreenBounds()
{
Vector3 bottom = _mainCamera.ViewportToWorldPoint(new Vector3(0.5f, 0f, _mainCamera.nearClipPlane));
Vector3 top = _mainCamera.ViewportToWorldPoint(new Vector3(0.5f, 1f, _mainCamera.nearClipPlane));
_screenBottom = bottom.y;
_screenTop = top.y;
}
private void MoveTiles()
{
float moveDelta = moveSpeed * Time.deltaTime;
foreach (var tile in _activeTiles)
{
if (tile != null)
{
tile.transform.position += Vector3.up * moveDelta;
}
}
}
private void HandleTileDestruction()
{
if (_activeTiles.Count == 0) return;
GameObject topTile = _activeTiles[0];
if (topTile.transform.position.y - TileHeight / 2 > _screenTop + tileSpawnBuffer)
{
_activeTiles.RemoveAt(0);
onTileDestroyed?.Invoke(topTile);
Destroy(topTile);
}
}
private void HandleTileSpawning()
{
if (_activeTiles.Count == 0) return;
GameObject bottomTile = _activeTiles[^1];
float bottomEdge = bottomTile.transform.position.y - TileHeight / 2;
if (bottomEdge > _screenBottom - tileSpawnBuffer)
{
float newY = bottomTile.transform.position.y - TileHeight;
SpawnTileAtY(newY);
}
}
private void HandleSpeedRamping()
{
_speedUpTimer += Time.deltaTime;
if (_speedUpTimer >= speedUpInterval)
{
moveSpeed += speedUpFactor;
_speedUpTimer = 0f;
}
}
private void SpawnTileAtY(float y)
{
int prefabIndex = GetWeightedRandomTileIndex();
GameObject prefab = tilePrefabs[prefabIndex];
// Use the prefab's original rotation
GameObject tile = Instantiate(prefab, new Vector3(0f, y, 0f), prefab.transform.rotation, transform);
_activeTiles.Add(tile);
_tileLastUsed[prefabIndex] = _spawnCounter++;
onTileSpawned?.Invoke(tile);
}
private int GetWeightedRandomTileIndex()
{
// Weight tiles not used recently higher
int n = tilePrefabs.Count;
List<float> weights = new List<float>(n);
for (int i = 0; i < n; i++)
{
int lastUsed = _tileLastUsed.TryGetValue(i, out var value) ? value : -n;
int age = _spawnCounter - lastUsed;
float weight = Mathf.Clamp(age, 1, n * 2); // More unused = higher weight
weights.Add(weight);
}
float total = 0f;
foreach (var w in weights) total += w;
float r = Random.value * total;
for (int i = 0; i < n; i++)
{
if (r < weights[i]) return i;
r -= weights[i];
}
return Random.Range(0, n); // fallback
}
#if UNITY_EDITOR
void OnDrawGizmosSelected()
{
// Draw tile bounds for debugging
Gizmos.color = Color.cyan;
for (int i = 0; i < initialTileCount; i++)
{
Vector3 center = new Vector3(0f, _screenBottom + i * TileHeight, 0f);
Gizmos.DrawWireCube(center, new Vector3(10f, TileHeight, 1f));
}
}
#endif
}
}