멘토 피드백을 반영하여 Bullet이 Enemy 내부를 직접 조작하던 구조를
Enemy.OnHit()으로 역할을 분리한 과정을 기록한다.
1. 멘토 피드백 요약
Bullet이 넉백을 직접 처리하기보다 Enemy에 넉백을 요청하는 등 역할을 분리하는 것이
이후에 상태 추가나 유지보수 측면에서 더 유연한 구조가 될 수 있습니다.
2. 문제: 높은 결합도
기존 Bullet.cs가 Enemy의 내부를 너무 많이 알고 있었다:
// Bullet.cs가 Enemy의 내부를 직접 조작 (나쁜 예)
agent.enabled = false; // NavMeshAgent 끄기
enemyRb.isKinematic = false; // Rigidbody 설정
enemyRb.constraints = ...; // 축 잠금
enemyRb.linearDamping = 5f; // 마찰 설정
enemyRb.AddForce(...); // 힘 가하기
enemy.Die(); // 죽이기
비유하자면
손님이 주방에 직접 들어가서 요리함
→ 메뉴가 바뀌면 손님도 바꿔야 함
→ 요리사가 여러 명이면 혼란
왜 안좋은가?
- 보스 몹(죽지 않고 넉백만 받는 적)을 만들려면 Bullet.cs를 수정해야 함
- Enemy 내부 구조가 바뀌면 Bullet.cs도 수정해야 함
- Bullet이 Enemy의 컴포넌트(NavMeshAgent, Rigidbody)를 다 알아야 함
3. 해결: 역할 분리
"자기 일은 자기가 한다"
Bullet의 역할: "맞았다"는 사실과 힘의 방향/크기를 알려주기
Enemy의 역할: 맞았을 때 어떻게 반응할지 스스로 결정하기
비유하자면
손님이 주문만 하고, 요리사가 알아서 만듦
→ 메뉴가 바뀌어도 손님은 몰라도 됨
→ 요리사만 바꾸면 끝
4. 변경 내용
Bullet.cs - 간결해짐
// 변경 전
agent.enabled = false;
enemyRb.isKinematic = false;
enemyRb.constraints = ...;
enemyRb.linearDamping = 5f;
enemyRb.AddForce(...);
enemy.Die();
// 변경 후
Vector3 knockbackDirection = (enemy.transform.position - transform.position);
knockbackDirection.y = 0f;
knockbackDirection.Normalize();
enemy.OnHit(knockbackDirection * knockbackForce);
Bullet은 이제 방향 계산 + OnHit 호출만 한다.
Enemy.cs — OnHit() 메서드 추가
public void OnHit(Vector3 knockbackForce)
{
if (state == State.Die) return;
if (_agent.enabled)
_agent.enabled = false;
Rigidbody rb = GetComponent<Rigidbody>();
if (rb != null)
{
rb.isKinematic = false;
rb.constraints = RigidbodyConstraints.FreezePositionY
| RigidbodyConstraints.FreezeRotation;
rb.linearDamping = 5f;
rb.AddForce(knockbackForce, ForceMode.Impulse);
}
nextState = State.Die;
}
Enemy가 자기 자신의 넉백을 스스로 처리한다.
5. 이 구조의 장점
확장성 예시) 보스 몹
// 일반 Enemy
public void OnHit(Vector3 knockbackForce)
{
// 넉백 + 죽기
ApplyKnockback(knockbackForce);
nextState = State.Die;
}
// 보스 Enemy (죽지 않고 넉백만)
public void OnHit(Vector3 knockbackForce)
{
_hp -= 10;
ApplyKnockback(knockbackForce * 0.3f); // 약하게 밀림
if (_hp <= 0)
nextState = State.Die;
else
nextState = State.Chase; // 다시 추적!
}
Bullet.cs는 한 줄도 안 바꿔도 된다! enemy.OnHit(힘)만 호출하면 되니까.
6. 이번 작업에서 배운 것 요약
- 결합도를 낮추자. 다른 객체의 내부를 직접 조작하지 말고, 메서드를 통해 요청
- 자기 일은 자기가 한다. Enemy의 반응은 Enemy가 결정
- 단일 책임 원칙. 하나의 스크립트는 하나의 역할만
- 확장성. 역할이 분리되면 새로운 타입(보스 등)을 추가할 때 기존 코드를 안 건드려도 됨
'Unity 궁둥이 쪼물쪼물' 카테고리의 다른 글
| 010. 플레이어 체력 & 게임오버 (0) | 2026.04.13 |
|---|---|
| 009. 적 체력(HP) 시스템 (0) | 2026.04.13 |
| 007. 넉백(Knockback) 구현 (0) | 2026.04.06 |
| 006. 총알에 오브젝트 풀링 적용하기 (0) | 2026.04.06 |
| 005. 발사체(Projectile) 구현 (0) | 2026.04.06 |