Added UI to Furnance, fixed carry and drag obj and added fish
This commit is contained in:
@@ -1,9 +1,11 @@
|
||||
using System;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using Unity.Netcode;
|
||||
using UnityEngine;
|
||||
using UnityEngine.InputSystem;
|
||||
|
||||
public class ItemPipe : MonoBehaviour
|
||||
[RequireComponent(typeof(NetworkObject))]
|
||||
public sealed class ItemPipe : NetworkBehaviour
|
||||
{
|
||||
[Header("Pipe")]
|
||||
[SerializeField] private Transform spawnPoint;
|
||||
@@ -11,84 +13,192 @@ public class ItemPipe : MonoBehaviour
|
||||
[Header("Configuration")]
|
||||
[SerializeField] private ItemPipeConfig config;
|
||||
|
||||
[Header("Temporary testing")]
|
||||
[SerializeField] private bool spawnOnE = true;
|
||||
private readonly NetworkVariable<bool> spawnSequenceActive = new(
|
||||
false,
|
||||
NetworkVariableReadPermission.Everyone,
|
||||
NetworkVariableWritePermission.Server);
|
||||
|
||||
private bool isSpawning;
|
||||
private readonly NetworkVariable<double> cooldownEndsAt = new(
|
||||
0d,
|
||||
NetworkVariableReadPermission.Everyone,
|
||||
NetworkVariableWritePermission.Server);
|
||||
|
||||
private Coroutine spawnRoutine;
|
||||
|
||||
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()
|
||||
public float CooldownRemaining
|
||||
{
|
||||
if (CooldownRemaining > 0f)
|
||||
CooldownRemaining = Mathf.Max(0f, CooldownRemaining - Time.deltaTime);
|
||||
get
|
||||
{
|
||||
double now = NetworkManager != null
|
||||
? NetworkManager.ServerTime.Time
|
||||
: Time.timeAsDouble;
|
||||
return Mathf.Max(0f, (float)(cooldownEndsAt.Value - now));
|
||||
}
|
||||
}
|
||||
public int CooldownSecondsRemaining => Mathf.CeilToInt(CooldownRemaining);
|
||||
public bool IsSpawning => spawnSequenceActive.Value;
|
||||
public bool CanSpawnItems =>
|
||||
IsSpawned &&
|
||||
config != null &&
|
||||
!spawnSequenceActive.Value &&
|
||||
CooldownRemaining <= 0f;
|
||||
|
||||
if (!spawnOnE || Keyboard.current == null)
|
||||
public event Action Changed;
|
||||
|
||||
private void Reset() => spawnPoint = transform;
|
||||
|
||||
public override void OnNetworkSpawn()
|
||||
{
|
||||
spawnSequenceActive.OnValueChanged += HandleSpawnStateChanged;
|
||||
cooldownEndsAt.OnValueChanged += HandleCooldownChanged;
|
||||
|
||||
if (IsServer)
|
||||
{
|
||||
spawnSequenceActive.Value = false;
|
||||
cooldownEndsAt.Value = 0d;
|
||||
}
|
||||
|
||||
Changed?.Invoke();
|
||||
}
|
||||
|
||||
public override void OnNetworkDespawn()
|
||||
{
|
||||
spawnSequenceActive.OnValueChanged -= HandleSpawnStateChanged;
|
||||
cooldownEndsAt.OnValueChanged -= HandleCooldownChanged;
|
||||
|
||||
if (spawnRoutine != null)
|
||||
{
|
||||
StopCoroutine(spawnRoutine);
|
||||
spawnRoutine = null;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Starts one configured batch. Safe to connect directly to a Button or UnityEvent.
|
||||
/// Calls made on a client are forwarded to the server.
|
||||
/// </summary>
|
||||
public void SpawnItems()
|
||||
{
|
||||
if (!IsSpawned)
|
||||
return;
|
||||
|
||||
if (Keyboard.current.eKey.wasPressedThisFrame)
|
||||
if (IsServer)
|
||||
TryStartSpawnSequenceOnServer();
|
||||
else
|
||||
RequestSpawnItemsRpc();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// UnityEvent<bool> adapter for a lever. Only the true edge starts a batch;
|
||||
/// returning a momentary lever to false does not cancel the current batch.
|
||||
/// </summary>
|
||||
public void SpawnItems(bool activated)
|
||||
{
|
||||
if (activated)
|
||||
SpawnItems();
|
||||
}
|
||||
|
||||
public void SpawnItems()
|
||||
/// <summary>
|
||||
/// Starts while true and cancels the current batch while false. Use this when
|
||||
/// the lever position must control whether the pipe is allowed to keep running.
|
||||
/// </summary>
|
||||
public void SetSpawning(bool shouldSpawn)
|
||||
{
|
||||
if (!CanSpawnItems)
|
||||
return;
|
||||
|
||||
StartCoroutine(SpawnSequence(config));
|
||||
if (shouldSpawn)
|
||||
SpawnItems();
|
||||
else
|
||||
StopSpawning();
|
||||
}
|
||||
|
||||
private IEnumerator SpawnSequence(ItemPipeConfig activeConfig)
|
||||
public void StopSpawning()
|
||||
{
|
||||
isSpawning = true;
|
||||
if (!IsSpawned)
|
||||
return;
|
||||
|
||||
if (IsServer)
|
||||
StopSpawnSequenceOnServer(applyCooldown: true);
|
||||
else
|
||||
RequestStopSpawningRpc();
|
||||
}
|
||||
|
||||
[Rpc(SendTo.Server, InvokePermission = RpcInvokePermission.Everyone)]
|
||||
private void RequestSpawnItemsRpc() => TryStartSpawnSequenceOnServer();
|
||||
|
||||
[Rpc(SendTo.Server, InvokePermission = RpcInvokePermission.Everyone)]
|
||||
private void RequestStopSpawningRpc() =>
|
||||
StopSpawnSequenceOnServer(applyCooldown: true);
|
||||
|
||||
private bool TryStartSpawnSequenceOnServer()
|
||||
{
|
||||
if (!IsServer || !CanSpawnItems)
|
||||
return false;
|
||||
|
||||
spawnSequenceActive.Value = true;
|
||||
spawnRoutine = StartCoroutine(SpawnSequenceOnServer(config));
|
||||
return true;
|
||||
}
|
||||
|
||||
private IEnumerator SpawnSequenceOnServer(ItemPipeConfig activeConfig)
|
||||
{
|
||||
List<GameObject> spawnQueue = BuildSpawnQueue(activeConfig);
|
||||
|
||||
for (int i = 0; i < spawnQueue.Count; i++)
|
||||
for (int index = 0; index < spawnQueue.Count; index++)
|
||||
{
|
||||
Spawn(spawnQueue[i], activeConfig);
|
||||
SpawnNetworkItemOnServer(spawnQueue[index], activeConfig);
|
||||
|
||||
if (i < spawnQueue.Count - 1)
|
||||
if (index < spawnQueue.Count - 1)
|
||||
yield return new WaitForSeconds(activeConfig.SpawnInterval);
|
||||
}
|
||||
|
||||
isSpawning = false;
|
||||
CooldownRemaining = activeConfig.SpawnCooldown;
|
||||
spawnRoutine = null;
|
||||
cooldownEndsAt.Value =
|
||||
NetworkManager.ServerTime.Time + activeConfig.SpawnCooldown;
|
||||
spawnSequenceActive.Value = false;
|
||||
}
|
||||
|
||||
private List<GameObject> BuildSpawnQueue(ItemPipeConfig activeConfig)
|
||||
private void StopSpawnSequenceOnServer(bool applyCooldown)
|
||||
{
|
||||
if (!IsServer || !spawnSequenceActive.Value)
|
||||
return;
|
||||
|
||||
if (spawnRoutine != null)
|
||||
{
|
||||
StopCoroutine(spawnRoutine);
|
||||
spawnRoutine = null;
|
||||
}
|
||||
|
||||
cooldownEndsAt.Value = applyCooldown && config != null
|
||||
? NetworkManager.ServerTime.Time + config.SpawnCooldown
|
||||
: 0d;
|
||||
spawnSequenceActive.Value = false;
|
||||
}
|
||||
|
||||
private static 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++)
|
||||
for (int index = 0; index < item.amount; index++)
|
||||
queue.Add(item.prefab);
|
||||
}
|
||||
|
||||
// Выбираем указанное количество случайных предметов.
|
||||
for (int i = 0; i < activeConfig.RandomItemCount; i++)
|
||||
for (int index = 0; index < activeConfig.RandomItemCount; index++)
|
||||
{
|
||||
GameObject selectedPrefab = ChooseWeightedRandom(activeConfig);
|
||||
|
||||
if (selectedPrefab != null)
|
||||
queue.Add(selectedPrefab);
|
||||
}
|
||||
|
||||
// Перемешиваем гарантированные и случайные предметы вместе.
|
||||
Shuffle(queue);
|
||||
|
||||
return queue;
|
||||
}
|
||||
|
||||
private GameObject ChooseWeightedRandom(ItemPipeConfig activeConfig)
|
||||
private static GameObject ChooseWeightedRandom(ItemPipeConfig activeConfig)
|
||||
{
|
||||
float totalWeight = 0f;
|
||||
|
||||
@@ -101,64 +211,81 @@ public class ItemPipe : MonoBehaviour
|
||||
if (totalWeight <= 0f)
|
||||
return null;
|
||||
|
||||
float roll = Random.value * totalWeight;
|
||||
float currentWeight = 0f;
|
||||
float roll = UnityEngine.Random.value * totalWeight;
|
||||
float accumulatedWeight = 0f;
|
||||
|
||||
foreach (ItemPipeConfig.RandomItem item in activeConfig.RandomItems)
|
||||
{
|
||||
if (item.prefab == null || item.weight <= 0f)
|
||||
continue;
|
||||
|
||||
currentWeight += item.weight;
|
||||
|
||||
if (roll < currentWeight)
|
||||
accumulatedWeight += item.weight;
|
||||
if (roll < accumulatedWeight)
|
||||
return item.prefab;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
private void Shuffle(List<GameObject> queue)
|
||||
private static void Shuffle(List<GameObject> queue)
|
||||
{
|
||||
for (int i = queue.Count - 1; i > 0; i--)
|
||||
for (int index = queue.Count - 1; index > 0; index--)
|
||||
{
|
||||
int randomIndex = Random.Range(0, i + 1);
|
||||
|
||||
(queue[i], queue[randomIndex]) =
|
||||
(queue[randomIndex], queue[i]);
|
||||
int randomIndex = UnityEngine.Random.Range(0, index + 1);
|
||||
(queue[index], queue[randomIndex]) =
|
||||
(queue[randomIndex], queue[index]);
|
||||
}
|
||||
}
|
||||
|
||||
private void Spawn(GameObject prefab, ItemPipeConfig activeConfig)
|
||||
private void SpawnNetworkItemOnServer(
|
||||
GameObject prefab,
|
||||
ItemPipeConfig activeConfig)
|
||||
{
|
||||
if (prefab == null || spawnPoint == null)
|
||||
Transform activeSpawnPoint = spawnPoint != null ? spawnPoint : transform;
|
||||
if (prefab == null)
|
||||
return;
|
||||
|
||||
float offsetX = RandomRange(-activeConfig.RandomSpawnX, activeConfig.RandomSpawnX);
|
||||
float offsetZ = RandomRange(-activeConfig.RandomSpawnZ, activeConfig.RandomSpawnZ);
|
||||
|
||||
float offsetX = UnityEngine.Random.Range(
|
||||
-activeConfig.RandomSpawnX,
|
||||
activeConfig.RandomSpawnX);
|
||||
float offsetZ = UnityEngine.Random.Range(
|
||||
-activeConfig.RandomSpawnZ,
|
||||
activeConfig.RandomSpawnZ);
|
||||
Vector3 spawnPosition =
|
||||
spawnPoint.position +
|
||||
spawnPoint.right * offsetX +
|
||||
spawnPoint.forward * offsetZ;
|
||||
activeSpawnPoint.position +
|
||||
activeSpawnPoint.right * offsetX +
|
||||
activeSpawnPoint.forward * offsetZ;
|
||||
|
||||
GameObject item = Instantiate(
|
||||
prefab,
|
||||
spawnPosition,
|
||||
Random.rotationUniform
|
||||
);
|
||||
UnityEngine.Random.rotationUniform);
|
||||
|
||||
if (!item.TryGetComponent(out NetworkObject networkObject))
|
||||
{
|
||||
Debug.LogError(
|
||||
$"{name} cannot spawn '{prefab.name}': the prefab has no NetworkObject.",
|
||||
this);
|
||||
Destroy(item);
|
||||
return;
|
||||
}
|
||||
|
||||
networkObject.Spawn();
|
||||
|
||||
if (item.TryGetComponent(out Rigidbody body))
|
||||
{
|
||||
Vector3 worldVelocityChange =
|
||||
spawnPoint.TransformDirection(activeConfig.LocalVelocityChange);
|
||||
|
||||
activeSpawnPoint.TransformDirection(activeConfig.LocalVelocityChange);
|
||||
body.AddForce(worldVelocityChange, ForceMode.VelocityChange);
|
||||
}
|
||||
}
|
||||
|
||||
private float RandomRange(float min, float max)
|
||||
{
|
||||
return Random.Range(min, max);
|
||||
}
|
||||
private void HandleSpawnStateChanged(bool _, bool __) => Changed?.Invoke();
|
||||
private void HandleCooldownChanged(double _, double __) => Changed?.Invoke();
|
||||
|
||||
[ContextMenu("Debug/Spawn Batch (Network Play Mode)")]
|
||||
private void DebugSpawnBatch() => SpawnItems();
|
||||
|
||||
[ContextMenu("Debug/Stop Batch (Network Play Mode)")]
|
||||
private void DebugStopBatch() => StopSpawning();
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user