fix: 플레이어-보스 충돌 슬라이딩 및 관통 방지

- CharacterController.enableOverlapRecovery 비활성화로 자동 밀어냄 제거
- 레이어 마스크 의존 제거, 컴포넌트(NavMeshAgent/CharacterController)로 식별
- EnemyBase LateUpdate에서 velocity 기반 보스 위치 보정
- EnemyBase OnAnimatorMove에서 루트모션의 플레이어 방향 이동 차단
- BossEnemy Update를 OnServerUpdate 패턴으로 리팩터링
- 보스 프리팹 하위 오브젝트 레이어 Enemy로 통일

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-03-17 00:14:50 +09:00
parent c265f980db
commit a347d9360d
5 changed files with 220 additions and 236 deletions

View File

@@ -9,7 +9,7 @@ GameObject:
serializedVersion: 6 serializedVersion: 6
m_Component: m_Component:
- component: {fileID: 6643653656805266137} - component: {fileID: 6643653656805266137}
m_Layer: 0 m_Layer: 6
m_Name: IndexFinger_03 1 m_Name: IndexFinger_03 1
m_TagString: Untagged m_TagString: Untagged
m_Icon: {fileID: 0} m_Icon: {fileID: 0}
@@ -41,7 +41,7 @@ GameObject:
serializedVersion: 6 serializedVersion: 6
m_Component: m_Component:
- component: {fileID: 872883229803693180} - component: {fileID: 872883229803693180}
m_Layer: 0 m_Layer: 6
m_Name: Root m_Name: Root
m_TagString: Untagged m_TagString: Untagged
m_Icon: {fileID: 0} m_Icon: {fileID: 0}
@@ -73,7 +73,7 @@ GameObject:
serializedVersion: 6 serializedVersion: 6
m_Component: m_Component:
- component: {fileID: 1121004277287172730} - component: {fileID: 1121004277287172730}
m_Layer: 0 m_Layer: 6
m_Name: Ankle_L m_Name: Ankle_L
m_TagString: Untagged m_TagString: Untagged
m_Icon: {fileID: 0} m_Icon: {fileID: 0}
@@ -106,7 +106,7 @@ GameObject:
m_Component: m_Component:
- component: {fileID: 8242298026093095983} - component: {fileID: 8242298026093095983}
- component: {fileID: 7340647355570253925} - component: {fileID: 7340647355570253925}
m_Layer: 0 m_Layer: 6
m_Name: Character_BarbarianGiant_01 m_Name: Character_BarbarianGiant_01
m_TagString: Untagged m_TagString: Untagged
m_Icon: {fileID: 0} m_Icon: {fileID: 0}
@@ -246,7 +246,7 @@ GameObject:
serializedVersion: 6 serializedVersion: 6
m_Component: m_Component:
- component: {fileID: 144812691924856346} - component: {fileID: 144812691924856346}
m_Layer: 0 m_Layer: 6
m_Name: Toes_L m_Name: Toes_L
m_TagString: Untagged m_TagString: Untagged
m_Icon: {fileID: 0} m_Icon: {fileID: 0}
@@ -277,7 +277,7 @@ GameObject:
serializedVersion: 6 serializedVersion: 6
m_Component: m_Component:
- component: {fileID: 6189945741236030909} - component: {fileID: 6189945741236030909}
m_Layer: 0 m_Layer: 6
m_Name: UpperLeg_L m_Name: UpperLeg_L
m_TagString: Untagged m_TagString: Untagged
m_Icon: {fileID: 0} m_Icon: {fileID: 0}
@@ -310,7 +310,7 @@ GameObject:
m_Component: m_Component:
- component: {fileID: 5234165385643310874} - component: {fileID: 5234165385643310874}
- component: {fileID: 4196026135510989304} - component: {fileID: 4196026135510989304}
m_Layer: 0 m_Layer: 6
m_Name: Character_Dwarf_01 m_Name: Character_Dwarf_01
m_TagString: Untagged m_TagString: Untagged
m_Icon: {fileID: 0} m_Icon: {fileID: 0}
@@ -450,7 +450,7 @@ GameObject:
serializedVersion: 6 serializedVersion: 6
m_Component: m_Component:
- component: {fileID: 4953413866640159768} - component: {fileID: 4953413866640159768}
m_Layer: 0 m_Layer: 6
m_Name: Eyebrows m_Name: Eyebrows
m_TagString: Untagged m_TagString: Untagged
m_Icon: {fileID: 0} m_Icon: {fileID: 0}
@@ -481,7 +481,7 @@ GameObject:
serializedVersion: 6 serializedVersion: 6
m_Component: m_Component:
- component: {fileID: 6305222539219953510} - component: {fileID: 6305222539219953510}
m_Layer: 0 m_Layer: 6
m_Name: IndexFinger_01 1 m_Name: IndexFinger_01 1
m_TagString: Untagged m_TagString: Untagged
m_Icon: {fileID: 0} m_Icon: {fileID: 0}
@@ -513,7 +513,7 @@ GameObject:
serializedVersion: 6 serializedVersion: 6
m_Component: m_Component:
- component: {fileID: 6261344531192022962} - component: {fileID: 6261344531192022962}
m_Layer: 0 m_Layer: 6
m_Name: Head m_Name: Head
m_TagString: Untagged m_TagString: Untagged
m_Icon: {fileID: 0} m_Icon: {fileID: 0}
@@ -547,7 +547,7 @@ GameObject:
serializedVersion: 6 serializedVersion: 6
m_Component: m_Component:
- component: {fileID: 1156866845888020345} - component: {fileID: 1156866845888020345}
m_Layer: 0 m_Layer: 6
m_Name: Helmet m_Name: Helmet
m_TagString: Untagged m_TagString: Untagged
m_Icon: {fileID: 0} m_Icon: {fileID: 0}
@@ -579,7 +579,7 @@ GameObject:
serializedVersion: 6 serializedVersion: 6
m_Component: m_Component:
- component: {fileID: 6876742027977645837} - component: {fileID: 6876742027977645837}
m_Layer: 0 m_Layer: 6
m_Name: Ball_L m_Name: Ball_L
m_TagString: Untagged m_TagString: Untagged
m_Icon: {fileID: 0} m_Icon: {fileID: 0}
@@ -612,7 +612,7 @@ GameObject:
m_Component: m_Component:
- component: {fileID: 9024640953938921730} - component: {fileID: 9024640953938921730}
- component: {fileID: 4040563405028547472} - component: {fileID: 4040563405028547472}
m_Layer: 0 m_Layer: 6
m_Name: Character_Slayer_01 m_Name: Character_Slayer_01
m_TagString: Untagged m_TagString: Untagged
m_Icon: {fileID: 0} m_Icon: {fileID: 0}
@@ -752,7 +752,7 @@ GameObject:
serializedVersion: 6 serializedVersion: 6
m_Component: m_Component:
- component: {fileID: 7074235824420974134} - component: {fileID: 7074235824420974134}
m_Layer: 0 m_Layer: 6
m_Name: Finger_04 1 m_Name: Finger_04 1
m_TagString: Untagged m_TagString: Untagged
m_Icon: {fileID: 0} m_Icon: {fileID: 0}
@@ -783,7 +783,7 @@ GameObject:
serializedVersion: 6 serializedVersion: 6
m_Component: m_Component:
- component: {fileID: 6961594731814923999} - component: {fileID: 6961594731814923999}
m_Layer: 0 m_Layer: 6
m_Name: IndexFinger_03 m_Name: IndexFinger_03
m_TagString: Untagged m_TagString: Untagged
m_Icon: {fileID: 0} m_Icon: {fileID: 0}
@@ -815,7 +815,7 @@ GameObject:
serializedVersion: 6 serializedVersion: 6
m_Component: m_Component:
- component: {fileID: 2982108666446807220} - component: {fileID: 2982108666446807220}
m_Layer: 0 m_Layer: 6
m_Name: Finger_04 m_Name: Finger_04
m_TagString: Untagged m_TagString: Untagged
m_Icon: {fileID: 0} m_Icon: {fileID: 0}
@@ -846,7 +846,7 @@ GameObject:
serializedVersion: 6 serializedVersion: 6
m_Component: m_Component:
- component: {fileID: 1902043979864175257} - component: {fileID: 1902043979864175257}
m_Layer: 0 m_Layer: 6
m_Name: Toes_R m_Name: Toes_R
m_TagString: Untagged m_TagString: Untagged
m_Icon: {fileID: 0} m_Icon: {fileID: 0}
@@ -877,7 +877,7 @@ GameObject:
serializedVersion: 6 serializedVersion: 6
m_Component: m_Component:
- component: {fileID: 2670510357683655273} - component: {fileID: 2670510357683655273}
m_Layer: 0 m_Layer: 6
m_Name: Spine_01 m_Name: Spine_01
m_TagString: Untagged m_TagString: Untagged
m_Icon: {fileID: 0} m_Icon: {fileID: 0}
@@ -909,7 +909,7 @@ GameObject:
serializedVersion: 6 serializedVersion: 6
m_Component: m_Component:
- component: {fileID: 6480956261595266767} - component: {fileID: 6480956261595266767}
m_Layer: 0 m_Layer: 6
m_Name: Clavicle_L m_Name: Clavicle_L
m_TagString: Untagged m_TagString: Untagged
m_Icon: {fileID: 0} m_Icon: {fileID: 0}
@@ -941,7 +941,7 @@ GameObject:
serializedVersion: 6 serializedVersion: 6
m_Component: m_Component:
- component: {fileID: 7643421511305802608} - component: {fileID: 7643421511305802608}
m_Layer: 0 m_Layer: 6
m_Name: Neck m_Name: Neck
m_TagString: Untagged m_TagString: Untagged
m_Icon: {fileID: 0} m_Icon: {fileID: 0}
@@ -973,7 +973,7 @@ GameObject:
serializedVersion: 6 serializedVersion: 6
m_Component: m_Component:
- component: {fileID: 132307383957219980} - component: {fileID: 132307383957219980}
m_Layer: 0 m_Layer: 6
m_Name: Ankle_R m_Name: Ankle_R
m_TagString: Untagged m_TagString: Untagged
m_Icon: {fileID: 0} m_Icon: {fileID: 0}
@@ -1005,7 +1005,7 @@ GameObject:
serializedVersion: 6 serializedVersion: 6
m_Component: m_Component:
- component: {fileID: 6718570607748687175} - component: {fileID: 6718570607748687175}
m_Layer: 0 m_Layer: 6
m_Name: Elbow_L m_Name: Elbow_L
m_TagString: Untagged m_TagString: Untagged
m_Icon: {fileID: 0} m_Icon: {fileID: 0}
@@ -1038,7 +1038,7 @@ GameObject:
m_Component: m_Component:
- component: {fileID: 2941051679179816350} - component: {fileID: 2941051679179816350}
- component: {fileID: 1049515515378192443} - component: {fileID: 1049515515378192443}
m_Layer: 0 m_Layer: 6
m_Name: Character_Pig_Butcher_01 m_Name: Character_Pig_Butcher_01
m_TagString: Untagged m_TagString: Untagged
m_Icon: {fileID: 0} m_Icon: {fileID: 0}
@@ -1178,7 +1178,7 @@ GameObject:
serializedVersion: 6 serializedVersion: 6
m_Component: m_Component:
- component: {fileID: 2274080434550477928} - component: {fileID: 2274080434550477928}
m_Layer: 0 m_Layer: 6
m_Name: Thumb_02 m_Name: Thumb_02
m_TagString: Untagged m_TagString: Untagged
m_Icon: {fileID: 0} m_Icon: {fileID: 0}
@@ -1210,7 +1210,7 @@ GameObject:
serializedVersion: 6 serializedVersion: 6
m_Component: m_Component:
- component: {fileID: 3224253035183249078} - component: {fileID: 3224253035183249078}
m_Layer: 0 m_Layer: 6
m_Name: Finger_03 m_Name: Finger_03
m_TagString: Untagged m_TagString: Untagged
m_Icon: {fileID: 0} m_Icon: {fileID: 0}
@@ -1242,7 +1242,7 @@ GameObject:
serializedVersion: 6 serializedVersion: 6
m_Component: m_Component:
- component: {fileID: 742209535846353542} - component: {fileID: 742209535846353542}
m_Layer: 0 m_Layer: 6
m_Name: Eyes m_Name: Eyes
m_TagString: Untagged m_TagString: Untagged
m_Icon: {fileID: 0} m_Icon: {fileID: 0}
@@ -1273,7 +1273,7 @@ GameObject:
serializedVersion: 6 serializedVersion: 6
m_Component: m_Component:
- component: {fileID: 6022304106011789127} - component: {fileID: 6022304106011789127}
m_Layer: 0 m_Layer: 6
m_Name: LowerLeg_L m_Name: LowerLeg_L
m_TagString: Untagged m_TagString: Untagged
m_Icon: {fileID: 0} m_Icon: {fileID: 0}
@@ -1305,7 +1305,7 @@ GameObject:
serializedVersion: 6 serializedVersion: 6
m_Component: m_Component:
- component: {fileID: 985120257555972102} - component: {fileID: 985120257555972102}
m_Layer: 0 m_Layer: 6
m_Name: IndexFinger_01 m_Name: IndexFinger_01
m_TagString: Untagged m_TagString: Untagged
m_Icon: {fileID: 0} m_Icon: {fileID: 0}
@@ -1337,7 +1337,7 @@ GameObject:
serializedVersion: 6 serializedVersion: 6
m_Component: m_Component:
- component: {fileID: 2158936302564180189} - component: {fileID: 2158936302564180189}
m_Layer: 0 m_Layer: 6
m_Name: IndexFinger_04 1 m_Name: IndexFinger_04 1
m_TagString: Untagged m_TagString: Untagged
m_Icon: {fileID: 0} m_Icon: {fileID: 0}
@@ -1369,7 +1369,7 @@ GameObject:
m_Component: m_Component:
- component: {fileID: 3702107543611294815} - component: {fileID: 3702107543611294815}
- component: {fileID: 752898117895215478} - component: {fileID: 752898117895215478}
m_Layer: 0 m_Layer: 6
m_Name: Character_ElementalGolem_01 m_Name: Character_ElementalGolem_01
m_TagString: Untagged m_TagString: Untagged
m_Icon: {fileID: 0} m_Icon: {fileID: 0}
@@ -1509,7 +1509,7 @@ GameObject:
serializedVersion: 6 serializedVersion: 6
m_Component: m_Component:
- component: {fileID: 9126929411809314898} - component: {fileID: 9126929411809314898}
m_Layer: 0 m_Layer: 6
m_Name: IndexFinger_02 m_Name: IndexFinger_02
m_TagString: Untagged m_TagString: Untagged
m_Icon: {fileID: 0} m_Icon: {fileID: 0}
@@ -1542,7 +1542,7 @@ GameObject:
m_Component: m_Component:
- component: {fileID: 2792811216651560850} - component: {fileID: 2792811216651560850}
- component: {fileID: 556967472877552751} - component: {fileID: 556967472877552751}
m_Layer: 0 m_Layer: 6
m_Name: Character_Troll_01 m_Name: Character_Troll_01
m_TagString: Untagged m_TagString: Untagged
m_Icon: {fileID: 0} m_Icon: {fileID: 0}
@@ -1683,7 +1683,7 @@ GameObject:
m_Component: m_Component:
- component: {fileID: 3983418530701611865} - component: {fileID: 3983418530701611865}
- component: {fileID: 8013341123487295681} - component: {fileID: 8013341123487295681}
m_Layer: 0 m_Layer: 6
m_Name: Character_Big_Ork_01 m_Name: Character_Big_Ork_01
m_TagString: Untagged m_TagString: Untagged
m_Icon: {fileID: 0} m_Icon: {fileID: 0}
@@ -1823,7 +1823,7 @@ GameObject:
serializedVersion: 6 serializedVersion: 6
m_Component: m_Component:
- component: {fileID: 8039592807724159502} - component: {fileID: 8039592807724159502}
m_Layer: 0 m_Layer: 6
m_Name: Thumb_03 1 m_Name: Thumb_03 1
m_TagString: Untagged m_TagString: Untagged
m_Icon: {fileID: 0} m_Icon: {fileID: 0}
@@ -1854,7 +1854,7 @@ GameObject:
serializedVersion: 6 serializedVersion: 6
m_Component: m_Component:
- component: {fileID: 1126755880182120861} - component: {fileID: 1126755880182120861}
m_Layer: 0 m_Layer: 6
m_Name: Elbow_R m_Name: Elbow_R
m_TagString: Untagged m_TagString: Untagged
m_Icon: {fileID: 0} m_Icon: {fileID: 0}
@@ -1886,7 +1886,7 @@ GameObject:
serializedVersion: 6 serializedVersion: 6
m_Component: m_Component:
- component: {fileID: 7553931185384545452} - component: {fileID: 7553931185384545452}
m_Layer: 0 m_Layer: 6
m_Name: Spine_03 m_Name: Spine_03
m_TagString: Untagged m_TagString: Untagged
m_Icon: {fileID: 0} m_Icon: {fileID: 0}
@@ -1930,7 +1930,7 @@ GameObject:
- component: {fileID: 8818883032728065057} - component: {fileID: 8818883032728065057}
- component: {fileID: -2857689419101920665} - component: {fileID: -2857689419101920665}
- component: {fileID: 7544406269366897481} - component: {fileID: 7544406269366897481}
m_Layer: 0 m_Layer: 6
m_Name: Prefab_Boss_TestBoss m_Name: Prefab_Boss_TestBoss
m_TagString: Untagged m_TagString: Untagged
m_Icon: {fileID: 0} m_Icon: {fileID: 0}
@@ -1998,7 +1998,7 @@ MonoBehaviour:
m_Script: {fileID: 11500000, guid: d5a57f767e5e46a458fc5d3c628d0cbb, type: 3} m_Script: {fileID: 11500000, guid: d5a57f767e5e46a458fc5d3c628d0cbb, type: 3}
m_Name: m_Name:
m_EditorClassIdentifier: Unity.Netcode.Runtime::Unity.Netcode.NetworkObject m_EditorClassIdentifier: Unity.Netcode.Runtime::Unity.Netcode.NetworkObject
GlobalObjectIdHash: 3958201435 GlobalObjectIdHash: 860882280
InScenePlacedSourceGlobalObjectIdHash: 223369646 InScenePlacedSourceGlobalObjectIdHash: 223369646
DeferredDespawnTick: 0 DeferredDespawnTick: 0
Ownership: 1 Ownership: 1
@@ -2134,7 +2134,7 @@ MonoBehaviour:
m_EditorClassIdentifier: Colosseum.Game::Colosseum.Skills.SkillController m_EditorClassIdentifier: Colosseum.Game::Colosseum.Skills.SkillController
animator: {fileID: 4019041888965840580} animator: {fileID: 4019041888965840580}
baseController: {fileID: 9100000, guid: 4bd980f1a222c5b468136f7e717925d5, type: 2} baseController: {fileID: 9100000, guid: 4bd980f1a222c5b468136f7e717925d5, type: 2}
baseSkillClip: {fileID: -7717634560727564301, guid: 0f6fd9302e489b94d96774e2713b1317, type: 3} baseSkillClip: {fileID: -7717634560727564301, guid: 4005a77aa7d531742b1de1bec27001b1, type: 3}
debugMode: 1 debugMode: 1
showAreaDebug: 1 showAreaDebug: 1
debugDrawDuration: 1 debugDrawDuration: 1
@@ -2175,7 +2175,7 @@ GameObject:
serializedVersion: 6 serializedVersion: 6
m_Component: m_Component:
- component: {fileID: 6095322058662237816} - component: {fileID: 6095322058662237816}
m_Layer: 0 m_Layer: 6
m_Name: Hand_R m_Name: Hand_R
m_TagString: Untagged m_TagString: Untagged
m_Icon: {fileID: 0} m_Icon: {fileID: 0}
@@ -2210,7 +2210,7 @@ GameObject:
m_Component: m_Component:
- component: {fileID: 8771294253604273166} - component: {fileID: 8771294253604273166}
- component: {fileID: 4361276197914901644} - component: {fileID: 4361276197914901644}
m_Layer: 0 m_Layer: 6
m_Name: Character_FortGolem_01 m_Name: Character_FortGolem_01
m_TagString: Untagged m_TagString: Untagged
m_Icon: {fileID: 0} m_Icon: {fileID: 0}
@@ -2350,7 +2350,7 @@ GameObject:
serializedVersion: 6 serializedVersion: 6
m_Component: m_Component:
- component: {fileID: 8296497460523017494} - component: {fileID: 8296497460523017494}
m_Layer: 0 m_Layer: 6
m_Name: IndexFinger_04 m_Name: IndexFinger_04
m_TagString: Untagged m_TagString: Untagged
m_Icon: {fileID: 0} m_Icon: {fileID: 0}
@@ -2382,7 +2382,7 @@ GameObject:
m_Component: m_Component:
- component: {fileID: 3590960725534842321} - component: {fileID: 3590960725534842321}
- component: {fileID: 4522993535860619622} - component: {fileID: 4522993535860619622}
m_Layer: 0 m_Layer: 6
m_Name: Character_MutantGuy_01 m_Name: Character_MutantGuy_01
m_TagString: Untagged m_TagString: Untagged
m_Icon: {fileID: 0} m_Icon: {fileID: 0}
@@ -2520,7 +2520,7 @@ GameObject:
serializedVersion: 6 serializedVersion: 6
m_Component: m_Component:
- component: {fileID: 2624557334772920005} - component: {fileID: 2624557334772920005}
m_Layer: 0 m_Layer: 6
m_Name: Thumb_01 1 m_Name: Thumb_01 1
m_TagString: Untagged m_TagString: Untagged
m_Icon: {fileID: 0} m_Icon: {fileID: 0}
@@ -2553,7 +2553,7 @@ GameObject:
m_Component: m_Component:
- component: {fileID: 294350506880030600} - component: {fileID: 294350506880030600}
- component: {fileID: 9049566930458412434} - component: {fileID: 9049566930458412434}
m_Layer: 0 m_Layer: 6
m_Name: Character_RedDemon_01 m_Name: Character_RedDemon_01
m_TagString: Untagged m_TagString: Untagged
m_Icon: {fileID: 0} m_Icon: {fileID: 0}
@@ -2693,7 +2693,7 @@ GameObject:
serializedVersion: 6 serializedVersion: 6
m_Component: m_Component:
- component: {fileID: 4543585677225495356} - component: {fileID: 4543585677225495356}
m_Layer: 0 m_Layer: 6
m_Name: Finger_01 m_Name: Finger_01
m_TagString: Untagged m_TagString: Untagged
m_Icon: {fileID: 0} m_Icon: {fileID: 0}
@@ -2725,7 +2725,7 @@ GameObject:
serializedVersion: 6 serializedVersion: 6
m_Component: m_Component:
- component: {fileID: 769461243750532741} - component: {fileID: 769461243750532741}
m_Layer: 0 m_Layer: 6
m_Name: Thumb_03 m_Name: Thumb_03
m_TagString: Untagged m_TagString: Untagged
m_Icon: {fileID: 0} m_Icon: {fileID: 0}
@@ -2756,7 +2756,7 @@ GameObject:
serializedVersion: 6 serializedVersion: 6
m_Component: m_Component:
- component: {fileID: 522825655025885808} - component: {fileID: 522825655025885808}
m_Layer: 0 m_Layer: 6
m_Name: IndexFinger_02 1 m_Name: IndexFinger_02 1
m_TagString: Untagged m_TagString: Untagged
m_Icon: {fileID: 0} m_Icon: {fileID: 0}
@@ -2788,7 +2788,7 @@ GameObject:
serializedVersion: 6 serializedVersion: 6
m_Component: m_Component:
- component: {fileID: 8417583513949358043} - component: {fileID: 8417583513949358043}
m_Layer: 0 m_Layer: 6
m_Name: Hand_L m_Name: Hand_L
m_TagString: Untagged m_TagString: Untagged
m_Icon: {fileID: 0} m_Icon: {fileID: 0}
@@ -2822,7 +2822,7 @@ GameObject:
serializedVersion: 6 serializedVersion: 6
m_Component: m_Component:
- component: {fileID: 2510886572597627575} - component: {fileID: 2510886572597627575}
m_Layer: 0 m_Layer: 6
m_Name: UpperLeg_R m_Name: UpperLeg_R
m_TagString: Untagged m_TagString: Untagged
m_Icon: {fileID: 0} m_Icon: {fileID: 0}
@@ -2854,7 +2854,7 @@ GameObject:
serializedVersion: 6 serializedVersion: 6
m_Component: m_Component:
- component: {fileID: 8586350568747534692} - component: {fileID: 8586350568747534692}
m_Layer: 0 m_Layer: 6
m_Name: Hips m_Name: Hips
m_TagString: Untagged m_TagString: Untagged
m_Icon: {fileID: 0} m_Icon: {fileID: 0}
@@ -2888,7 +2888,7 @@ GameObject:
serializedVersion: 6 serializedVersion: 6
m_Component: m_Component:
- component: {fileID: 7751381845014379211} - component: {fileID: 7751381845014379211}
m_Layer: 0 m_Layer: 6
m_Name: Shoulder_R m_Name: Shoulder_R
m_TagString: Untagged m_TagString: Untagged
m_Icon: {fileID: 0} m_Icon: {fileID: 0}
@@ -2920,7 +2920,7 @@ GameObject:
serializedVersion: 6 serializedVersion: 6
m_Component: m_Component:
- component: {fileID: 5393520628060724146} - component: {fileID: 5393520628060724146}
m_Layer: 0 m_Layer: 6
m_Name: Ball_R m_Name: Ball_R
m_TagString: Untagged m_TagString: Untagged
m_Icon: {fileID: 0} m_Icon: {fileID: 0}
@@ -2953,7 +2953,7 @@ GameObject:
m_Component: m_Component:
- component: {fileID: 6414035440940731888} - component: {fileID: 6414035440940731888}
- component: {fileID: 9069861054110322079} - component: {fileID: 9069861054110322079}
m_Layer: 0 m_Layer: 6
m_Name: Character_MechanicalGolem_01 m_Name: Character_MechanicalGolem_01
m_TagString: Untagged m_TagString: Untagged
m_Icon: {fileID: 0} m_Icon: {fileID: 0}
@@ -3093,7 +3093,7 @@ GameObject:
serializedVersion: 6 serializedVersion: 6
m_Component: m_Component:
- component: {fileID: 393037008073616590} - component: {fileID: 393037008073616590}
m_Layer: 0 m_Layer: 6
m_Name: Spine_02 m_Name: Spine_02
m_TagString: Untagged m_TagString: Untagged
m_Icon: {fileID: 0} m_Icon: {fileID: 0}
@@ -3125,7 +3125,7 @@ GameObject:
serializedVersion: 6 serializedVersion: 6
m_Component: m_Component:
- component: {fileID: 7070020292155802438} - component: {fileID: 7070020292155802438}
m_Layer: 0 m_Layer: 6
m_Name: Finger_02 1 m_Name: Finger_02 1
m_TagString: Untagged m_TagString: Untagged
m_Icon: {fileID: 0} m_Icon: {fileID: 0}
@@ -3157,7 +3157,7 @@ GameObject:
serializedVersion: 6 serializedVersion: 6
m_Component: m_Component:
- component: {fileID: 728528768444425882} - component: {fileID: 728528768444425882}
m_Layer: 0 m_Layer: 6
m_Name: Finger_02 m_Name: Finger_02
m_TagString: Untagged m_TagString: Untagged
m_Icon: {fileID: 0} m_Icon: {fileID: 0}
@@ -3189,7 +3189,7 @@ GameObject:
serializedVersion: 6 serializedVersion: 6
m_Component: m_Component:
- component: {fileID: 5783442803983604061} - component: {fileID: 5783442803983604061}
m_Layer: 0 m_Layer: 6
m_Name: Thumb_02 1 m_Name: Thumb_02 1
m_TagString: Untagged m_TagString: Untagged
m_Icon: {fileID: 0} m_Icon: {fileID: 0}
@@ -3221,7 +3221,7 @@ GameObject:
serializedVersion: 6 serializedVersion: 6
m_Component: m_Component:
- component: {fileID: 7392812169768159849} - component: {fileID: 7392812169768159849}
m_Layer: 0 m_Layer: 6
m_Name: LowerLeg_R m_Name: LowerLeg_R
m_TagString: Untagged m_TagString: Untagged
m_Icon: {fileID: 0} m_Icon: {fileID: 0}
@@ -3253,7 +3253,7 @@ GameObject:
serializedVersion: 6 serializedVersion: 6
m_Component: m_Component:
- component: {fileID: 540896332569595868} - component: {fileID: 540896332569595868}
m_Layer: 0 m_Layer: 6
m_Name: Finger_03 1 m_Name: Finger_03 1
m_TagString: Untagged m_TagString: Untagged
m_Icon: {fileID: 0} m_Icon: {fileID: 0}
@@ -3285,7 +3285,7 @@ GameObject:
serializedVersion: 6 serializedVersion: 6
m_Component: m_Component:
- component: {fileID: 9042031077820181245} - component: {fileID: 9042031077820181245}
m_Layer: 0 m_Layer: 6
m_Name: Shoulder_L m_Name: Shoulder_L
m_TagString: Untagged m_TagString: Untagged
m_Icon: {fileID: 0} m_Icon: {fileID: 0}
@@ -3317,7 +3317,7 @@ GameObject:
serializedVersion: 6 serializedVersion: 6
m_Component: m_Component:
- component: {fileID: 6329431241889728063} - component: {fileID: 6329431241889728063}
m_Layer: 0 m_Layer: 6
m_Name: Clavicle_R m_Name: Clavicle_R
m_TagString: Untagged m_TagString: Untagged
m_Icon: {fileID: 0} m_Icon: {fileID: 0}
@@ -3349,7 +3349,7 @@ GameObject:
serializedVersion: 6 serializedVersion: 6
m_Component: m_Component:
- component: {fileID: 6652879279026575357} - component: {fileID: 6652879279026575357}
m_Layer: 0 m_Layer: 6
m_Name: Thumb_01 m_Name: Thumb_01
m_TagString: Untagged m_TagString: Untagged
m_Icon: {fileID: 0} m_Icon: {fileID: 0}
@@ -3381,7 +3381,7 @@ GameObject:
serializedVersion: 6 serializedVersion: 6
m_Component: m_Component:
- component: {fileID: 7812724648278576591} - component: {fileID: 7812724648278576591}
m_Layer: 0 m_Layer: 6
m_Name: Finger_01 1 m_Name: Finger_01 1
m_TagString: Untagged m_TagString: Untagged
m_Icon: {fileID: 0} m_Icon: {fileID: 0}
@@ -3460,6 +3460,10 @@ PrefabInstance:
propertyPath: m_Name propertyPath: m_Name
value: SM_Prop_Troll_Helmet value: SM_Prop_Troll_Helmet
objectReference: {fileID: 0} objectReference: {fileID: 0}
- target: {fileID: 8587111787659894491, guid: 95219051c0a1142448e9ed4beee02fe1, type: 3}
propertyPath: m_Layer
value: 6
objectReference: {fileID: 0}
- target: {fileID: 8587111787659894491, guid: 95219051c0a1142448e9ed4beee02fe1, type: 3} - target: {fileID: 8587111787659894491, guid: 95219051c0a1142448e9ed4beee02fe1, type: 3}
propertyPath: m_IsActive propertyPath: m_IsActive
value: 0 value: 0

