131 lines
3.6 KiB
C#
131 lines
3.6 KiB
C#
using UnityEngine;
|
|
using Unity.Netcode;
|
|
using Unity.Netcode.Components;
|
|
using System;
|
|
|
|
public class PlayerHealth : NetworkBehaviour
|
|
{
|
|
[SerializeField] private int maxHealth = 100;
|
|
|
|
public NetworkVariable<int> currentHealth = new NetworkVariable<int>(
|
|
100,
|
|
NetworkVariableReadPermission.Everyone,
|
|
NetworkVariableWritePermission.Server
|
|
);
|
|
|
|
public NetworkVariable<bool> isDead = new NetworkVariable<bool>(false);
|
|
|
|
public event Action<int, int> HealthChanged;
|
|
public event Action Died;
|
|
|
|
private bool isServer;
|
|
|
|
public void Init(bool _isServer)
|
|
{
|
|
isServer = _isServer;
|
|
|
|
currentHealth.OnValueChanged += OnHealthChanged;
|
|
|
|
if(isServer)
|
|
{
|
|
currentHealth.Value = maxHealth;
|
|
}
|
|
|
|
HealthChanged?.Invoke(currentHealth.Value, maxHealth);
|
|
}
|
|
|
|
private void OnHealthChanged(int _previousValue, int _newValue)
|
|
{
|
|
HealthChanged?.Invoke(_newValue, maxHealth);
|
|
|
|
if(_newValue <= 0)
|
|
{
|
|
Die();
|
|
}
|
|
}
|
|
|
|
public void TakeDamage(int _damageAmount)
|
|
{
|
|
if (!isServer) return;
|
|
if(isDead.Value) return;
|
|
|
|
PlayerShield _shield = GetComponent<PlayerShield>();
|
|
if (_shield != null && _shield.IsShieldUp) return;
|
|
|
|
currentHealth.Value = Mathf.Max(0, currentHealth.Value - _damageAmount);
|
|
}
|
|
|
|
public override void OnNetworkDespawn()
|
|
{
|
|
currentHealth.OnValueChanged -= OnHealthChanged;
|
|
}
|
|
|
|
private void Die()
|
|
{
|
|
if(!isServer) return;
|
|
|
|
GetComponent<PlayerShield>()?.SetShieldState(false);
|
|
isDead.Value = true;
|
|
OnDiedClientRpc();
|
|
}
|
|
|
|
public void Respawn(Vector3 _spawnPosition)
|
|
{
|
|
if(!isServer) return;
|
|
|
|
GetComponent<PlayerShield>()?.SetShieldState(false);
|
|
isDead.Value = false;
|
|
currentHealth.Value = maxHealth;
|
|
RespawnClientRpc(_spawnPosition);
|
|
}
|
|
|
|
public void PushCurrentHealthValue()
|
|
{
|
|
HealthChanged?.Invoke(currentHealth.Value, maxHealth);
|
|
}
|
|
|
|
[ClientRpc]
|
|
private void OnDiedClientRpc()
|
|
{
|
|
Debug.Log($"Player {OwnerClientId} died");
|
|
SetPhysicsAndVisualsActive(false);
|
|
Died?.Invoke();
|
|
}
|
|
|
|
[ClientRpc]
|
|
private void RespawnClientRpc(Vector3 _spawnPosition)
|
|
{
|
|
SetPhysicsAndVisualsActive(true);
|
|
|
|
// Позицию задаёт только владелец — он авторитет над NetworkTransform (Owner authority)
|
|
if (!IsOwner) return;
|
|
|
|
TeleportOwner(_spawnPosition);
|
|
}
|
|
|
|
// Телепорт без интерполяции. CharacterController кэширует свою позицию,
|
|
// поэтому его надо выключить на время установки трансформа.
|
|
private void TeleportOwner(Vector3 _spawnPosition)
|
|
{
|
|
CharacterController _controller = GetComponent<CharacterController>();
|
|
if (_controller != null) _controller.enabled = false;
|
|
|
|
NetworkTransform _netTransform = GetComponent<NetworkTransform>();
|
|
if (_netTransform != null)
|
|
_netTransform.Teleport(_spawnPosition, transform.rotation, transform.localScale);
|
|
else
|
|
transform.position = _spawnPosition;
|
|
|
|
if (_controller != null) _controller.enabled = true;
|
|
}
|
|
|
|
private void SetPhysicsAndVisualsActive(bool _isActive)
|
|
{
|
|
foreach (Collider collider in GetComponentsInChildren<Collider>())
|
|
collider.enabled = _isActive;
|
|
|
|
foreach (MeshRenderer meshRenderer in GetComponentsInChildren<MeshRenderer>())
|
|
meshRenderer.enabled = _isActive;
|
|
}
|
|
}
|