Skill Data

UENUM(BlueprintType)
enum class ESkillState : uint8
{
  None = 0,
  Added,      // ์Šคํ‚ฌ์ด ์ถ”๊ฐ€๋œ ์ƒํƒœ. ๋ฏธ์žฅ์ฐฉ
  Equipped,		// ์Šคํ‚ฌ์ด ์žฅ์ฐฉ๋œ ์ƒํƒœ. ๋น„ํ™œ์„ฑ ์ƒํƒœ. ์˜ˆ: A ๋ฌด๊ธฐ ์Šคํ‚ฌ์ด๋ฉด์„œ ์Šคํ‚ฌํŠธ๋ฆฌ์—์„œ ์žฅ์ฐฉ. ๊ทธ๋Ÿฌ๋‚˜ ํ˜„์žฌ ๋ฌด๊ธฐ๋Š” B
  Activated,	// ์Šคํ‚ฌ์ด ํ™œ์„ฑํ™” ๋œ ์ƒํƒœ. 
};

USTRUCT(BlueprintType)
struct FSkillData
{
  GENERATED_BODY()

  // ์ง„์งœ Skill ์ •๋ณด ์ ‘๊ทผ GUID
  UPROPERTY()
  FGuid SkillJournalID;
  
  UPROPERTY()
  TWeakObjectPtr<class USkillJournal> SkillJournal;
  
  UPROPERTY()
  int32 CurrentSkillLevel = 0;

  UPROPERTY()
  int32 MaxSkillLevel = 0;

  UPROPERTY()
  ESkillState SkillState = ESkillState::None;
};
  • ์œ„์˜ Skill Data๋Š” ์‹ค์ œ ์Šคํ‚ฌ ์ •๋ณด๋ณด๋‹ค๋Š”, ๊ทธ ์ •๋ณด์— ์ ‘๊ทผํ•  ์ˆ˜ ์žˆ๋Š” ํฌ์ธํ„ฐ๋ฅผ ๊ฐ€์ง„๋‹ค
    • ์‹ค์ œ Skill ์ •๋ณด๋Š” ๋ณ„๋„์˜ ๋ฐ์ดํ„ฐ ํ˜•์‹์œผ๋กœ ๊ด€๋ฆฌํ–ˆ๋‹ค (DataTable, ๋˜๋Š” Journal)
    • ๊ทธ๋ฆฌ๊ณ  ์ด ์‹ค์ œ Skill ์ •๋ณด๋Š” ์ถœ๋ ฅ์‹œํ‚ฌ Abl๊ณผ ์กฐ๊ฑด, ์ด๋ฆ„, Description ์ •๋ณด ๋“ฑ์„ ๊ฐ€์ง„๋‹ค
  • ๋˜ํ•œ ํ•ด๋‹น Skill์˜ ์ƒํƒœ๋ฅผ enum์œผ๋กœ ๊ตฌ๋ถ„ํ–ˆ๋‹ค

Note

์ด๊ฒŒ ์กฐ๊ธˆ ์ง๊ด€์ ์ด์ง€ ์•Š๋‹ค๋Š” ์ƒ๊ฐ์ด ์•„์ง๋„ ๋“ ๋‹ค
๋ฒ„๊ทธ ์ƒํ™ฉ์ด๊ธด ํ•˜์ง€๋งŒ A ๋ฌด๊ธฐ ์ฐฉ์šฉ ์‹œ B ๋ฌด๊ธฐ ์ „์šฉ ์Šคํ‚ฌ์ด ์ถœ๋ ฅ๋˜๋Š” ํ˜„์ƒ์ด ์žˆ์—ˆ๋‹ค
์ถœ๋ ฅ ์กฐ๊ฑด์—์„œ ํ˜„์žฌ ๋ฌด๊ธฐ ํƒ€์ž…์„ ์ฒดํฌํ•˜๊ณ  ์ถœ๋ ฅํ•˜๋Š” ๊ฒŒ ๋” ๋‚ซ์ง€ ์•Š์•˜์„๊นŒ?
๊ทธ๋Ÿผ State ํ•˜๋‚˜๊ฐ€ ์ค„์–ด๋“ค๊ณ  ํƒ€์ž…์„ boolean์œผ๋กœ ๋ณ€๊ฒฝํ•  ์ˆ˜ ์žˆ์„ ๊ฒƒ ๊ฐ™์€๋ฐโ€ฆ

