전체 코드 : https://github.com/HyangRim/DirectX11-Engine-Client
GitHub - HyangRim/DirectX11-Engine-Client
Contribute to HyangRim/DirectX11-Engine-Client development by creating an account on GitHub.
github.com
스킬 시스템과 쿨타임 관리
1. 스킬 메타데이터 시스템 설계
1.1 스킬 타입 정의
먼저 스킬의 기본적인 타입들을 정의
// SkillConfig.h
enum class SkillTargetType
{
None, // 타겟 불필요 (비앙카 R)
Single, // 단일 타겟 (니키 R)
};
enum class SkillCastType
{
Instant, // 즉시 발동
Channeling, // 채널링 (니키 Q처럼)
};
struct SkillMetaData
{
SkillTargetType targetType = SkillTargetType::None;
SkillCastType castType = SkillCastType::Instant;
float range = 0.0f;
bool canCastWhileMoving = false; // 스킬 중 이동할 수 있는지
};
1.2 캐릭터별 스킬 설정 시스템
각 캐릭터별로 스킬 메타데이터를 체계적으로 관리하는 SkillConfig 구현
(사실 이 부분도 아이템과 마찬가지로 외부 파일에 미리 정의해 둔 다음 불러오는 방식이 더 효율 적일듯?)
// SkillConfig.cpp
void SkillConfig::InitializeConfigs()
{
// Nicky 스킬 설정
s_skillConfigs[L"Nicky"] = {
// Q 스킬 - 차징, 타겟 불필요
{SkillTargetType::None, SkillCastType::Channeling, 15.0f, false},
// W 스킬 - 즉시, 타겟 불필요
{SkillTargetType::None, SkillCastType::Instant, 0.0f, false},
// E 스킬 - 즉시, 타겟 불필요
{SkillTargetType::None, SkillCastType::Instant, 10.0f, false},
// R 스킬 - 즉시, 단일 타겟 필요
{SkillTargetType::Single, SkillCastType::Instant, 20.0f, false}
};
// Bianca 스킬 설정
s_skillConfigs[L"Bianca"] = {
// Q 스킬 - 즉시, 지역 타겟
{SkillTargetType::None, SkillCastType::Instant, 10.0f, false},
// W 스킬 - 즉시, 타겟 불필요
{SkillTargetType::None, SkillCastType::Instant, 0.0f, false},
// E 스킬 - 채널링, 타겟 불필요
{SkillTargetType::None, SkillCastType::Channeling, 8.0f, false},
// R 스킬 - 즉시, 이동하면서 시전 가능
{SkillTargetType::None, SkillCastType::Instant, 15.0f, true}
};
}
이 시스템의 장점은 데이터 기반 설계로 각 스킬의 특성을 명확히 정의하고, 런타임에서 쉽게 조회할 수 있다는 것
2. 스킬 인터페이스와 쿨타임 시스템
2.1 스킬 기본 인터페이스
모든 스킬이 공통으로 구현해야 하는 ISkill 인터페이스를 정의
// ISkill.h
class ISkill
{
public:
virtual ~ISkill() = default;
// 스킬 실행 관련
virtual bool CanExecuteSkill() const = 0;
virtual void ExecuteSkill() = 0;
// 쿨다운 관련
virtual float GetCurrentCooldown() const = 0;
virtual float GetMaxCooldown() const = 0;
virtual bool IsOnCooldown() const = 0;
virtual void StartCooldown() = 0;
virtual void UpdateCooldown(float deltaTime) = 0;
// 스킬 정보
virtual int GetSkillIndex() const = 0;
virtual const wstring& GetSkillName() const = 0;
virtual void PlaySkill() = 0;
// 스킬 레벨 정보
virtual void SkillLevelUp() = 0;
virtual int GetCurSkillLevel() = 0;
protected:
int m_curSkillLevel = 0;
int m_maxSkillLevel = 5;
};
2.2 쿨타임 관리 시스템
각 스킬은 독립적인 쿨타임을 가지며, 매 프레임마다 업데이트 된다.
// 스킬 구현 예시 (NickyQSkill)
void UpdateCooldown(float deltaTime) override
{
if (m_currentCooldown > 0.0f)
{
m_currentCooldown -= deltaTime;
m_currentCooldown = max(0.0f, m_currentCooldown);
}
}
bool IsOnCooldown() const override
{
return m_currentCooldown > 0.0f;
}

