From 680aacf580e2fe29884a69eede54dfc1b3d15c04 Mon Sep 17 00:00:00 2001 From: dal4segno Date: Tue, 10 Mar 2026 12:34:37 +0900 Subject: [PATCH] =?UTF-8?q?=EC=8A=A4=ED=82=AC=20=EC=8B=9C=EC=8A=A4?= =?UTF-8?q?=ED=85=9C=EC=97=90=20=EB=A3=A8=ED=8A=B8=20=EB=AA=A8=EC=85=98=20?= =?UTF-8?q?=EC=A7=80=EC=9B=90=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - SkillData: useRootMotion, ignoreRootMotionY 설정 추가 - SkillController: 루트 모션 관련 프로퍼티 노출 - PlayerMovement: OnAnimatorMove로 스킬 애니메이션의 이동/회전 데이터 적용 - Y축 무시 옵션으로 중력 및 충돌 처리 유지 가능 Co-Authored-By: Claude Opus 4.6 --- .claude/commands/commit.md | 30 +++++++++++++ Assets/Scripts/Player/PlayerMovement.cs | 56 ++++++++++++++++++++++-- Assets/Scripts/Skills/SkillController.cs | 3 ++ Assets/Scripts/Skills/SkillData.cs | 8 ++++ 4 files changed, 94 insertions(+), 3 deletions(-) create mode 100644 .claude/commands/commit.md diff --git a/.claude/commands/commit.md b/.claude/commands/commit.md new file mode 100644 index 00000000..6c09e2ec --- /dev/null +++ b/.claude/commands/commit.md @@ -0,0 +1,30 @@ +# Git Commit Workflow + +변경된 파일들을 분석하고 git add 후 커밋 메시지를 작성한 뒤, 사용자 확인 후 커밋합니다. + +## 작업 순서 + +1. `git status`로 변경된 파일 확인 +2. `git diff`로 변경 내용 분석 +3. `git log --oneline -5`로 최근 커밋 스타일 참고 +4. 변경 내용을 표로 정리해서 사용자에게 보여주기 +5. 커밋 메시지 초안 작성 후 사용자 확인 요청 +6. 확인되면 `git add` 후 `git commit` 실행 +7. `git status`로 커밋 완료 확인 + +## 커밋 메시지 형식 + +``` +[주제] 간결한 요약 + +- 변경 사항 1 +- 변경 사항 2 +- 변경 사항 3 +``` + +## 주의사항 + +- 한글로 커밋 메시지 작성 +- 변경 사항은 파일 단위가 아닌 기능/목적 단위로 정리 +- 사용자 승인 없이 커밋하지 않음 +- `.meta` 파일은 Unity 자동 생성이므로 내용 분석에서 제외 가능 diff --git a/Assets/Scripts/Player/PlayerMovement.cs b/Assets/Scripts/Player/PlayerMovement.cs index 6a388175..3f13bbed 100644 --- a/Assets/Scripts/Player/PlayerMovement.cs +++ b/Assets/Scripts/Player/PlayerMovement.cs @@ -22,6 +22,7 @@ namespace Colosseum.Player [Header("References")] [SerializeField] private SkillController skillController; + [SerializeField] private Animator animator; private CharacterController characterController; private Vector3 velocity; @@ -64,6 +65,12 @@ namespace Colosseum.Player skillController = GetComponent(); } + // Animator 참조 + if (animator == null) + { + animator = GetComponentInChildren(); + } + // 스폰 포인트에서 위치 설정 SetSpawnPosition(); @@ -174,11 +181,14 @@ namespace Colosseum.Player { if (characterController == null) return; - // 스킬 애니메이션 재생 중에는 이동 불가 + // 스킬 애니메이션 재생 중에는 이동 불가 (루트 모션은 OnAnimatorMove에서 처리) if (skillController != null && skillController.IsPlayingAnimation) { - // 중력만 적용 - characterController.Move(velocity * Time.deltaTime); + // 루트 모션을 사용하지 않는 경우 중력만 적용 + if (!skillController.UsesRootMotion) + { + characterController.Move(velocity * Time.deltaTime); + } return; } @@ -248,5 +258,45 @@ namespace Colosseum.Player return cameraRight * direction.x + cameraForward * direction.z; } + + + /// + /// 루트 모션 처리. 스킬 애니메이션 중에 애니메이션의 이동/회전 데이터를 적용합니다. + /// + private void OnAnimatorMove() + { + if (!IsOwner) return; + if (animator == null || characterController == null) return; + if (skillController == null || !skillController.IsPlayingAnimation) return; + if (!skillController.UsesRootMotion) return; + + // 루트 모션 이동 적용 + Vector3 deltaPosition = animator.deltaPosition; + + // Y축 무시 설정 시 중력 유지 + if (skillController.IgnoreRootMotionY) + { + deltaPosition.y = 0f; + characterController.Move(deltaPosition + velocity * Time.deltaTime); + } + else + { + characterController.Move(deltaPosition); + } + + // 루트 모션 회전 적용 + if (animator.deltaRotation != Quaternion.identity) + { + transform.rotation *= animator.deltaRotation; + } + + // 착지 체크 + if (!wasGrounded && characterController.isGrounded && isJumping) + { + OnJumpEnd(); + } + + wasGrounded = characterController.isGrounded; + } } } diff --git a/Assets/Scripts/Skills/SkillController.cs b/Assets/Scripts/Skills/SkillController.cs index 252df1c5..cab2f714 100644 --- a/Assets/Scripts/Skills/SkillController.cs +++ b/Assets/Scripts/Skills/SkillController.cs @@ -32,7 +32,10 @@ namespace Colosseum.Skills public bool IsExecutingSkill => currentSkill != null && !skillEndRequested; public bool IsPlayingAnimation => currentSkill != null; + public bool UsesRootMotion => currentSkill != null && currentSkill.UseRootMotion; + public bool IgnoreRootMotionY => currentSkill != null && currentSkill.IgnoreRootMotionY; public SkillData CurrentSkill => currentSkill; + public Animator Animator => animator; private void Awake() { diff --git a/Assets/Scripts/Skills/SkillData.cs b/Assets/Scripts/Skills/SkillData.cs index 86e4b746..b2824807 100644 --- a/Assets/Scripts/Skills/SkillData.cs +++ b/Assets/Scripts/Skills/SkillData.cs @@ -21,6 +21,12 @@ namespace Colosseum.Skills [Tooltip("종료 애니메이션 (선택)")] [SerializeField] private AnimationClip endClip; + [Header("루트 모션")] + [Tooltip("애니메이션의 이동/회전 데이터를 캐릭터에 적용")] + [SerializeField] private bool useRootMotion = false; + [Tooltip("루트 모션 적용 시 Y축 이동 무시 (중력과 충돌)")] + [SerializeField] private bool ignoreRootMotionY = true; + [Header("쿨타임 & 비용")] [Min(0f)] [SerializeField] private float cooldown = 1f; [Min(0f)] [SerializeField] private float manaCost = 0f; @@ -37,6 +43,8 @@ namespace Colosseum.Skills public AnimationClip EndClip => endClip; public float Cooldown => cooldown; public float ManaCost => manaCost; + public bool UseRootMotion => useRootMotion; + public bool IgnoreRootMotionY => ignoreRootMotionY; public IReadOnlyList Effects => effects; } }