Добавлен таймер, конфиг и публичный метод SpawnItems() CooldownRemaining - оставшееся время до спавна float CooldownSecondsRemaining - оставшееся время до спавна int
165 lines
4.6 KiB
C#
165 lines
4.6 KiB
C#
using System.Collections;
|
|
using System.Collections.Generic;
|
|
using UnityEngine;
|
|
using UnityEngine.InputSystem;
|
|
|
|
public class ItemPipe : MonoBehaviour
|
|
{
|
|
[Header("Pipe")]
|
|
[SerializeField] private Transform spawnPoint;
|
|
|
|
[Header("Configuration")]
|
|
[SerializeField] private ItemPipeConfig config;
|
|
|
|
[Header("Temporary testing")]
|
|
[SerializeField] private bool spawnOnE = true;
|
|
|
|
private bool isSpawning;
|
|
|
|
public float CooldownDuration => config != null ? config.SpawnCooldown : 0f;
|
|
public float CooldownRemaining { get; private set; }
|
|
public int CooldownSecondsRemaining => Mathf.CeilToInt(CooldownRemaining);
|
|
public bool CanSpawnItems => config != null && !isSpawning && CooldownRemaining <= 0f;
|
|
|
|
private void Update()
|
|
{
|
|
if (CooldownRemaining > 0f)
|
|
CooldownRemaining = Mathf.Max(0f, CooldownRemaining - Time.deltaTime);
|
|
|
|
if (!spawnOnE || Keyboard.current == null)
|
|
return;
|
|
|
|
if (Keyboard.current.eKey.wasPressedThisFrame)
|
|
SpawnItems();
|
|
}
|
|
|
|
public void SpawnItems()
|
|
{
|
|
if (!CanSpawnItems)
|
|
return;
|
|
|
|
StartCoroutine(SpawnSequence(config));
|
|
}
|
|
|
|
private IEnumerator SpawnSequence(ItemPipeConfig activeConfig)
|
|
{
|
|
isSpawning = true;
|
|
|
|
List<GameObject> spawnQueue = BuildSpawnQueue(activeConfig);
|
|
|
|
for (int i = 0; i < spawnQueue.Count; i++)
|
|
{
|
|
Spawn(spawnQueue[i], activeConfig);
|
|
|
|
if (i < spawnQueue.Count - 1)
|
|
yield return new WaitForSeconds(activeConfig.SpawnInterval);
|
|
}
|
|
|
|
isSpawning = false;
|
|
CooldownRemaining = activeConfig.SpawnCooldown;
|
|
}
|
|
|
|
private List<GameObject> BuildSpawnQueue(ItemPipeConfig activeConfig)
|
|
{
|
|
List<GameObject> queue = new();
|
|
|
|
// Добавляем предметы в точном количестве.
|
|
foreach (ItemPipeConfig.GuaranteedItem item in activeConfig.GuaranteedItems)
|
|
{
|
|
if (item.prefab == null)
|
|
continue;
|
|
|
|
for (int i = 0; i < item.amount; i++)
|
|
queue.Add(item.prefab);
|
|
}
|
|
|
|
// Выбираем указанное количество случайных предметов.
|
|
for (int i = 0; i < activeConfig.RandomItemCount; i++)
|
|
{
|
|
GameObject selectedPrefab = ChooseWeightedRandom(activeConfig);
|
|
|
|
if (selectedPrefab != null)
|
|
queue.Add(selectedPrefab);
|
|
}
|
|
|
|
// Перемешиваем гарантированные и случайные предметы вместе.
|
|
Shuffle(queue);
|
|
|
|
return queue;
|
|
}
|
|
|
|
private GameObject ChooseWeightedRandom(ItemPipeConfig activeConfig)
|
|
{
|
|
float totalWeight = 0f;
|
|
|
|
foreach (ItemPipeConfig.RandomItem item in activeConfig.RandomItems)
|
|
{
|
|
if (item.prefab != null && item.weight > 0f)
|
|
totalWeight += item.weight;
|
|
}
|
|
|
|
if (totalWeight <= 0f)
|
|
return null;
|
|
|
|
float roll = Random.value * totalWeight;
|
|
float currentWeight = 0f;
|
|
|
|
foreach (ItemPipeConfig.RandomItem item in activeConfig.RandomItems)
|
|
{
|
|
if (item.prefab == null || item.weight <= 0f)
|
|
continue;
|
|
|
|
currentWeight += item.weight;
|
|
|
|
if (roll < currentWeight)
|
|
return item.prefab;
|
|
}
|
|
|
|
return null;
|
|
}
|
|
|
|
private void Shuffle(List<GameObject> queue)
|
|
{
|
|
for (int i = queue.Count - 1; i > 0; i--)
|
|
{
|
|
int randomIndex = Random.Range(0, i + 1);
|
|
|
|
(queue[i], queue[randomIndex]) =
|
|
(queue[randomIndex], queue[i]);
|
|
}
|
|
}
|
|
|
|
private void Spawn(GameObject prefab, ItemPipeConfig activeConfig)
|
|
{
|
|
if (prefab == null || spawnPoint == null)
|
|
return;
|
|
|
|
float offsetX = RandomRange(-activeConfig.RandomSpawnX, activeConfig.RandomSpawnX);
|
|
float offsetZ = RandomRange(-activeConfig.RandomSpawnZ, activeConfig.RandomSpawnZ);
|
|
|
|
Vector3 spawnPosition =
|
|
spawnPoint.position +
|
|
spawnPoint.right * offsetX +
|
|
spawnPoint.forward * offsetZ;
|
|
|
|
GameObject item = Instantiate(
|
|
prefab,
|
|
spawnPosition,
|
|
Random.rotationUniform
|
|
);
|
|
|
|
if (item.TryGetComponent(out Rigidbody body))
|
|
{
|
|
Vector3 worldVelocityChange =
|
|
spawnPoint.TransformDirection(activeConfig.LocalVelocityChange);
|
|
|
|
body.AddForce(worldVelocityChange, ForceMode.VelocityChange);
|
|
}
|
|
}
|
|
|
|
private float RandomRange(float min, float max)
|
|
{
|
|
return Random.Range(min, max);
|
|
}
|
|
}
|