Files
Learning-UnityNGO/Assets/CONTENT/FEATURES/DEV-TEST/Scripts/Clone/GhostRoundManager.cs
T
2026-07-02 17:04:29 +03:00

115 lines
4.1 KiB
C#

using System.Collections.Generic;
using Unity.Netcode;
using UnityEngine;
/// <summary>
/// Обычный MonoBehaviour (не сетевой) — вся логика чисто серверная.
/// Повесь на тот же объект, что и RoundManager (или на любой объект сцены).
/// Хранит архив записей, спавнит клонов на старте раунда и тикает их в FixedUpdate.
/// </summary>
public class GhostRoundManager : MonoBehaviour
{
public static GhostRoundManager Current { get; private set; }
[SerializeField] private GameObject ghostPrefab; // префаб клона (см. чеклист настройки)
[SerializeField] private int maxGhostsPerPlayer = 3; // сколько прошлых раундов помним на игрока
// ClientId -> записи его прошлых раундов
private readonly Dictionary<ulong, List<GhostRecording>> archive = new Dictionary<ulong, List<GhostRecording>>();
private readonly List<GhostPlayer> activeGhosts = new List<GhostPlayer>();
private int roundTick;
private bool roundRunning;
private bool IsServer => NetworkManager.Singleton != null && NetworkManager.Singleton.IsServer;
private void Awake()
{
Current = this;
}
private void OnDestroy()
{
if (Current == this) Current = null;
}
/// <summary> Вызывается из RoundManager.StartRoundWithDelay после RespawnAllPlayers. </summary>
public void ServerStartRound()
{
if (!IsServer) return;
roundTick = 0;
activeGhosts.Clear();
// Спавним клонов из архива
foreach (KeyValuePair<ulong, List<GhostRecording>> _kvp in archive)
{
foreach (GhostRecording _recording in _kvp.Value)
{
if (_recording.positions.Count == 0) continue;
GameObject _go = Instantiate(ghostPrefab, _recording.positions[0], _recording.rotations[0]);
_go.GetComponent<NetworkObject>().Spawn();
GhostPlayer _ghost = _go.GetComponent<GhostPlayer>();
_ghost.Init(_recording);
activeGhosts.Add(_ghost);
}
}
// Включаем запись у всех живых игроков
foreach (NetworkClient _client in NetworkManager.Singleton.ConnectedClientsList)
{
_client.PlayerObject?.GetComponent<GhostRecorder>()?.BeginRecording();
}
roundRunning = true;
}
private void FixedUpdate()
{
if (!IsServer || !roundRunning) return;
if (RoundManager.Current == null || !RoundManager.Current.IsRoundActive) return;
foreach (GhostPlayer _ghost in activeGhosts)
{
if (_ghost != null && _ghost.IsSpawned)
_ghost.Step(roundTick);
}
roundTick++;
}
/// <summary> Вызывается из RoundManager.EndRound первой строкой (до WaitForSeconds). </summary>
public void ServerEndRound()
{
if (!IsServer || !roundRunning) return;
roundRunning = false;
// Убираем клонов со сцены
foreach (GhostPlayer _ghost in activeGhosts)
{
if (_ghost != null && _ghost.IsSpawned)
_ghost.NetworkObject.Despawn();
}
activeGhosts.Clear();
// Останавливаем запись и складываем раунд в архив
foreach (NetworkClient _client in NetworkManager.Singleton.ConnectedClientsList)
{
GhostRecorder _recorder = _client.PlayerObject?.GetComponent<GhostRecorder>();
if (_recorder == null || _recorder.Recording == null) continue;
_recorder.StopRecording();
if (!archive.TryGetValue(_client.ClientId, out List<GhostRecording> _list))
{
_list = new List<GhostRecording>();
archive[_client.ClientId] = _list;
}
_list.Add(_recorder.Recording);
if (_list.Count > maxGhostsPerPlayer) _list.RemoveAt(0);
}
}
}