Rough treat spawning
This commit is contained in:
@@ -0,0 +1,122 @@
|
||||
using UnityEngine;
|
||||
using System.Collections.Generic;
|
||||
|
||||
public class TreatSpawner : MonoBehaviour
|
||||
{
|
||||
[SerializeField] private GameObject treatPrefab;
|
||||
[SerializeField] private Transform treatParent;
|
||||
[SerializeField] private Vector2 spawnAreaCenter = Vector2.zero;
|
||||
[SerializeField] private Vector2 spawnAreaSize = new Vector2(10f, 5f);
|
||||
[SerializeField] private LayerMask groundMask = ~0;
|
||||
[SerializeField] private float groundRaycastHeight = 10f;
|
||||
[SerializeField] private float minSpawnInterval = 1f;
|
||||
[SerializeField] private float maxSpawnInterval = 2f;
|
||||
[SerializeField] private int maxTreats = 0;
|
||||
[SerializeField] private float minSpawnSeparation = 1.5f;
|
||||
[SerializeField] private int maxSampleAttempts = 8;
|
||||
[SerializeField] private int recentPositionsCount = 6;
|
||||
|
||||
private float nextSpawnTime;
|
||||
private readonly Queue<Vector2> recentPositions = new Queue<Vector2>();
|
||||
|
||||
private void OnEnable()
|
||||
{
|
||||
ScheduleNextSpawn();
|
||||
}
|
||||
|
||||
private void Update()
|
||||
{
|
||||
if (treatPrefab == null || Time.time < nextSpawnTime)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (maxTreats > 0 && treatParent != null && treatParent.childCount >= maxTreats)
|
||||
{
|
||||
ScheduleNextSpawn();
|
||||
return;
|
||||
}
|
||||
|
||||
SpawnTreat();
|
||||
ScheduleNextSpawn();
|
||||
}
|
||||
|
||||
private void SpawnTreat()
|
||||
{
|
||||
var position = FindSpawnPosition();
|
||||
|
||||
var instance = Instantiate(treatPrefab, position, Quaternion.identity);
|
||||
if (treatParent != null)
|
||||
{
|
||||
instance.transform.SetParent(treatParent);
|
||||
}
|
||||
|
||||
if (recentPositionsCount > 0)
|
||||
{
|
||||
recentPositions.Enqueue(position);
|
||||
while (recentPositions.Count > recentPositionsCount)
|
||||
{
|
||||
recentPositions.Dequeue();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void ScheduleNextSpawn()
|
||||
{
|
||||
var min = Mathf.Max(0.01f, minSpawnInterval);
|
||||
var max = Mathf.Max(min, maxSpawnInterval);
|
||||
nextSpawnTime = Time.time + Random.Range(min, max);
|
||||
}
|
||||
|
||||
private Vector2 FindSpawnPosition()
|
||||
{
|
||||
var halfSize = spawnAreaSize * 0.5f;
|
||||
var attempts = Mathf.Max(1, maxSampleAttempts);
|
||||
|
||||
for (var i = 0; i < attempts; i++)
|
||||
{
|
||||
var randomX = Random.Range(spawnAreaCenter.x - halfSize.x, spawnAreaCenter.x + halfSize.x);
|
||||
var rayStart = new Vector2(randomX, spawnAreaCenter.y + groundRaycastHeight);
|
||||
var hit = Physics2D.Raycast(rayStart, Vector2.down, groundRaycastHeight * 2f, groundMask);
|
||||
var candidate = hit.collider != null
|
||||
? hit.point
|
||||
: new Vector2(randomX, spawnAreaCenter.y);
|
||||
|
||||
if (IsFarEnough(candidate))
|
||||
{
|
||||
return candidate;
|
||||
}
|
||||
}
|
||||
|
||||
var fallbackX = Random.Range(spawnAreaCenter.x - halfSize.x, spawnAreaCenter.x + halfSize.x);
|
||||
var fallbackStart = new Vector2(fallbackX, spawnAreaCenter.y + groundRaycastHeight);
|
||||
var fallbackHit = Physics2D.Raycast(fallbackStart, Vector2.down, groundRaycastHeight * 2f, groundMask);
|
||||
return fallbackHit.collider != null
|
||||
? fallbackHit.point
|
||||
: new Vector2(fallbackX, spawnAreaCenter.y);
|
||||
}
|
||||
|
||||
private bool IsFarEnough(Vector2 candidate)
|
||||
{
|
||||
if (minSpawnSeparation <= 0f || recentPositions.Count == 0)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
foreach (var position in recentPositions)
|
||||
{
|
||||
if ((candidate - position).sqrMagnitude < minSpawnSeparation * minSpawnSeparation)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
private void OnDrawGizmosSelected()
|
||||
{
|
||||
Gizmos.color = new Color(1f, 0.85f, 0.1f, 0.35f);
|
||||
Gizmos.DrawWireCube(spawnAreaCenter, spawnAreaSize);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user