161 lines
5.3 KiB
C#
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
|
|||
|
|
}
|
|||
|
|
}
|