전체 코드 : https://github.com/HyangRim/DirectX11-Engine-Client
데미지 시스템과 전투 매커니즘
1. 상태 기반 전투 제어 시스템
1.1 Player 전투 상태 관리
enum class PlayerStateType
{
Wait, Run, Skill_1, Skill_2, Skill_3, Skill_4,
Craft, Die, BaseAttack, Counter
};
void PlayerStateMachine::HandleRightClickInput()
{
// 1. 공격 우선 판단
auto attackTarget = GetPickedTargetAtMouse();
if (attackTarget && IsValidAttackTarget(attackTarget))
{
// 공격 쿨타임 체크
if (m_baseAttackDelayDuration >= m_baseAttackDelay)
{
m_baseAttackDelayDuration = 0.f;
RequestStateChange(PlayerStateType::BaseAttack);
GetState(PlayerStateType::BaseAttack)->SetTarget(attackTarget);
}
}
else
{
// 공격 대상이 없으면 이동 처리
HandleMovementInput();
}
}
특징
- Context-Aware 입력 처리 : 하나의 입력 ( 우클릭 ) 이 상황에 따라 공격 / 이동으로 분기
- 쿨타임 기반 밸런싱 : 프레임 독립적 공격 속도 제어
- 상태 전환 검증 : 현재 상태에서 가능한 액션만 허용
1.2 Monster AI 반응형 전투 로직
void MonsterStateMachine::ProcessAI()
{
// 1. 체력 체크 (최우선)
if (m_monsterInterface && m_monsterInterface->GetHP() <= 0)
{
if (!IsInState(MonsterStateType::Death))
{
RequestStateChange(MonsterStateType::Death);
return; // 사망 처리 후 다른 AI 로직 중단
}
}
// 2. 상황별 전투 상태 전환
switch (currentState)
{
case MonsterStateType::Trace:
if (distanceToTarget <= m_attackRange)
{
RequestStateChange(MonsterStateType::Attack);
}
break;
case MonsterStateType::Attack:
if (distanceToTarget > m_attackRange)
{
RequestStateChange(MonsterStateType::Trace);
}
break;
}
}
1.3 타이밍 기반 카운터 시스템
void PlayerStateMachine::CheckCounterSkillCompletion()
{
// Counter 상태일 때만 완료 체크
if (IsInState(PlayerStateType::Counter))
{
// 이미 완료 체크를 했으면 건너뛰기 (중복 방지)
if (m_counterSkillCompletionChecked)
return;
bool counterSkillCompleted = false;
OnCounterSkillCompleted(counterSkillCompleted); // 델리게이트 호출
if (counterSkillCompleted)
{
cout << "카운터 스킬 완료 감지 - Wait 상태로 전환" << endl;
m_counterSkillCompletionChecked = true; // 플래그 설정
// 대기 상태로 복귀
RequestStateChange(PlayerStateType::Wait);
if (m_animationStateMachine)
{
m_animationStateMachine->RequestStateChange(AnimationStateType::Wait);
}
}
}
else
{
// Counter 상태가 아니면 플래그 리셋
m_counterSkillCompletionChecked = false;
}
}
1.4 스킬 기반 방어 시스템 ( 비앙카 W 스킬 )
void BiancaWSkill::PlaySkill()
{
if (m_isPlaying) {
// 너무 빠르게 다시 눌러 해제되는 것 방지
if (m_repeatKey < 0.25f)
return;
// 이미 실행중일 경우 -> W스킬 끝내기
m_coffin->SetActive(false);
m_isPlaying = false;
// 방어력 보너스 해제
PlayerStatus status = m_playerObject->GetStatus();
m_playerObject->SetDefense(status.defense - 50);
m_repeatKey = 0.f;
m_elapsedTime = 0.f;
SOUND->PlaySound(m_soundEnd, 1, 0.5f);
SkillEnd();
}
else if(m_isPlaying == false && m_skillcurCooldown <= 0){
// 스킬 활성화
m_coffin->SetActive(true);
m_isPlaying = true;
// 방어력 보너스 적용 (+50 방어력)
PlayerStatus status = m_playerObject->GetStatus();
m_playerObject->SetDefense(status.defense + 50);
SOUND->PlaySound(m_soundStart, 1, 0.5f);
}
}
특징
- 우선순위 기반 판단 : 생존 -> 전투 -> 탐지 순서로 로직 처리
- 거리 기반 상태 전환 : 실시간 거리 계산으로 동적 전투 상태 변경
- 중복 방지 로직 : 이미 해당 상태일 때 불필요한 전환 요청 방지
2. 피킹 시스템
2.1 쿼드 트리 기반 타겟 선택
shared_ptr<GameObject> PlayerStateMachine::GetPickedTargetAtMouse()
{
// SceneObjectManager의 쿼드트리 피킹 시스템 활용
Ray ray = CURSCENE->GetObjectManager()->CreateRayFromScreen(Vec2(mousePos.x, mousePos.y), cam);
auto quadTree = CURSCENE->GetQuadTree();
vector<shared_ptr<GameObject>> candidates = quadTree->Query(ray, cam);
float minDistance = FLT_MAX;
shared_ptr<GameObject> closestTarget = nullptr;
for (auto& obj : candidates)
{
// 화면 좌표 유효성 검사
RECT objBounds = quadTree->GetObjectScreenBounds(obj, cam);
if (screenCenterX < 0 || screenCenterY < 0) continue;
// Ray 교차 검사로 정밀 피킹
float distance = 0.f;
if (obj->GetCollider()->Intersects(ray, distance))
{
if (distance < minDistance)
{
minDistance = distance;
closestTarget = obj;
}
}
}
return closestTarget;
}
특징
- 공간 분할 가속 : 쿼드 트리로 O(n) -> O(log n) 검색 최적화
- 2단계 필터링 : 바운딩 박스 -> Ray 교차 검사
- 거리 기반 우선순위 : 가장 가까운 대상 우선 선택
3. 데미지 처리 시스템
3.1 공식 기반 데미지 계산
void Player::Damaged(shared_ptr<Monster> _attacker, int _damage)
{
// 카운터 어택 조건 검사
if (m_playerStateMachine->GetCurrentState() == PlayerStateType::Skill_2)
{
// 방어 스킬 사용 중 피격시 카운터 발동
m_playerStateMachine->GetState(PlayerStateType::Counter)->SetTarget(_attacker);
m_playerStateMachine->RequestStateChange(PlayerStateType::Counter);
GetAnimationStateMachine()->RequestStateChange(AnimationStateType::Counter);
}
// 데미지 계산 공식
int32 baseAttack = _damage * 100;
int32 baseDefense = info.defense + 100;
int32 finalDamage = baseAttack / baseDefense;
SetHP(info.hp - finalDamage);
}
void Monster::Damaged(shared_ptr<GameObject> _attacker, int _damage)
{
// 피격시 즉시 어그로 설정
if (m_monsterStateMachine && _attacker &&
_attacker->GetType() == OBJECTTYPE::PLAYER)
{
m_monsterStateMachine->SetTarget(_attacker);
// 대기 상태였다면 즉시 추적으로 전환
if (!m_monsterStateMachine->IsInState(MonsterStateType::Attack) &&
!m_monsterStateMachine->IsInState(MonsterStateType::Trace))
{
m_monsterStateMachine->RequestStateChange(MonsterStateType::Trace);
}
}
}
특징
- 어그로 시스템 : 피격시 즉시 공격자를 타겟으로 지정
4. 경험치 및 성장 시스템
4.1 이벤트 기반 경험치 처리
void Player::Start()
{
// 경험치 보상 이벤트 구독
EVENT->Subscribe(EventType::MONSTER_EXP_REWARD,
[this](shared_ptr<EventData> eventData) {
auto expData = dynamic_pointer_cast<ExpRewardEventData>(eventData);
if (expData && expData->m_killer.get() == this) {
// 자신이 처치한 몬스터일 때만 경험치 획득
SetCurExp(m_status.curExp + expData->m_expAmount);
}
});
}
void Monster::Death(shared_ptr<GameObject> killer)
{
// 경험치 보상 이벤트 발생
int expReward = CalculateExpReward();
auto expEvent = make_shared<ExpRewardEventData>(killer, expReward, shared_from_this());
EVENT->QueueEvent(expEvent);
}
void Player::LevelUp()
{
// 다중 레벨업 처리
int shouldLevelUpValue = 0;
while (m_status.curExp >= m_status.curExpLimit)
{
m_status.curExp -= m_status.curExpLimit;
m_status.curExpLimit += m_growStatus.ExpLimit;
shouldLevelUpValue++;
}
// 스킬 포인트 및 스탯 증가
m_status.availableSkillPoints += shouldLevelUpValue;
ApplyLevelUpStats(shouldLevelUpValue);
}
특징
- 이벤트 기반 분리 : 이벤트 매니저를 통해 경험치 획득과 레벨 업 로직 분리
- 다중 레벨업 지원 : 대량 경험치 획득시 한번에 여러 레벨 처리
- 실시간 UI 동기화 : 상태 변경시 UI 업데이트
5. Delegate 기반 이벤트 시스템
// PlayerStateMachine.h - 델리게이트 선언
public:
// 스킬 완료 체크 델리게이트들
Delegate::Delegate<bool&> OnQSkillCompleted;
Delegate::Delegate<bool&> OnWSkillCompleted;
Delegate::Delegate<bool&> OnESkillCompleted;
Delegate::Delegate<bool&> OnRSkillCompleted;
Delegate::Delegate<bool&> OnCounterSkillCompleted;
// LumiaIsland.cpp - 델리게이트 등록 예시
m_player->GetPlayerStateMachine()->OnCounterSkillCompleted.Push([this](bool& completed) {
// 카운터 스킬 완료 상태를 확인하는 로직
completed = IsCounterSkillCompleted();
});
6. 통합 입력 처리 시스템
void PlayerStateMachine::ProcessInput()
{
// 1. 스킬 입력 (최우선)
HandleSkillInput();
// 2. 제작 입력
HandleCraftInput();
// 3. 통합 우클릭 처리 (공격 vs 이동)
HandleRightClickInput();
}
bool PlayerStateMachine::IsCurrentStateMovable() const
{
if (!m_currentState)
return true;
return m_currentState->IsMovable(); // 각 상태별 이동 가능 여부
}
bool PlayerStateMachine::IsValidAttackTarget(shared_ptr<GameObject> target)
{
if (!target) return false;
if (!target->GetCollider()) return false;
return target->GetType() == OBJECTTYPE::MONSTER;
}
입력 시스템 특징
- 우선순위 기반 처리 : 스킬 -> 제작 -> 이동 / 공격 순서로 입력 해석
- 상태 인식 입력 : 현재 상태에 따라 허용되는 액션 제한
- 타입 안전 타겟팅 : 컴파일 타임 타입 검증으로 잘못된 타겟팅 방지
결론
- 상태 기반 전투 아키텍쳐
- Player와 Monster 각각 독립적인 상태 머신
- 이벤트 기반 상태 전환으로 느슨한 결합
- 상태별 허용 액션 제한으로 안정성 보장
- 성능 최적화된 피킹 시스템
- 쿼드트리 공간 분할로 O(log n) 검색
- 2단계 필터링으로 정확도와 성능 균형
- 지능형 전투 AI
- 피격시 즉시 어그로 시스템
- 거리 기반 동적 상태 전환
- 우선순위 기반 행동 결정
- 이벤트 기반 확장성
- 데미지 , 경험치 , 레벨업 모두 이벤트로 분리
- 느슨한 결합으로 시스템 간 독립성 확보
- 쉬운 기능 확장
- UI 도익화
- 상태 변경 즉시 UI 자동 업데이트
- 비율 기반 자원 관리로 일관성 유지
'DirectX11 > Eternal Return 모작' 카테고리의 다른 글
| [DirectX 11 Eternal Return 모작] 15. HUD와 상태바 (0) | 2025.09.03 |
|---|---|
| [DirectX 11 Eternal Return 모작] 14. UI (0) | 2025.09.03 |
| [DirectX 11 Eternal Return 모작] 12. NavMesh (0) | 2025.09.03 |
| [DirectX 11 Eternal Return 모작] 11. 스킬 시스템 (0) | 2025.09.03 |
| [DirectX 11 Eternal Return 모작] 10. 인벤토리와 장비 시스템 (0) | 2025.09.03 |