몬스터용 데이터파이프라인 개선
애니메이션 컨트롤러 및 모델 설정 기능 추가 몬스터용 애니메이션 컨트롤러 생성
This commit is contained in:
@@ -26,13 +26,20 @@ namespace Northbound.Editor
|
||||
monsterDataComponent.ApplyMonsterData();
|
||||
}
|
||||
|
||||
if (!string.IsNullOrEmpty(monsterData.meshPath))
|
||||
var animationController = prefab.GetComponent<MonsterAnimationController>();
|
||||
if (animationController == null)
|
||||
{
|
||||
animationController = prefab.AddComponent<MonsterAnimationController>();
|
||||
Debug.Log($"[MonsterPrefabSetup] Added MonsterAnimationController component");
|
||||
}
|
||||
|
||||
if (!string.IsNullOrEmpty(monsterData.modelPath))
|
||||
{
|
||||
RemoveOldModel(prefab);
|
||||
|
||||
if (monsterData.meshPath.ToLower().EndsWith(".fbx"))
|
||||
if (monsterData.modelPath.ToLower().EndsWith(".fbx"))
|
||||
{
|
||||
GameObject fbxModel = AssetDatabase.LoadAssetAtPath<GameObject>(monsterData.meshPath);
|
||||
GameObject fbxModel = AssetDatabase.LoadAssetAtPath<GameObject>(monsterData.modelPath);
|
||||
if (fbxModel != null)
|
||||
{
|
||||
GameObject fbxInstance = GameObject.Instantiate(fbxModel);
|
||||
@@ -42,11 +49,23 @@ namespace Northbound.Editor
|
||||
fbxInstance.transform.localRotation = Quaternion.identity;
|
||||
fbxInstance.transform.localScale = Vector3.one;
|
||||
|
||||
Debug.Log($"[MonsterPrefabSetup] Applied FBX model: {monsterData.meshPath}");
|
||||
Debug.Log($"[MonsterPrefabSetup] Applied FBX model: {monsterData.modelPath}");
|
||||
|
||||
Avatar modelAvatar = fbxModel.GetComponent<Animator>()?.avatar;
|
||||
|
||||
if (modelAvatar != null)
|
||||
{
|
||||
Animator prefabAnimator = prefab.GetComponent<Animator>();
|
||||
if (prefabAnimator != null)
|
||||
{
|
||||
prefabAnimator.avatar = modelAvatar;
|
||||
Debug.Log($"[MonsterPrefabSetup] Applied Avatar: {modelAvatar.name}");
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
Debug.LogWarning($"[MonsterPrefabSetup] Could not load FBX model: {monsterData.meshPath}");
|
||||
Debug.LogWarning($"[MonsterPrefabSetup] Could not load FBX model: {monsterData.modelPath}");
|
||||
}
|
||||
}
|
||||
else
|
||||
@@ -54,34 +73,34 @@ namespace Northbound.Editor
|
||||
var meshFilter = prefab.GetComponent<MeshFilter>();
|
||||
if (meshFilter != null)
|
||||
{
|
||||
Mesh mesh = AssetDatabase.LoadAssetAtPath<Mesh>(monsterData.meshPath);
|
||||
Mesh mesh = AssetDatabase.LoadAssetAtPath<Mesh>(monsterData.modelPath);
|
||||
if (mesh != null)
|
||||
{
|
||||
meshFilter.sharedMesh = mesh;
|
||||
Debug.Log($"[MonsterPrefabSetup] Applied mesh: {monsterData.meshPath}");
|
||||
Debug.Log($"[MonsterPrefabSetup] Applied mesh: {monsterData.modelPath}");
|
||||
}
|
||||
else
|
||||
{
|
||||
Debug.LogWarning($"[MonsterPrefabSetup] Could not load mesh: {monsterData.meshPath}");
|
||||
Debug.LogWarning($"[MonsterPrefabSetup] Could not load mesh: {monsterData.modelPath}");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!string.IsNullOrEmpty(monsterData.animatorControllerPath))
|
||||
if (!string.IsNullOrEmpty(monsterData.animationControllerPath))
|
||||
{
|
||||
Animator animator = prefab.GetComponent<Animator>();
|
||||
if (animator != null)
|
||||
{
|
||||
RuntimeAnimatorController controller = AssetDatabase.LoadAssetAtPath<RuntimeAnimatorController>(monsterData.animatorControllerPath);
|
||||
RuntimeAnimatorController controller = AssetDatabase.LoadAssetAtPath<RuntimeAnimatorController>(monsterData.animationControllerPath);
|
||||
if (controller != null)
|
||||
{
|
||||
animator.runtimeAnimatorController = controller;
|
||||
Debug.Log($"[MonsterPrefabSetup] Applied Animator Controller: {monsterData.animatorControllerPath}");
|
||||
Debug.Log($"[MonsterPrefabSetup] Applied Animator Controller: {monsterData.animationControllerPath}");
|
||||
}
|
||||
else
|
||||
{
|
||||
Debug.LogWarning($"[MonsterPrefabSetup] Could not load Animator Controller: {monsterData.animatorControllerPath}");
|
||||
Debug.LogWarning($"[MonsterPrefabSetup] Could not load Animator Controller: {monsterData.animationControllerPath}");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -58,6 +58,9 @@ namespace Northbound
|
||||
[Tooltip("디버그 정보 표시")]
|
||||
public bool showDebugInfo = true;
|
||||
|
||||
[Header("Events")]
|
||||
public System.Action<GameObject> OnAttackPerformed;
|
||||
|
||||
private NavMeshAgent _agent;
|
||||
private EnemyUnit _enemyUnit;
|
||||
private Transform _coreTransform;
|
||||
@@ -390,6 +393,8 @@ namespace Northbound
|
||||
damageable.TakeDamage(attackDamage, NetworkObjectId);
|
||||
_lastAttackTime = Time.time;
|
||||
|
||||
OnAttackPerformed?.Invoke(target);
|
||||
|
||||
if (showDebugInfo)
|
||||
Debug.Log($"<color=red>[EnemyAI] {gameObject.name} -> {target.name} 타격 성공! (데미지: {attackDamage})</color>");
|
||||
}
|
||||
@@ -502,6 +507,15 @@ namespace Northbound
|
||||
|
||||
#endregion
|
||||
|
||||
#region Public API
|
||||
|
||||
public EnemyAIState GetCurrentState()
|
||||
{
|
||||
return _currentState.Value;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Gizmos (기존 코드 유지)
|
||||
private void OnDrawGizmos()
|
||||
{
|
||||
|
||||
148
Assets/Scripts/MonsterAnimationController.cs
Normal file
148
Assets/Scripts/MonsterAnimationController.cs
Normal file
@@ -0,0 +1,148 @@
|
||||
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.Server
|
||||
);
|
||||
|
||||
private NetworkVariable<bool> _networkIsMoving = new NetworkVariable<bool>(
|
||||
false,
|
||||
NetworkVariableReadPermission.Everyone,
|
||||
NetworkVariableWritePermission.Server
|
||||
);
|
||||
|
||||
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)
|
||||
{
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
2
Assets/Scripts/MonsterAnimationController.cs.meta
Normal file
2
Assets/Scripts/MonsterAnimationController.cs.meta
Normal file
@@ -0,0 +1,2 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 2b2a547d86f65d64a93a7a3c415d1ce2
|
||||
Reference in New Issue
Block a user