Как обещал, выкладываю свое решение. Да, согласен, скорее всего это лишняя оптимизация в моём случае (перебрать пару десятков клеток раз в несколько секунд), но мне так больше нравится.
public class Lightening : MonoBehaviour
{
[SerializeField] private float interval;
[SerializeField] private float minRange;
[SerializeField] private float maxRange;
[SerializeField] private int minCount;
[SerializeField] private int maxCount;
[Inject] private PlayerPosition playerPosition;
private readonly Vector3Int[] cellsInRange;
public Lightening()
{
// делаем массив клеток в радиусе относительно нулевых координат. Мы будем прибавлять их к позиции игрока.
cellsInRange = GridUtils.GetBetweenRanges(Vector3.zero, minRange, maxRange).ToArray();
}
private IEnumerator Start()
{
while (isActiveAndEnabled)
{
yield return new WaitForSeconds(interval); // задержка между выстрелами
var start = 0; // левая граница массива
var hit = 0;
var toHit = Random.Range(minCount, maxCount);
while (start < cellsInRange.Length && hit < toHit)
{
// выбираем рандомный элемент в массиве, начиная с левой границы
var randomIndex = Random.Range(start, cellsInRange.Length);
// меняем его местами с первым элементом
var cell = cellsInRange[randomIndex];
cellsInRange[randomIndex] = cellsInRange[start];
cellsInRange[start] = cell;
// сдвигаем левую границу на 1
start++;
var targetCell = playerPosition + cell; // получаем абсолютную позицию клетки
if (CanHit(targetCell)) // если она подходит
{
Hit(targetCell); // стреляем по ней
hit++;
}
}
}
}
private void Hit(Vector3Int cell)
{
...
}
private bool CanHit(Vector3Int cell)
{
...
}
}
Мне нравится это решение тем, что оно действительно выбирает каждый раз рандомные клетки, и при этом ему не нужно проверять сразу все клетки в области. Если рандомная клетка подошла - мы сразу её берем.
Метод
GetBetweenRanges скину в комменты, там просто перебор всех клеток.