Files
Northbound/Assets/Scripts/MonsterAnimationController.cs
dal4segno 4bd46b2a0a 네트워크 동기화 문제 해결
몬스터와 크립에 네트워크 관련 컴포넌트가 없는 문제 수정
포탈/캠프와 몬스터/크립 간의 계층 구조 해제
 - 네트워크 오브젝트끼리 계층 구조로 둘 수 없음
2026-02-04 18:16:59 +09:00

149 lines
4.6 KiB
C#

using Unity.Netcode;
using UnityEngine;
using UnityEngine.AI;
namespace Northbound
{
[RequireComponent(typeof(Animator))]
[RequireComponent(typeof(EnemyAIController))]
public class MonsterAnimationController : NetworkBehaviour
{
[Header("Animation Parameters")]
[Tooltip("Speed parameter name in Animator")]
public string speedParam = "Speed";
[Tooltip("Attack trigger parameter name in Animator")]
public string attackTriggerParam = "Attack";
[Tooltip("IsMoving bool parameter name in Animator")]
public string isMovingParam = "IsMoving";
[Header("Settings")]
[Tooltip("Auto-load animator controller from MonsterData")]
public bool autoLoadFromMonsterData = true;
[Tooltip("Debug logging")]
public bool debugLogging = false;
private Animator _animator;
private EnemyAIController _aiController;
private NavMeshAgent _agent;
private NetworkVariable<float> _networkSpeed = new NetworkVariable<float>(
0f,
NetworkVariableReadPermission.Everyone,
NetworkVariableWritePermission.Owner
);
private NetworkVariable<bool> _networkIsMoving = new NetworkVariable<bool>(
false,
NetworkVariableReadPermission.Everyone,
NetworkVariableWritePermission.Owner
);
public override void OnNetworkSpawn()
{
base.OnNetworkSpawn();
_animator = GetComponent<Animator>();
_aiController = GetComponent<EnemyAIController>();
_agent = GetComponent<NavMeshAgent>();
_aiController.OnAttackPerformed += HandleAttackPerformed;
if (autoLoadFromMonsterData)
{
LoadAnimatorController();
}
}
public override void OnNetworkDespawn()
{
if (_aiController != null)
{
_aiController.OnAttackPerformed -= HandleAttackPerformed;
}
base.OnNetworkDespawn();
}
private void HandleAttackPerformed(GameObject target)
{
if (!IsServer) return;
TriggerAttackClientRpc();
if (debugLogging)
Debug.Log($"[MonsterAnimationController] Triggered attack animation for {target.name}", this);
}
private void LoadAnimatorController()
{
var monsterDataComponent = GetComponent<MonsterDataComponent>();
if (monsterDataComponent != null && monsterDataComponent.monsterData != null)
{
string animatorPath = monsterDataComponent.monsterData.animationControllerPath;
if (!string.IsNullOrEmpty(animatorPath))
{
RuntimeAnimatorController controller = Resources.Load<RuntimeAnimatorController>(animatorPath);
if (controller != null)
{
_animator.runtimeAnimatorController = controller;
if (debugLogging)
Debug.Log($"[MonsterAnimationController] Loaded animator from {animatorPath}", this);
}
else if (debugLogging)
{
Debug.LogWarning($"[MonsterAnimationController] Could not load animator from {animatorPath}", this);
}
}
}
}
private void Update()
{
if (!IsSpawned) return;
if (IsServer && IsOwner)
{
UpdateServerSide();
}
UpdateClientSide();
}
private void UpdateServerSide()
{
if (_agent == null) return;
float currentSpeed = _agent.velocity.magnitude;
bool isMoving = currentSpeed > 0.1f && _agent.isOnNavMesh && !_agent.isStopped;
_networkSpeed.Value = currentSpeed;
_networkIsMoving.Value = isMoving;
}
private void UpdateClientSide()
{
if (_animator == null) return;
_animator.SetFloat(speedParam, _networkSpeed.Value);
_animator.SetBool(isMovingParam, _networkIsMoving.Value);
}
[Rpc(SendTo.ClientsAndHost)]
private void TriggerAttackClientRpc()
{
if (_animator != null)
{
_animator.SetTrigger(attackTriggerParam);
}
}
public void ResetAttackTrigger()
{
if (_animator != null)
{
_animator.ResetTrigger(attackTriggerParam);
}
}
}
}