스킬 시스템 2. 스킬 시스템의 구조와 특징

KWONYEONGMIN·2025년 4월 26일

Project DreamScape

목록 보기
3/5

github 링크

SkillBase / SkillSpec / SkillActorInfo 소스 코드
SkillControlComponent 소스 코드


스킬 시스템 특징


  • GAS 구조를 참고하면서도, 불필요한 무거운 시스템 제거
  • 스킬마다 복제 정책, 인스턴싱 정책, 네트워크 실행 정책 자유롭게 설정 가능
  • 서버/클라이언트 각각 독립적으로 스킬 실행, 취소, 종료 처리
  • SkillActorInfo를 통해 스킬 소유자/캐릭터/컨트롤러 자동 연결
  • 스킬 실행 도중 쿨타임 체크 및 리소스 소모 체크 가능
  • 다양화된 쿨다운 정책 설정 가능 (None, AfterActive, AfterEnd)
  • 스킬 별 Tick 기능 지원 (bCanTick 설정)
  • 스킬에 Duration 설정 시, 지속 시간 경과 후 자동으로 EndSkill 호출 (bHasSkillDuration 설정)
  • 스킬별로 "CancelIfActiveTags", "BlockIfActiveTags" 설정 가능 (Tag 기반 스킬 충돌 및 취소 제어 지원)
  • Delegate 연동으로 인풋 이벤트와 스킬 시전을 직결하여 빠른 반응성 구현



클래스/구조체 역할


클래스 및 구조체설명
UDSSkillBase모든 스킬의 최상위 클래스. 스킬 생명주기와 실행 흐름 정의
FDSSkillSpec스킬의 데이터(Handle, Tag, Type, Instancing 등)와 상태 보관
FDSSkillSpecContainerSkillSpec 배열을 관리하고 복제 최적화를 담당
UDSSkillControlComponent스킬 소유자(Actor)에 붙어 스킬 추가, 활성화, 취소, 종료를 관리
FDSSkillActorInfo스킬 실행 주체(Owner, Avatar, Controller 등)에 대한 정보 저장



클래스 관계도


  • SkillControlComponent가 SkillSpecContainer를 통해 여러 스킬 상태를 관리하고, 각각의 SkillSpec은 SkillBase 인스턴스와 실행 Actor 정보를 참조해 동작하는 구조다.

UDSSkillControlComponent → FDSSkillSpecContainer
스킬 스펙 배열(컨테이너)을 관리
FDSSkillSpecContainer → FDSSkillSpec
각 스킬의 데이터/상태를 관리
FDSSkillSpec → UDSSkillBase
실제 스킬 로직 인스턴스를 참조
FDSSkillSpec → FDSSkillActorInfo
스킬 실행 주체 정보(Owner, Avatar 등)를 참조
UDSSkillControlComponent → UDSSkillBase
FDSSkillSpec을 통해 UDSSkillBase 인스턴스를 간접 관리하여 스킬 실행 흐름을 제어


클래스별 주요 함수 및 변수


UDSSkillControlComponent 주요 함수

  • AddSkill : 새로운 스킬을 SkillSpecContainer에 등록하고 초기화한다.
  • TryActivateSkill : 조건을 검사하고 스킬을 활성화하려고 시도한다.
  • RemoteEndOrCancelSkill : 외부 요청에 따라 스킬을 강제 종료하거나 취소한다.
  • ReplicateEndOrCancelSkill : 스킬 종료/취소 이벤트를 네트워크로 동기화한다.
  • OnRemoveSkill : 스킬을 컨트롤러에서 제거하고 인스턴스를 정리한다.
  • MarkSkillSpecDirty : SkillSpec이 변경되었음을 엔진에 알리고 복제를 트리거한다.
  • AddReplicatedInstancedSkill : 복제 대상 스킬 인스턴스를 등록한다.
  • CreateNewInstanceOfSkill : Skill CDO를 기반으로 새로운 스킬 인스턴스를 생성한다.
  • ScheduleSkillEnd : 설정한 Duration 이후 스킬을 자동으로 종료(EndSkill)하도록 예약한다.
  • InternalTryActivateSkill : 스킬 활성화 조건을 검사하고, 조건이 맞으면 스킬을 실제로 실행 및 인스턴싱을 처리한다.
  • TickComponent : 현재 활성 상태인 스킬 인스턴스들을 순회하면서, 각 스킬의 InternalTick(DeltaTime)을 호출해 업데이트한다.

FDSSkillSpec 주요 함수

  • GetPrimaryInstance : 가장 우선순위 높은 스킬 인스턴스를 반환한다.
  • ShouldReplicateSkillSpec : 이 스킬 스펙이 네트워크 복제 대상인지 판단한다.
  • GetSkillInstances : 현재 가지고 있는 모든 스킬 인스턴스(복제/비복제)를 반환한다.
  • IsActive : 스킬이 현재 활성 상태인지 확인한다.
  • PreReplicatedRemove : 복제된 스킬이 제거될 때 처리(컨트롤 컴포넌트에 알림).
  • PostReplicatedAdd : 복제된 스킬이 추가될 때 처리(컨트롤 컴포넌트에 알림).
  • GetDebugString : 스킬에 대한 디버깅용 문자열을 반환한다.

