Added UI to Furnance, fixed carry and drag obj and added fish
This commit is contained in:
@@ -16,15 +16,21 @@ MonoBehaviour:
|
||||
spawnCooldown: 10
|
||||
localVelocityChange: {x: 0, y: -5, z: 0}
|
||||
guaranteedItems:
|
||||
- prefab: {fileID: 2182525189444712253, guid: 7f5a91b903d654342bfb77c0b9126aa3, type: 3}
|
||||
amount: 2
|
||||
randomItemCount: 5
|
||||
- prefab: {fileID: 8351909960905108939, guid: 6eee6df5dab550446b41008fa6fff89a, type: 3}
|
||||
amount: 1
|
||||
- prefab: {fileID: 6752047176419627530, guid: 8cc9c4d7d9e04d640bbd0616e7926670, type: 3}
|
||||
amount: 1
|
||||
randomItemCount: 7
|
||||
randomItems:
|
||||
- prefab: {fileID: 4609832768900680681, guid: 5cf4d9e867c084847ba82c56bddc456b, type: 3}
|
||||
weight: 1
|
||||
- prefab: {fileID: 9206541098482503491, guid: 29519dc5d2cfae441b261f3ca68f2290, type: 3}
|
||||
weight: 1
|
||||
- prefab: {fileID: 2182525189444712253, guid: 7f5a91b903d654342bfb77c0b9126aa3, type: 3}
|
||||
weight: 0.5
|
||||
- prefab: {fileID: 8351909960905108939, guid: 6eee6df5dab550446b41008fa6fff89a, type: 3}
|
||||
weight: 0.3
|
||||
- prefab: {fileID: 6752047176419627530, guid: 8cc9c4d7d9e04d640bbd0616e7926670, type: 3}
|
||||
weight: 0.3
|
||||
- prefab: {fileID: 3256751277309890899, guid: 04a6d9d9e28782a4a9f8f71ce4438fc0, type: 3}
|
||||
weight: 1.5
|
||||
randomSpawnX: 1
|
||||
randomSpawnZ: 1
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
|
||||
@@ -9,6 +9,7 @@ GameObject:
|
||||
serializedVersion: 6
|
||||
m_Component:
|
||||
- component: {fileID: 286431562970531899}
|
||||
- component: {fileID: 641239187450981236}
|
||||
- component: {fileID: 4539542294833622770}
|
||||
m_Layer: 0
|
||||
m_Name: SpawnJunk
|
||||
@@ -32,6 +33,32 @@ Transform:
|
||||
m_Children: []
|
||||
m_Father: {fileID: 0}
|
||||
m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
|
||||
--- !u!114 &641239187450981236
|
||||
MonoBehaviour:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_GameObject: {fileID: 1621409648607792275}
|
||||
m_Enabled: 1
|
||||
m_EditorHideFlags: 0
|
||||
m_Script: {fileID: 11500000, guid: d5a57f767e5e46a458fc5d3c628d0cbb, type: 3}
|
||||
m_Name:
|
||||
m_EditorClassIdentifier:
|
||||
GlobalObjectIdHash: 0
|
||||
InScenePlacedSourceGlobalObjectIdHash: 0
|
||||
DeferredDespawnTick: 0
|
||||
Ownership: 1
|
||||
AlwaysReplicateAsRoot: 0
|
||||
SynchronizeTransform: 0
|
||||
m_InScenePlaced: 1
|
||||
ActiveSceneSynchronization: 0
|
||||
SceneMigrationSynchronization: 0
|
||||
SpawnWithObservers: 1
|
||||
DontDestroyWithOwner: 0
|
||||
AutoObjectParentSync: 1
|
||||
SyncOwnerTransformWhenParented: 1
|
||||
AllowOwnerToParent: 0
|
||||
--- !u!114 &4539542294833622770
|
||||
MonoBehaviour:
|
||||
m_ObjectHideFlags: 0
|
||||
@@ -45,19 +72,4 @@ MonoBehaviour:
|
||||
m_Name:
|
||||
m_EditorClassIdentifier:
|
||||
spawnPoint: {fileID: 286431562970531899}
|
||||
spawnInterval: 0.05
|
||||
localVelocityChange: {x: 0, y: -5, z: 0}
|
||||
guaranteedItems:
|
||||
- prefab: {fileID: 4504260422113134419, guid: 8790cd6f776403940ac664c5db12dab4, type: 3}
|
||||
amount: 1
|
||||
randomItemCount: 7
|
||||
randomItems:
|
||||
- prefab: {fileID: 2474374521670934351, guid: c91d2337125ccaf45a0c57af9a6703c8, type: 3}
|
||||
weight: 1
|
||||
- prefab: {fileID: 4728426142119307488, guid: 795e138cd804c7c42aa508895e6a61df, type: 3}
|
||||
weight: 1
|
||||
- prefab: {fileID: 4504260422113134419, guid: 8790cd6f776403940ac664c5db12dab4, type: 3}
|
||||
weight: 3
|
||||
randomSpawnX: 1
|
||||
randomSpawnZ: 1
|
||||
spawnOnE: 1
|
||||
config: {fileID: 11400000, guid: 3883e23238f84f748e616770b66fa885, type: 2}
|
||||
|
||||
Reference in New Issue
Block a user