diff --git a/Assembly-CSharp.csproj b/Assembly-CSharp.csproj index 4c237bb..44647a7 100644 --- a/Assembly-CSharp.csproj +++ b/Assembly-CSharp.csproj @@ -86,7 +86,6 @@ - @@ -99,6 +98,7 @@ + diff --git a/Assets/Animations/Attack.anim b/Assets/Animations/Attack.anim index 0f5467a..e49f15d 100644 --- a/Assets/Animations/Attack.anim +++ b/Assets/Animations/Attack.anim @@ -27124,21 +27124,21 @@ AnimationClip: m_HasMotionFloatCurves: 0 m_Events: - time: 0 - functionName: AttachEquipment + functionName: OnEquipWeapon data: handslot.r objectReferenceParameter: {fileID: 1314983689436087486, guid: 6c700f44a7d74e141a002e9484d2057c, type: 3} floatParameter: 0 intParameter: 0 messageOptions: 0 - - time: 1.0666667 - functionName: DetachEquipment + - time: 1.0333333 + functionName: OnUnequipWeapon data: handslot.r objectReferenceParameter: {fileID: 0} floatParameter: 0 intParameter: 0 messageOptions: 0 - - time: 1.1 - functionName: OnInteractionComplete + - time: 1.0666667 + functionName: OnAttackComplete data: objectReferenceParameter: {fileID: 0} floatParameter: 0 diff --git a/Assets/Animations/Mining.anim b/Assets/Animations/Mining.anim index 076a61e..45dcc58 100644 --- a/Assets/Animations/Mining.anim +++ b/Assets/Animations/Mining.anim @@ -5067,7 +5067,7 @@ AnimationClip: m_Level: 0 m_CycleOffset: 0 m_HasAdditiveReferencePose: 0 - m_LoopTime: 1 + m_LoopTime: 0 m_LoopBlend: 0 m_LoopBlendOrientation: 0 m_LoopBlendPositionY: 0 diff --git a/Assets/DefaultNetworkPrefabs.asset b/Assets/DefaultNetworkPrefabs.asset index 05c6dec..259c924 100644 --- a/Assets/DefaultNetworkPrefabs.asset +++ b/Assets/DefaultNetworkPrefabs.asset @@ -29,11 +29,6 @@ MonoBehaviour: SourcePrefabToOverride: {fileID: 0} SourceHashToOverride: 0 OverridingTargetPrefab: {fileID: 0} - - Override: 0 - Prefab: {fileID: 2938167817760513538, guid: 04e95700704d92248b63ce5674bd9638, type: 3} - SourcePrefabToOverride: {fileID: 0} - SourceHashToOverride: 0 - OverridingTargetPrefab: {fileID: 0} - Override: 0 Prefab: {fileID: 8124290768227340041, guid: e56926eda34629f4fbf3e4c53f0f8bd4, type: 3} SourcePrefabToOverride: {fileID: 0} @@ -59,3 +54,8 @@ MonoBehaviour: SourcePrefabToOverride: {fileID: 0} SourceHashToOverride: 0 OverridingTargetPrefab: {fileID: 0} + - Override: 0 + Prefab: {fileID: 2998551506809628252, guid: 1be692ccde46d2a4baedc2ee75fbfbdb, type: 3} + SourcePrefabToOverride: {fileID: 0} + SourceHashToOverride: 0 + OverridingTargetPrefab: {fileID: 0} diff --git a/Assets/Prefabs/Player.prefab b/Assets/Prefabs/Player.prefab index d3778f9..1e92680 100644 --- a/Assets/Prefabs/Player.prefab +++ b/Assets/Prefabs/Player.prefab @@ -171,7 +171,7 @@ MonoBehaviour: m_Name: m_EditorClassIdentifier: Assembly-CSharp::Northbound.PlayerInteraction ShowTopMostFoldoutHeaderGroup: 1 - interactionRange: 7.96 + interactionRange: 5 interactableLayer: serializedVersion: 2 m_Bits: 128 @@ -218,6 +218,17 @@ MonoBehaviour: serializedVersion: 2 m_Bits: 256 attackAnimationTrigger: Attack + useAnimationEvents: 1 + blockDuringAnimation: 1 + useEquipment: 1 + equipmentData: + socketName: handslot.r + equipmentPrefab: {fileID: 919132149155446097, guid: 1261145a64d4f3e43bee728a02c1b5e3, type: 3} + attachOnStart: 1 + detachOnEnd: 1 + keepEquipped: 0 + attachDelay: 0 + detachDelay: 0 attackEffectPrefab: {fileID: 0} attackPoint: {fileID: 0} --- !u!114 &6066313428661204362 diff --git a/Assets/Prefabs/Resource.prefab b/Assets/Prefabs/Resource.prefab index 261cb4c..bc1aff6 100644 --- a/Assets/Prefabs/Resource.prefab +++ b/Assets/Prefabs/Resource.prefab @@ -11,6 +11,7 @@ GameObject: - component: {fileID: 3247786716306397435} - component: {fileID: 5088182607393517512} - component: {fileID: -7869551286978933574} + - component: {fileID: 2900844162207268165} m_Layer: 7 m_Name: Resource m_TagString: Untagged @@ -80,12 +81,36 @@ MonoBehaviour: rechargeAmount: 5 interactionAnimationTrigger: Mining equipmentData: - socketName: RightHand - equipmentPrefab: {fileID: 919132149155446097, guid: 4541fc3a8c0c22f439818d754f210456, type: 3} + socketName: handslot.r + equipmentPrefab: {fileID: 919132149155446097, guid: 804d477fc7f114c498aa6f95452be893, type: 3} attachOnStart: 1 detachOnEnd: 1 + keepEquipped: 0 + attachDelay: 0 + detachDelay: 0 gatheringEffectPrefab: {fileID: 0} effectSpawnPoint: {fileID: 0} +--- !u!65 &2900844162207268165 +BoxCollider: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 5585059388146411250} + m_Material: {fileID: 0} + m_IncludeLayers: + serializedVersion: 2 + m_Bits: 0 + m_ExcludeLayers: + serializedVersion: 2 + m_Bits: 0 + m_LayerOverridePriority: 0 + m_IsTrigger: 0 + m_ProvidesContacts: 0 + m_Enabled: 1 + serializedVersion: 3 + m_Size: {x: 4, y: 5, z: 4} + m_Center: {x: 0, y: 0, z: 0} --- !u!1001 &2116156636655425566 PrefabInstance: m_ObjectHideFlags: 0 @@ -157,38 +182,8 @@ PrefabInstance: m_RemovedComponents: [] m_RemovedGameObjects: [] m_AddedGameObjects: [] - m_AddedComponents: - - targetCorrespondingSourceObject: {fileID: 919132149155446097, guid: 61adaca355888734ab2e5d1cb08f040c, type: 3} - insertIndex: -1 - addedObject: {fileID: 2263651141701971438} + m_AddedComponents: [] m_SourcePrefab: {fileID: 100100000, guid: 61adaca355888734ab2e5d1cb08f040c, type: 3} ---- !u!1 &1269857788199413071 stripped -GameObject: - m_CorrespondingSourceObject: {fileID: 919132149155446097, guid: 61adaca355888734ab2e5d1cb08f040c, type: 3} - m_PrefabInstance: {fileID: 2116156636655425566} - m_PrefabAsset: {fileID: 0} ---- !u!64 &2263651141701971438 -MeshCollider: - m_ObjectHideFlags: 0 - m_CorrespondingSourceObject: {fileID: 0} - m_PrefabInstance: {fileID: 0} - m_PrefabAsset: {fileID: 0} - m_GameObject: {fileID: 1269857788199413071} - m_Material: {fileID: 0} - m_IncludeLayers: - serializedVersion: 2 - m_Bits: 0 - m_ExcludeLayers: - serializedVersion: 2 - m_Bits: 0 - m_LayerOverridePriority: 0 - m_IsTrigger: 0 - m_ProvidesContacts: 0 - m_Enabled: 1 - serializedVersion: 5 - m_Convex: 1 - m_CookingOptions: 30 - m_Mesh: {fileID: -8477760475877621899, guid: 61adaca355888734ab2e5d1cb08f040c, type: 3} --- !u!4 &1933346348436029429 stripped Transform: m_CorrespondingSourceObject: {fileID: -8679921383154817045, guid: 61adaca355888734ab2e5d1cb08f040c, type: 3} diff --git a/Assets/Scenes/GameMain.unity b/Assets/Scenes/GameMain.unity index d24bb00..4a32be7 100644 --- a/Assets/Scenes/GameMain.unity +++ b/Assets/Scenes/GameMain.unity @@ -959,6 +959,14 @@ PrefabInstance: serializedVersion: 3 m_TransformParent: {fileID: 1068750869} m_Modifications: + - target: {fileID: 2557322720114216881, guid: 1be692ccde46d2a4baedc2ee75fbfbdb, type: 3} + propertyPath: GlobalObjectIdHash + value: 4017687754 + objectReference: {fileID: 0} + - target: {fileID: 2557322720114216881, guid: 1be692ccde46d2a4baedc2ee75fbfbdb, type: 3} + propertyPath: InScenePlacedSourceGlobalObjectIdHash + value: 1171432577 + objectReference: {fileID: 0} - target: {fileID: 2998551506809628252, guid: 1be692ccde46d2a4baedc2ee75fbfbdb, type: 3} propertyPath: m_Name value: Gate (1) @@ -1527,32 +1535,6 @@ MonoBehaviour: m_MinRegionArea: 2 m_NavMeshData: {fileID: 23800000, guid: a847cf63b54abee4cbd5b6c92c8ad5e6, type: 2} m_BuildHeightMesh: 0 ---- !u!1 &1049568313 stripped -GameObject: - m_CorrespondingSourceObject: {fileID: 5585059388146411250, guid: f395fcc064a3a834ba957327f1387c19, type: 3} - m_PrefabInstance: {fileID: 4875211098963642791} - m_PrefabAsset: {fileID: 0} ---- !u!65 &1049568317 -BoxCollider: - m_ObjectHideFlags: 0 - m_CorrespondingSourceObject: {fileID: 0} - m_PrefabInstance: {fileID: 0} - m_PrefabAsset: {fileID: 0} - m_GameObject: {fileID: 1049568313} - m_Material: {fileID: 0} - m_IncludeLayers: - serializedVersion: 2 - m_Bits: 0 - m_ExcludeLayers: - serializedVersion: 2 - m_Bits: 0 - m_LayerOverridePriority: 0 - m_IsTrigger: 0 - m_ProvidesContacts: 0 - m_Enabled: 1 - serializedVersion: 3 - m_Size: {x: 4, y: 5, z: 4} - m_Center: {x: 0, y: 0, z: 0} --- !u!1 &1053830687 GameObject: m_ObjectHideFlags: 0 @@ -1872,11 +1854,6 @@ Transform: m_Children: [] m_Father: {fileID: 0} m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} ---- !u!1 &1354840086 stripped -GameObject: - m_CorrespondingSourceObject: {fileID: 2998551506809628252, guid: 1be692ccde46d2a4baedc2ee75fbfbdb, type: 3} - m_PrefabInstance: {fileID: 8960047168891090775} - m_PrefabAsset: {fileID: 0} --- !u!1 &1433142230 GameObject: m_ObjectHideFlags: 0 @@ -2902,6 +2879,10 @@ PrefabInstance: serializedVersion: 3 m_TransformParent: {fileID: 0} m_Modifications: + - target: {fileID: 1287070985890992582, guid: e56926eda34629f4fbf3e4c53f0f8bd4, type: 3} + propertyPath: equipmentData.socketName + value: + objectReference: {fileID: 0} - target: {fileID: 1287070985890992582, guid: e56926eda34629f4fbf3e4c53f0f8bd4, type: 3} propertyPath: interactionAnimationTrigger value: @@ -3015,14 +2996,10 @@ PrefabInstance: propertyPath: m_Name value: Mine objectReference: {fileID: 0} - m_RemovedComponents: - - {fileID: 2263651141701971438, guid: f395fcc064a3a834ba957327f1387c19, type: 3} + m_RemovedComponents: [] m_RemovedGameObjects: [] m_AddedGameObjects: [] - m_AddedComponents: - - targetCorrespondingSourceObject: {fileID: 5585059388146411250, guid: f395fcc064a3a834ba957327f1387c19, type: 3} - insertIndex: -1 - addedObject: {fileID: 1049568317} + m_AddedComponents: [] m_SourcePrefab: {fileID: 100100000, guid: f395fcc064a3a834ba957327f1387c19, type: 3} --- !u!1001 &6204924723497734287 PrefabInstance: @@ -3105,6 +3082,10 @@ PrefabInstance: serializedVersion: 3 m_TransformParent: {fileID: 1068750869} m_Modifications: + - target: {fileID: 2557322720114216881, guid: 1be692ccde46d2a4baedc2ee75fbfbdb, type: 3} + propertyPath: GlobalObjectIdHash + value: 2819059600 + objectReference: {fileID: 0} - target: {fileID: 2998551506809628252, guid: 1be692ccde46d2a4baedc2ee75fbfbdb, type: 3} propertyPath: m_Name value: Gate @@ -3152,70 +3133,13 @@ PrefabInstance: m_RemovedComponents: [] m_RemovedGameObjects: [] m_AddedGameObjects: [] - m_AddedComponents: - - targetCorrespondingSourceObject: {fileID: 2998551506809628252, guid: 1be692ccde46d2a4baedc2ee75fbfbdb, type: 3} - insertIndex: -1 - addedObject: {fileID: 8960047168891090780} - - targetCorrespondingSourceObject: {fileID: 2998551506809628252, guid: 1be692ccde46d2a4baedc2ee75fbfbdb, type: 3} - insertIndex: -1 - addedObject: {fileID: 8960047168891090779} + m_AddedComponents: [] m_SourcePrefab: {fileID: 100100000, guid: 1be692ccde46d2a4baedc2ee75fbfbdb, type: 3} --- !u!4 &8960047168891090776 stripped Transform: m_CorrespondingSourceObject: {fileID: 7180212943015590733, guid: 1be692ccde46d2a4baedc2ee75fbfbdb, type: 3} m_PrefabInstance: {fileID: 8960047168891090775} m_PrefabAsset: {fileID: 0} ---- !u!114 &8960047168891090779 -MonoBehaviour: - m_ObjectHideFlags: 0 - m_CorrespondingSourceObject: {fileID: 0} - m_PrefabInstance: {fileID: 0} - m_PrefabAsset: {fileID: 0} - m_GameObject: {fileID: 1354840086} - m_Enabled: 1 - m_EditorHideFlags: 0 - m_Script: {fileID: 11500000, guid: d5a57f767e5e46a458fc5d3c628d0cbb, type: 3} - m_Name: - m_EditorClassIdentifier: Unity.Netcode.Runtime::Unity.Netcode.NetworkObject - GlobalObjectIdHash: 1203277090 - InScenePlacedSourceGlobalObjectIdHash: 0 - DeferredDespawnTick: 0 - Ownership: 1 - AlwaysReplicateAsRoot: 0 - SynchronizeTransform: 1 - ActiveSceneSynchronization: 0 - SceneMigrationSynchronization: 0 - SpawnWithObservers: 1 - DontDestroyWithOwner: 0 - AutoObjectParentSync: 1 - SyncOwnerTransformWhenParented: 1 - AllowOwnerToParent: 0 ---- !u!114 &8960047168891090780 -MonoBehaviour: - m_ObjectHideFlags: 0 - m_CorrespondingSourceObject: {fileID: 0} - m_PrefabInstance: {fileID: 0} - m_PrefabAsset: {fileID: 0} - m_GameObject: {fileID: 1354840086} - m_Enabled: 1 - m_EditorHideFlags: 0 - m_Script: {fileID: 11500000, guid: 71da9742096858d4d97441d6b84f13dd, type: 3} - m_Name: - m_EditorClassIdentifier: Assembly-CSharp::Northbound.TeamGate - ShowTopMostFoldoutHeaderGroup: 1 - allowedTeam: 1 - allowAllTeams: 0 - frontTrigger: {fileID: 0} - backTrigger: {fileID: 0} - frontExitPoint: {fileID: 0} - backExitPoint: {fileID: 0} - teleportCooldown: 1 - teleportEffectPrefab: {fileID: 0} - teleportSound: {fileID: 0} - blockedSound: {fileID: 0} - teamIndicatorRenderer: {fileID: 0} - allowedTeamMaterial: {fileID: 0} - blockedMaterial: {fileID: 0} --- !u!1660057539 &9223372036854775807 SceneRoots: m_ObjectHideFlags: 0 diff --git a/Assets/Scripts/AttackAction.cs b/Assets/Scripts/AttackAction.cs index 131f833..b779abb 100644 --- a/Assets/Scripts/AttackAction.cs +++ b/Assets/Scripts/AttackAction.cs @@ -4,7 +4,7 @@ using UnityEngine; namespace Northbound { /// - /// 액션 - 공격 (팀 시스템 적용) + /// 액션 - 공격 (팀 시스템 + 장비 시스템 적용) /// public class AttackAction : NetworkBehaviour, IAction { @@ -16,6 +16,12 @@ namespace Northbound [Header("Animation")] public string attackAnimationTrigger = "Attack"; + public bool useAnimationEvents = true; + public bool blockDuringAnimation = true; + + [Header("Equipment")] + public bool useEquipment = true; + public EquipmentData equipmentData; [Header("Visual")] public GameObject attackEffectPrefab; @@ -24,15 +30,22 @@ namespace Northbound private float _lastAttackTime; private Animator _animator; private ITeamMember _teamMember; + private EquipmentSocket _equipmentSocket; + private bool _isAttacking = false; + private bool _isWeaponEquipped = false; private void Awake() { _animator = GetComponent(); _teamMember = GetComponent(); + _equipmentSocket = GetComponent(); } public bool CanExecute(ulong playerId) { + if (blockDuringAnimation && _isAttacking) + return false; + return Time.time - _lastAttackTime >= attackCooldown; } @@ -42,27 +55,43 @@ namespace Northbound return; _lastAttackTime = Time.time; + _isAttacking = true; + + // 장비 장착 (애니메이션 이벤트 사용 안 할 경우) + if (!useAnimationEvents && useEquipment && !_isWeaponEquipped) + { + if (equipmentData != null && equipmentData.attachOnStart) + { + AttachWeapon(); + } + } // 애니메이션 재생 PlayAttackAnimation(); - // 범위 내 적 검색 + // 애니메이션이 없으면 즉시 공격 실행 + if (_animator == null || string.IsNullOrEmpty(attackAnimationTrigger)) + { + PerformAttack(); + _isAttacking = false; + } + } + + private void PerformAttack() + { Vector3 attackOrigin = attackPoint != null ? attackPoint.position : transform.position; Collider[] hits = Physics.OverlapSphere(attackOrigin, attackRange, attackableLayer); foreach (Collider hit in hits) { - // 자기 자신은 제외 if (hit.transform.root == transform.root) continue; - // 대상 확인 var targetDamageable = hit.GetComponent(); var targetTeamMember = hit.GetComponent(); if (targetDamageable != null) { - // 팀 확인 - 적대 관계인 경우에만 공격 if (_teamMember != null && targetTeamMember != null) { if (!TeamManager.CanAttack(_teamMember, targetTeamMember)) @@ -92,13 +121,11 @@ namespace Northbound private void PlayAttackAnimation() { - // 애니메이션 트리거 if (_animator != null && !string.IsNullOrEmpty(attackAnimationTrigger)) { _animator.SetTrigger(attackAnimationTrigger); } - // 이펙트 생성 if (attackEffectPrefab != null && attackPoint != null) { GameObject effect = Instantiate(attackEffectPrefab, attackPoint.position, attackPoint.rotation); @@ -106,6 +133,108 @@ namespace Northbound } } + // ======================================== + // Animation Event 함수들 + // ======================================== + + public void OnEquipWeapon() + { + if (!useAnimationEvents || !useEquipment) return; + AttachWeapon(); + } + + public void OnEquipWeapon(string socketName) + { + if (!useAnimationEvents || !useEquipment) return; + AttachWeapon(socketName); + } + + public void OnUnequipWeapon() + { + if (!useAnimationEvents || !useEquipment) return; + DetachWeapon(); + } + + public void OnUnequipWeapon(string socketName) + { + if (!useAnimationEvents || !useEquipment) return; + DetachWeapon(socketName); + } + + public void OnAttackHit() + { + PerformAttack(); + } + + public void OnAttackComplete() + { + _isAttacking = false; + + if (useEquipment && equipmentData != null && equipmentData.detachOnEnd && !equipmentData.keepEquipped) + { + DetachWeapon(); + } + + Debug.Log("[AttackAction] 공격 완료"); + } + + // ======================================== + // 장비 관리 함수들 + // ======================================== + + private void AttachWeapon(string socketName = null) + { + if (_equipmentSocket == null || equipmentData == null) + { + Debug.LogWarning("[AttackAction] EquipmentSocket 또는 EquipmentData가 없습니다."); + return; + } + + if (equipmentData.equipmentPrefab == null) + { + Debug.LogWarning("[AttackAction] 무기 프리팹이 설정되지 않았습니다."); + return; + } + + string socket = socketName ?? equipmentData.socketName; + _equipmentSocket.AttachToSocket(socket, equipmentData.equipmentPrefab); + _isWeaponEquipped = true; + + Debug.Log($"[AttackAction] 무기 장착: {socket}"); + } + + private void DetachWeapon(string socketName = null) + { + if (_equipmentSocket == null) + return; + + string socket = socketName ?? equipmentData?.socketName; + + if (!string.IsNullOrEmpty(socket)) + { + _equipmentSocket.DetachFromSocket(socket); + _isWeaponEquipped = false; + + Debug.Log($"[AttackAction] 무기 해제: {socket}"); + } + } + + public void EquipWeapon() + { + if (!_isWeaponEquipped && useEquipment) + { + AttachWeapon(); + } + } + + public void UnequipWeapon() + { + if (_isWeaponEquipped) + { + DetachWeapon(); + } + } + public string GetActionName() { return "Attack"; @@ -116,11 +245,29 @@ namespace Northbound return attackAnimationTrigger; } + public EquipmentData GetEquipmentData() + { + return equipmentData; + } + private void OnDrawGizmosSelected() { Vector3 attackOrigin = attackPoint != null ? attackPoint.position : transform.position; Gizmos.color = Color.red; Gizmos.DrawWireSphere(attackOrigin, attackRange); } + + public override void OnDestroy() + { + // 무기 정리 + if (_isWeaponEquipped) + { + DetachWeapon(); + } + + base.OnDestroy(); + } + + public bool IsAttacking => _isAttacking; } } \ No newline at end of file diff --git a/Assets/Scripts/BuildingData.cs b/Assets/Scripts/BuildingData.cs index 22fd45c..06228b3 100644 --- a/Assets/Scripts/BuildingData.cs +++ b/Assets/Scripts/BuildingData.cs @@ -35,7 +35,7 @@ namespace Northbound [Tooltip("건설 시 플레이어가 재생할 애니메이션 트리거 (예: Build, Hammer, Construct)")] public string constructionAnimationTrigger = "Build"; [Tooltip("건설 시 사용할 도구 (선택사항)")] - public InteractionEquipmentData constructionEquipment; + public EquipmentData constructionEquipment; [Header("Health Settings")] [Tooltip("Maximum health of the building")] diff --git a/Assets/Scripts/BuildingFoundation.cs b/Assets/Scripts/BuildingFoundation.cs index afda2e1..6b3ca48 100644 --- a/Assets/Scripts/BuildingFoundation.cs +++ b/Assets/Scripts/BuildingFoundation.cs @@ -178,7 +178,7 @@ namespace Northbound return "[E] 건설하기"; float percentage = (_currentProgress.Value / buildingData.requiredWorkAmount) * 100f; - return $"[E] 건설하기 ({percentage:F0}%)"; + return $"[E] {buildingData.buildingName} 건설 ({percentage:F0}%)"; } public string GetInteractionAnimation() @@ -193,7 +193,7 @@ namespace Northbound return ""; } - public InteractionEquipmentData GetEquipmentData() + public EquipmentData GetEquipmentData() { // BuildingData에 건설 도구가 정의되어 있으면 반환 if (buildingData != null && buildingData.constructionEquipment != null) @@ -234,12 +234,15 @@ namespace Northbound // 플레이어의 NetworkObject 찾기 if (NetworkManager.Singleton != null && NetworkManager.Singleton.SpawnManager != null) { - if (NetworkManager.Singleton.SpawnManager.SpawnedObjects.TryGetValue(playerId, out NetworkObject playerNetObj)) + if (NetworkManager.Singleton.ConnectedClients.TryGetValue(playerId, out var client)) { - var teamMember = playerNetObj.GetComponent(); - if (teamMember != null) + if (client.PlayerObject != null) { - return teamMember.GetTeam(); + var teamMember = client.PlayerObject.GetComponent(); + if (teamMember != null) + { + return teamMember.GetTeam(); + } } } } diff --git a/Assets/Scripts/Core.cs b/Assets/Scripts/Core.cs index c1160ca..fe0cfd1 100644 --- a/Assets/Scripts/Core.cs +++ b/Assets/Scripts/Core.cs @@ -25,7 +25,7 @@ namespace Northbound public string interactionAnimationTrigger = "Deposit"; // 플레이어 애니메이션 트리거 [Header("Equipment")] - public InteractionEquipmentData equipmentData = null; // 도구 필요 없음 + public EquipmentData equipmentData = null; // 도구 필요 없음 [Header("Visual")] public GameObject depositEffectPrefab; @@ -230,11 +230,11 @@ namespace Northbound { if (unlimitedStorage) { - return "자원 보관 (무제한)"; + return "[E] 자원 보관 (무제한)"; } else { - return $"자원 보관 ({_totalResources.Value}/{maxStorageCapacity})"; + return $"[E] 자원 보관 ({_totalResources.Value}/{maxStorageCapacity})"; } } @@ -243,7 +243,7 @@ namespace Northbound return interactionAnimationTrigger; } - public InteractionEquipmentData GetEquipmentData() + public EquipmentData GetEquipmentData() { return equipmentData; } diff --git a/Assets/Scripts/EquipmentData.cs b/Assets/Scripts/EquipmentData.cs new file mode 100644 index 0000000..65b856a --- /dev/null +++ b/Assets/Scripts/EquipmentData.cs @@ -0,0 +1,33 @@ +using UnityEngine; + +namespace Northbound +{ + /// + /// 액션(상호작용, 공격 등) 실행 시 필요한 장비 정보 + /// + [System.Serializable] + public class EquipmentData + { + [Tooltip("장비를 부착할 소켓 이름 (예: RightHand, LeftHand, Back)")] + public string socketName = "RightHand"; + + [Tooltip("부착할 장비 프리팹 (예: 곡괭이, 도끼, 검, 활)")] + public GameObject equipmentPrefab; + + [Tooltip("액션 시작 시 자동으로 부착")] + public bool attachOnStart = true; + + [Tooltip("액션 종료 시 자동으로 제거")] + public bool detachOnEnd = true; + + [Header("Advanced Settings")] + [Tooltip("장비를 지속적으로 장착 상태로 유지 (전투 모드 등)")] + public bool keepEquipped = false; + + [Tooltip("장비 부착 시 딜레이 (초) - 애니메이션 타이밍 조정용")] + public float attachDelay = 0f; + + [Tooltip("장비 제거 시 딜레이 (초) - 애니메이션 타이밍 조정용")] + public float detachDelay = 0f; + } +} \ No newline at end of file diff --git a/Assets/Scripts/EquipmentData.cs.meta b/Assets/Scripts/EquipmentData.cs.meta new file mode 100644 index 0000000..a66a586 --- /dev/null +++ b/Assets/Scripts/EquipmentData.cs.meta @@ -0,0 +1,2 @@ +fileFormatVersion: 2 +guid: 165ad8862ed57f245b545005aa1c2c38 \ No newline at end of file diff --git a/Assets/Scripts/IAction.cs b/Assets/Scripts/IAction.cs index d4e0e9b..fb99994 100644 --- a/Assets/Scripts/IAction.cs +++ b/Assets/Scripts/IAction.cs @@ -1,7 +1,7 @@ namespace Northbound { /// - /// 상호작용 대상 없이도 실행 가능한 행동 (공격, 점프 등) + /// 상호작용 대상 없이도 실행 가능한 행동 (공격, 점프, 스킬 등) /// public interface IAction { @@ -16,7 +16,7 @@ namespace Northbound void Execute(ulong playerId); /// - /// 액션 이름 + /// 액션 이름 (예: "Attack", "Jump", "Skill_Fireball") /// string GetActionName(); @@ -24,5 +24,10 @@ namespace Northbound /// 플레이어가 재생할 애니메이션 트리거 이름 (없으면 null 또는 빈 문자열) /// string GetActionAnimation(); + + /// + /// 액션 실행 시 사용할 장비 정보 (없으면 null) + /// + EquipmentData GetEquipmentData(); } } \ No newline at end of file diff --git a/Assets/Scripts/IInteractable.cs b/Assets/Scripts/IInteractable.cs index d4f2600..c0f7f2b 100644 --- a/Assets/Scripts/IInteractable.cs +++ b/Assets/Scripts/IInteractable.cs @@ -30,7 +30,7 @@ namespace Northbound /// /// 상호작용 시 사용할 장비 정보 (없으면 null) /// - InteractionEquipmentData GetEquipmentData(); + EquipmentData GetEquipmentData(); /// /// 상호작용 오브젝트의 Transform diff --git a/Assets/Scripts/InteractionEquipmentData.cs b/Assets/Scripts/InteractionEquipmentData.cs deleted file mode 100644 index 12f1a78..0000000 --- a/Assets/Scripts/InteractionEquipmentData.cs +++ /dev/null @@ -1,23 +0,0 @@ -using UnityEngine; - -namespace Northbound -{ - /// - /// 상호작용 시 필요한 장비 정보 - /// - [System.Serializable] - public class InteractionEquipmentData - { - [Tooltip("장비를 부착할 소켓 이름 (예: RightHand, LeftHand)")] - public string socketName = "RightHand"; - - [Tooltip("부착할 장비 프리팹 (예: 곡괭이, 도끼)")] - public GameObject equipmentPrefab; - - [Tooltip("상호작용 시작 시 자동으로 부착")] - public bool attachOnStart = true; - - [Tooltip("상호작용 종료 시 자동으로 제거")] - public bool detachOnEnd = true; - } -} \ No newline at end of file diff --git a/Assets/Scripts/InteractionEquipmentData.cs.meta b/Assets/Scripts/InteractionEquipmentData.cs.meta deleted file mode 100644 index 816cb8e..0000000 --- a/Assets/Scripts/InteractionEquipmentData.cs.meta +++ /dev/null @@ -1,2 +0,0 @@ -fileFormatVersion: 2 -guid: 883a3042cf05b3b4e9629710b6f4e83f \ No newline at end of file diff --git a/Assets/Scripts/NetworkPlayerController.cs b/Assets/Scripts/NetworkPlayerController.cs index 347b60d..49dcc90 100644 --- a/Assets/Scripts/NetworkPlayerController.cs +++ b/Assets/Scripts/NetworkPlayerController.cs @@ -101,6 +101,23 @@ public class NetworkPlayerController : NetworkBehaviour, ITeamMember, IDamageabl // 죽었으면 이동 불가 if (_currentHealth.Value <= 0) return; + // 액션/상호작용 중이면 이동 불가 + var attackAction = GetComponent(); + var playerInteraction = GetComponent(); + + bool isActionBlocked = (attackAction != null && attackAction.IsAttacking) || + (playerInteraction != null && playerInteraction.IsInteracting); + + if (isActionBlocked) + { + // 이동 불가 시 애니메이션 속도를 0으로 + if (_animator != null) + { + _animator.SetFloat("MoveSpeed", 0f); + } + return; + } + _moveInput = _inputActions.Player.Move.ReadValue(); Vector3 move = new Vector3(_moveInput.x, 0, _moveInput.y).normalized; diff --git a/Assets/Scripts/PlayerInteraction.cs b/Assets/Scripts/PlayerInteraction.cs index 5d2bddc..59c6f7a 100644 --- a/Assets/Scripts/PlayerInteraction.cs +++ b/Assets/Scripts/PlayerInteraction.cs @@ -34,10 +34,13 @@ namespace Northbound private Animator _animator; private EquipmentSocket _equipmentSocket; - private InteractionEquipmentData _pendingEquipmentData; + private EquipmentData _pendingEquipmentData; private string _currentEquipmentSocket; private bool _isInteracting = false; + // 다른 컴포넌트가 이동 차단 여부를 확인할 수 있도록 public 프로퍼티 제공 + public bool IsInteracting => _isInteracting; + public override void OnNetworkSpawn() { if (!IsOwner) return; @@ -243,14 +246,12 @@ namespace Northbound GUI.Label(new Rect(Screen.width / 2 - 200, Screen.height - 100, 400, 50), prompt, style); } - override public void OnDestroy() + public override void OnDestroy() { if (_inputActions != null) { _inputActions.Dispose(); } - - base.OnDestroy(); } } } \ No newline at end of file diff --git a/Assets/Scripts/Resource.cs b/Assets/Scripts/Resource.cs index 8eeb929..f79b949 100644 --- a/Assets/Scripts/Resource.cs +++ b/Assets/Scripts/Resource.cs @@ -22,9 +22,10 @@ namespace Northbound public string interactionAnimationTrigger = "Mining"; // 플레이어 애니메이션 트리거 [Header("Equipment")] - public InteractionEquipmentData equipmentData = new InteractionEquipmentData + public EquipmentData equipmentData = new EquipmentData { socketName = "RightHand", + equipmentPrefab = null, // Inspector에서 곡괭이 프리팹 할당 attachOnStart = true, detachOnEnd = true }; @@ -46,7 +47,7 @@ namespace Northbound { if (IsServer) { - _currentResources.Value = 0; + _currentResources.Value = maxResources; _lastRechargeTime = Time.time; } } @@ -63,10 +64,10 @@ namespace Northbound { int rechargeAmountToAdd = Mathf.Min(rechargeAmount, maxResources - _currentResources.Value); _currentResources.Value += rechargeAmountToAdd; - + // Debug.Log($"{resourceName} {rechargeAmountToAdd} 충전됨. 현재: {_currentResources.Value}/{maxResources}"); } - + _lastRechargeTime = Time.time; } } @@ -82,7 +83,7 @@ namespace Northbound return false; // 플레이어 인벤토리 확인 - if (NetworkManager.Singleton != null && + if (NetworkManager.Singleton != null && NetworkManager.Singleton.ConnectedClients.TryGetValue(playerId, out var client)) { if (client.PlayerObject != null) @@ -128,7 +129,7 @@ namespace Northbound // 플레이어가 받을 수 있는 최대량 계산 int playerAvailableSpace = playerInventory.GetAvailableSpace(); - + // 자원 노드가 줄 수 있는 양과 플레이어가 받을 수 있는 양 중 작은 값 선택 int gatheredAmount = Mathf.Min( resourcesPerGathering, @@ -168,7 +169,7 @@ namespace Northbound { if (_currentResources.Value <= 0) return "자원 충전 중..."; - + return $"[E] {resourceName} 채집 ({_currentResources.Value}/{maxResources})"; } @@ -177,7 +178,7 @@ namespace Northbound return interactionAnimationTrigger; } - public InteractionEquipmentData GetEquipmentData() + public EquipmentData GetEquipmentData() { return equipmentData; } diff --git a/Assets/Scripts/ResourcePickup.cs b/Assets/Scripts/ResourcePickup.cs index d720ccb..2a21fee 100644 --- a/Assets/Scripts/ResourcePickup.cs +++ b/Assets/Scripts/ResourcePickup.cs @@ -16,9 +16,10 @@ namespace Northbound public string interactionAnimationTrigger = "PickUp"; // 플레이어 애니메이션 트리거 [Header("Equipment")] - public InteractionEquipmentData equipmentData = new InteractionEquipmentData + public EquipmentData equipmentData = new EquipmentData { socketName = "", + equipmentPrefab = null, attachOnStart = false, detachOnEnd = false }; @@ -143,7 +144,7 @@ namespace Northbound return interactionAnimationTrigger; } - public InteractionEquipmentData GetEquipmentData() + public EquipmentData GetEquipmentData() { return equipmentData; } diff --git a/Assets/Scripts/TeamGate.cs b/Assets/Scripts/TeamGate.cs index 551870e..60f3046 100644 --- a/Assets/Scripts/TeamGate.cs +++ b/Assets/Scripts/TeamGate.cs @@ -242,7 +242,7 @@ namespace Northbound /// /// 텔레포트 이펙트 재생 (모든 클라이언트) /// - [ClientRpc] + [Rpc(SendTo.ClientsAndHost)] private void PlayTeleportEffectClientRpc(Vector3 position) { if (teleportEffectPrefab == null) return; @@ -254,7 +254,7 @@ namespace Northbound /// /// 사운드 재생 (모든 클라이언트) /// - [ClientRpc] + [Rpc(SendTo.ClientsAndHost)] private void PlaySoundClientRpc(bool isTeleport) { if (_audioSource == null) return; @@ -269,7 +269,7 @@ namespace Northbound /// /// 허용 팀 변경 (서버에서 호출) /// - [ServerRpc(RequireOwnership = false)] + [Rpc(SendTo.Server, InvokePermission = RpcInvokePermission.Everyone)] public void ChangeAllowedTeamServerRpc(TeamType newTeam) { allowedTeam = newTeam; @@ -281,7 +281,7 @@ namespace Northbound /// /// 비주얼 업데이트 (모든 클라이언트) /// - [ClientRpc] + [Rpc(SendTo.ClientsAndHost)] private void UpdateVisualClientRpc(TeamType team) { allowedTeam = team; @@ -376,11 +376,13 @@ namespace Northbound } } - private void OnDestroy() + public override void OnDestroy() { // 정리 _lastTeleportTime.Clear(); _triggerStates.Clear(); + + base.OnDestroy(); } } } \ No newline at end of file