네트워크 멀티플레이 대응
This commit is contained in:
162
Assets/Scripts/DamageableNetworkBehaviour.cs
Normal file
162
Assets/Scripts/DamageableNetworkBehaviour.cs
Normal file
@@ -0,0 +1,162 @@
|
||||
using System;
|
||||
using Unity.Netcode;
|
||||
using UnityEngine;
|
||||
|
||||
namespace Northbound
|
||||
{
|
||||
public abstract class DamageableNetworkBehaviour : NetworkBehaviour, IDamageable
|
||||
{
|
||||
[Header("Health Settings")]
|
||||
[SerializeField] protected int maxHealth = 100;
|
||||
[SerializeField] protected bool showHealthBar = true;
|
||||
|
||||
[Header("Visual Effects")]
|
||||
[SerializeField] protected GameObject damageEffectPrefab;
|
||||
[SerializeField] protected GameObject destroyEffectPrefab;
|
||||
[SerializeField] protected Transform effectSpawnPoint;
|
||||
|
||||
protected NetworkVariable<int> _currentHealth = new NetworkVariable<int>(
|
||||
0,
|
||||
NetworkVariableReadPermission.Everyone,
|
||||
NetworkVariableWritePermission.Server
|
||||
);
|
||||
|
||||
public event Action<int, int> OnHealthChanged;
|
||||
public event Action OnDestroyed;
|
||||
|
||||
protected void InvokeOnHealthChanged(int currentHealth, int maxHealth)
|
||||
{
|
||||
OnHealthChanged?.Invoke(currentHealth, maxHealth);
|
||||
}
|
||||
|
||||
protected void InvokeOnDestroyed()
|
||||
{
|
||||
OnDestroyed?.Invoke();
|
||||
}
|
||||
|
||||
public override void OnNetworkSpawn()
|
||||
{
|
||||
base.OnNetworkSpawn();
|
||||
|
||||
if (IsOwner)
|
||||
{
|
||||
InitializeHealthServerRpc(maxHealth);
|
||||
}
|
||||
|
||||
_currentHealth.OnValueChanged += OnHealthValueChanged;
|
||||
InitializeHealthBar();
|
||||
UpdateHealthUI();
|
||||
}
|
||||
|
||||
[ServerRpc]
|
||||
private void InitializeHealthServerRpc(int health)
|
||||
{
|
||||
if (_currentHealth.Value == 0)
|
||||
_currentHealth.Value = health;
|
||||
}
|
||||
|
||||
public override void OnNetworkDespawn()
|
||||
{
|
||||
_currentHealth.OnValueChanged -= OnHealthValueChanged;
|
||||
base.OnNetworkDespawn();
|
||||
}
|
||||
|
||||
protected virtual void InitializeHealthBar()
|
||||
{
|
||||
}
|
||||
|
||||
protected virtual void UpdateHealthUI()
|
||||
{
|
||||
}
|
||||
|
||||
public virtual void TakeDamage(int damage, ulong attackerId)
|
||||
{
|
||||
if (!IsOwner)
|
||||
{
|
||||
TakeDamageServerRpc(damage, attackerId);
|
||||
return;
|
||||
}
|
||||
|
||||
TakeDamageServerRpc(damage, attackerId);
|
||||
}
|
||||
|
||||
[ServerRpc]
|
||||
private void TakeDamageServerRpc(int damage, ulong attackerId)
|
||||
{
|
||||
if (_currentHealth.Value <= 0)
|
||||
return;
|
||||
|
||||
int actualDamage = Mathf.Min(damage, _currentHealth.Value);
|
||||
_currentHealth.Value -= actualDamage;
|
||||
|
||||
Debug.Log($"<color=red>[{GetType().Name}] {gameObject.name} received {actualDamage} damage. Health: {_currentHealth.Value}/{maxHealth}</color>");
|
||||
|
||||
ShowDamageEffectClientRpc();
|
||||
|
||||
if (_currentHealth.Value <= 0)
|
||||
{
|
||||
Die(attackerId);
|
||||
}
|
||||
}
|
||||
|
||||
protected virtual void Die(ulong killerId)
|
||||
{
|
||||
Debug.Log($"<color=red>[{GetType().Name}] {gameObject.name} destroyed! Killer: {killerId}</color>");
|
||||
OnDestroyed?.Invoke();
|
||||
ShowDeathEffectClientRpc();
|
||||
}
|
||||
|
||||
[ClientRpc]
|
||||
protected void ShowDamageEffectClientRpc()
|
||||
{
|
||||
if (damageEffectPrefab != null)
|
||||
{
|
||||
Transform spawnPoint = effectSpawnPoint != null ? effectSpawnPoint : transform;
|
||||
GameObject effect = Instantiate(damageEffectPrefab, spawnPoint.position + Vector3.up, Quaternion.identity);
|
||||
Destroy(effect, 2f);
|
||||
}
|
||||
}
|
||||
|
||||
[ClientRpc]
|
||||
protected void ShowDeathEffectClientRpc()
|
||||
{
|
||||
if (destroyEffectPrefab != null)
|
||||
{
|
||||
Transform spawnPoint = effectSpawnPoint != null ? effectSpawnPoint : transform;
|
||||
GameObject effect = Instantiate(destroyEffectPrefab, spawnPoint.position, Quaternion.identity);
|
||||
Destroy(effect, 3f);
|
||||
}
|
||||
}
|
||||
|
||||
protected virtual void OnHealthValueChanged(int previousValue, int newValue)
|
||||
{
|
||||
OnHealthChanged?.Invoke(newValue, maxHealth);
|
||||
UpdateHealthUI();
|
||||
}
|
||||
|
||||
public int GetCurrentHealth() => _currentHealth.Value;
|
||||
public int GetMaxHealth() => maxHealth;
|
||||
public float GetHealthPercentage() => maxHealth > 0 ? (float)_currentHealth.Value / maxHealth : 0f;
|
||||
public bool IsDead() => _currentHealth.Value <= 0;
|
||||
|
||||
public virtual void Heal(int amount)
|
||||
{
|
||||
if (!IsOwner)
|
||||
{
|
||||
HealServerRpc(amount);
|
||||
return;
|
||||
}
|
||||
|
||||
HealServerRpc(amount);
|
||||
}
|
||||
|
||||
[ServerRpc]
|
||||
private void HealServerRpc(int amount)
|
||||
{
|
||||
int healAmount = Mathf.Min(amount, maxHealth - _currentHealth.Value);
|
||||
_currentHealth.Value += healAmount;
|
||||
|
||||
Debug.Log($"<color=green>[{GetType().Name}] {gameObject.name} healed {healAmount}. Health: {_currentHealth.Value}/{maxHealth}</color>");
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user