스킬 시스템은 온라인 액션 게임 환경에서 다음 세 가지를 충족하도록 설계되었습니다.
- 클라이언트 입력 즉시 로컬 피드백이 발생해야 함
- 최종적인 판정 권한은 서버가 보유하여 부정행위 방지
- 코드 수정 없이 데이터 조합만으로 신규 스킬 추가 가능
공통 로직은 Source/common에 두어 서버와 클라이언트가 동일한 규칙을 공유하게 하였으며, 입력 수집과 네트워크 전송 등 실행 환경에 따른 차이만 각 플랫폼에서 담당하도록 구성했습니다.
스킬의 상태 전이, 쿨다운 계산, 조건 검사, 버프 적용 및 투사체 생성 등 모든 핵심 로직은 공통 SkillManager가 담당합니다.
서버와 클라이언트가 서로 다른 코드로 스킬을 재구현할 경우 발생하는 동작의 불일치를 근본적으로 차단하고, 유지보수 효율을 극대화했습니다.
스킬을 개별 클래스로 정의하는 대신, Skill 오브젝트라는 데이터 컨테이너 내부에 다양한 요소를 조합하는 방식을 채택했습니다.
스킬이 가지는 정보:
- 시간 정보: Startup, Active, Recovery, Cooldown
- 실행 조건: 공중 사용 가능 여부, 이동 제한, Condition객체
- 부가 구성: 적용될 버프, 생성될 투사체 및 연출 이펙트
이러한 구조는 프로그래머의 개입 없이도 기획자가 데이터 조합만으로 복잡한 스킬을 구성할 수 있게 하며, 신규 기능을 추가할 때 시스템 전체를 수정할 필요 없이 새로운 데이터 타입을 정의하는 것만으로 확장이 가능합니다.
스킬 실행의 라이프사이클을 관리합니다. 등록된 스킬의 상태를 보관하고, 매 프레임 쿨다운 감소 및 상태 머신(Wait → Start → Active → Recovery)을 갱신하며 정의된 시점에 맞춰 버프나 투사체를 생성합니다.
- Wait : 대기 상태
- Start : 선딜레이 - 보통 여기서 이펙트를 생성합니다.
- Active : 판정 상태 - 보통 여기서 투사체를 만들고 판정합니다.
- Recovery : 후딜레이
Skill은 상태가 없고 데이터만 있는 스킬 데이터로, 시간 정보와 제약 조건, 버프, 투사체, 연출 이펙트를 묶습니다.
Buff는 상태가 없고 데이터와 OnApply()처리 로직만이 존재합니다.
이동속도 변화, 슈퍼아머, 상태이상 등 스킬의 효과 단위를 정의합니다. SkillManager는 특정 페이즈에 버프를 호출할 뿐, 실제 로직은 버프 객체가 수행하므로 확장이 용이합니다.
예를들어 더블 점프를 구현한다면, 플레이어의 속도를 변화 시키는 버프를 거는 방식으로 구현할 수 있습니다.
'착지 후 사용', '특정 스킬 연계', '점프 했는지' 등 복잡한 사용 조건을 별도 객체로 분리하여 CanUse() 내부에 복잡한 분기문이 쌓이는 것을 방지했습니다.
클라이언트 전용 시각 효과(Effect)와 서버/클라이언트 공통 판정 객체(Projectile)를 분리했습니다.
이는 온라인 환경에서 로컬 반응성을 챙기면서도 판정의 무결성을 유지하기 위함입니다.
Note
Projectile과 Effect도 상태 없이 데이터만 존재합니다. 월드상에 생성될 시 데이터를 토대로 ProjectileInstance, EffectInstance객체를 생성합니다.
로컬 플레이어의 입력 시 패킷을 보내며 스킬을 즉시 시작하고 이펙트를 재생하여 체감 지연 시간을 제거합니다. 단, 실제 몹 타격등의 판정은 서버에서 수행합니다.
원격 플레이어의 경우 서버로부터 전달받은 상태를 반영하는 SyncRemoteState()를 통해 일관된 동작을 보여줍니다.
클라이언트로부터 SkillUsingPacket을 수신하면 즉시 실행하지 않고, 해당 패킷의 유효성을 검증한 뒤 pendingSkills 큐에 예약합니다.
이후 언제 실행될지 계산해둔 서버의 고정 틱에 맞춰 실행함으로써, 네트워크 지연 환경에서도 패킷 순서를 보정하고 공정한 판정을 보장합니다.