View File

@@ -60,7 +60,6 @@ public partial class ChaseTargetAction : Action
return Status.Success; return Status.Success;
} }
// 타겟 위치로 이동
agent.SetDestination(Target.Value.transform.position); agent.SetDestination(Target.Value.transform.position);
return Status.Running; return Status.Running;
} }

View File

@@ -105,14 +105,11 @@ namespace Colosseum.Enemy
customConditions.Clear(); customConditions.Clear();
} }
private void Update() protected override void OnServerUpdate()
{ {
if (!IsServer || IsDead || isTransitioning) if (isTransitioning) return;
return;
phaseElapsedTime = Time.time - phaseStartTime; phaseElapsedTime = Time.time - phaseStartTime;
// 다음 페이즈 전환 조건 확인
CheckPhaseTransition(); CheckPhaseTransition();
} }

View File

@@ -30,6 +30,9 @@ namespace Colosseum.Enemy
protected NetworkVariable<float> currentMana = new NetworkVariable<float>(50f); protected NetworkVariable<float> currentMana = new NetworkVariable<float>(50f);
protected NetworkVariable<bool> isDead = new NetworkVariable<bool>(false); protected NetworkVariable<bool> isDead = new NetworkVariable<bool>(false);
// 플레이어 분리용 (레이어 의존 없이 CharacterController로 식별)
private readonly Collider[] overlapBuffer = new Collider[8];
// 이벤트 // 이벤트
public event Action<float, float> OnHealthChanged; // currentHealth, maxHealth public event Action<float, float> OnHealthChanged; // currentHealth, maxHealth
public event Action<float> OnDamageTaken; // damage public event Action<float> OnDamageTaken; // damage
@@ -65,6 +68,88 @@ namespace Colosseum.Enemy
currentHealth.OnValueChanged += OnHealthChangedInternal; currentHealth.OnValueChanged += OnHealthChangedInternal;
} }
protected virtual void Update()
{
if (!IsServer || IsDead) return;
OnServerUpdate();
}
/// <summary>
/// 서버 Update 확장 포인트 (하위 클래스에서 override)
/// </summary>
protected virtual void OnServerUpdate() { }
/// <summary>
/// NavMeshAgent position sync 및 OnAnimatorMove 이후에 실행됩니다.
/// 보스가 이미 플레이어 안으로 들어온 경우 stoppingDistance 바깥으로 밀어냅니다.
/// Update()에서의 isStopped 조작은 NavMeshAgent에 의해 덮어써지지만,
/// LateUpdate()는 그 이후이므로 확실하게 보정됩니다.
/// </summary>
private void LateUpdate()
{
if (!IsServer || IsDead || navMeshAgent == null) return;
// stoppingDistance가 0이면 radius 기반 fallback 사용
float stopDist = navMeshAgent.stoppingDistance > 0f
? navMeshAgent.stoppingDistance
: navMeshAgent.radius + 0.5f;
int count = Physics.OverlapSphereNonAlloc(transform.position, stopDist, overlapBuffer);
for (int i = 0; i < count; i++)
{
// 레이어 무관하게 CharacterController 유무로 플레이어 식별
if (!overlapBuffer[i].TryGetComponent<CharacterController>(out _)) continue;
Vector3 toPlayer = overlapBuffer[i].transform.position - transform.position;
toPlayer.y = 0f;
float dist = toPlayer.magnitude;
if (dist >= stopDist) continue;
// 보스가 실제로 이동 중일 때만 밀어냄.
// isStopped는 수동 설정 시만 true가 되므로, velocity로 실제 이동 여부를 판단.
if (navMeshAgent.velocity.sqrMagnitude > 0.01f)
{
Vector3 pushDir = dist > 0.001f ? -toPlayer.normalized : -transform.forward;
navMeshAgent.Warp(transform.position + pushDir * (stopDist - dist));
navMeshAgent.isStopped = true;
}
}
}
/// <summary>
/// 보스 스킬 루트모션이 플레이어 방향으로 진입하는 것을 차단합니다.
/// </summary>
private void OnAnimatorMove()
{
if (!IsServer || animator == null || navMeshAgent == null) return;
Vector3 deltaPosition = animator.deltaPosition;
float blockRadius = Mathf.Max(navMeshAgent.stoppingDistance, navMeshAgent.radius + 0.5f);
int count = Physics.OverlapSphereNonAlloc(transform.position, blockRadius, overlapBuffer);
for (int i = 0; i < count; i++)
{
if (!overlapBuffer[i].TryGetComponent<CharacterController>(out _)) continue;
Vector3 toPlayer = overlapBuffer[i].transform.position - transform.position;
toPlayer.y = 0f;
if (toPlayer.sqrMagnitude < 0.0001f) continue;
Vector3 deltaXZ = new Vector3(deltaPosition.x, 0f, deltaPosition.z);
if (Vector3.Dot(deltaXZ, toPlayer.normalized) > 0f)
{
deltaPosition.x = 0f;
deltaPosition.z = 0f;
}
}
navMeshAgent.Move(deltaPosition);
if (animator.deltaRotation != Quaternion.identity)
transform.rotation *= animator.deltaRotation;
}
public override void OnNetworkDespawn() public override void OnNetworkDespawn()
{ {
currentHealth.OnValueChanged -= OnHealthChangedInternal; currentHealth.OnValueChanged -= OnHealthChangedInternal;

View File

@@ -31,30 +31,16 @@ namespace Colosseum.Player
private bool isJumping; private bool isJumping;
private bool wasGrounded; private bool wasGrounded;
// 적 충돌 방향 (이동 차단용) // 적 충돌 차단용
private Vector3 blockedDirection; private Vector3 blockedDirection;
private int enemyLayerMask; private readonly Collider[] overlapBuffer = new Collider[8];
/// <summary>
/// 현재 이동 속도 (애니메이션용)
/// </summary>
public float CurrentMoveSpeed => moveInput.magnitude * moveSpeed; public float CurrentMoveSpeed => moveInput.magnitude * moveSpeed;
/// <summary>
/// 현재 지면 접촉 상태
/// </summary>
public bool IsGrounded => characterController != null ? characterController.isGrounded : false; public bool IsGrounded => characterController != null ? characterController.isGrounded : false;
/// <summary>
/// 점프 중 상태
/// </summary>
public bool IsJumping => isJumping; public bool IsJumping => isJumping;
public override void OnNetworkSpawn() public override void OnNetworkSpawn()
{ {
// 로컬 플레이어가 아니면 입력 비활성화
if (!IsOwner) if (!IsOwner)
{ {
enabled = false; enabled = false;
@@ -62,51 +48,33 @@ namespace Colosseum.Player
} }
characterController = GetComponent<CharacterController>(); characterController = GetComponent<CharacterController>();
// 보스 콜라이더가 겹칠 때 Unity 내부 자동 밀어냄 비활성화.
// 적과의 분리는 EnemyBase.ResolvePlayerOverlap에서 보스 측이 담당.
characterController.enableOverlapRecovery = false;
// SkillController 참조
if (skillController == null) if (skillController == null)
{
skillController = GetComponent<SkillController>(); skillController = GetComponent<SkillController>();
}
// Animator 참조
if (animator == null) if (animator == null)
{
animator = GetComponentInChildren<Animator>(); animator = GetComponentInChildren<Animator>();
}
// 스폰 포인트에서 위치 설정
SetSpawnPosition(); SetSpawnPosition();
// Input Actions 초기화
InitializeInputActions(); InitializeInputActions();
// 카메라 설정
SetupCamera(); SetupCamera();
// 적 레이어 마스크 초기화
enemyLayerMask = LayerMask.GetMask("Enemy");
} }
/// <summary>
/// 입력 액션 초기화
/// </summary>
private void InitializeInputActions() private void InitializeInputActions()
{ {
inputActions = new InputSystem_Actions(); inputActions = new InputSystem_Actions();
inputActions.Player.Enable(); inputActions.Player.Enable();
// Move 액션 콜백 등록
inputActions.Player.Move.performed += OnMovePerformed; inputActions.Player.Move.performed += OnMovePerformed;
inputActions.Player.Move.canceled += OnMoveCanceled; inputActions.Player.Move.canceled += OnMoveCanceled;
// Jump 액션 콜백 등록
inputActions.Player.Jump.performed += OnJumpPerformed; inputActions.Player.Jump.performed += OnJumpPerformed;
} }
/// <summary>
/// 입력 액션 해제
/// </summary>
private void CleanupInputActions() private void CleanupInputActions()
{ {
if (inputActions != null) if (inputActions != null)
@@ -120,16 +88,12 @@ namespace Colosseum.Player
private void OnDisable() private void OnDisable()
{ {
// 컴포넌트 비활성화 시 입력 해제
CleanupInputActions(); CleanupInputActions();
// 입력 초기화
moveInput = Vector2.zero; moveInput = Vector2.zero;
} }
private void OnEnable() private void OnEnable()
{ {
// 컴포넌트 재활성화 시 입력 다시 등록
if (IsOwner && inputActions != null) if (IsOwner && inputActions != null)
{ {
inputActions.Player.Enable(); inputActions.Player.Enable();
@@ -139,16 +103,11 @@ namespace Colosseum.Player
} }
} }
/// <summary>
/// 스폰 위치 설정
/// </summary>
private void SetSpawnPosition() private void SetSpawnPosition()
{ {
Transform spawnPoint = PlayerSpawnPoint.GetRandomSpawnPoint(); Transform spawnPoint = PlayerSpawnPoint.GetRandomSpawnPoint();
if (spawnPoint != null) if (spawnPoint != null)
{ {
// CharacterController 비활성화 후 위치 설정 (충돌 문제 방지)
characterController.enabled = false; characterController.enabled = false;
transform.position = spawnPoint.position; transform.position = spawnPoint.position;
transform.rotation = spawnPoint.rotation; transform.rotation = spawnPoint.rotation;
@@ -156,69 +115,79 @@ namespace Colosseum.Player
} }
} }
/// <summary>
/// 네트워크 정리
/// </summary>
public override void OnNetworkDespawn() public override void OnNetworkDespawn()
{ {
CleanupInputActions(); CleanupInputActions();
} }
private void OnMovePerformed(InputAction.CallbackContext context) private void OnMovePerformed(InputAction.CallbackContext context) => moveInput = context.ReadValue<Vector2>();
{ private void OnMoveCanceled(InputAction.CallbackContext context) => moveInput = Vector2.zero;
moveInput = context.ReadValue<Vector2>();
}
private void OnMoveCanceled(InputAction.CallbackContext context)
{
moveInput = Vector2.zero;
}
private void OnJumpPerformed(InputAction.CallbackContext context) private void OnJumpPerformed(InputAction.CallbackContext context)
{ {
if (!isJumping && characterController.isGrounded) if (!isJumping && characterController.isGrounded)
{
Jump(); Jump();
} }
}
private void SetupCamera() private void SetupCamera()
{ {
var cameraController = GetComponent<PlayerCamera>(); var cameraController = GetComponent<PlayerCamera>();
if (cameraController == null) if (cameraController == null)
{
cameraController = gameObject.AddComponent<PlayerCamera>(); cameraController = gameObject.AddComponent<PlayerCamera>();
}
cameraController.Initialize(transform, inputActions); cameraController.Initialize(transform, inputActions);
} }
/// <summary> public void RefreshCamera() => SetupCamera();
/// 카메라 재설정 (씬 로드 후 호출)
/// </summary>
public void RefreshCamera()
{
SetupCamera();
}
private void Update() private void Update()
{ {
if (!IsOwner) return; if (!IsOwner) return;
ApplyGravity(); ApplyGravity();
UpdateBlockedDirection();
Move(); Move();
} }
/// <summary>
/// 매 프레임 주변 적을 능동적으로 감지하여 blockedDirection을 설정합니다.
/// 콜백 기반이 아니므로 보스가 플레이어 쪽으로 밀고 올 때도 즉시 감지합니다.
/// </summary>
private void UpdateBlockedDirection()
{
blockedDirection = Vector3.zero;
Vector3 center = transform.position + characterController.center;
float radius = characterController.radius + 0.15f;
float halfHeight = Mathf.Max(0f, characterController.height * 0.5f - characterController.radius);
// 레이어 무관하게 NavMeshAgent 유무로 적 식별
int count = Physics.OverlapCapsuleNonAlloc(
center + Vector3.up * halfHeight,
center - Vector3.up * halfHeight,
radius, overlapBuffer);
for (int i = 0; i < count; i++)
{
if (overlapBuffer[i].gameObject == gameObject) continue;
if (!overlapBuffer[i].TryGetComponent<UnityEngine.AI.NavMeshAgent>(out _)) continue;
Vector3 toEnemy = overlapBuffer[i].transform.position - transform.position;
toEnemy.y = 0f;
if (toEnemy.sqrMagnitude > 0.0001f)
{
blockedDirection = toEnemy.normalized;
break;
}
}
}
private void ApplyGravity() private void ApplyGravity()
{ {
if (wasGrounded && velocity.y < 0) if (wasGrounded && velocity.y < 0)
{
velocity.y = -2f; velocity.y = -2f;
}
else else
{
velocity.y += gravity * Time.deltaTime; velocity.y += gravity * Time.deltaTime;
} }
}
private void Move() private void Move()
{ {
@@ -227,51 +196,30 @@ namespace Colosseum.Player
// 스킬 애니메이션 재생 중에는 이동 불가 (루트 모션은 OnAnimatorMove에서 처리) // 스킬 애니메이션 재생 중에는 이동 불가 (루트 모션은 OnAnimatorMove에서 처리)
if (skillController != null && skillController.IsPlayingAnimation) if (skillController != null && skillController.IsPlayingAnimation)
{ {
// 루트 모션을 사용하지 않는 경우 중력만 적용
if (!skillController.UsesRootMotion) if (!skillController.UsesRootMotion)
{
characterController.Move(velocity * Time.deltaTime); characterController.Move(velocity * Time.deltaTime);
}
return; return;
} }
// 이동 방향 계산 (카메라 기준)
Vector3 moveDirection = new Vector3(moveInput.x, 0f, moveInput.y); Vector3 moveDirection = new Vector3(moveInput.x, 0f, moveInput.y);
moveDirection = TransformDirectionByCamera(moveDirection); moveDirection = TransformDirectionByCamera(moveDirection);
moveDirection.Normalize(); moveDirection.Normalize();
// 충돌 방향으로 이동 차단 (미끄러짐 방지) // 방향으로 이동 시도 중이면 수평 이동 전체 취소
if (blockedDirection != Vector3.zero) if (blockedDirection != Vector3.zero && Vector3.Dot(moveDirection, blockedDirection) > 0f)
{ moveDirection = Vector3.zero;
float blockedAmount = Vector3.Dot(moveDirection, blockedDirection);
if (blockedAmount > 0f)
{
moveDirection -= blockedDirection * blockedAmount;
moveDirection.Normalize();
}
}
// 이동 적용 characterController.Move((moveDirection * moveSpeed + velocity) * Time.deltaTime);
Vector3 moveVector = moveDirection * moveSpeed * Time.deltaTime;
characterController.Move(moveVector + velocity * Time.deltaTime);
// 충돌 방향 리셋
blockedDirection = Vector3.zero;
// 회전 (이동 중일 때만)
if (moveDirection != Vector3.zero) if (moveDirection != Vector3.zero)
{ {
Quaternion targetRotation = Quaternion.LookRotation(moveDirection); Quaternion targetRotation = Quaternion.LookRotation(moveDirection);
transform.rotation = Quaternion.Slerp(transform.rotation, targetRotation, rotationSpeed * Time.deltaTime); transform.rotation = Quaternion.Slerp(transform.rotation, targetRotation, rotationSpeed * Time.deltaTime);
} }
// 착지 체크 (Move 후에 isGrounded가 업데이트됨)
if (!wasGrounded && characterController.isGrounded && isJumping) if (!wasGrounded && characterController.isGrounded && isJumping)
{
OnJumpEnd(); OnJumpEnd();
}
// 다음 프레임을 위해 현재 상태 저장
wasGrounded = characterController.isGrounded; wasGrounded = characterController.isGrounded;
} }
@@ -280,41 +228,27 @@ namespace Colosseum.Player
isJumping = true; isJumping = true;
velocity.y = jumpForce; velocity.y = jumpForce;
// 애니메이션 컨트롤러에 점프 알림
var animController = GetComponent<PlayerAnimationController>(); var animController = GetComponent<PlayerAnimationController>();
if (animController != null) if (animController != null)
{
animController.PlayJump(); animController.PlayJump();
} }
}
/// <summary> public void OnJumpEnd() => isJumping = false;
/// 점프 중 상태가 끝나면 IsJumping = false;
/// </summary>
public void OnJumpEnd()
{
isJumping = false;
}
private Vector3 TransformDirectionByCamera(Vector3 direction) private Vector3 TransformDirectionByCamera(Vector3 direction)
{ {
if (Camera.main == null) return direction; if (Camera.main == null) return direction;
Transform cameraTransform = Camera.main.transform; Transform cam = Camera.main.transform;
Vector3 cameraForward = cameraTransform.forward; Vector3 forward = new Vector3(cam.forward.x, 0f, cam.forward.z).normalized;
Vector3 cameraRight = cameraTransform.right; Vector3 right = new Vector3(cam.right.x, 0f, cam.right.z).normalized;
// Y축 제거 return right * direction.x + forward * direction.z;
cameraForward.y = 0f;
cameraRight.y = 0f;
cameraForward.Normalize();
cameraRight.Normalize();
return cameraRight * direction.x + cameraForward * direction.z;
} }
/// <summary> /// <summary>
/// 루트 모션 처리. 스킬 애니메이션 중 애니메이션의 이동/회전 데이터를 적용합니다. /// 루트 모션 처리. 스킬 애니메이션 중 애니메이션의 이동/회전 데이터를 적용합니다.
/// 적 방향으로의 이동은 취소합니다.
/// </summary> /// </summary>
private void OnAnimatorMove() private void OnAnimatorMove()
{ {
@@ -323,10 +257,19 @@ namespace Colosseum.Player
if (skillController == null || !skillController.IsPlayingAnimation) return; if (skillController == null || !skillController.IsPlayingAnimation) return;
if (!skillController.UsesRootMotion) return; if (!skillController.UsesRootMotion) return;
// 루트 모션 이동 적용
Vector3 deltaPosition = animator.deltaPosition; Vector3 deltaPosition = animator.deltaPosition;
// Y축 무시 설정 시 중력 유지 // 적 방향으로 루트 모션이 향하면 수평 이동 취소
if (blockedDirection != Vector3.zero)
{
Vector3 deltaXZ = new Vector3(deltaPosition.x, 0f, deltaPosition.z);
if (Vector3.Dot(deltaXZ, blockedDirection) > 0f)
{
deltaPosition.x = 0f;
deltaPosition.z = 0f;
}
}
if (skillController.IgnoreRootMotionY) if (skillController.IgnoreRootMotionY)
{ {
deltaPosition.y = 0f; deltaPosition.y = 0f;
@@ -337,57 +280,13 @@ namespace Colosseum.Player
characterController.Move(deltaPosition); characterController.Move(deltaPosition);
} }
// 루트 모션 회전 적용
if (animator.deltaRotation != Quaternion.identity) if (animator.deltaRotation != Quaternion.identity)
{
transform.rotation *= animator.deltaRotation; transform.rotation *= animator.deltaRotation;
}
// 착지 체크
if (!wasGrounded && characterController.isGrounded && isJumping) if (!wasGrounded && characterController.isGrounded && isJumping)
{
OnJumpEnd(); OnJumpEnd();
}
wasGrounded = characterController.isGrounded; wasGrounded = characterController.isGrounded;
} }
/// <summary>
/// CharacterController 충돌 처리. 적과 충돌 시 해당 방향 이동을 차단합니다.
/// 충돌 normal을 8방향으로 양자화하여 각진 충돌 느낌을 줍니다.
/// </summary>
private void OnControllerColliderHit(ControllerColliderHit hit)
{
// 적과의 충돌인지 확인
if ((enemyLayerMask & (1 << hit.gameObject.layer)) != 0)
{
// 충돌 방향 저장 (이동 차단용)
blockedDirection = hit.normal;
blockedDirection.y = 0f;
blockedDirection.Normalize();
// 8방향으로 양자화 (45도 간격)
blockedDirection = QuantizeToOctagon(blockedDirection);
}
}
/// <summary>
/// 방향을 8각형(45도 간격) 방향으로 양자화합니다.
/// </summary>
private Vector3 QuantizeToOctagon(Vector3 direction)
{
if (direction == Vector3.zero)
return direction;
// 각도 계산
float angle = Mathf.Atan2(direction.x, direction.z) * Mathf.Rad2Deg;
// 45도 단위로 반올림
float snappedAngle = Mathf.Round(angle / 45f) * 45f;
// 다시 벡터로 변환
float radians = snappedAngle * Mathf.Deg2Rad;
return new Vector3(Mathf.Sin(radians), 0f, Mathf.Cos(radians));
}
} }
} }