스킬 사용 후 남은 쿨타임이 GamuHUDPanelUI에 표시됨.
동영상 서비스가 종료되어 해당 콘텐츠를 재생할 수 없습니다.
니키 스킬 시연 영상
동영상 서비스가 종료되어 해당 콘텐츠를 재생할 수 없습니다.
비앙카 스킬 시연 영상
3. 채널링 vs 즉시발동 스킬 처리
3.1 채널링 스킬 구현 (니키 Q 스킬, 비앙카 E 스킬)
채널링 스킬은 사용자 입력을 지속적으로 감지하고, 중도 취소가 가능
// PlayerStateMachine.cpp에서 채널링 스킬 처리
void PlayerStateMachine::HandleSkillInput()
{
// Q 스킬 (채널링)
if (INPUT->GetButton(KEY_TYPE::Q))
{
const auto& skillMeta = SkillConfig::GetSkillMetaData(m_characterIndex, 0);
if (skillMeta.castType == SkillCastType::Channeling)
{
// 채널링 시작 또는 지속
if (!IsInState(PlayerStateType::Skill_1))
{
RequestStateChange(PlayerStateType::Skill_1);
if (m_animationStateMachine)
m_animationStateMachine->RequestStateChange(AnimationStateType::Skill_1);
}
}
}
// Q 키 해제 시 채널링 종료
if (INPUT->GetButtonUp(KEY_TYPE::Q) && IsInState(PlayerStateType::Skill_1))
{
CheckQSkillCompletion();
}
}
3.2 즉시발동 스킬 구현
즉시발동 스킬은 한 번의 키 입력으로 바로 실행
// W, E, R 스킬 (즉시발동)
if (INPUT->GetButtonDown(KEY_TYPE::W))
{
const auto& skillMeta = SkillConfig::GetSkillMetaData(m_characterIndex, 1);
if (skillMeta.castType == SkillCastType::Instant)
{
RequestStateChange(PlayerStateType::Skill_2);
if (m_animationStateMachine)
m_animationStateMachine->RequestStateChange(AnimationStateType::Skill_2);
OnSkillUsed(1, nullptr); // 즉시 스킬 실행
}
}
3.3 동적 타겟 추적 시스템 ( 니키 R 스킬 )
리그 오브 레전드의 바이의 R 스킬과 유사
void NickyRSkill::UpdateTargetPosition() {
Vec3 currentTargetPos = m_target->GetTransform()->GetPosition();
float distanceMoved = Vec3::Distance(currentTargetPos, m_lastTargetPos);
if (distanceMoved < 0.1f) return; // 불필요한 계산 방지
// 부드러운 전환: 기존 목표점과 새 목표점을 보간
Vec3 newTargetPos = playerPos + direction * moveDistance;
m_targetPos = Utils::Lerp(m_targetPos, newTargetPos, 0.3f);
}
4. 스킬 관리 시스템
4.1 State Machine 기반 스킬 관리 시스템
PlayerStateMachine 과 AnimationStateMachine을 분리하여 로직과 비주얼을 독립적으로 관리
class PlayerStateMachine : public Component {
// 스킬 상태 관리
void RequestStateChange(PlayerStateType newState);
bool CanChangeState(PlayerStateType newState);
// 각 스킬별 완료 체크 Delegate
Delegate::Delegate<bool&> OnQSkillCompleted;
Delegate::Delegate<bool&> OnWSkillCompleted;
Delegate::Delegate<bool&> OnESkillCompleted;
Delegate::Delegate<bool&> OnRSkillCompleted;
};
특징
- 스킬 상태 전환의 명확한 규칙 정의
- 애니메이션과 게임 로직의 완전한 분리
- 각 상태별 CanTransitionTo() 메서드로 안전한 상태 전환 보장
4.2 Delegate패턴 기반 이벤트 시스템
스킬 완료 감지를 위해 Delegate 를 이용
// 스킬 완료 감지 예시
m_player->GetPlayerStateMachine()->OnQSkillCompleted.Push([this](bool& completed) {
completed = IsQSkillCompleted();
});
// NickyQState에서 완료 상태 확인
auto qState = dynamic_pointer_cast<NickyQState>(currentState);
if (qState) {
return qState->IsSkillComplete();
}
4.3 애니메이션 시퀀스 동적 관리
복잡한 스킬 애니메이션을 동적으로 구성할 수 있는 시스템 구현
// 차징 시간에 따른 동적 시퀀스 생성
void NickyAnimQState::ReleaseSkill() {
vector<wstring> rushSequence = { L"Skill_01_Rush", L"Skill_01_End" };
vector<float> rushDurations = {
min(m_chargeTime, 5.0f), // 차징 시간만큼 재생
(13.f / 25.f) / m_playSpeed
};
// 런타임에서 시퀀스 생성 및 재생
m_cachedAnimator->CreateSequence(L"Dynamic_Rush_Sequence",
rushSequence, rushDurations, false);
m_cachedAnimator->PlaySequence(L"Dynamic_Rush_Sequence");
}
특징
- 런타임 시퀀스 생성으로 유연한 스킬 표현
- 재생 속도 배수 적용 가능
- 애니메이션 완료 감지 및 자동 정리
5. 성능 최적화
5.1 중복 처리 방지 시스템
// 각 스킬별 완료 체크 플래그 관리
bool m_qSkillCompletionChecked = false;
void CheckQSkillCompletion() {
if (IsInState(PlayerStateType::Skill_1)) {
if (m_qSkillCompletionChecked) return; // 이미 체크함
bool qSkillCompleted = false;
OnQSkillCompleted(qSkillCompleted);
if (qSkillCompleted) {
m_qSkillCompletionChecked = true; // 플래그 설정
// 상태 전환 로직...
}
} else {
m_qSkillCompletionChecked = false; // 상태 벗어나면 리셋
}
}
5.2 스킬 피해 중복 방지
// 각 스킬별 완료 체크 플래그 관리
bool m_qSkillCompletionChecked = false;
void CheckQSkillCompletion() {
if (IsInState(PlayerStateType::Skill_1)) {
if (m_qSkillCompletionChecked) return; // 이미 체크함
bool qSkillCompleted = false;
OnQSkillCompleted(qSkillCompleted);
if (qSkillCompleted) {
m_qSkillCompletionChecked = true; // 플래그 설정
// 상태 전환 로직...
}
} else {
m_qSkillCompletionChecked = false; // 상태 벗어나면 리셋
}
}
결론
시스템의 장점과 특징
- 데이터 기반 설계
- 메타데이터 중심 : 스킬 특성을 데이터로 분리하여 밸런스 조정이 용이
- 캐릭터별 독립성 : 각 캐릭터마다 고유한 스킬 설정 가능
- 확장성
- UI 리소스 등록
- Delegate 바인딩
- 실시간 상태 관리
- 상태머신 연동 : 플레이어와 애니메이션 상태머신이 유기적으로 연결
- 프레임별 업데이트 : 쿨타임과 채널링 상태를 실시간으로 관리
- 성과
- 8개의 서로 다른 스킬 (니키 4개, 비앙카 4개) 안정적 구현
'DirectX11 > Eternal Return 모작' 카테고리의 다른 글
| [DirectX 11 Eternal Return 모작] 13. 전투 , 데미지 시스템 (0) | 2025.09.03 |
|---|---|
| [DirectX 11 Eternal Return 모작] 12. NavMesh (0) | 2025.09.03 |
| [DirectX 11 Eternal Return 모작] 10. 인벤토리와 장비 시스템 (0) | 2025.09.03 |
| [DirectX 11 Eternal Return 모작] 9. 아이템 시스템 & 제작 시스템 (0) | 2025.09.03 |
| [DirectX 11 Eternal Return 모작] 8. State Machine & Delegate (0) | 2025.09.03 |