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