221 lines
7.9 KiB
C#
221 lines
7.9 KiB
C#
using UnityEngine;
|
||
using Core;
|
||
using Core.Settings;
|
||
using Core.Lifecycle;
|
||
using AppleHillsCamera;
|
||
|
||
namespace Minigames.BirdPooper
|
||
{
|
||
/// <summary>
|
||
/// Spawns targets at regular intervals for Bird Pooper minigame.
|
||
/// Uses Transform references for spawn and despawn positions.
|
||
/// All targets are spawned at Y = 0 (EdgeAnchor positions them vertically).
|
||
/// </summary>
|
||
public class TargetSpawner : ManagedBehaviour
|
||
{
|
||
[Header("Spawn Configuration")]
|
||
[Tooltip("Transform marking where targets spawn (off-screen right)")]
|
||
[SerializeField] private Transform spawnPoint;
|
||
|
||
[Tooltip("Transform marking where targets despawn (off-screen left)")]
|
||
[SerializeField] private Transform despawnPoint;
|
||
|
||
[Header("EdgeAnchor References")]
|
||
[Tooltip("ScreenReferenceMarker to pass to spawned targets")]
|
||
[SerializeField] private ScreenReferenceMarker referenceMarker;
|
||
|
||
[Tooltip("CameraScreenAdapter to pass to spawned targets")]
|
||
[SerializeField] private CameraScreenAdapter cameraAdapter;
|
||
|
||
[Header("Target Prefabs")]
|
||
[Tooltip("Array of target prefabs to spawn randomly")]
|
||
[SerializeField] private GameObject[] targetPrefabs;
|
||
|
||
private IBirdPooperSettings settings;
|
||
private float spawnTimer;
|
||
private bool isSpawning;
|
||
private float _currentTargetInterval = 1f;
|
||
|
||
[Header("Spawn Timing")]
|
||
[Tooltip("Minimum interval between target spawns (seconds)")]
|
||
[SerializeField] private float minTargetSpawnInterval = 1f;
|
||
[Tooltip("Maximum interval between target spawns (seconds)")]
|
||
[SerializeField] private float maxTargetSpawnInterval = 2f;
|
||
|
||
internal override void OnManagedAwake()
|
||
{
|
||
base.OnManagedAwake();
|
||
|
||
// Load settings
|
||
settings = GameManager.GetSettingsObject<IBirdPooperSettings>();
|
||
if (settings == null)
|
||
{
|
||
Debug.LogError("[TargetSpawner] BirdPooperSettings not found!");
|
||
// continue – we'll use inspector intervals
|
||
}
|
||
|
||
// Validate interval range
|
||
if (minTargetSpawnInterval < 0f) minTargetSpawnInterval = 0f;
|
||
if (maxTargetSpawnInterval < 0f) maxTargetSpawnInterval = 0f;
|
||
if (minTargetSpawnInterval > maxTargetSpawnInterval)
|
||
{
|
||
float tmp = minTargetSpawnInterval;
|
||
minTargetSpawnInterval = maxTargetSpawnInterval;
|
||
maxTargetSpawnInterval = tmp;
|
||
Debug.LogWarning("[TargetSpawner] minTargetSpawnInterval was greater than maxTargetSpawnInterval. Values were swapped.");
|
||
}
|
||
|
||
// Validate references
|
||
if (spawnPoint == null)
|
||
{
|
||
Debug.LogError("[TargetSpawner] Spawn Point not assigned! Please assign a Transform in the Inspector.");
|
||
}
|
||
|
||
if (despawnPoint == null)
|
||
{
|
||
Debug.LogError("[TargetSpawner] Despawn Point not assigned! Please assign a Transform in the Inspector.");
|
||
}
|
||
|
||
if (targetPrefabs == null || targetPrefabs.Length == 0)
|
||
{
|
||
Debug.LogError("[TargetSpawner] No target prefabs assigned! Please assign at least one prefab in the Inspector.");
|
||
}
|
||
|
||
if (referenceMarker == null)
|
||
{
|
||
Debug.LogError("[TargetSpawner] ScreenReferenceMarker not assigned! Targets need this for EdgeAnchor positioning.");
|
||
}
|
||
|
||
if (cameraAdapter == null)
|
||
{
|
||
Debug.LogWarning("[TargetSpawner] CameraScreenAdapter not assigned. EdgeAnchor will attempt to auto-find camera.");
|
||
}
|
||
|
||
Debug.Log("[TargetSpawner] Initialized successfully");
|
||
}
|
||
|
||
private void Update()
|
||
{
|
||
if (!isSpawning)
|
||
return;
|
||
|
||
spawnTimer += Time.deltaTime;
|
||
|
||
if (spawnTimer >= _currentTargetInterval)
|
||
{
|
||
SpawnTarget();
|
||
spawnTimer = 0f;
|
||
// pick next random interval
|
||
_currentTargetInterval = Random.Range(minTargetSpawnInterval, maxTargetSpawnInterval);
|
||
}
|
||
}
|
||
|
||
/// <summary>
|
||
/// Spawns a random target prefab at the spawn point.
|
||
/// Target is spawned at Y = 0, EdgeAnchor will position it vertically.
|
||
/// </summary>
|
||
private void SpawnTarget()
|
||
{
|
||
if (targetPrefabs == null || targetPrefabs.Length == 0 || spawnPoint == null)
|
||
{
|
||
Debug.LogError("[TargetSpawner] Cannot spawn target - missing prefabs or spawn point!");
|
||
return;
|
||
}
|
||
|
||
// Randomly select target prefab
|
||
GameObject prefab = targetPrefabs[Random.Range(0, targetPrefabs.Length)];
|
||
|
||
// Spawn at spawn point X, but Y = 0 (EdgeAnchor will position vertically)
|
||
Vector3 spawnPosition = new Vector3(spawnPoint.position.x, 0f, 0f);
|
||
GameObject targetObj = Instantiate(prefab, spawnPosition, Quaternion.identity);
|
||
|
||
// Initialize target
|
||
Target target = targetObj.GetComponent<Target>();
|
||
if (target != null)
|
||
{
|
||
float despawnX = despawnPoint != null ? despawnPoint.position.x : -12f;
|
||
target.Initialize(despawnX, referenceMarker, cameraAdapter);
|
||
|
||
// Subscribe to target hit event to notify manager
|
||
target.onTargetHit.AddListener(OnTargetHit);
|
||
|
||
Debug.Log($"[TargetSpawner] Spawned target at {spawnPosition}");
|
||
}
|
||
else
|
||
{
|
||
Debug.LogError($"[TargetSpawner] Spawned prefab '{prefab.name}' does not have Target component!");
|
||
Destroy(targetObj);
|
||
}
|
||
}
|
||
|
||
/// <summary>
|
||
/// Called when a target is hit by a projectile.
|
||
/// Notifies the game manager for score tracking.
|
||
/// </summary>
|
||
private void OnTargetHit()
|
||
{
|
||
// Find and notify manager
|
||
BirdPooperGameManager manager = BirdPooperGameManager.Instance;
|
||
if (manager != null)
|
||
{
|
||
manager.OnTargetHit();
|
||
}
|
||
else
|
||
{
|
||
Debug.LogWarning("[TargetSpawner] BirdPooperGameManager not found!");
|
||
}
|
||
}
|
||
|
||
/// <summary>
|
||
/// Start spawning targets at regular intervals.
|
||
/// </summary>
|
||
public void StartSpawning()
|
||
{
|
||
isSpawning = true;
|
||
spawnTimer = 0f;
|
||
// choose initial interval
|
||
_currentTargetInterval = Random.Range(minTargetSpawnInterval, maxTargetSpawnInterval);
|
||
Debug.Log("[TargetSpawner] Started spawning targets");
|
||
}
|
||
|
||
/// <summary>
|
||
/// Stop spawning new targets (existing targets continue).
|
||
/// </summary>
|
||
public void StopSpawning()
|
||
{
|
||
isSpawning = false;
|
||
Debug.Log("[TargetSpawner] Stopped spawning targets");
|
||
}
|
||
|
||
/// <summary>
|
||
/// Check if spawner is currently spawning.
|
||
/// </summary>
|
||
public bool IsSpawning => isSpawning;
|
||
|
||
/// <summary>
|
||
/// Draw gizmos to visualize spawn and despawn points in the editor.
|
||
/// </summary>
|
||
private void OnDrawGizmos()
|
||
{
|
||
if (spawnPoint != null)
|
||
{
|
||
Gizmos.color = Color.cyan;
|
||
Gizmos.DrawLine(
|
||
new Vector3(spawnPoint.position.x, -10f, 0f),
|
||
new Vector3(spawnPoint.position.x, 10f, 0f)
|
||
);
|
||
}
|
||
|
||
if (despawnPoint != null)
|
||
{
|
||
Gizmos.color = Color.magenta;
|
||
Gizmos.DrawLine(
|
||
new Vector3(despawnPoint.position.x, -10f, 0f),
|
||
new Vector3(despawnPoint.position.x, 10f, 0f)
|
||
);
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|