115 lines
4.1 KiB
C#
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);
|
|
}
|
|
}
|
|
} |