Added map and fixed other bugs
This commit is contained in:
@@ -14,7 +14,14 @@ public class GhostPlayer : NetworkBehaviour
|
||||
[SerializeField] private GameObject hitEffectPrefab;
|
||||
|
||||
[Header("Visuals")]
|
||||
[SerializeField] private Color ghostColor = new Color(0.5f, 0.5f, 0.5f, 1f);
|
||||
[Tooltip("Тусклый цвет СВОЕГО клона (клон локального игрока)")]
|
||||
[SerializeField] private Color ownGhostColor = new Color(0f, 0f, 0.5f, 1f);
|
||||
[Tooltip("Тусклый цвет ЧУЖОГО клона (клон соперника)")]
|
||||
[SerializeField] private Color enemyGhostColor = new Color(0.5f, 0f, 0f, 1f);
|
||||
|
||||
// ClientId оригинального игрока. Синхронизируется на клиентов для окраски
|
||||
// (recording живёт только на сервере, поэтому цвет по нему клиент не узнает).
|
||||
private NetworkVariable<ulong> ownerId = new NetworkVariable<ulong>(ulong.MaxValue);
|
||||
|
||||
private GhostRecording recording;
|
||||
private int shotIdx;
|
||||
@@ -22,15 +29,34 @@ public class GhostPlayer : NetworkBehaviour
|
||||
|
||||
private Collider[] ownColliders;
|
||||
private PlayerHealth health;
|
||||
private PlayerShield shield;
|
||||
|
||||
/// <summary> Кому засчитывать вход в яму / фраги — ClientId оригинального игрока. </summary>
|
||||
public ulong OriginalOwnerId => recording != null ? recording.ownerClientId : ulong.MaxValue;
|
||||
public ulong OriginalOwnerId => ownerId.Value;
|
||||
|
||||
public override void OnNetworkSpawn()
|
||||
{
|
||||
// Красим клона на всех клиентах, чтобы отличался от живых игроков
|
||||
ownerId.OnValueChanged += OnOwnerChanged;
|
||||
ApplyColor();
|
||||
}
|
||||
|
||||
public override void OnNetworkDespawn()
|
||||
{
|
||||
ownerId.OnValueChanged -= OnOwnerChanged;
|
||||
}
|
||||
|
||||
private void OnOwnerChanged(ulong _previous, ulong _current) => ApplyColor();
|
||||
|
||||
// Свой клон — тусклый синий, чужой — тусклый красный (как живые игроки, но тускло)
|
||||
private void ApplyColor()
|
||||
{
|
||||
bool _isMine = NetworkManager.Singleton != null
|
||||
&& ownerId.Value == NetworkManager.Singleton.LocalClientId;
|
||||
|
||||
Color _color = _isMine ? ownGhostColor : enemyGhostColor;
|
||||
|
||||
foreach (MeshRenderer _renderer in GetComponentsInChildren<MeshRenderer>())
|
||||
_renderer.material.color = ghostColor;
|
||||
_renderer.material.color = _color;
|
||||
}
|
||||
|
||||
/// <summary> Вызывается на сервере сразу после NetworkObject.Spawn(). </summary>
|
||||
@@ -40,9 +66,12 @@ public class GhostPlayer : NetworkBehaviour
|
||||
shotIdx = 0;
|
||||
finished = false;
|
||||
|
||||
ownerId.Value = _recording.ownerClientId;
|
||||
|
||||
ownColliders = GetComponentsInChildren<Collider>();
|
||||
|
||||
health = GetComponent<PlayerHealth>();
|
||||
shield = GetComponent<PlayerShield>();
|
||||
// Init(true): мы на сервере. Без этого TakeDamage у клона молча не работает
|
||||
health.Init(true);
|
||||
}
|
||||
@@ -66,6 +95,9 @@ public class GhostPlayer : NetworkBehaviour
|
||||
transform.position = recording.positions[_tick];
|
||||
transform.rotation = recording.rotations[_tick];
|
||||
|
||||
if (shield != null && _tick < recording.shieldStates.Count)
|
||||
shield.SetShieldState(recording.shieldStates[_tick]);
|
||||
|
||||
while (shotIdx < recording.shots.Count && recording.shots[shotIdx].tick <= _tick)
|
||||
{
|
||||
ReplayShot(recording.shots[shotIdx]);
|
||||
@@ -80,7 +112,8 @@ public class GhostPlayer : NetworkBehaviour
|
||||
{
|
||||
if (System.Array.Exists(ownColliders, c => c == _hit.collider)) return;
|
||||
|
||||
if (_hit.collider.TryGetComponent(out PlayerHealth _targetHealth))
|
||||
PlayerHealth _targetHealth = _hit.collider.GetComponentInParent<PlayerHealth>();
|
||||
if (_targetHealth != null)
|
||||
_targetHealth.TakeDamage((int)damageAmount);
|
||||
|
||||
SpawnHitEffectClientRpc(_hit.point);
|
||||
|
||||
@@ -39,6 +39,9 @@ public class GhostRecorder : NetworkBehaviour
|
||||
Recording.positions.Add(transform.position);
|
||||
Recording.rotations.Add(transform.rotation);
|
||||
Recording.pitches.Add(cameraRoot != null ? cameraRoot.localEulerAngles.x : 0f);
|
||||
|
||||
PlayerShield _shield = GetComponent<PlayerShield>();
|
||||
Recording.shieldStates.Add(_shield != null && _shield.IsShieldUp);
|
||||
}
|
||||
|
||||
/// <summary> Дёргается из PlayerShoot.ShootServerRpc — только на сервере. </summary>
|
||||
|
||||
@@ -11,6 +11,7 @@ public class GhostRecording
|
||||
public List<Vector3> positions = new List<Vector3>(); // сэмпл каждый FixedUpdate
|
||||
public List<Quaternion> rotations = new List<Quaternion>(); // yaw тела
|
||||
public List<float> pitches = new List<float>(); // наклон камеры (на будущее, для визуала)
|
||||
public List<bool> shieldStates = new List<bool>(); // щит поднят — сэмпл каждый FixedUpdate
|
||||
|
||||
public List<GhostShotEvent> shots = new List<GhostShotEvent>();
|
||||
}
|
||||
|
||||
@@ -80,6 +80,13 @@ public class GhostRoundManager : MonoBehaviour
|
||||
roundTick++;
|
||||
}
|
||||
|
||||
/// <summary> Полностью очищает архив прошлых раундов (для рестарта матча). </summary>
|
||||
public void ClearArchive()
|
||||
{
|
||||
if (!IsServer) return;
|
||||
archive.Clear();
|
||||
}
|
||||
|
||||
/// <summary> Вызывается из RoundManager.EndRound первой строкой (до WaitForSeconds). </summary>
|
||||
public void ServerEndRound()
|
||||
{
|
||||
|
||||
Reference in New Issue
Block a user