Skill Journal

UCLASS(meta=(DisplayName="Skill"))
class AACLIENT_API USkillJournal : public UJournalContainerEntry
{
  GENERATED_BODY()
  
public:

  //...

  UPROPERTY(EditDefaultsOnly, BlueprintReadOnly)
  int32 ID;

  // ํ™œ์„ฑํ™”๋˜์–ด ์žˆ์–ด์•ผ ํ•  Skill Node ID, ์—†์œผ๋ฉด ์„ธํŒ… ์•ˆ ํ•˜๋ฉด ๋จ...
  UPROPERTY(EditDefaultsOnly, BlueprintReadOnly)
  TArray<int32> ActivatePrerequisiteIDs;
  
  UPROPERTY(EditAnywhere)
  FText DisplayName;

  UPROPERTY(EditDefaultsOnly, meta=(categories="Skill"))
  FGameplayTagContainer SkillTraits;
  
  // ...
  
  // ์Šคํ‚ฌ ์‹คํ–‰ ์‹œ ์‹คํ–‰ํ•  Able
  // Active Skill
  UPROPERTY(EditAnywhere)
  TSubclassOf<class UAblAbility> SkillAbl;
  
  // ์Šคํ‚ฌ ์žฅ์ฐฉ ์‹œ ์‹คํ–‰ํ•  GA
  // Passive Skill
  UPROPERTY(EditAnywhere)
  TArray<TSubclassOf<class UGameplayAbility>> SkillGA;
  
};
  • ์œ„์˜ Skill Data๋Š” Skill Journal(DT์˜ ์ผ์ข…) ํƒ€์ž…์œผ๋กœ์„œ, ์‹ค์ œ Skill ์ •๋ณด๋“ค์ด ์ •์˜๋˜์–ด ์žˆ๋‹ค
  • ์‹คํ–‰ํ•  Abl, ์„ ํ–‰ ์Šคํ‚ฌ ID ๋ชฉ๋ก, Icon ์ด๋ฏธ์ง€, Trait, Granted GA ๋“ฑ์ด ์—ฌ๊ธฐ์— ํฌํ•จ๋œ๋‹ค
    • ํŠนํžˆ Skill Trait์€, ํƒœ๊ทธ๋กœ์„œ Skill์˜ Passive/Active, Slot1/Slot2 ๋“ฑ ์Šคํ‚ฌ ๊ณ ์œ ์˜ ์†์„ฑ์„

Skill Component

  • ํ”Œ๋ ˆ์ด์–ด์˜ ์Šคํ‚ฌ์„ ๊ด€๋ฆฌํ•˜๋Š” ์ปดํฌ๋„ŒํŠธ
    • PlayerState์˜ Component
  • ์Šคํ‚ฌ ์ถ”๊ฐ€, ์ œ๊ฑฐ, ์ƒํƒœ ๋ณ€๊ฒฝ ๋“ฑ ๊ธฐ๋Šฅ
    • ๋ฌด๊ธฐ ๋“ฑ ์žฅ๋น„ ๋ณ€๊ฒฝ๊ณผ UI๋ฅผ ํ†ตํ•œ ์Šคํ‚ฌ ๋ณ€๊ฒฝ ๋“ฑ ๋ฉ”์‹œ์ง€๋ฅผ ๋ฐ›๋Š”๋‹ค

Note