FDSSkillSpecContainer 주요 함수

  • RegisterWithOwner : 스킬 스펙 컨테이너를 소유 컴포넌트와 연결하고 복제 시스템에 등록한다.
  • ShouldWriteFastArrayItem : 스킬 스펙 개별 항목의 복제 여부를 판단한다 (Dirty 여부 체크).
  • Items : 스킬 상태 전체를 담고 네트워크 복제를 최적화하는 핵심 데이터 배열.

UDSSkillBase 주요 함수

  • CanActivateSkill : 스킬이 발동 가능한지 조건(쿨다운, 리소스 등)을 검사한다.
  • PreActivate : 스킬 활성화 직전 상태 초기화 및 사전 처리를 수행한다.
  • ActivateSkill : 스킬의 실제 동작을 구현하는 함수(자식 클래스가 오버라이드).
  • CommitSkill : 쿨다운 시작, 리소스 소모 등을 확정(commit) 처리한다.
  • CancelSkill : 외부 요인에 의해 스킬을 강제 취소하고 정리한다.
  • EndSkill : 스킬이 스스로 종료될 때 호출되어 종료 처리를 수행한다.
  • ApplyCooldown : 스킬에 쿨다운을 적용하여 재사용 대기 시간을 설정한다.
  • OnSkillInitialized : 스킬이 SkillControlComponent에 추가되었을 때 초기화 처리를 한다.
  • Tick : SkillControlComponent가 매 프레임 수동 호출하여 스킬 상태를 갱신한다.



주요 구현 부분


UObject 기반 SkillBase에 Tick() 수동 갱신 시스템 추가

void UDSSkillBase::Tick(float DeltaTime)
{
	ElapsedTime += DeltaTime;

	if (ElapsedTime >= TickInterval)
	{
		InternalTick(DeltaTime);

		ElapsedTime = 0.0f;
	}
}
  • SkillBase를 상속받은 파생 클래스는 InternalTick에서 TickInterval마다 호출해야 하는 코드를 구현한다.
void UDSSkillControlComponent::TickComponent(float DeltaTime, ELevelTick TickType, FActorComponentTickFunction* ThisTickFunction)
{
	Super::TickComponent(DeltaTime, TickType, ThisTickFunction);

	for (const FDSSkillSpec& ActiveSpec : ActivatableSkills.Items)
	{
		UDSSkillBase* SkillInstance = ActiveSpec.GetPrimaryInstance();
		UDSSkillBase* SkillSource = ActiveSpec.Skill;

		if (false == IsValid(SkillSource))
		{
			continue;
		}

		if (SkillSource->bCanTick && ShouldTick(SkillSource))
		{
			if (IsValid(SkillInstance))
			{
				if(SkillInstance->IsActive())
				{
					SkillInstance->Tick(DeltaTime);
				}
			}
			else if (SkillSource->IsActive())
			{
				SkillSource->Tick(DeltaTime);
			}
		}
	}
}
  • SkillControlComponent에서는 실행 중이고, bCanTick이 true이고, 네트워크 정책에 따라 Tick이 실행될 수 있는 스킬 인스턴스가 Tick 함수를 호출한다.

Duration 기반 스킬 자동 종료 (ScheduleSkillEnd) 로직 추가

  • SkillControlComponent의 InternalTryActivateSkill 함수에서, bSkillHasDuration 플래그가 true면 ScheduleSkillEnd 함수를 호출한다.
    -ScheduleSkillEnd 함수는 SkillDuration 이후에 EndSkill 호출한다.
	if(Skill->bSkillHasDuration)
	{
		ScheduleSkillEnd(Handle, Skill->SkillDuration, true);
	}

Skill 활성화 조건에 따른 CancelIfActiveTags, BlockIfActiveTags 지원

  • SkillBase클래스의 PreActivate 함수에서 처리
  • 현재 활성화된 스킬들의 Tag를 조회하여,
    새로운 스킬이 가진 CancelIfActiveTags에 해당하는 Tag가 활성화돼 있다면,
    해당 스킬을 찾아서 강제로 취소한다.
for (const FGameplayTag& ActiveTag : ActiveTags)
{
    if (Spec->CancelIfActiveTags.HasTag(ActiveTag))
    {
        FDSSkillSpec* CancelSpec = SkillControlComponent->FindSkillSpecFromTag(ActiveTag);
        if (nullptr != CancelSpec)
        {
            SkillControlComponent->CancelSkillHandle(CancelSpec->Handle);
        }
    }
}
  • SkillBase의 CanActivateSkill 함수에서 현재 활성화된 스킬들의 Tag를 조회하여, 새로운 스킬이 가진 BlockIfActiveTags에 해당하는 Tag가 활성화돼 있다면,
    이 스킬 자체의 활성화를 막는다.
	// 함께 실행되면 안되는 스킬이 실행중이라면 실행 취소
	FGameplayTagContainer ActiveTags = SkillControlComponent->GetActiveSkillTags();

	for (const FGameplayTag& ActiveTag : ActiveTags)
	{
		if (Spec->BlockIfActiveTags.HasTag(ActiveTag))
		{
			return false;
		}
	}
profile
Hello World

0개의 댓글