์ฒ˜์Œ Skill ๊ฐ์ฒด๋ฅผ Item์œผ๋กœ ๋งŒ๋“ค์–ด Inventory์—์„œ ๊ด€๋ฆฌํ•˜๋„๋ก ํ–ˆ๋Š”๋ฐ, ์ด๋Š” ์ž˜๋ชป๋œ ์„ ํƒ์ด์—ˆ๋˜ ๋“ฏโ€ฆ
๋‹จ์ˆœํžˆ Skill์˜ ์Šต๋“ ์—ฌ๋ถ€ ์ด์™ธ์—๋„ ์ƒํƒœ ๊ด€๋ฆฌ๋ฅผ ๋ณ„๋„๋กœ ํ•ด์ค˜์•ผ ํ•œ๋‹ค๋Š” ์ ์—์„œ ๊ตณ์ด ์•„์ดํ…œ๊ณผ ๋™์ผํ•˜๊ฒŒ ์ฒ˜๋ฆฌํ•  ํ•„์š”๋Š” X
๊ดœํžˆ Item Category๋งŒ ๋Š˜์–ด๋‚˜๊ณ  ๊ด€๋ฆฌ๋งŒ ๋” ํž˜๋“ค์–ด์กŒ๋‹คโ€ฆ

Skill ์‹คํ–‰

bool UComboNode::CanSelect() const
{
  UComboNodeData* NodeData = CastChecked<UComboNodeData>(ComboNodeData);
  UAblComponent* AblComponent = FL::GetActorComponent<UAblComponent>(OwnerActor);
  if (IsValid(NodeData) && IsValid(AblComponent))
  {
    // ...
    
    // NodeData๊ฐ€ ๊ฐ€์ง„ ์Šคํ‚ฌ ์ •๋ณด์— ์ ‘๊ทผํ•ด State ํ™•์ธ
    if (APlayerState* PlayerState = FL::GetPlayerState(OwnerActor))
    {
      if (USkillComponent* SkillComponent = PlayerState->FindComponentByClass<USkillComponent>())
      {
        return SkillComponent->IsSkillActivated(NodeData->Journal.Guid);
      }
    }

    // ...
  }

  return false;
}
  • ์‹ค์ œ๋กœ ์Šคํ‚ฌ์„ ์ถœ๋ ฅํ•˜๋Š” ๋ถ€๋ถ„์€ ComboComponent
    • ComboComponent์—์„œ ComboAseet์„ ํ†ตํ•ด CurrentNode ์‹คํ–‰
    • CurrentNode๋Š” ์ •์˜๋œ ๋ฐ์ดํ„ฐ์— Skill ์ •๋ณด๊ฐ€ ์žˆ๋Š”์ง€ ํ™•์ธ
bool UComboNode::Select()
{
  if (UAblAbility* AblCDO = GetAblAbility())
  {
    if (UAblComponent* AblComponent = FL::GetActorComponent<UAblComponent>(OwnerActor))
    {
      UAblAbilityContext* AblAbilityContext = UAblAbilityContext::MakeContext(AblCDO, AblComponent, OwnerActor, OwnerActor->GetInstigator());
      check(AblAbilityContext);
      
      if (UComboNodeData* NodeData = CastChecked<UComboNodeData>(ComboNodeData))
      {
        AblAbilityContext->SetCopyFromPrevAblContext(NodeData->bCopyTargetsFromPrevNode);	
      }

      AblCDO->SetupTargeting(AblAbilityContext);

      EAblAbilityStartResult Result;
      if(ComboAsset->IsComboPlaying())
      {
        Result = AblComponent->BranchAbility(AblAbilityContext);
      }
      else
      {
        Result = AblComponent->ActivateAbility(AblAbilityContext);
      }

      if(Result == EAblAbilityStartResult::Success)
      {
        Super::Select();
        return true;
      }
    }
  }

  return false;
}
  • ์‹คํ–‰ ๋กœ์ง์€ ์–ด์ฐจํ”ผ ComboNode๊ฐ€ ๊ฐ€์ง€๊ณ  ์žˆ๋Š” Abl ๋ฐ์ดํ„ฐ๋ฅผ AblComponent๋ฅผ ํ†ตํ•ด ์‹คํ–‰์‹œํ‚ค๋Š” ๋กœ์ง
    • Skill์„ ์‹คํ–‰ํ•˜๋“  ์ผ๋ฐ˜ ๊ณต๊ฒฉ์„ ์‹คํ–‰ํ•˜๋“  ๋™์ผํ•˜๋‹ค