<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0">
  <channel>
    <title>개발자 동그래</title>
    <link>https://dong-grae.tistory.com/</link>
    <description></description>
    <language>ko</language>
    <pubDate>Thu, 16 Apr 2026 16:24:29 +0900</pubDate>
    <generator>TISTORY</generator>
    <ttl>100</ttl>
    <managingEditor>동그래님</managingEditor>
    <image>
      <title>개발자 동그래</title>
      <url>https://tistory1.daumcdn.net/tistory/6757861/attach/a859b903ac974a76ba27af49c6509dab</url>
      <link>https://dong-grae.tistory.com</link>
    </image>
    <item>
      <title>GAS_16_Death Ability 구현 및 캐릭터 사후처리</title>
      <link>https://dong-grae.tistory.com/238</link>
      <description>&lt;h2 style=&quot;color: #e8e6e3; text-align: start;&quot; data-ke-size=&quot;size26&quot; data-darkreader-inline-color=&quot;&quot;&gt;&lt;b&gt;  Death Ability 구현&lt;/b&gt;&lt;/h2&gt;
&lt;h3 style=&quot;color: #e8e6e3; text-align: start;&quot; data-ke-size=&quot;size23&quot; data-darkreader-inline-color=&quot;&quot;&gt;&lt;b&gt;✅ Gameplay Tag 추가&lt;/b&gt;&lt;/h3&gt;
&lt;h4 style=&quot;color: #e8e6e3; text-align: start;&quot; data-ke-size=&quot;size20&quot; data-darkreader-inline-color=&quot;&quot;&gt;&lt;b&gt;✔️ RPGGameplayTags&lt;/b&gt;&lt;/h4&gt;
&lt;div data-ke-type=&quot;moreLess&quot; data-text-more=&quot;더보기&quot; data-text-less=&quot;닫기&quot;&gt;&lt;a class=&quot;btn-toggle-moreless&quot;&gt;더보기&lt;/a&gt;
&lt;div class=&quot;moreless-content&quot;&gt;
&lt;p style=&quot;color: #e8e6e3; text-align: start;&quot; data-ke-size=&quot;size18&quot; data-darkreader-inline-color=&quot;&quot;&gt;&lt;b&gt; &lt;span&gt; RPGGameplayTags.h&lt;/span&gt;&lt;/b&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1746880096638&quot; class=&quot;cpp&quot; data-ke-language=&quot;cpp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;#pragma once

#include &quot;NativeGameplayTags.h&quot;

namespace RPGGameplayTags
{
	/** 생략 */

	/** Shared Tags */
	RPG_API UE_DECLARE_GAMEPLAY_TAG_EXTERN(Shared_Ability_HitReact);
	RPG_API UE_DECLARE_GAMEPLAY_TAG_EXTERN(Shared_Ability_Death); // Death Ability Tag 추가

	RPG_API UE_DECLARE_GAMEPLAY_TAG_EXTERN(Shared_Event_MeleeHit);
	RPG_API UE_DECLARE_GAMEPLAY_TAG_EXTERN(Shared_Event_HitReact);

	RPG_API UE_DECLARE_GAMEPLAY_TAG_EXTERN(Shared_SetByCaller_BaseDamage);

	RPG_API UE_DECLARE_GAMEPLAY_TAG_EXTERN(Shared_Status_Dead); // Dead Status Tag 추가

}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;b&gt; &lt;span&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;RPGGameplayTags.cpp&lt;/span&gt;&lt;/b&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1746880147983&quot; class=&quot;cpp&quot; data-ke-language=&quot;cpp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;#include &quot;RPGGameplayTags.h&quot;

namespace RPGGameplayTags
{
	/** 생략 */
	
	/** Shared Tags */
	UE_DEFINE_GAMEPLAY_TAG(Shared_Ability_HitReact, &quot;Shared.Ability.HitReact&quot;);
	UE_DEFINE_GAMEPLAY_TAG(Shared_Ability_Death, &quot;Shared.Ability.Death&quot;); // Death Ability Tag 추가

	UE_DEFINE_GAMEPLAY_TAG(Shared_Event_MeleeHit, &quot;Shared.Event.MeleeHit&quot;);
	UE_DEFINE_GAMEPLAY_TAG(Shared_Event_HitReact, &quot;Shared.Event.HitReact&quot;);

	UE_DEFINE_GAMEPLAY_TAG(Shared_SetByCaller_BaseDamage, &quot;Shared.SetByCaller.BaseDamage&quot;);

	UE_DEFINE_GAMEPLAY_TAG(Shared_Status_Dead, &quot;Shared.Status.Dead&quot;); // Dead Status Tag 추가
}&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;사망 관련 Gameplay Tag 추가&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 style=&quot;color: #e8e6e3; text-align: start;&quot; data-darkreader-inline-color=&quot;&quot; data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;✅ AttributeSet에서&amp;nbsp; Dead.Status Tag 부착&lt;/b&gt;&lt;/h3&gt;
&lt;h4 style=&quot;color: #e8e6e3; text-align: start;&quot; data-darkreader-inline-color=&quot;&quot; data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;✔️&lt;span&gt; RPGAttributeSet.cpp&lt;/span&gt;&lt;/b&gt;&lt;/h4&gt;
&lt;div data-ke-type=&quot;moreLess&quot; data-text-more=&quot;더보기&quot; data-text-less=&quot;닫기&quot;&gt;&lt;a class=&quot;btn-toggle-moreless&quot;&gt;더보기&lt;/a&gt;
&lt;div class=&quot;moreless-content&quot;&gt;
&lt;pre id=&quot;code_1746880517215&quot; class=&quot;cpp&quot; data-ke-language=&quot;cpp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;#include &quot;AbilitySystem/RPGAttributeSet.h&quot;
#include &quot;GameplayEffectExtension.h&quot;
#include &quot;RPGFunctionLibrary.h&quot;
#include &quot;RPGGameplayTags.h&quot;

#include &quot;WarriorDebugHelper.h&quot;

URPGAttributeSet::URPGAttributeSet()
{
	InitCurrentHealth(1.0f);
	InitMaxHealth(1.0f);
	InitCurrentRage(1.0f);
	InitMaxRage(1.0f);
	InitAttackPower(1.0f);
	InitDefensePower(1.0f);
}

void URPGAttributeSet::PostGameplayEffectExecute(const FGameplayEffectModCallbackData&amp;amp; Data)
{
	// GE 실행 결과로 Modifier가 적용된 Attribute가 CurrentHealth라면 해당 로직 실행
	if (Data.EvaluatedData.Attribute == GetCurrentHealthAttribute())
	{
		const float NewCurrentHealth = FMath::Clamp(GetCurrentHealth(), 0.0f, GetMaxHealth());

		SetCurrentHealth(NewCurrentHealth);
	}

	if (Data.EvaluatedData.Attribute == GetCurrentRageAttribute())
	{
		const float NewCurrentRage = FMath::Clamp(GetCurrentRage(), 0.0f, GetMaxRage());

		SetCurrentRage(NewCurrentRage);
	}

	if (Data.EvaluatedData.Attribute == GetDamageTakenAttribute())
	{
		const float OldHealth = GetCurrentHealth();
		const float DamageDone = GetDamageTaken();

		const float NewCurrentHealth = FMath::Clamp(OldHealth - DamageDone, 0.0f, GetMaxHealth());

		SetCurrentHealth(NewCurrentHealth);

		const FString DebugString = FString::Printf(
			TEXT(&quot;Old Health %f, Damage Done: %f, NewCurrentHealth: %f&quot;),
			OldHealth,
			DamageDone,
			NewCurrentHealth
		);

		Debug::Print(DebugString, FColor::Green);

		// TODO::사망 처리
		if (NewCurrentHealth == 0.0f)
		{
			URPGFunctionLibrary::AddGameplayTagToActorIfNone(Data.Target.GetAvatarActor(), RPGGameplayTags::Shared_Status_Dead);
		}
	}
}&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;이전에 체력 및 적용할 데미지 Attribute 값에 Modifier가 적용되면 호출 되는 함수에서 현재 체력이 0인지 확인하고 만약 0이 된다면 사망처리 시키는 Tag를 붙혀주기로 했었다.&lt;br /&gt;앞서 만든 &quot;Shared.Status.Dead&quot;를 체력이 0이 된 TargetActor에 붙혀주도록 한다.&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 style=&quot;color: #e8e6e3; text-align: start;&quot; data-ke-size=&quot;size23&quot; data-darkreader-inline-color=&quot;&quot;&gt;&lt;b&gt;✅ Enemy 사망 처리할 Ability 생성&lt;/b&gt;&lt;/h3&gt;
&lt;p&gt;&lt;figure class=&quot;imagegridblock&quot;&gt;
  &lt;div class=&quot;image-container&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bs8WZU/btsNS56msI8/sQpi2Gag1hTRU0MKExMatK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bs8WZU/btsNS56msI8/sQpi2Gag1hTRU0MKExMatK/img.png&quot; data-origin-width=&quot;502&quot; data-origin-height=&quot;919&quot; data-is-animation=&quot;false&quot; style=&quot;width: 17.9105%; margin-right: 10px;&quot; data-widthpercent=&quot;18.12&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bs8WZU/btsNS56msI8/sQpi2Gag1hTRU0MKExMatK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fbs8WZU%2FbtsNS56msI8%2FsQpi2Gag1hTRU0MKExMatK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;502&quot; height=&quot;919&quot;/&gt;&lt;/span&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/xMOVW/btsNRkqHJmt/PkpegEanccWJxcwMa4eFa0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/xMOVW/btsNRkqHJmt/PkpegEanccWJxcwMa4eFa0/img.png&quot; data-origin-width=&quot;1550&quot; data-origin-height=&quot;628&quot; data-is-animation=&quot;false&quot; style=&quot;width: 80.9267%;&quot; data-widthpercent=&quot;81.88&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/xMOVW/btsNRkqHJmt/PkpegEanccWJxcwMa4eFa0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FxMOVW%2FbtsNRkqHJmt%2FPkpegEanccWJxcwMa4eFa0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1550&quot; height=&quot;628&quot;/&gt;&lt;/span&gt;&lt;/div&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;&quot;RPGEnemyGameplayAbility&quot;를 상속 받는 &quot;GA_Enemy_Death_Base&quot; 클래스를 생성한다.&lt;br /&gt;Montage 배열에서 랜덤한 사망 관련 AnimMontage를 재생하고 GameplayCue로 사망 사운드를 재생한다.&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imagegridblock&quot;&gt;
  &lt;div class=&quot;image-container&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bH2WlT/btsNSla3QqA/RIlQn0SW2EHo0OEqhBe3iK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bH2WlT/btsNSla3QqA/RIlQn0SW2EHo0OEqhBe3iK/img.png&quot; data-origin-width=&quot;2556&quot; data-origin-height=&quot;1326&quot; data-is-animation=&quot;false&quot; style=&quot;width: 48.0644%; margin-right: 10px;&quot; data-widthpercent=&quot;48.63&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bH2WlT/btsNSla3QqA/RIlQn0SW2EHo0OEqhBe3iK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbH2WlT%2FbtsNSla3QqA%2FRIlQn0SW2EHo0OEqhBe3iK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;2556&quot; height=&quot;1326&quot;/&gt;&lt;/span&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/dDT8pf/btsNRjZC0ih/EmSuj0qzYPquNSTxfzglLK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/dDT8pf/btsNRjZC0ih/EmSuj0qzYPquNSTxfzglLK/img.png&quot; data-origin-width=&quot;1293&quot; data-origin-height=&quot;635&quot; data-is-animation=&quot;false&quot; style=&quot;width: 50.7728%;&quot; data-widthpercent=&quot;51.37&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/dDT8pf/btsNRjZC0ih/EmSuj0qzYPquNSTxfzglLK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FdDT8pf%2FbtsNRjZC0ih%2FEmSuj0qzYPquNSTxfzglLK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1293&quot; height=&quot;635&quot;/&gt;&lt;/span&gt;&lt;/div&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;자식 클래스를 생성하고 필요한 데이터를 할당한다.&lt;br /&gt;1. 사망 관련 Anim Montage 바인딩&lt;br /&gt;2. 사망 사운드 Gameplay Cue 바인딩&lt;br /&gt;3. 해당 Ability 활성화 시, 다른 모든 Ability 및 HitReact 취소 및 block 처리&lt;br /&gt;4. Instancing Policy는 어차피 딱 한 번 사망할 것이기 때문에 기본 값&lt;br /&gt;5. Trigger Tag는 &quot;Shared.Status.Dead&quot; 로 설정(AttributeSet C++에서 Target Actor의 체력이 0이면 해당 태그 부여)&lt;br /&gt;6. Trigger Source는 ASC가 해당 Trigger Tag를 획득하면 Ability가 트리거 되는 설정인 &quot;Owned Tag Added&quot;로 설정&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 style=&quot;color: #e8e6e3; text-align: start;&quot; data-darkreader-inline-color=&quot;&quot; data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;✅ Enemy StartUpData에 GA 할당&lt;/b&gt;&lt;/h3&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1440&quot; data-origin-height=&quot;562&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bsfILJ/btsNReDATuT/KqjqaXMWpoviCiOpYjP9jk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bsfILJ/btsNReDATuT/KqjqaXMWpoviCiOpYjP9jk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bsfILJ/btsNReDATuT/KqjqaXMWpoviCiOpYjP9jk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbsfILJ%2FbtsNReDATuT%2FKqjqaXMWpoviCiOpYjP9jk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1440&quot; height=&quot;562&quot; data-origin-width=&quot;1440&quot; data-origin-height=&quot;562&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;Reactive Abilities에 &quot;GA_Guardian_Death&quot; 할당&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 style=&quot;color: #e8e6e3; text-align: start;&quot; data-ke-size=&quot;size23&quot; data-darkreader-inline-color=&quot;&quot;&gt;&lt;b&gt;✅ BP Interface 클래스 생성&lt;/b&gt;&lt;/h3&gt;
&lt;p&gt;&lt;figure class=&quot;imagegridblock&quot;&gt;
  &lt;div class=&quot;image-container&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/qqoJ1/btsNScyf5pU/a2lFL8Zm6TK5IgcPJo6ZEK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/qqoJ1/btsNScyf5pU/a2lFL8Zm6TK5IgcPJo6ZEK/img.png&quot; data-origin-width=&quot;648&quot; data-origin-height=&quot;1097&quot; data-is-animation=&quot;false&quot; style=&quot;width: 21.5764%; margin-right: 10px;&quot; data-widthpercent=&quot;21.83&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/qqoJ1/btsNScyf5pU/a2lFL8Zm6TK5IgcPJo6ZEK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FqqoJ1%2FbtsNScyf5pU%2Fa2lFL8Zm6TK5IgcPJo6ZEK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;648&quot; height=&quot;1097&quot;/&gt;&lt;/span&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bH2uyO/btsNTyAluGD/HzXtxhegX3rHdWkTd897VK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bH2uyO/btsNTyAluGD/HzXtxhegX3rHdWkTd897VK/img.png&quot; data-origin-width=&quot;808&quot; data-origin-height=&quot;382&quot; data-is-animation=&quot;false&quot; style=&quot;width: 77.2608%;&quot; data-widthpercent=&quot;78.17&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bH2uyO/btsNTyAluGD/HzXtxhegX3rHdWkTd897VK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbH2uyO%2FbtsNTyAluGD%2FHzXtxhegX3rHdWkTd897VK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;808&quot; height=&quot;382&quot;/&gt;&lt;/span&gt;&lt;/div&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;Death Ability 이후 Enemy 캐릭터의 Mesh를 Pause 상태로 만들고 Collision을 비활성화 하기 위해, BP Interface 클래스를 만들고 BP_EnemyBase 클래스가 이를 구현하도록 한다.&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 style=&quot;color: #e8e6e3; text-align: start;&quot; data-darkreader-inline-color=&quot;&quot; data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;✅ BP Interface 함수 구현&lt;/b&gt;&lt;/h3&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;2536&quot; data-origin-height=&quot;1158&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cSL8Y4/btsNRJDko9s/GMSLHvD2DwJYDCLfnpQVk1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cSL8Y4/btsNRJDko9s/GMSLHvD2DwJYDCLfnpQVk1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cSL8Y4/btsNRJDko9s/GMSLHvD2DwJYDCLfnpQVk1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcSL8Y4%2FbtsNRJDko9s%2FGMSLHvD2DwJYDCLfnpQVk1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;2536&quot; height=&quot;1158&quot; data-origin-width=&quot;2536&quot; data-origin-height=&quot;1158&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;&quot;BP_EnemyCharacter_Base&quot; 클래스에서 &quot;BPI_EnemyDeath&quot;를 상속 받고 해당 함수를 구현한다.&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1968&quot; data-origin-height=&quot;1130&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bDK0S3/btsNRQvNlzv/tv5bTPZLOeh6Hpmi8eLP01/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bDK0S3/btsNRQvNlzv/tv5bTPZLOeh6Hpmi8eLP01/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bDK0S3/btsNRQvNlzv/tv5bTPZLOeh6Hpmi8eLP01/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbDK0S3%2FbtsNRQvNlzv%2Ftv5bTPZLOeh6Hpmi8eLP01%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1968&quot; height=&quot;1130&quot; data-origin-width=&quot;1968&quot; data-origin-height=&quot;1130&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;&quot;GA_Enemy_Death_Base&quot; 클래스에서 EndAbility Event에 Enemy캐릭터의 OnEnemyDied 인터페이스 함수를 호출해준다.&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 style=&quot;color: #c8c3bc; text-align: start;&quot; data-ke-size=&quot;size23&quot; data-darkreader-inline-color=&quot;&quot;&gt;&lt;b&gt; &lt;span&gt; 사망처리 테스트&lt;/span&gt;&lt;/b&gt;&lt;/h3&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock widthContent&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;2025-05-10 22-55-55.mp4_20250510_225649.gif&quot; data-origin-width=&quot;854&quot; data-origin-height=&quot;480&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/sksux/btsNRisTb1V/uMJzmryAfXnsBYLRb25iCK/img.gif&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/sksux/btsNRisTb1V/uMJzmryAfXnsBYLRb25iCK/img.gif&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/sksux/btsNRisTb1V/uMJzmryAfXnsBYLRb25iCK/img.gif&quot; srcset=&quot;https://blog.kakaocdn.net/dn/sksux/btsNRisTb1V/uMJzmryAfXnsBYLRb25iCK/img.gif&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;854&quot; height=&quot;480&quot; data-filename=&quot;2025-05-10 22-55-55.mp4_20250510_225649.gif&quot; data-origin-width=&quot;854&quot; data-origin-height=&quot;480&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;체력이 0으로 떨어져, 사망 판정이 되면 Death Anim Montage가 나오는 것을 확인할 수 있다.&lt;br /&gt;또한 BPI 함수를 Enemy Base Class에서 Mesh에 Pause를 걸고 CapsuleComponent의 Collision을 비활성화 해, 사망 애니메이션이 나온 후 정지해 있고 그 위를 캐릭터가 지나갈 수 있는 것을 확인했다.&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 style=&quot;color: #e8e6e3; text-align: start;&quot; data-darkreader-inline-color=&quot;&quot; data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;  Dissolve Material 적용&lt;/b&gt;&lt;/h2&gt;
&lt;h3 style=&quot;color: #e8e6e3; text-align: start;&quot; data-ke-size=&quot;size23&quot; data-darkreader-inline-color=&quot;&quot;&gt;&lt;b&gt;✅ Material Instance에서 Dissolve 관련 세팅&lt;/b&gt;&lt;/h3&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock widthContent&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;2057&quot; data-origin-height=&quot;1013&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/b7IZkQ/btsNTxaoS44/Z9kfllw37KkSap9OHHA9g0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/b7IZkQ/btsNTxaoS44/Z9kfllw37KkSap9OHHA9g0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/b7IZkQ/btsNTxaoS44/Z9kfllw37KkSap9OHHA9g0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fb7IZkQ%2FbtsNTxaoS44%2FZ9kfllw37KkSap9OHHA9g0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;2057&quot; height=&quot;1013&quot; data-origin-width=&quot;2057&quot; data-origin-height=&quot;1013&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;현재 Enemy의 Material Instance는 이전에 만들었던 Material Function의 기능을 추가했었다.&lt;br /&gt;인스턴스를 살펴보면 &quot;DissolveAmount&quot;를 조절하면 점점 사라지는 것을 확인할 수 있고, &quot;DissolveEdgeThickness&quot;를 조절하면 사라지는 Edge 두께를 조절, &quot;DissolveEdgeColor&quot;에선 색상을 변경할 수 있다.&lt;br /&gt;&lt;br /&gt;이를 통해 Death Ability가 활성화 되면 Death Montage가 재생되고 Ability가 끝난 시점에 &quot;DissolveAmount&quot;를 조절하여 사라지는 느낌을 연출할 수 있다.&lt;br /&gt;&lt;br /&gt;적절한 Color를 설정한 뒤, &quot;DissolveAmount&quot;를 0으로 변경하고 저장한다.&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 style=&quot;color: #e8e6e3; text-align: start;&quot; data-darkreader-inline-color=&quot;&quot; data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;✅ 사망 이후, 처리 로직 수정&lt;/b&gt;&lt;/h3&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;2392&quot; data-origin-height=&quot;1312&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cEJNGl/btsNSdcQsTX/Z4VIYl1dMFTeVRvDZqJvdk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cEJNGl/btsNSdcQsTX/Z4VIYl1dMFTeVRvDZqJvdk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cEJNGl/btsNSdcQsTX/Z4VIYl1dMFTeVRvDZqJvdk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcEJNGl%2FbtsNSdcQsTX%2FZ4VIYl1dMFTeVRvDZqJvdk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;2392&quot; height=&quot;1312&quot; data-origin-width=&quot;2392&quot; data-origin-height=&quot;1312&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;figure class=&quot;imagegridblock&quot;&gt;
  &lt;div class=&quot;image-container&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/ESb8S/btsNRPXVtgm/gzFRk2kIMftxsJK4S1cGJ1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/ESb8S/btsNRPXVtgm/gzFRk2kIMftxsJK4S1cGJ1/img.png&quot; data-origin-width=&quot;1983&quot; data-origin-height=&quot;537&quot; data-is-animation=&quot;false&quot; style=&quot;width: 84.7733%; margin-right: 10px;&quot; data-widthpercent=&quot;85.77&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/ESb8S/btsNRPXVtgm/gzFRk2kIMftxsJK4S1cGJ1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FESb8S%2FbtsNRPXVtgm%2FgzFRk2kIMftxsJK4S1cGJ1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1983&quot; height=&quot;537&quot;/&gt;&lt;/span&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/dBZLoa/btsNRHS9tFl/ODZHKuulkOn5IKl39PJsr0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/dBZLoa/btsNRHS9tFl/ODZHKuulkOn5IKl39PJsr0/img.png&quot; data-origin-width=&quot;427&quot; data-origin-height=&quot;697&quot; data-is-animation=&quot;false&quot; style=&quot;width: 14.0639%;&quot; data-widthpercent=&quot;14.23&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/dBZLoa/btsNRHS9tFl/ODZHKuulkOn5IKl39PJsr0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FdBZLoa%2FbtsNRHS9tFl%2FODZHKuulkOn5IKl39PJsr0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;427&quot; height=&quot;697&quot;/&gt;&lt;/span&gt;&lt;/div&gt;
  &lt;figcaption&gt;DissolveTimeline 내부&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;TimeLine을 1초로 설정하고 PlayRate로 시간을 조절하도록 한다. TimeLine 내부의 핀을 둘 다 선택하고 숫자 '1'을 누르면 곡선 형태로 그래프가 변하게 된다.&lt;br /&gt;Set Play Rate를 사용하여, TotalDisolveTime 변수의 값 시간만큼 Dissolve하여 완전히 사라지는 시간이 결정된다.&lt;br /&gt;Dissolve가 끝나면 Destory 처리해준다.&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 style=&quot;color: #e8e6e3; text-align: start;&quot; data-ke-size=&quot;size23&quot; data-darkreader-inline-color=&quot;&quot;&gt;&lt;b&gt;✅ 무기 Material Dissolve 적용 및 Destroy 처리&lt;/b&gt;&lt;/h3&gt;
&lt;p&gt;&lt;figure class=&quot;imagegridblock&quot;&gt;
  &lt;div class=&quot;image-container&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/ncthK/btsNTBDKCJ8/9dS5zQOknek1rUvloYdZXK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/ncthK/btsNTBDKCJ8/9dS5zQOknek1rUvloYdZXK/img.png&quot; data-origin-width=&quot;1267&quot; data-origin-height=&quot;863&quot; data-is-animation=&quot;false&quot; style=&quot;width: 21.5351%; margin-right: 10px;&quot; data-widthpercent=&quot;22.05&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/ncthK/btsNTBDKCJ8/9dS5zQOknek1rUvloYdZXK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FncthK%2FbtsNTBDKCJ8%2F9dS5zQOknek1rUvloYdZXK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1267&quot; height=&quot;863&quot;/&gt;&lt;/span&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/Op1V8/btsNTzzhlEN/xjBu5ZpX6p0I9pFr2xvGJK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/Op1V8/btsNTzzhlEN/xjBu5ZpX6p0I9pFr2xvGJK/img.png&quot; data-origin-width=&quot;1923&quot; data-origin-height=&quot;655&quot; data-is-animation=&quot;false&quot; style=&quot;width: 43.0645%; margin-right: 10px;&quot; data-widthpercent=&quot;44.09&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/Op1V8/btsNTzzhlEN/xjBu5ZpX6p0I9pFr2xvGJK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FOp1V8%2FbtsNTzzhlEN%2FxjBu5ZpX6p0I9pFr2xvGJK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1923&quot; height=&quot;655&quot;/&gt;&lt;/span&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/c7oSwk/btsNS8IHyt8/bgTHtaaWzckgjP0hDRo5C1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/c7oSwk/btsNS8IHyt8/bgTHtaaWzckgjP0hDRo5C1/img.png&quot; data-origin-width=&quot;2327&quot; data-origin-height=&quot;1032&quot; data-is-animation=&quot;false&quot; style=&quot;width: 33.0748%;&quot; data-widthpercent=&quot;33.86&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/c7oSwk/btsNS8IHyt8/bgTHtaaWzckgjP0hDRo5C1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fc7oSwk%2FbtsNS8IHyt8%2FbgTHtaaWzckgjP0hDRo5C1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;2327&quot; height=&quot;1032&quot;/&gt;&lt;/span&gt;&lt;/div&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;무기의 Material에도 Material Function을 적용하고 Instance를 생성해주었다. 그리고 해당 Instance를 무기의 Material Slot에 할당해준다.&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;2497&quot; data-origin-height=&quot;1132&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/b4aOCi/btsNSb0qFyg/M4tuGqjjc8unBUTylGMpk0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/b4aOCi/btsNSb0qFyg/M4tuGqjjc8unBUTylGMpk0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/b4aOCi/btsNSb0qFyg/M4tuGqjjc8unBUTylGMpk0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fb4aOCi%2FbtsNSb0qFyg%2FM4tuGqjjc8unBUTylGMpk0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;2497&quot; height=&quot;1132&quot; data-origin-width=&quot;2497&quot; data-origin-height=&quot;1132&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;다시 BaseEnemy 클래스로 돌아와서, EnemyCombatComponent로 무기가 있는지 확인하고, 무기가 있다면 캐릭터와 똑같이 Dissolve 시켜준다.&lt;br /&gt;Timeline이 종료되면 무기와 캐릭터를 Destroy 해주었다.&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 style=&quot;color: #c8c3bc; text-align: start;&quot; data-ke-size=&quot;size23&quot; data-darkreader-inline-color=&quot;&quot;&gt;&lt;b&gt; &lt;span&gt; Material Dissolve 및 Destroy 테스트&lt;/span&gt;&lt;/b&gt;&lt;/h3&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock widthContent&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;2025-05-11 00-00-48.mp4_20250511_000117.gif&quot; data-origin-width=&quot;854&quot; data-origin-height=&quot;480&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bKrTUb/btsNSckJ5oS/jglmylFYWJHmEu0OsIA0s1/img.gif&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bKrTUb/btsNSckJ5oS/jglmylFYWJHmEu0OsIA0s1/img.gif&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bKrTUb/btsNSckJ5oS/jglmylFYWJHmEu0OsIA0s1/img.gif&quot; srcset=&quot;https://blog.kakaocdn.net/dn/bKrTUb/btsNSckJ5oS/jglmylFYWJHmEu0OsIA0s1/img.gif&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;854&quot; height=&quot;480&quot; data-filename=&quot;2025-05-11 00-00-48.mp4_20250511_000117.gif&quot; data-origin-width=&quot;854&quot; data-origin-height=&quot;480&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;캐릭터가 사망처리 되면 정해진 시간만큼 Dissolve 되며 Destroy되는 로직이 정상적으로 진행된다.&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 style=&quot;color: #e8e6e3; text-align: start;&quot; data-ke-size=&quot;size26&quot; data-darkreader-inline-color=&quot;&quot;&gt;&lt;b&gt;  Dissolve Niagara 스폰&lt;/b&gt;&lt;/h2&gt;
&lt;h3 style=&quot;color: #e8e6e3; text-align: start;&quot; data-ke-size=&quot;size23&quot; data-darkreader-inline-color=&quot;&quot;&gt;&lt;b&gt;✅ BP Interface 함수(OnEnemyDied) 에 Niagara System 인자 추가&lt;/b&gt;&lt;/h3&gt;
&lt;p&gt;&lt;figure class=&quot;imagegridblock&quot;&gt;
  &lt;div class=&quot;image-container&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/Nk9Eo/btsNQYgIdky/QSiO5uGMbnfL78H4hmRxIK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/Nk9Eo/btsNQYgIdky/QSiO5uGMbnfL78H4hmRxIK/img.png&quot; data-origin-width=&quot;2553&quot; data-origin-height=&quot;1331&quot; data-is-animation=&quot;false&quot; style=&quot;width: 66.2871%; margin-right: 10px;&quot; data-widthpercent=&quot;67.07&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/Nk9Eo/btsNQYgIdky/QSiO5uGMbnfL78H4hmRxIK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FNk9Eo%2FbtsNQYgIdky%2FQSiO5uGMbnfL78H4hmRxIK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;2553&quot; height=&quot;1331&quot;/&gt;&lt;/span&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/uQlEn/btsNS7JLixp/q8aFrYKsGEjBkRkhlTISz1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/uQlEn/btsNS7JLixp/q8aFrYKsGEjBkRkhlTISz1/img.png&quot; data-origin-width=&quot;551&quot; data-origin-height=&quot;585&quot; data-is-animation=&quot;false&quot; data-widthpercent=&quot;32.93&quot; style=&quot;width: 32.5501%;&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/uQlEn/btsNS7JLixp/q8aFrYKsGEjBkRkhlTISz1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FuQlEn%2FbtsNS7JLixp%2Fq8aFrYKsGEjBkRkhlTISz1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;551&quot; height=&quot;585&quot;/&gt;&lt;/span&gt;&lt;/div&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;BPI의 OnEnemyDied 함수의 인자로 &quot;Niagara System&quot;을 전달하도록 하는데, 사망시 한 번만 사용되기 때문에 &quot;Soft Object Reference&quot;로 GA가 저장하고 있다가 해당 함수가 호출될 때, BP_EnemyBase클래스에 전달하도록 한다.&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 style=&quot;color: #e8e6e3; text-align: start;&quot; data-ke-size=&quot;size23&quot; data-darkreader-inline-color=&quot;&quot;&gt;&lt;b&gt;✅ GA_Enemy_Death에 NiagaraSystem 저장&lt;/b&gt;&lt;/h3&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1871&quot; data-origin-height=&quot;1032&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/biag8Q/btsNSbmcJk9/KYojRtaFywkVYGkAQkzqv0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/biag8Q/btsNSbmcJk9/KYojRtaFywkVYGkAQkzqv0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/biag8Q/btsNSbmcJk9/KYojRtaFywkVYGkAQkzqv0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fbiag8Q%2FbtsNSbmcJk9%2FKYojRtaFywkVYGkAQkzqv0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1871&quot; height=&quot;1032&quot; data-origin-width=&quot;1871&quot; data-origin-height=&quot;1032&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;사망 처리 Ability인 부모클래스에서 &quot;On Enemy Died&quot; 인터페이스 함수의 Niagara System 인자를 변수로 승격시킨다.&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;2327&quot; data-origin-height=&quot;1322&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/IEsSt/btsNSn036Ux/yu25kZQ1ETrfXYFZthRyfK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/IEsSt/btsNSn036Ux/yu25kZQ1ETrfXYFZthRyfK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/IEsSt/btsNSn036Ux/yu25kZQ1ETrfXYFZthRyfK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FIEsSt%2FbtsNSn036Ux%2Fyu25kZQ1ETrfXYFZthRyfK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;2327&quot; height=&quot;1322&quot; data-origin-width=&quot;2327&quot; data-origin-height=&quot;1322&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;자식 클래스에서 사용하고자 하는 Niagara System을 할당한다.&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 style=&quot;color: #e8e6e3; text-align: start;&quot; data-ke-size=&quot;size23&quot; data-darkreader-inline-color=&quot;&quot;&gt;&lt;b&gt;✅ Dissolve Niagara 스폰 로직 구현&lt;/b&gt;&lt;/h3&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock widthContent&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;2126&quot; data-origin-height=&quot;1311&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bKJyJs/btsNSIXRYzM/mgz160g9jaoIsdFbkwgLu1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bKJyJs/btsNSIXRYzM/mgz160g9jaoIsdFbkwgLu1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bKJyJs/btsNSIXRYzM/mgz160g9jaoIsdFbkwgLu1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbKJyJs%2FbtsNSIXRYzM%2Fmgz160g9jaoIsdFbkwgLu1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;2126&quot; height=&quot;1311&quot; data-origin-width=&quot;2126&quot; data-origin-height=&quot;1311&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;figure class=&quot;imagegridblock&quot;&gt;
  &lt;div class=&quot;image-container&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/tcd3e/btsNQWwud1e/9iWeGHk4jgTRpcU3cqjtu1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/tcd3e/btsNQWwud1e/9iWeGHk4jgTRpcU3cqjtu1/img.png&quot; data-origin-width=&quot;2041&quot; data-origin-height=&quot;537&quot; data-is-animation=&quot;false&quot; style=&quot;width: 58.0909%; margin-right: 10px;&quot; data-widthpercent=&quot;59.47&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/tcd3e/btsNQWwud1e/9iWeGHk4jgTRpcU3cqjtu1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Ftcd3e%2FbtsNQWwud1e%2F9iWeGHk4jgTRpcU3cqjtu1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;2041&quot; height=&quot;537&quot;/&gt;&lt;/span&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/PjzYR/btsNRDcuiz3/h3pUsat9zjnCnrLHRxR3OK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/PjzYR/btsNRDcuiz3/h3pUsat9zjnCnrLHRxR3OK/img.png&quot; data-origin-width=&quot;2050&quot; data-origin-height=&quot;1015&quot; data-is-animation=&quot;false&quot; style=&quot;width: 30.8693%; margin-right: 10px;&quot; data-widthpercent=&quot;31.6&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/PjzYR/btsNRDcuiz3/h3pUsat9zjnCnrLHRxR3OK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FPjzYR%2FbtsNRDcuiz3%2Fh3pUsat9zjnCnrLHRxR3OK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;2050&quot; height=&quot;1015&quot;/&gt;&lt;/span&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/ps6DQ/btsNS3Of19E/Xx7Lb1uHsaadx8AEykoOek/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/ps6DQ/btsNS3Of19E/Xx7Lb1uHsaadx8AEykoOek/img.png&quot; data-origin-width=&quot;508&quot; data-origin-height=&quot;891&quot; data-is-animation=&quot;false&quot; style=&quot;width: 8.71416%;&quot; data-widthpercent=&quot;8.93&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/ps6DQ/btsNS3Of19E/Xx7Lb1uHsaadx8AEykoOek/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fps6DQ%2FbtsNS3Of19E%2FXx7Lb1uHsaadx8AEykoOek%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;508&quot; height=&quot;891&quot;/&gt;&lt;/span&gt;&lt;/div&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;캐릭터 및 무기가 Dissolve되기 전에 먼저 Niagara System을 스폰한다.&lt;br /&gt;함수에서 SoftlReference로 전달 받은 Niagara를 비동기 로딩한 후 Mesh에 Attach 해주었다.&lt;br /&gt;현재 Enemy의 Mesh에서 &quot;Dissolve Edge Color&quot;로 설정된 Color 값을 Niagara &quot;DissolveParticleColor&quot;로 Set하여 같은 색상이 나오도록 설정하였다.&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 style=&quot;color: #c8c3bc; text-align: start;&quot; data-ke-size=&quot;size23&quot; data-darkreader-inline-color=&quot;&quot;&gt;&lt;b&gt; &lt;span&gt; Dissolve Niagara 스폰 테스트&lt;/span&gt;&lt;/b&gt;&lt;/h3&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock widthContent&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;2025-05-11 00-56-15.mp4_20250511_005651.gif&quot; data-origin-width=&quot;854&quot; data-origin-height=&quot;480&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/vVbIc/btsNTDuNIu2/R9JK44PNrLCcyK9BN9BfMk/img.gif&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/vVbIc/btsNTDuNIu2/R9JK44PNrLCcyK9BN9BfMk/img.gif&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/vVbIc/btsNTDuNIu2/R9JK44PNrLCcyK9BN9BfMk/img.gif&quot; srcset=&quot;https://blog.kakaocdn.net/dn/vVbIc/btsNTDuNIu2/R9JK44PNrLCcyK9BN9BfMk/img.gif&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;854&quot; height=&quot;480&quot; data-filename=&quot;2025-05-11 00-56-15.mp4_20250511_005651.gif&quot; data-origin-width=&quot;854&quot; data-origin-height=&quot;480&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;사망 처리시 디졸브 Niagara가 스폰되고 자동으로 사라지는 것을 확인할 수 있다.&lt;/blockquote&gt;</description>
      <category>내배캠/GAS(Gameplay Ability System)</category>
      <author>동그래님</author>
      <guid isPermaLink="true">https://dong-grae.tistory.com/238</guid>
      <comments>https://dong-grae.tistory.com/238#entry238comment</comments>
      <pubDate>Sat, 10 May 2025 23:36:10 +0900</pubDate>
    </item>
    <item>
      <title>GAS_15_GameplayCue로 타격 사운드 재생</title>
      <link>https://dong-grae.tistory.com/237</link>
      <description>&lt;h2 style=&quot;color: #e8e6e3; text-align: start;&quot; data-ke-size=&quot;size26&quot; data-darkreader-inline-color=&quot;&quot;&gt;&lt;b&gt;  Gameplay Cue로 사운드 재생&lt;/b&gt;&lt;/h2&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;피격 당하는 Montage에 피격 당하는 대상의 Sound를 입히는 것은 Montage에서 해결할 수 있지만,&lt;br /&gt;공격을 하는 경우에도 Montage에서 소리를 재생하면 공격이 성공하지 않았는데도 피격 사운드가 날 것이고 이는 어색하게 느껴지게 된다.&lt;br /&gt;공격 모션에는 휘두르는 사운드만 들리게 하고, 타격이 성공 했을 때만 타격 피드백 사운드를 재생할 수 있도록 한다.&lt;/blockquote&gt;
&lt;h3 style=&quot;color: #e8e6e3; text-align: start;&quot; data-ke-size=&quot;size23&quot; data-darkreader-inline-color=&quot;&quot;&gt;&lt;b&gt;  GameplayCue 시스템에서 사용되는 두 가지 타입 클래스&amp;nbsp;&lt;/b&gt;&lt;/h3&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;&lt;b&gt;GameplayCueNotify_Static&lt;/b&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;정적 클래스(UObject 기반, 액터가 아님)&lt;/li&gt;
&lt;li&gt;스폰되지 않고, 메모리 상에서 실행&lt;/li&gt;
&lt;li&gt;한 번 실행하고 끝나는 효과에 적합(피격 사운드 재생, 임펙트 스파크 등)&lt;/li&gt;
&lt;li&gt;Tick이 필요 없고, 따로 씬 상에 남아있을 필요 없을 때&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;b&gt;GameplayCueNotify_Actor&lt;/b&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Actor 기반 클래스&lt;/li&gt;
&lt;li&gt;맵에 스폰되어 존재하며 지속적인 처리 가능(Tick, BeginPlay, Destroy 등 지원)&lt;/li&gt;
&lt;li&gt;상태에 따라 시작/갱신/종료가 가능한 이펙트&lt;/li&gt;
&lt;li&gt;버프/디버프, 지속시간 있는 이펙트 등&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;h3 style=&quot;color: #e8e6e3; text-align: start;&quot; data-ke-size=&quot;size23&quot; data-darkreader-inline-color=&quot;&quot;&gt;&lt;b&gt;✅ Gameplay Cue BP클래스 생성&lt;/b&gt;&lt;/h3&gt;
&lt;p&gt;&lt;figure class=&quot;imagegridblock&quot;&gt;
  &lt;div class=&quot;image-container&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/b0PUmO/btsNReKj66q/b16L3SSWbU24G7ZnBIc1K1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/b0PUmO/btsNReKj66q/b16L3SSWbU24G7ZnBIc1K1/img.png&quot; data-origin-width=&quot;763&quot; data-origin-height=&quot;890&quot; data-is-animation=&quot;false&quot; style=&quot;width: 57.6093%; margin-right: 10px;&quot; data-widthpercent=&quot;58.29&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/b0PUmO/btsNReKj66q/b16L3SSWbU24G7ZnBIc1K1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fb0PUmO%2FbtsNReKj66q%2Fb16L3SSWbU24G7ZnBIc1K1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;763&quot; height=&quot;890&quot;/&gt;&lt;/span&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/9DdKd/btsNRNlsrJL/QG4byYyQIDzrYdoZaRAFi0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/9DdKd/btsNRNlsrJL/QG4byYyQIDzrYdoZaRAFi0/img.png&quot; data-origin-width=&quot;127&quot; data-origin-height=&quot;207&quot; data-is-animation=&quot;false&quot; style=&quot;width: 41.2279%;&quot; data-widthpercent=&quot;41.71&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/9DdKd/btsNRNlsrJL/QG4byYyQIDzrYdoZaRAFi0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F9DdKd%2FbtsNRNlsrJL%2FQG4byYyQIDzrYdoZaRAFi0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;127&quot; height=&quot;207&quot;/&gt;&lt;/span&gt;&lt;/div&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;현재 타격 사운드를 재생하는 것이므로 &quot;GamePlayCueNotify_Static&quot;을 상속 받는 BP 클래스를 생성해준다.&lt;/blockquote&gt;
&lt;p&gt;&lt;figure class=&quot;imagegridblock&quot;&gt;
  &lt;div class=&quot;image-container&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/oLswj/btsNSnmo0Kt/fsOxOKke4wQVl0MWXiaBx1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/oLswj/btsNSnmo0Kt/fsOxOKke4wQVl0MWXiaBx1/img.png&quot; data-origin-width=&quot;2067&quot; data-origin-height=&quot;826&quot; data-is-animation=&quot;false&quot; style=&quot;width: 57.1506%; margin-right: 10px;&quot; data-widthpercent=&quot;57.82&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/oLswj/btsNSnmo0Kt/fsOxOKke4wQVl0MWXiaBx1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FoLswj%2FbtsNSnmo0Kt%2FfsOxOKke4wQVl0MWXiaBx1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;2067&quot; height=&quot;826&quot;/&gt;&lt;/span&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cQC5M7/btsNSfaEFZt/ri9y19AwUGq4ETdtQXqLy1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cQC5M7/btsNSfaEFZt/ri9y19AwUGq4ETdtQXqLy1/img.png&quot; data-origin-width=&quot;1327&quot; data-origin-height=&quot;727&quot; data-is-animation=&quot;false&quot; style=&quot;width: 41.6866%;&quot; data-widthpercent=&quot;42.18&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cQC5M7/btsNSfaEFZt/ri9y19AwUGq4ETdtQXqLy1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcQC5M7%2FbtsNSfaEFZt%2Fri9y19AwUGq4ETdtQXqLy1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1327&quot; height=&quot;727&quot;/&gt;&lt;/span&gt;&lt;/div&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;생성한 클래스의 Tag를 살펴보면 아무것도 존재하지 않는다. 따라서 새롭게 GameplayCue 전용 Tag를 추가해야한다.&amp;nbsp;&lt;br /&gt;이번엔 에디터에서 바로 생성하도록 한다.&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imagegridblock&quot;&gt;
  &lt;div class=&quot;image-container&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/lJa42/btsNSa1SU28/XGSt6DFLRPjtLyQ9xCsU1k/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/lJa42/btsNSa1SU28/XGSt6DFLRPjtLyQ9xCsU1k/img.png&quot; data-origin-width=&quot;377&quot; data-origin-height=&quot;562&quot; data-is-animation=&quot;false&quot; style=&quot;width: 29.9472%; margin-right: 10px;&quot; data-widthpercent=&quot;30.3&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/lJa42/btsNSa1SU28/XGSt6DFLRPjtLyQ9xCsU1k/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FlJa42%2FbtsNSa1SU28%2FXGSt6DFLRPjtLyQ9xCsU1k%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;377&quot; height=&quot;562&quot;/&gt;&lt;/span&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/OSArN/btsNSdjEAJV/kOvQQqJdwHPmY3HeseeKgk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/OSArN/btsNSdjEAJV/kOvQQqJdwHPmY3HeseeKgk/img.png&quot; data-origin-width=&quot;1091&quot; data-origin-height=&quot;707&quot; data-is-animation=&quot;false&quot; style=&quot;width: 68.89%;&quot; data-widthpercent=&quot;69.7&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/OSArN/btsNSdjEAJV/kOvQQqJdwHPmY3HeseeKgk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FOSArN%2FbtsNSdjEAJV%2FkOvQQqJdwHPmY3HeseeKgk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1091&quot; height=&quot;707&quot;/&gt;&lt;/span&gt;&lt;/div&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;&quot;GameplayCue.Sounds.MeleeHit.Axe&quot;로 Tag 이름을 입력하고 Source를 &quot;DefaultGameplay Tags.ini로 설정한 뒤 &quot;Add New Tag&quot;를 클릭한다.&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imagegridblock&quot;&gt;
  &lt;div class=&quot;image-container&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/dqIwc4/btsNR48kOIw/WAR1NBYI54bmZM2bI84sfK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/dqIwc4/btsNR48kOIw/WAR1NBYI54bmZM2bI84sfK/img.png&quot; data-origin-width=&quot;505&quot; data-origin-height=&quot;505&quot; data-is-animation=&quot;false&quot; style=&quot;width: 25.5208%; margin-right: 10px;&quot; data-widthpercent=&quot;26.13&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/dqIwc4/btsNR48kOIw/WAR1NBYI54bmZM2bI84sfK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FdqIwc4%2FbtsNR48kOIw%2FWAR1NBYI54bmZM2bI84sfK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;505&quot; height=&quot;505&quot;/&gt;&lt;/span&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/kF6sY/btsNR3n31Sr/v8vZijhKZ5VPctfKX2x7m1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/kF6sY/btsNR3n31Sr/v8vZijhKZ5VPctfKX2x7m1/img.png&quot; data-origin-width=&quot;507&quot; data-origin-height=&quot;505&quot; data-is-animation=&quot;false&quot; style=&quot;width: 25.6219%; margin-right: 10px;&quot; data-widthpercent=&quot;26.23&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/kF6sY/btsNR3n31Sr/v8vZijhKZ5VPctfKX2x7m1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FkF6sY%2FbtsNR3n31Sr%2Fv8vZijhKZ5VPctfKX2x7m1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;507&quot; height=&quot;505&quot;/&gt;&lt;/span&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/pFHva/btsNRIR0Kxg/3xkRKHDncjMVfVWP2P1iC1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/pFHva/btsNRIR0Kxg/3xkRKHDncjMVfVWP2P1iC1/img.png&quot; data-origin-width=&quot;1723&quot; data-origin-height=&quot;945&quot; data-is-animation=&quot;false&quot; style=&quot;width: 46.5317%;&quot; data-widthpercent=&quot;47.64&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/pFHva/btsNRIR0Kxg/3xkRKHDncjMVfVWP2P1iC1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FpFHva%2FbtsNRIR0Kxg%2F3xkRKHDncjMVfVWP2P1iC1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1723&quot; height=&quot;945&quot;/&gt;&lt;/span&gt;&lt;/div&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;&quot;On Execute&quot; 함수를 Override해서 다음과 같이 재정의 한다.&lt;br /&gt;Target의 Location에 &quot;Play Sound at Location&quot; 노드를 통해 원하는 사운드를 재생할 수 있도록 한다.&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 style=&quot;color: #e8e6e3; text-align: start;&quot; data-darkreader-inline-color=&quot;&quot; data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;✅ Gameplay Ability에 GE 실행 로직 구현&lt;/b&gt;&lt;/h3&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock widthContent&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;2518&quot; data-origin-height=&quot;917&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/MH8lg/btsNTBwXtbU/zBmmTc74Af7ZuXFmjn26J0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/MH8lg/btsNTBwXtbU/zBmmTc74Af7ZuXFmjn26J0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/MH8lg/btsNTBwXtbU/zBmmTc74Af7ZuXFmjn26J0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FMH8lg%2FbtsNTBwXtbU%2FzBmmTc74Af7ZuXFmjn26J0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;2518&quot; height=&quot;917&quot; data-origin-width=&quot;2518&quot; data-origin-height=&quot;917&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1351&quot; data-origin-height=&quot;837&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bG6bfB/btsNSdKH8wj/xRuMa7swn19vRcTYCuJ1yk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bG6bfB/btsNSdKH8wj/xRuMa7swn19vRcTYCuJ1yk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bG6bfB/btsNSdKH8wj/xRuMa7swn19vRcTYCuJ1yk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbG6bfB%2FbtsNSdKH8wj%2FxRuMa7swn19vRcTYCuJ1yk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1351&quot; height=&quot;837&quot; data-origin-width=&quot;1351&quot; data-origin-height=&quot;837&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;플레이어의 강공격, 약공격 Ability의 데미지 적용 함수에서 &quot;Execute GameplayCue On Owner&quot; 노드에 GC Tag를 변수로 승격 시키고, 자식 클래스에서 해당 변수에 Tag를 할당해준다.&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 style=&quot;color: #e8e6e3; text-align: start;&quot; data-ke-size=&quot;size23&quot; data-darkreader-inline-color=&quot;&quot;&gt;&lt;b&gt;✅ DefautGame.ini에 GameplayCue Path 저장&lt;/b&gt;&lt;/h3&gt;
&lt;p&gt;&lt;figure class=&quot;imagegridblock&quot;&gt;
  &lt;div class=&quot;image-container&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/Yf0HF/btsNSmnuC1L/k5tSXWO5eJ872qXkheeN7k/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/Yf0HF/btsNSmnuC1L/k5tSXWO5eJ872qXkheeN7k/img.png&quot; data-origin-width=&quot;940&quot; data-origin-height=&quot;616&quot; data-is-animation=&quot;false&quot; style=&quot;width: 55.9049%; margin-right: 10px;&quot; data-widthpercent=&quot;56.56&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/Yf0HF/btsNSmnuC1L/k5tSXWO5eJ872qXkheeN7k/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FYf0HF%2FbtsNSmnuC1L%2Fk5tSXWO5eJ872qXkheeN7k%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;940&quot; height=&quot;616&quot;/&gt;&lt;/span&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bRvDg2/btsNTlVqoea/jkhRPNlbiEcl5iCisHXKN1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bRvDg2/btsNTlVqoea/jkhRPNlbiEcl5iCisHXKN1/img.png&quot; data-origin-width=&quot;300&quot; data-origin-height=&quot;256&quot; data-is-animation=&quot;false&quot; style=&quot;width: 42.9323%;&quot; data-widthpercent=&quot;43.44&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bRvDg2/btsNTlVqoea/jkhRPNlbiEcl5iCisHXKN1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbRvDg2%2FbtsNTlVqoea%2FjkhRPNlbiEcl5iCisHXKN1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;300&quot; height=&quot;256&quot;/&gt;&lt;/span&gt;&lt;/div&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;pre id=&quot;code_1746878552174&quot; class=&quot;cpp&quot; data-ke-language=&quot;cpp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;[/Script/GameplayAbilities.AbilitySystemGlobals]
bUseDebugTargetFromHud = true
GameplayCueNotifyPaths = &quot;/Game/GameplayCues&quot;

[/Script/EngineSettings.GeneralProjectSettings]
ProjectID=04AF6AD14CFECE9CD8FFFB86EAF63CE4
CopyrightNotice=&lt;/code&gt;&lt;/pre&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;Gameplay Cue를 성공적으로 실행하려면 DefualtGame.ini 파일에 현재 GameplayCue가 들어있는 경로를 추가해야한다.&lt;br /&gt;현재 Content폴더 아래에 바로 GameplayCues 폴더가 있고 해당 폴더 안에 &quot;GC_Hero_AxeHit&quot;이 있으므로 &quot;/Game/GameplayCues&quot; 경로를 위와 같이 저장해주고 에디터를 재시작 해준다.&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 style=&quot;color: #c8c3bc; text-align: start; --darkreader-inline-color: var(--darkreader-text-c8c3bc, #c1bcb4);&quot; data-ke-size=&quot;size23&quot; data-darkreader-inline-color=&quot;&quot;&gt; &lt;span&gt;&amp;nbsp;&lt;b&gt;Gameplay Cue로 타격 사운드 재생 테스트&lt;/b&gt;&lt;/span&gt;&lt;/h3&gt;
&lt;figure data-ke-type=&quot;video&quot; data-ke-style=&quot;alignLeft&quot; data-video-host=&quot;kakaotv&quot; data-video-url=&quot;https://tv.kakao.com/v/455023340&quot; data-video-thumbnail=&quot;https://scrap.kakaocdn.net/dn/c0rgFQ/hyYRoOd4ck/UF8vUVmHtlzKhMgfpzHRn1/img.jpg?width=1920&amp;amp;height=1080&amp;amp;face=0_0_1920_1080,https://scrap.kakaocdn.net/dn/DQUhT/hyYRogoQED/suwGUFFR30yy00O7B5yrXK/img.jpg?width=1920&amp;amp;height=1080&amp;amp;face=0_0_1920_1080&quot; data-video-width=&quot;860&quot; data-video-height=&quot;484&quot; data-video-origin-width=&quot;860&quot; data-video-origin-height=&quot;484&quot; data-ke-mobilestyle=&quot;widthContent&quot; data-video-play-service=&quot;daum_tistory&quot; data-original-url=&quot;&quot; data-video-title=&quot;&quot;&gt;&lt;iframe src=&quot;https://play-tv.kakao.com/embed/player/cliplink/455023340?service=daum_tistory&quot; width=&quot;860&quot; height=&quot;484&quot; frameborder=&quot;0&quot; allowfullscreen=&quot;true&quot;&gt;&lt;/iframe&gt;
&lt;figcaption style=&quot;display: none;&quot;&gt;&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&amp;nbsp;&lt;/h4&gt;</description>
      <category>내배캠/GAS(Gameplay Ability System)</category>
      <author>동그래님</author>
      <guid isPermaLink="true">https://dong-grae.tistory.com/237</guid>
      <comments>https://dong-grae.tistory.com/237#entry237comment</comments>
      <pubDate>Sat, 10 May 2025 21:12:30 +0900</pubDate>
    </item>
    <item>
      <title>GAS_14_피격 시 활성화 되는 GA_HitReact 구현</title>
      <link>https://dong-grae.tistory.com/236</link>
      <description>&lt;h2 style=&quot;color: #e8e6e3; text-align: start;&quot; data-ke-size=&quot;size26&quot; data-darkreader-inline-color=&quot;&quot;&gt;&lt;b&gt;  Hit React_Ability 구현&lt;/b&gt;&lt;/h2&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;1. Gameplay Tag 설정&lt;br /&gt;2. HitReact 어빌리티 생성&lt;br /&gt;3. 하위 Ability 생성&lt;br /&gt;4. Anim Montage 생성&lt;br /&gt;5. Ability 부여&lt;br /&gt;&lt;br /&gt;위 순서대로 Hit React Ability를 구현해보자.&lt;/blockquote&gt;
&lt;h3 style=&quot;color: #e8e6e3; text-align: start;&quot; data-ke-size=&quot;size23&quot; data-darkreader-inline-color=&quot;&quot;&gt;&lt;b&gt;✅&lt;/b&gt;&lt;b&gt;Gameplay Tag 설정&lt;/b&gt;&lt;/h3&gt;
&lt;h4 style=&quot;color: #e8e6e3; text-align: start;&quot; data-ke-size=&quot;size20&quot; data-darkreader-inline-color=&quot;&quot;&gt;&lt;b&gt;✔️ RPGGameplayTags&lt;/b&gt;&lt;/h4&gt;
&lt;div data-ke-type=&quot;moreLess&quot; data-text-more=&quot;더보기&quot; data-text-less=&quot;닫기&quot;&gt;&lt;a class=&quot;btn-toggle-moreless&quot;&gt;더보기&lt;/a&gt;
&lt;div class=&quot;moreless-content&quot;&gt;
&lt;p style=&quot;color: #e8e6e3; text-align: start;&quot; data-ke-size=&quot;size18&quot; data-darkreader-inline-color=&quot;&quot;&gt;&lt;b&gt; &lt;span&gt; RPGGameplayTags.h&lt;/span&gt;&lt;/b&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1746859599298&quot; class=&quot;cpp&quot; data-ke-language=&quot;cpp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;#pragma once

#include &quot;NativeGameplayTags.h&quot;

namespace RPGGameplayTags
{
	/** 생략 */
	
	/** Enemy Tags */
	RPG_API UE_DECLARE_GAMEPLAY_TAG_EXTERN(Enemy_Ability_Melee); // 근거리 공격 추가
	RPG_API UE_DECLARE_GAMEPLAY_TAG_EXTERN(Enemy_Ability_Ranged); // 원거리 공격 추가

	RPG_API UE_DECLARE_GAMEPLAY_TAG_EXTERN(Enemy_Weapon);

	/** Shared Tags */
	RPG_API UE_DECLARE_GAMEPLAY_TAG_EXTERN(Shared_Ability_HitReact); // HitReact 추가

	RPG_API UE_DECLARE_GAMEPLAY_TAG_EXTERN(Shared_Event_MeleeHit);
	RPG_API UE_DECLARE_GAMEPLAY_TAG_EXTERN(Shared_Event_HitReact); // HitReact Event추가

	RPG_API UE_DECLARE_GAMEPLAY_TAG_EXTERN(Shared_SetByCaller_BaseDamage);

}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;b&gt; &lt;span&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;RPGGameplayTags.cpp&lt;/span&gt;&lt;/b&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1746859617826&quot; class=&quot;cpp&quot; data-ke-language=&quot;cpp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;#include &quot;RPGGameplayTags.h&quot;

namespace RPGGameplayTags
{
	/** 생략 */

	/** Enemy Tags */
	UE_DEFINE_GAMEPLAY_TAG(Enemy_Ability_Melee, &quot;Enemy.Ability.Melee&quot;);
	UE_DEFINE_GAMEPLAY_TAG(Enemy_Ability_Ranged, &quot;Enemy.Ability.Ranged&quot;);

	UE_DEFINE_GAMEPLAY_TAG(Enemy_Weapon, &quot;Enemy.Weapon&quot;);

	/** Shared Tags */
	UE_DEFINE_GAMEPLAY_TAG(Shared_Ability_HitReact, &quot;Shared.Ability.HitReact&quot;);

	UE_DEFINE_GAMEPLAY_TAG(Shared_Event_MeleeHit, &quot;Shared.Event.MeleeHit&quot;);
	UE_DEFINE_GAMEPLAY_TAG(Shared_Event_HitReact, &quot;Shared.Event.HitReact&quot;);

	UE_DEFINE_GAMEPLAY_TAG(Shared_SetByCaller_BaseDamage, &quot;Shared.SetByCaller.BaseDamage&quot;);
}&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;p style=&quot;color: #e8e6e3; text-align: start;&quot; data-ke-size=&quot;size16&quot; data-darkreader-inline-color=&quot;&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 style=&quot;color: #e8e6e3; text-align: start;&quot; data-darkreader-inline-color=&quot;&quot; data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;✅ HitReact Ability 구현&lt;/b&gt;&lt;/h3&gt;
&lt;p&gt;&lt;figure class=&quot;imagegridblock&quot;&gt;
  &lt;div class=&quot;image-container&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bAG7ck/btsNTAY9HF6/0s4FKaKR5mbi1bQCErqnW1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bAG7ck/btsNTAY9HF6/0s4FKaKR5mbi1bQCErqnW1/img.png&quot; data-origin-width=&quot;717&quot; data-origin-height=&quot;1118&quot; data-is-animation=&quot;false&quot; style=&quot;width: 34.9723%; margin-right: 10px;&quot; data-widthpercent=&quot;35.81&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bAG7ck/btsNTAY9HF6/0s4FKaKR5mbi1bQCErqnW1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbAG7ck%2FbtsNTAY9HF6%2F0s4FKaKR5mbi1bQCErqnW1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;717&quot; height=&quot;1118&quot;/&gt;&lt;/span&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/oi4Oi/btsNSagsRP0/XAzujJFgMdJlxaU7mwZjt0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/oi4Oi/btsNSagsRP0/XAzujJFgMdJlxaU7mwZjt0/img.png&quot; data-origin-width=&quot;503&quot; data-origin-height=&quot;921&quot; data-is-animation=&quot;false&quot; style=&quot;width: 29.7821%; margin-right: 10px;&quot; data-widthpercent=&quot;30.49&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/oi4Oi/btsNSagsRP0/XAzujJFgMdJlxaU7mwZjt0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Foi4Oi%2FbtsNSagsRP0%2FXAzujJFgMdJlxaU7mwZjt0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;503&quot; height=&quot;921&quot;/&gt;&lt;/span&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bZZeib/btsNRP4za2F/uZrzoFMZIJ7a1oRkuhFLn1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bZZeib/btsNRP4za2F/uZrzoFMZIJ7a1oRkuhFLn1/img.png&quot; data-origin-width=&quot;131&quot; data-origin-height=&quot;217&quot; data-is-animation=&quot;false&quot; style=&quot;width: 32.9199%;&quot; data-widthpercent=&quot;33.7&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bZZeib/btsNRP4za2F/uZrzoFMZIJ7a1oRkuhFLn1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbZZeib%2FbtsNRP4za2F%2FuZrzoFMZIJ7a1oRkuhFLn1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;131&quot; height=&quot;217&quot;/&gt;&lt;/span&gt;&lt;/div&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;&quot;RPGEnemyGameplayAbility&quot;를 상속 받는 GameplayAbility BP 클래스를 생성한다.&lt;/blockquote&gt;
&lt;p style=&quot;color: #e8e6e3; text-align: start;&quot; data-ke-size=&quot;size16&quot; data-darkreader-inline-color=&quot;&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imagegridblock&quot;&gt;
  &lt;div class=&quot;image-container&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/Fj6Ta/btsNRiGlWMt/hzp7jkaffEUCBBDDi6dRy0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/Fj6Ta/btsNRiGlWMt/hzp7jkaffEUCBBDDi6dRy0/img.png&quot; data-origin-width=&quot;2327&quot; data-origin-height=&quot;1178&quot; data-is-animation=&quot;false&quot; style=&quot;width: 70.46%; margin-right: 10px;&quot; data-widthpercent=&quot;71.29&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/Fj6Ta/btsNRiGlWMt/hzp7jkaffEUCBBDDi6dRy0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FFj6Ta%2FbtsNRiGlWMt%2Fhzp7jkaffEUCBBDDi6dRy0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;2327&quot; height=&quot;1178&quot;/&gt;&lt;/span&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/eax95S/btsNSdw5ALI/zGSkANPxEvXyRCzmxvVoTk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/eax95S/btsNSdw5ALI/zGSkANPxEvXyRCzmxvVoTk/img.png&quot; data-origin-width=&quot;467&quot; data-origin-height=&quot;587&quot; data-is-animation=&quot;false&quot; style=&quot;width: 28.3772%;&quot; data-widthpercent=&quot;28.71&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/eax95S/btsNSdw5ALI/zGSkANPxEvXyRCzmxvVoTk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Feax95S%2FbtsNSdw5ALI%2FzGSkANPxEvXyRCzmxvVoTk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;467&quot; height=&quot;587&quot;/&gt;&lt;/span&gt;&lt;/div&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;다음과 같이 로직을 구현한다. FaceAttacker의 경우 Default 값을 true로 설정한다.&lt;br /&gt;&lt;br /&gt;앞서 구현했던 Ability와 다른 점은&quot;Event ActivateAbilityFromEvent&quot; 노드를 사용해야 한다는 점이다.&lt;br /&gt;해당 GA는 인풋으로 발동되는 Ability가 아닌 Event로 발동되는 Ability이다. 따라서 기존에 Light Attack과 Heavy Attack 관련 Ability를 발동시키는 조건이 다르다.&lt;/blockquote&gt;
&lt;p style=&quot;color: #e8e6e3; text-align: start;&quot; data-ke-size=&quot;size16&quot; data-darkreader-inline-color=&quot;&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;2542&quot; data-origin-height=&quot;1301&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/beGjhH/btsNRZfa6xJ/wK2y0OJpfkmpPQUugVW9Uk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/beGjhH/btsNRZfa6xJ/wK2y0OJpfkmpPQUugVW9Uk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/beGjhH/btsNRZfa6xJ/wK2y0OJpfkmpPQUugVW9Uk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbeGjhH%2FbtsNRZfa6xJ%2FwK2y0OJpfkmpPQUugVW9Uk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;2542&quot; height=&quot;1301&quot; data-origin-width=&quot;2542&quot; data-origin-height=&quot;1301&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;앞서 구현했던 GA를 상속 받은 &quot;GA_Guardian&quot; 클래스를 생성하고, HitReact Montage를 할당한다.&lt;br /&gt;Tag Setting으로 HitReact Ability 발동 중에는 다른 모든 Ability를 취소하고 Block 처리한다.&lt;br /&gt;Instancing Policy의 경우 피격이 전투 중 수시로 발생하기 때문에 InstancedPerExecution 처럼 매번 인스턴스를 생성/파괴하게 되면 성능 낭비가 커진다.&lt;br /&gt;따라서 Actor로 설정해, 최초 Ability를 부여 했을 때 한 번만 인스턴스를 생성하도록 하고 이후 추가적인 피격 시 재활성화 되는 구조가 더 적합하다.&lt;/blockquote&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;771&quot; data-origin-height=&quot;177&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/Ch94l/btsNRIj5vAY/nHblhf5u9jmZRJiTWvCMD0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/Ch94l/btsNRIj5vAY/nHblhf5u9jmZRJiTWvCMD0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/Ch94l/btsNRIj5vAY/nHblhf5u9jmZRJiTWvCMD0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FCh94l%2FbtsNRIj5vAY%2FnHblhf5u9jmZRJiTWvCMD0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;771&quot; height=&quot;177&quot; data-origin-width=&quot;771&quot; data-origin-height=&quot;177&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;추가적으로 Ability Trigger에 Tag 설정을 진행한다.&lt;br /&gt;공격자가 공격 Ability에서 이 Tag로 Event를 전달하면, Enemy가 이 이벤트 태그를 가지고 있다면 수신할 수 있게 된다.&lt;/blockquote&gt;
&lt;p style=&quot;color: #e8e6e3; text-align: start;&quot; data-ke-size=&quot;size16&quot; data-darkreader-inline-color=&quot;&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 style=&quot;color: #e8e6e3; text-align: start;&quot; data-ke-size=&quot;size23&quot; data-darkreader-inline-color=&quot;&quot;&gt;&lt;b&gt;✅ StartUpData에 GA_HitReact 할당&lt;/b&gt;&lt;/h3&gt;
&lt;p&gt;&lt;figure class=&quot;imagegridblock&quot;&gt;
  &lt;div class=&quot;image-container&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bXU6FZ/btsNSLUpEBD/SVIJrSlKUPqInfkLKxx0V1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bXU6FZ/btsNSLUpEBD/SVIJrSlKUPqInfkLKxx0V1/img.png&quot; data-origin-width=&quot;1420&quot; data-origin-height=&quot;520&quot; data-is-animation=&quot;false&quot; style=&quot;width: 64.9488%; margin-right: 10px;&quot; data-widthpercent=&quot;65.71&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bXU6FZ/btsNSLUpEBD/SVIJrSlKUPqInfkLKxx0V1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbXU6FZ%2FbtsNSLUpEBD%2FSVIJrSlKUPqInfkLKxx0V1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1420&quot; height=&quot;520&quot;/&gt;&lt;/span&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/chf1m3/btsNRQCqQp3/aNPgxoiHkU4xUyOnA3fLRK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/chf1m3/btsNRQCqQp3/aNPgxoiHkU4xUyOnA3fLRK/img.png&quot; data-origin-width=&quot;1090&quot; data-origin-height=&quot;765&quot; data-is-animation=&quot;false&quot; style=&quot;width: 33.8884%;&quot; data-widthpercent=&quot;34.29&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/chf1m3/btsNRQCqQp3/aNPgxoiHkU4xUyOnA3fLRK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fchf1m3%2FbtsNRQCqQp3%2FaNPgxoiHkU4xUyOnA3fLRK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1090&quot; height=&quot;765&quot;/&gt;&lt;/span&gt;&lt;/div&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;Enemy의 StartUpData에 Reactive Ability 배열이 선언되어있다. 해당 배열에 방금 생성했던 &quot;GA_Guardian_HitReact&quot;를 할당 해준다.&lt;/blockquote&gt;
&lt;p style=&quot;color: #e8e6e3; text-align: start;&quot; data-ke-size=&quot;size16&quot; data-darkreader-inline-color=&quot;&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 style=&quot;color: #e8e6e3; text-align: start;&quot; data-darkreader-inline-color=&quot;&quot; data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;✅ 플레이어 공격 Ability에서 Target에게 Event 전달&lt;/b&gt;&lt;/h3&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock widthContent&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1642&quot; data-origin-height=&quot;1168&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bUCeAM/btsNSbsUDKE/OkKk4CWNCUVbErOb6eKWgk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bUCeAM/btsNSbsUDKE/OkKk4CWNCUVbErOb6eKWgk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bUCeAM/btsNSbsUDKE/OkKk4CWNCUVbErOb6eKWgk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbUCeAM%2FbtsNSbsUDKE%2FOkKk4CWNCUVbErOb6eKWgk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1642&quot; height=&quot;1168&quot; data-origin-width=&quot;1642&quot; data-origin-height=&quot;1168&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;현재 플레이어의 공격 Ability 클래스인데, 그래프가 복잡해 보여 데미지를 주는 로직을 함수화 시킬 것이다.&lt;br /&gt;그리고 함수 내부에서 데미지를 성공적으로 적용시켰다면, HitReact Event를 피격 대상에게 전송시킴으로써 GA_HitReact가 활성화 되도록 구현할 것이다.&lt;/blockquote&gt;
&lt;p style=&quot;color: #e8e6e3; text-align: start;&quot; data-ke-size=&quot;size16&quot; data-darkreader-inline-color=&quot;&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imagegridblock&quot;&gt;
  &lt;div class=&quot;image-container&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/nhuGI/btsNRG1l0xu/FVALz3bpn5WebzXxWBkqf0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/nhuGI/btsNRG1l0xu/FVALz3bpn5WebzXxWBkqf0/img.png&quot; data-origin-width=&quot;1711&quot; data-origin-height=&quot;1031&quot; data-is-animation=&quot;false&quot; style=&quot;width: 54.915%; margin-right: 10px;&quot; data-widthpercent=&quot;55.56&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/nhuGI/btsNRG1l0xu/FVALz3bpn5WebzXxWBkqf0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FnhuGI%2FbtsNRG1l0xu%2FFVALz3bpn5WebzXxWBkqf0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1711&quot; height=&quot;1031&quot;/&gt;&lt;/span&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bsTlbk/btsNRdEDS1L/ELfK8uFdPwiw5skKDjyAZK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bsTlbk/btsNRdEDS1L/ELfK8uFdPwiw5skKDjyAZK/img.png&quot; data-origin-width=&quot;1330&quot; data-origin-height=&quot;1002&quot; data-is-animation=&quot;false&quot; style=&quot;width: 43.9222%;&quot; data-widthpercent=&quot;44.44&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bsTlbk/btsNRdEDS1L/ELfK8uFdPwiw5skKDjyAZK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbsTlbk%2FbtsNRdEDS1L%2FELfK8uFdPwiw5skKDjyAZK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1330&quot; height=&quot;1002&quot;/&gt;&lt;/span&gt;&lt;/div&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;위와 같이 함수화 시켜 그래프를 정리하고, 함수 명을 &quot;HandleApplayDamage&quot;로 변경한다.&lt;/blockquote&gt;
&lt;p style=&quot;color: #e8e6e3; text-align: start;&quot; data-ke-size=&quot;size16&quot; data-darkreader-inline-color=&quot;&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock widthContent&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1906&quot; data-origin-height=&quot;588&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cwrl9L/btsNQX26vqw/IOhyLhrKSLreDHQDKPkQbK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cwrl9L/btsNQX26vqw/IOhyLhrKSLreDHQDKPkQbK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cwrl9L/btsNQX26vqw/IOhyLhrKSLreDHQDKPkQbK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fcwrl9L%2FbtsNQX26vqw%2FIOhyLhrKSLreDHQDKPkQbK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1906&quot; height=&quot;588&quot; data-origin-width=&quot;1906&quot; data-origin-height=&quot;588&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;함수 내부에서 데미지 적용이 Successful이면 &quot;Send Gameplay Event to Actor&quot; 노드로 피격 대상에게 HitReact Event Tag를 전달한다.&lt;/blockquote&gt;
&lt;p style=&quot;color: #e8e6e3; text-align: start;&quot; data-ke-size=&quot;size16&quot; data-darkreader-inline-color=&quot;&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 style=&quot;color: #c8c3bc; text-align: start;&quot; data-ke-size=&quot;size23&quot; data-darkreader-inline-color=&quot;&quot;&gt;&lt;b&gt; &lt;span&gt; GA_HitReact 테스트&lt;/span&gt;&lt;/b&gt;&lt;/h3&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock widthContent&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;2025-05-10 17-12-14.mp4_20250510_171300.gif&quot; data-origin-width=&quot;854&quot; data-origin-height=&quot;480&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/Ug21Q/btsNSMTkFbU/0CKluI7kZfNnHqb0kf6wKk/img.gif&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/Ug21Q/btsNSMTkFbU/0CKluI7kZfNnHqb0kf6wKk/img.gif&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/Ug21Q/btsNSMTkFbU/0CKluI7kZfNnHqb0kf6wKk/img.gif&quot; srcset=&quot;https://blog.kakaocdn.net/dn/Ug21Q/btsNSMTkFbU/0CKluI7kZfNnHqb0kf6wKk/img.gif&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;854&quot; height=&quot;480&quot; data-filename=&quot;2025-05-10 17-12-14.mp4_20250510_171300.gif&quot; data-origin-width=&quot;854&quot; data-origin-height=&quot;480&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;정상적으로 피격 시, HitReact Animation이 나오는 것을 확인할 수 있었고, 플레이어 위치로 회전까지 정상 작동한다.&lt;/blockquote&gt;
&lt;p style=&quot;color: #e8e6e3; text-align: start;&quot; data-ke-size=&quot;size16&quot; data-darkreader-inline-color=&quot;&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 style=&quot;color: #e8e6e3; text-align: start;&quot; data-ke-size=&quot;size23&quot; data-darkreader-inline-color=&quot;&quot;&gt;&lt;b&gt;✅ 피격 시, 머터리얼 색상 변경&lt;/b&gt;&lt;/h3&gt;
&lt;p&gt;&lt;figure class=&quot;imagegridblock&quot;&gt;
  &lt;div class=&quot;image-container&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/tFiW4/btsNTmmqJGO/N9VZrjKIluWGBXZCapKSeK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/tFiW4/btsNTmmqJGO/N9VZrjKIluWGBXZCapKSeK/img.png&quot; data-origin-width=&quot;2038&quot; data-origin-height=&quot;1263&quot; data-is-animation=&quot;false&quot; style=&quot;width: 54.3362%; margin-right: 10px;&quot; data-widthpercent=&quot;54.98&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/tFiW4/btsNTmmqJGO/N9VZrjKIluWGBXZCapKSeK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FtFiW4%2FbtsNTmmqJGO%2FN9VZrjKIluWGBXZCapKSeK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;2038&quot; height=&quot;1263&quot;/&gt;&lt;/span&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/dqBPIX/btsNS6qBAjU/a7SRKEQF9eg3OAXTXnq8xk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/dqBPIX/btsNS6qBAjU/a7SRKEQF9eg3OAXTXnq8xk/img.png&quot; data-origin-width=&quot;1681&quot; data-origin-height=&quot;1272&quot; data-is-animation=&quot;false&quot; data-widthpercent=&quot;45.02&quot; style=&quot;width: 44.501%;&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/dqBPIX/btsNS6qBAjU/a7SRKEQF9eg3OAXTXnq8xk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FdqBPIX%2FbtsNS6qBAjU%2Fa7SRKEQF9eg3OAXTXnq8xk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1681&quot; height=&quot;1272&quot;/&gt;&lt;/span&gt;&lt;/div&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;Material Function을 다음과 같이 구현한 뒤 이를 이용해, HitReact시 Material 색을 잠시 바꿨다가 다시 원래대로 돌리도록 한다.&lt;/blockquote&gt;
&lt;p style=&quot;color: #e8e6e3; text-align: start;&quot; data-ke-size=&quot;size16&quot; data-darkreader-inline-color=&quot;&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imagegridblock&quot;&gt;
  &lt;div class=&quot;image-container&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/ciary8/btsNS3OaX3c/6MRiHjKW8s7YOaTq8o28TK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/ciary8/btsNS3OaX3c/6MRiHjKW8s7YOaTq8o28TK/img.png&quot; data-origin-width=&quot;827&quot; data-origin-height=&quot;952&quot; data-is-animation=&quot;false&quot; style=&quot;width: 33.4917%; margin-right: 10px;&quot; data-widthpercent=&quot;33.89&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/ciary8/btsNS3OaX3c/6MRiHjKW8s7YOaTq8o28TK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fciary8%2FbtsNS3OaX3c%2F6MRiHjKW8s7YOaTq8o28TK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;827&quot; height=&quot;952&quot;/&gt;&lt;/span&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/8xycl/btsNS7Qt9jJ/NFihqjFVaaCVc26FwXLNvK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/8xycl/btsNS7Qt9jJ/NFihqjFVaaCVc26FwXLNvK/img.png&quot; data-origin-width=&quot;500&quot; data-origin-height=&quot;295&quot; data-is-animation=&quot;false&quot; style=&quot;width: 65.3456%;&quot; data-widthpercent=&quot;66.11&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/8xycl/btsNS7Qt9jJ/NFihqjFVaaCVc26FwXLNvK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F8xycl%2FbtsNS7Qt9jJ%2FNFihqjFVaaCVc26FwXLNvK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;500&quot; height=&quot;295&quot;/&gt;&lt;/span&gt;&lt;/div&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;Material Instance를 생성한다.&lt;/blockquote&gt;
&lt;p style=&quot;color: #e8e6e3; text-align: start;&quot; data-ke-size=&quot;size16&quot; data-darkreader-inline-color=&quot;&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imagegridblock&quot;&gt;
  &lt;div class=&quot;image-container&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/tH4Bp/btsNSxa1v5t/Gkec1ZSxUdd3NICmv4tetk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/tH4Bp/btsNSxa1v5t/Gkec1ZSxUdd3NICmv4tetk/img.png&quot; data-origin-width=&quot;2542&quot; data-origin-height=&quot;1372&quot; data-is-animation=&quot;false&quot; style=&quot;width: 48.6646%; margin-right: 10px;&quot; data-widthpercent=&quot;49.24&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/tH4Bp/btsNSxa1v5t/Gkec1ZSxUdd3NICmv4tetk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FtH4Bp%2FbtsNSxa1v5t%2FGkec1ZSxUdd3NICmv4tetk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;2542&quot; height=&quot;1372&quot;/&gt;&lt;/span&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/xDJUT/btsNSygOGE5/DC0vfugYzseEeZUkVlO1vK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/xDJUT/btsNSygOGE5/DC0vfugYzseEeZUkVlO1vK/img.png&quot; data-origin-width=&quot;2552&quot; data-origin-height=&quot;1336&quot; data-is-animation=&quot;false&quot; data-widthpercent=&quot;50.76&quot; style=&quot;width: 50.1726%;&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/xDJUT/btsNSygOGE5/DC0vfugYzseEeZUkVlO1vK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FxDJUT%2FbtsNSygOGE5%2FDC0vfugYzseEeZUkVlO1vK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;2552&quot; height=&quot;1336&quot;/&gt;&lt;/span&gt;&lt;/div&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;컨텐츠 브라우저에서 스켈레탈 메시를 선택한 후 Material Instance로 돌아와 뷰포트 우측 하단에 버튼을 클릭하면 프리뷰 상태로 작업할 수 있다.&lt;/blockquote&gt;
&lt;p style=&quot;color: #e8e6e3; text-align: start;&quot; data-ke-size=&quot;size16&quot; data-darkreader-inline-color=&quot;&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imagegridblock&quot;&gt;
  &lt;div class=&quot;image-container&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/pHjAM/btsNSNEG2HO/8TpPQaKicidNo3TKYe1kvK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/pHjAM/btsNSNEG2HO/8TpPQaKicidNo3TKYe1kvK/img.png&quot; data-origin-width=&quot;2558&quot; data-origin-height=&quot;1326&quot; data-is-animation=&quot;false&quot; style=&quot;width: 50.3427%; margin-right: 10px;&quot; data-widthpercent=&quot;50.94&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/pHjAM/btsNSNEG2HO/8TpPQaKicidNo3TKYe1kvK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FpHjAM%2FbtsNSNEG2HO%2F8TpPQaKicidNo3TKYe1kvK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;2558&quot; height=&quot;1326&quot;/&gt;&lt;/span&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/dEqkUN/btsNSKVutzr/C5Tbel94klhKhjI4im6cIK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/dEqkUN/btsNSKVutzr/C5Tbel94klhKhjI4im6cIK/img.png&quot; data-origin-width=&quot;2557&quot; data-origin-height=&quot;1376&quot; data-is-animation=&quot;false&quot; style=&quot;width: 48.4945%;&quot; data-widthpercent=&quot;49.06&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/dEqkUN/btsNSKVutzr/C5Tbel94klhKhjI4im6cIK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FdEqkUN%2FbtsNSKVutzr%2FC5Tbel94klhKhjI4im6cIK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;2557&quot; height=&quot;1376&quot;/&gt;&lt;/span&gt;&lt;/div&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;HitFxSwitch 값을 1로 올려 어떤 색으로 표시할지 정한 후, 다시 0으로 맞춰놓고 저장한다.&lt;/blockquote&gt;
&lt;p style=&quot;color: #e8e6e3; text-align: start;&quot; data-ke-size=&quot;size16&quot; data-darkreader-inline-color=&quot;&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock widthContent&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;2557&quot; data-origin-height=&quot;1375&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cavH41/btsNTxBn4Qp/mkNYniqofDqS8ZEhh2f27K/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cavH41/btsNTxBn4Qp/mkNYniqofDqS8ZEhh2f27K/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cavH41/btsNTxBn4Qp/mkNYniqofDqS8ZEhh2f27K/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcavH41%2FbtsNTxBn4Qp%2FmkNYniqofDqS8ZEhh2f27K%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;2557&quot; height=&quot;1375&quot; data-origin-width=&quot;2557&quot; data-origin-height=&quot;1375&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;Enemy의 SkeletalMesh에서 방금 만든 Material Instance로 설정해준다.&lt;/blockquote&gt;
&lt;p style=&quot;color: #e8e6e3; text-align: start;&quot; data-ke-size=&quot;size16&quot; data-darkreader-inline-color=&quot;&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imagegridblock&quot;&gt;
  &lt;div class=&quot;image-container&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/by3YkG/btsNTlny33q/i0rJxyXd1vpAojR8Thsie0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/by3YkG/btsNTlny33q/i0rJxyXd1vpAojR8Thsie0/img.png&quot; data-origin-width=&quot;2216&quot; data-origin-height=&quot;1277&quot; data-is-animation=&quot;false&quot; style=&quot;width: 40.6076%; margin-right: 10px;&quot; data-widthpercent=&quot;41.09&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/by3YkG/btsNTlny33q/i0rJxyXd1vpAojR8Thsie0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fby3YkG%2FbtsNTlny33q%2Fi0rJxyXd1vpAojR8Thsie0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;2216&quot; height=&quot;1277&quot;/&gt;&lt;/span&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bXtobe/btsNSlhMoq6/rxDQFp0XHcAZmWiQnKjkD1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bXtobe/btsNSlhMoq6/rxDQFp0XHcAZmWiQnKjkD1/img.png&quot; data-origin-width=&quot;1605&quot; data-origin-height=&quot;645&quot; data-is-animation=&quot;false&quot; style=&quot;width: 58.2296%;&quot; data-widthpercent=&quot;58.91&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bXtobe/btsNSlhMoq6/rxDQFp0XHcAZmWiQnKjkD1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbXtobe%2FbtsNSlhMoq6%2FrxDQFp0XHcAZmWiQnKjkD1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1605&quot; height=&quot;645&quot;/&gt;&lt;/span&gt;&lt;/div&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;Hit React Ability에서 몽타주가 있을 경우와 없을 경우 모두 스켈레탈 메시에서 머터리얼의 Scalar 값을 조절해주었다.&lt;br /&gt;Parameter 이름도 Material Instance에서 복사해서 정확하게 입력해주도록 한다.&lt;br /&gt;마지막으로 Ability가 종료되면 다시 0의 값을 주어 원래 상태로 되돌린다.&lt;/blockquote&gt;
&lt;p style=&quot;color: #e8e6e3; text-align: start;&quot; data-ke-size=&quot;size16&quot; data-darkreader-inline-color=&quot;&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 style=&quot;color: #c8c3bc; text-align: start;&quot; data-ke-size=&quot;size23&quot; data-darkreader-inline-color=&quot;&quot;&gt;&lt;b&gt; &lt;span&gt; 피격 시, 머터리얼 변화 테스트&lt;/span&gt;&lt;/b&gt;&lt;/h3&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock widthContent&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;2025-05-10 17-45-40.mp4_20250510_174654.gif&quot; data-origin-width=&quot;854&quot; data-origin-height=&quot;480&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cnDzxk/btsNS9HvMCq/zAx9bmtXHwLJhaVBfu4JYK/img.gif&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cnDzxk/btsNS9HvMCq/zAx9bmtXHwLJhaVBfu4JYK/img.gif&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cnDzxk/btsNS9HvMCq/zAx9bmtXHwLJhaVBfu4JYK/img.gif&quot; srcset=&quot;https://blog.kakaocdn.net/dn/cnDzxk/btsNS9HvMCq/zAx9bmtXHwLJhaVBfu4JYK/img.gif&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;854&quot; height=&quot;480&quot; data-filename=&quot;2025-05-10 17-45-40.mp4_20250510_174654.gif&quot; data-origin-width=&quot;854&quot; data-origin-height=&quot;480&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;정상적으로 HitReact Ability가 활성화 되면, Material 색상이 변하는 것을 확인할 수 있다.&lt;/blockquote&gt;
&lt;p style=&quot;color: #e8e6e3; text-align: start;&quot; data-ke-size=&quot;size16&quot; data-darkreader-inline-color=&quot;&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 style=&quot;color: #e8e6e3; text-align: start;&quot; data-ke-size=&quot;size26&quot; data-darkreader-inline-color=&quot;&quot;&gt;&lt;b&gt;  타격 성공시 피드백 효과&amp;nbsp; 구현&lt;/b&gt;&lt;/h2&gt;
&lt;h3 style=&quot;color: #e8e6e3; text-align: start;&quot; data-ke-size=&quot;size23&quot; data-darkreader-inline-color=&quot;&quot;&gt;&lt;b&gt;✅ 타격 성공 시, Pause 효과&lt;/b&gt;&lt;/h3&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;&lt;span style=&quot;background-color: #781b33;&quot; data-darkreader-inline-bgcolor=&quot;&quot;&gt;* 싱글플레이 게임 전용&amp;nbsp;&lt;/span&gt;&lt;br /&gt;플레이어의 공격이 적에게 적중하면 잠깐 월드의 시간을 미세하게 정지시켜 조금 더 다이내믹한 타격 피드백을 줄 수 있다.&lt;br /&gt;다만 월드의 시간을 조정하는 것이기 때문에 멀티플레이에선 해당 Ability를 사용하면 안된다.&lt;/blockquote&gt;
&lt;h4 style=&quot;color: #e8e6e3; text-align: start;&quot; data-darkreader-inline-color=&quot;&quot; data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;✔️ RPGGameplayTags&lt;/b&gt;&lt;/h4&gt;
&lt;div style=&quot;background-color: #1b1d1e; color: #c8c3bc; text-align: start;&quot; data-text-less=&quot;닫기&quot; data-text-more=&quot;더보기&quot; data-ke-type=&quot;moreLess&quot; data-darkreader-inline-bgcolor=&quot;&quot; data-darkreader-inline-color=&quot;&quot;&gt;&lt;a class=&quot;btn-toggle-moreless&quot;&gt;더보기&lt;/a&gt;
&lt;div class=&quot;moreless-content&quot;&gt;
&lt;p style=&quot;color: #e8e6e3; text-align: start;&quot; data-darkreader-inline-color=&quot;&quot; data-ke-size=&quot;size18&quot;&gt;&lt;b&gt; &lt;span&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;RPGGameplayTags.h&lt;/span&gt;&lt;/b&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1746870989968&quot; class=&quot;cpp&quot; data-ke-language=&quot;cpp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;#pragma once

#include &quot;NativeGameplayTags.h&quot;

namespace RPGGameplayTags
{
	/** 생략 */

	/** Player Tags */
	RPG_API UE_DECLARE_GAMEPLAY_TAG_EXTERN(Player_Ability_Equip_Axe);
	RPG_API UE_DECLARE_GAMEPLAY_TAG_EXTERN(Player_Ability_Unequip_Axe);
	RPG_API UE_DECLARE_GAMEPLAY_TAG_EXTERN(Player_Ability_Attack_Light_Axe);
	RPG_API UE_DECLARE_GAMEPLAY_TAG_EXTERN(Player_Ability_Attack_Heavy_Axe);
	RPG_API UE_DECLARE_GAMEPLAY_TAG_EXTERN(Player_Ability_HitPause); // 공격 성공시 Pause 태그 추가

	RPG_API UE_DECLARE_GAMEPLAY_TAG_EXTERN(Player_Weapon_Axe);

	RPG_API UE_DECLARE_GAMEPLAY_TAG_EXTERN(Player_Event_Equip_Axe);
	RPG_API UE_DECLARE_GAMEPLAY_TAG_EXTERN(Player_Event_Unequip_Axe);
	RPG_API UE_DECLARE_GAMEPLAY_TAG_EXTERN(Player_Event_HitPause); // 공격 성공시 Pause Event 태그 추가
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;b&gt; &lt;span&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;RPGGameplayTags.h&lt;/span&gt;&lt;/b&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1746871028095&quot; class=&quot;cpp&quot; data-ke-language=&quot;cpp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;#include &quot;RPGGameplayTags.h&quot;

namespace RPGGameplayTags
{
	/** 생략 */

	/** Player Tags */
	UE_DEFINE_GAMEPLAY_TAG(Player_Ability_Equip_Axe, &quot;Player.Ability.Equip.Axe&quot;);
	UE_DEFINE_GAMEPLAY_TAG(Player_Ability_Unequip_Axe, &quot;Player.Ability.Unequip.Axe&quot;);
	UE_DEFINE_GAMEPLAY_TAG(Player_Ability_Attack_Light_Axe, &quot;Player.Ability.Attack.Light.Axe&quot;);
	UE_DEFINE_GAMEPLAY_TAG(Player_Ability_Attack_Heavy_Axe, &quot;Player.Ability.Attack.Heavy.Axe&quot;);
	UE_DEFINE_GAMEPLAY_TAG(Player_Ability_HitPause, &quot;Player.Ability.HitPause&quot;); // 공격 성공시 Pause 태그 추가


	UE_DEFINE_GAMEPLAY_TAG(Player_Weapon_Axe, &quot;Player.Weapon.Axe&quot;);

	UE_DEFINE_GAMEPLAY_TAG(Player_Event_Equip_Axe, &quot;Player.Event.Equip.Axe&quot;);
	UE_DEFINE_GAMEPLAY_TAG(Player_Event_Unequip_Axe, &quot;Player.Event.Unequip.Axe&quot;);
	UE_DEFINE_GAMEPLAY_TAG(Player_Event_HitPause, &quot;Player.Event.HitPause&quot;); // 공격 성공시 Pause Event 태그 추가
}&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;h4 style=&quot;color: #e8e6e3; text-align: start;&quot; data-ke-size=&quot;size20&quot; data-darkreader-inline-color=&quot;&quot;&gt;&lt;b&gt;✔️ Gameplay Ability(HitPause) 구현&lt;/b&gt;&lt;/h4&gt;
&lt;p&gt;&lt;figure class=&quot;imagegridblock&quot;&gt;
  &lt;div class=&quot;image-container&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/DQbqT/btsNSOXURFV/QpNu08lCfbOwBUuqXoGwA0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/DQbqT/btsNSOXURFV/QpNu08lCfbOwBUuqXoGwA0/img.png&quot; data-origin-width=&quot;500&quot; data-origin-height=&quot;920&quot; data-is-animation=&quot;false&quot; style=&quot;width: 21.6734%; margin-right: 10px;&quot; data-widthpercent=&quot;21.93&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/DQbqT/btsNSOXURFV/QpNu08lCfbOwBUuqXoGwA0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FDQbqT%2FbtsNSOXURFV%2FQpNu08lCfbOwBUuqXoGwA0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;500&quot; height=&quot;920&quot;/&gt;&lt;/span&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/rwk3Q/btsNSdw7rDP/YNkk771le9r86KvZCWXBxK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/rwk3Q/btsNSdw7rDP/YNkk771le9r86KvZCWXBxK/img.png&quot; data-origin-width=&quot;2558&quot; data-origin-height=&quot;1322&quot; data-is-animation=&quot;false&quot; style=&quot;width: 77.1638%;&quot; data-widthpercent=&quot;78.07&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/rwk3Q/btsNSdw7rDP/YNkk771le9r86KvZCWXBxK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Frwk3Q%2FbtsNSdw7rDP%2FYNkk771le9r86KvZCWXBxK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;2558&quot; height=&quot;1322&quot;/&gt;&lt;/span&gt;&lt;/div&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;에디터에서 다음과 같이 &quot;GA_Hero_HitPause&quot;를 구현한다. 이로써 대상을 공격해서 피격처리가 되었을 때 다이나믹한 느낌을 만들어 낼 수 있다.&amp;nbsp;&lt;br /&gt;&lt;br /&gt;하지만 구현부를 살펴보면 월드 전체의 시간 흐름을 조절하여 효과를 Hit 피드백을 주는 것이기 때문에, &lt;span style=&quot;background-color: #781b33; --darkreader-inline-bgcolor: var(--darkreader-background-a6bc00, #859600);&quot; data-darkreader-inline-bgcolor=&quot;&quot;&gt;싱글플레이에서는 괜찮지만 멀티플레이 게임에서는 해당 Ability를 사용하면 안된다.&lt;/span&gt;&lt;br /&gt;멀티플레이에선 Custom Time Dilation 기능을 만들어 자신(로컬)에게만 Dilation을 주도록 설정하는 등 다른 방법으로 구현해야한다.&lt;/blockquote&gt;
&lt;p style=&quot;color: #e8e6e3; text-align: start;&quot; data-ke-size=&quot;size16&quot; data-darkreader-inline-color=&quot;&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 style=&quot;color: #e8e6e3; text-align: start;&quot; data-darkreader-inline-color=&quot;&quot; data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;✔️ Ability 할당&lt;/b&gt;&lt;/h4&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock widthContent&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1382&quot; data-origin-height=&quot;522&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/dxgdxE/btsNRKIX4rO/3zCSTk6S9KVzaK6odakQV0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/dxgdxE/btsNRKIX4rO/3zCSTk6S9KVzaK6odakQV0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/dxgdxE/btsNRKIX4rO/3zCSTk6S9KVzaK6odakQV0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FdxgdxE%2FbtsNRKIX4rO%2F3zCSTk6S9KVzaK6odakQV0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1382&quot; height=&quot;522&quot; data-origin-width=&quot;1382&quot; data-origin-height=&quot;522&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;Hero 캐릭터의 StartUpData에 방금 만든 Ability를 할당해준다.&lt;/blockquote&gt;
&lt;p style=&quot;color: #e8e6e3; text-align: start;&quot; data-ke-size=&quot;size16&quot; data-darkreader-inline-color=&quot;&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 style=&quot;color: #e8e6e3; text-align: start;&quot; data-ke-size=&quot;size20&quot; data-darkreader-inline-color=&quot;&quot;&gt;&lt;b&gt;✔️ Hit 처리 되었을 때, 이벤트 전송해 GA_HitPause 활성화 로직&lt;/b&gt;&lt;/h4&gt;
&lt;pre id=&quot;code_1746872534756&quot; class=&quot;cpp&quot; data-ke-language=&quot;cpp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;void UHeroCombatComponent::OnHitTargetActor(AActor* HitActor)
{
	if (OverlappedActors.Contains(HitActor))
	{
		return;
	}

	OverlappedActors.AddUnique(HitActor);

	FGameplayEventData Data;
	Data.Instigator = GetOwningPawn();
	Data.Target = HitActor;

	UAbilitySystemBlueprintLibrary::SendGameplayEventToActor(
		GetOwningPawn(),
		RPGGameplayTags::Shared_Event_MeleeHit,
		Data
	);
	
    // Hit Pause Ability 발동 Event 전달
	UAbilitySystemBlueprintLibrary::SendGameplayEventToActor(
		GetOwningPawn(),
		RPGGameplayTags::Player_Event_HitPause,
		FGameplayEventData()
	);
}&lt;/code&gt;&lt;/pre&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;이전에 무기에 오버랩 되면 델리게이트 호출로 OnHitTargetActor가 호출되도록 구현했었다.&lt;br /&gt;해당 함수 내부에서 HitPause 어빌리티를 실행할 Event를 전송한다.&lt;/blockquote&gt;
&lt;p style=&quot;color: #e8e6e3; text-align: start;&quot; data-ke-size=&quot;size16&quot; data-darkreader-inline-color=&quot;&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 style=&quot;color: #c8c3bc; text-align: start;&quot; data-ke-size=&quot;size23&quot; data-darkreader-inline-color=&quot;&quot;&gt;&lt;b&gt; &lt;span&gt; HitPause Ability 테스트 결과&lt;/span&gt;&lt;/b&gt;&lt;/h3&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock widthContent&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;2025-05-10 19-16-31.mp4_20250510_191733.gif&quot; data-origin-width=&quot;854&quot; data-origin-height=&quot;480&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/qIirM/btsNSc54HVk/dlgcIX267q5uUPLlqkT2i1/img.gif&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/qIirM/btsNSc54HVk/dlgcIX267q5uUPLlqkT2i1/img.gif&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/qIirM/btsNSc54HVk/dlgcIX267q5uUPLlqkT2i1/img.gif&quot; srcset=&quot;https://blog.kakaocdn.net/dn/qIirM/btsNSc54HVk/dlgcIX267q5uUPLlqkT2i1/img.gif&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;854&quot; height=&quot;480&quot; data-filename=&quot;2025-05-10 19-16-31.mp4_20250510_191733.gif&quot; data-origin-width=&quot;854&quot; data-origin-height=&quot;480&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;공격이 Target에 적중되지 않으면 Ability가 발생하지 않고, Target에 적중되는 순간 아주 미세하게 Pause 효과를 주어 전투의 느낌을 더 다이내믹하게 느껴진다.&lt;/blockquote&gt;
&lt;p style=&quot;color: #e8e6e3; text-align: start;&quot; data-ke-size=&quot;size16&quot; data-darkreader-inline-color=&quot;&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 style=&quot;color: #e8e6e3; text-align: start;&quot; data-ke-size=&quot;size20&quot; data-darkreader-inline-color=&quot;&quot;&gt;&lt;b&gt; CameraShake&lt;/b&gt;&lt;/h4&gt;
&lt;p&gt;&lt;figure class=&quot;imagegridblock&quot;&gt;
  &lt;div class=&quot;image-container&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/sjRsN/btsNR6581OR/aSdjFDcQLeQIHkSfsoIuQk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/sjRsN/btsNR6581OR/aSdjFDcQLeQIHkSfsoIuQk/img.png&quot; data-origin-width=&quot;756&quot; data-origin-height=&quot;888&quot; data-is-animation=&quot;false&quot; style=&quot;width: 16.189%; margin-right: 10px;&quot; data-widthpercent=&quot;16.57&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/sjRsN/btsNR6581OR/aSdjFDcQLeQIHkSfsoIuQk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FsjRsN%2FbtsNR6581OR%2FaSdjFDcQLeQIHkSfsoIuQk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;756&quot; height=&quot;888&quot;/&gt;&lt;/span&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/be4rFg/btsNS9U4W2D/2EYqFSprjFoIojXp0m70u0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/be4rFg/btsNS9U4W2D/2EYqFSprjFoIojXp0m70u0/img.png&quot; data-origin-width=&quot;2542&quot; data-origin-height=&quot;1373&quot; data-is-animation=&quot;false&quot; style=&quot;width: 35.2059%; margin-right: 10px;&quot; data-widthpercent=&quot;36.04&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/be4rFg/btsNS9U4W2D/2EYqFSprjFoIojXp0m70u0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fbe4rFg%2FbtsNS9U4W2D%2F2EYqFSprjFoIojXp0m70u0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;2542&quot; height=&quot;1373&quot;/&gt;&lt;/span&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/dCjeFe/btsNSzUnEwI/8GNPKZ5Hk7lTvVZJLuS41k/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/dCjeFe/btsNSzUnEwI/8GNPKZ5Hk7lTvVZJLuS41k/img.png&quot; data-origin-width=&quot;1672&quot; data-origin-height=&quot;687&quot; data-is-animation=&quot;false&quot; style=&quot;width: 46.2796%;&quot; data-widthpercent=&quot;47.39&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/dCjeFe/btsNSzUnEwI/8GNPKZ5Hk7lTvVZJLuS41k/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FdCjeFe%2FbtsNSzUnEwI%2F8GNPKZ5Hk7lTvVZJLuS41k%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1672&quot; height=&quot;687&quot;/&gt;&lt;/span&gt;&lt;/div&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;추가적으로 HitPause에서 CameraShake를 주어 조금 더 역동적으로 전투 경험을 느끼게 할 수도 있다.&lt;/blockquote&gt;
&lt;p style=&quot;color: #e8e6e3; text-align: start;&quot; data-ke-size=&quot;size16&quot; data-darkreader-inline-color=&quot;&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 style=&quot;color: #e8e6e3; text-align: start;&quot; data-darkreader-inline-color=&quot;&quot; data-ke-size=&quot;size20&quot;&gt;&lt;b&gt; 사운드 정리&lt;/b&gt;&lt;/h4&gt;
&lt;p&gt;&lt;figure class=&quot;imagegridblock&quot;&gt;
  &lt;div class=&quot;image-container&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/uWymA/btsNSJvGFl8/Zt3HJ6XJluwfu0ZysX6E5K/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/uWymA/btsNSJvGFl8/Zt3HJ6XJluwfu0ZysX6E5K/img.png&quot; data-origin-width=&quot;2558&quot; data-origin-height=&quot;1376&quot; data-is-animation=&quot;false&quot; style=&quot;width: 41.4544%; margin-right: 10px;&quot; data-widthpercent=&quot;42.44&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/uWymA/btsNSJvGFl8/Zt3HJ6XJluwfu0ZysX6E5K/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FuWymA%2FbtsNSJvGFl8%2FZt3HJ6XJluwfu0ZysX6E5K%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;2558&quot; height=&quot;1376&quot;/&gt;&lt;/span&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bejemz/btsNScSxU29/peXH1GCXntaYyerTtbuslk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bejemz/btsNScSxU29/peXH1GCXntaYyerTtbuslk/img.png&quot; data-origin-width=&quot;699&quot; data-origin-height=&quot;1117&quot; data-is-animation=&quot;false&quot; style=&quot;width: 13.9545%; margin-right: 10px;&quot; data-widthpercent=&quot;14.29&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bejemz/btsNScSxU29/peXH1GCXntaYyerTtbuslk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fbejemz%2FbtsNScSxU29%2FpeXH1GCXntaYyerTtbuslk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;699&quot; height=&quot;1117&quot;/&gt;&lt;/span&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bgaVJG/btsNSf9AHGy/StB1hzgkCkiIyCEJFOS30k/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bgaVJG/btsNSf9AHGy/StB1hzgkCkiIyCEJFOS30k/img.png&quot; data-origin-width=&quot;1232&quot; data-origin-height=&quot;650&quot; data-is-animation=&quot;false&quot; style=&quot;width: 42.2655%;&quot; data-widthpercent=&quot;43.27&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bgaVJG/btsNSf9AHGy/StB1hzgkCkiIyCEJFOS30k/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbgaVJG%2FbtsNSf9AHGy%2FStB1hzgkCkiIyCEJFOS30k%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1232&quot; height=&quot;650&quot;/&gt;&lt;/span&gt;&lt;/div&gt;
&lt;/figure&gt;
&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1761&quot; data-origin-height=&quot;1213&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bHHKMS/btsNRJcgBGi/pp9HMLBoVEpVKMkIBRu1dK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bHHKMS/btsNRJcgBGi/pp9HMLBoVEpVKMkIBRu1dK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bHHKMS/btsNRJcgBGi/pp9HMLBoVEpVKMkIBRu1dK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbHHKMS%2FbtsNRJcgBGi%2Fpp9HMLBoVEpVKMkIBRu1dK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1761&quot; height=&quot;1213&quot; data-origin-width=&quot;1761&quot; data-origin-height=&quot;1213&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;HitReaction Sound를 몽타주에 넣었을 때, 적이 한번에 여러 명이 피격당하면 소리가 그 수 만큼 재생되어 정신없는 사운드가 될 것이다.&lt;br /&gt;Concurrency를 생성해 한 번에 1개의 사운드만 재생되도록 설정한다면 동시에 여러 명을 타격하더라도, 하나의 HitReact 사운드를 재생시킬 수 있다.&lt;/blockquote&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&amp;nbsp;&lt;/h4&gt;</description>
      <category>내배캠/GAS(Gameplay Ability System)</category>
      <author>동그래님</author>
      <guid isPermaLink="true">https://dong-grae.tistory.com/236</guid>
      <comments>https://dong-grae.tistory.com/236#entry236comment</comments>
      <pubDate>Sat, 10 May 2025 17:14:44 +0900</pubDate>
    </item>
    <item>
      <title>GAS_13_피격 대상에게 GameEffect 적용</title>
      <link>https://dong-grae.tistory.com/235</link>
      <description>&lt;h2 style=&quot;color: #e8e6e3; text-align: start;&quot; data-ke-size=&quot;size26&quot; data-darkreader-inline-color=&quot;&quot;&gt;&lt;b&gt;  FGameEffectSpecHandle 생성&lt;/b&gt;&lt;/h2&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;앞서 진행했던 흐름은 WeaponBase 클래스에서 Collision에 OverlapEvent가 발생하면 델리게이트로 BroadCasting을 하고,&lt;br /&gt;이를 구독하고 있는 CombatComponent가 감지된 객체 정보를 Event Tag 기반으로 GameplayAbility로 Event를 송신하였고, 이를 Melee Attack 관련 Gameplay Ability에서 Event를 수신 받아 현재 피격된 대상이 누구인지까지 알게 되었다.&lt;br /&gt;&lt;br /&gt;이제 무기 데미지, 공격 타입, 콤보 횟수 등의 요소를 저장하는 FGamaEffectSpecHandle을 생성하고 추후 최종 피해량을 계산하고 데미지를 적용시키는 것을 구현하려한다.&lt;br /&gt;FGameplayEffectSpecHandle에 무기 데미지, 공격 타입, 콤보 횟수 등의 요소들을 담고 ExecutionCalculation에게 넘겨주어 계산을 하는 흐름이다.&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 style=&quot;color: #e8e6e3; text-align: start;&quot; data-darkreader-inline-color=&quot;&quot; data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;✅ FGameplayEffectSpecHandle 반환 함수 구현&amp;nbsp;&lt;/b&gt;&lt;/h3&gt;
&lt;h4 style=&quot;color: #e8e6e3; text-align: start;&quot; data-ke-size=&quot;size20&quot; data-darkreader-inline-color=&quot;&quot;&gt;&lt;b&gt;✔️&lt;span&gt;&lt;span&gt;&lt;span&gt; RPGGameplayTags&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/b&gt;&lt;/h4&gt;
&lt;div style=&quot;background-color: #1b1d1e; color: #c8c3bc; text-align: start;&quot; data-darkreader-inline-color=&quot;&quot; data-darkreader-inline-bgcolor=&quot;&quot; data-ke-type=&quot;moreLess&quot; data-text-more=&quot;더보기&quot; data-text-less=&quot;닫기&quot;&gt;&lt;a class=&quot;btn-toggle-moreless&quot;&gt;더보기&lt;/a&gt;
&lt;div class=&quot;moreless-content&quot;&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;b&gt; RPGGameplayTags&lt;/b&gt;&lt;b&gt;&lt;span&gt;.h&lt;/span&gt;&lt;/b&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1746783360719&quot; class=&quot;cpp&quot; data-ke-language=&quot;cpp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;#pragma once

#include &quot;NativeGameplayTags.h&quot;

namespace RPGGameplayTags
{
	/** 생략 */
    
	/** Shared Tags */
	RPG_API UE_DECLARE_GAMEPLAY_TAG_EXTERN(Shared_Event_MeleeHit);

	RPG_API UE_DECLARE_GAMEPLAY_TAG_EXTERN(Shared_SetByCaller_BaseDamage); // 기본 데미지 Tag 추가

}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;b&gt; RPGGameplayTags&lt;/b&gt;&lt;b&gt;&lt;span&gt;.cpp&lt;/span&gt;&lt;/b&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1746783406131&quot; class=&quot;cpp&quot; data-ke-language=&quot;cpp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;#include &quot;RPGGameplayTags.h&quot;

namespace RPGGameplayTags
{
	/** 생략 */

	/** Shared Tags */
	UE_DEFINE_GAMEPLAY_TAG(Shared_Event_MeleeHit, &quot;Shared.Event.MeleeHit&quot;);

	UE_DEFINE_GAMEPLAY_TAG(Shared_SetByCaller_BaseDamage, &quot;Shared.SetByCaller.BaseDamage&quot;);
}&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;외부에서 지정해주는 기본 데미지를 뜻하는 &quot;Shared.SetByCaller.BaseDamage&quot; Tag를 정의한다.&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 style=&quot;color: #e8e6e3; text-align: start;&quot; data-darkreader-inline-color=&quot;&quot; data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;✔️&lt;span&gt;&lt;span&gt;&lt;span&gt;&lt;span&gt; WarriorHeroGameplayAbility&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/b&gt;&lt;/h4&gt;
&lt;div style=&quot;background-color: #1b1d1e; color: #c8c3bc; text-align: start;&quot; data-text-less=&quot;닫기&quot; data-text-more=&quot;더보기&quot; data-ke-type=&quot;moreLess&quot; data-darkreader-inline-bgcolor=&quot;&quot; data-darkreader-inline-color=&quot;&quot;&gt;&lt;a class=&quot;btn-toggle-moreless&quot;&gt;더보기&lt;/a&gt;
&lt;div class=&quot;moreless-content&quot;&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;b&gt; WarriorHeroGameplayAbility&lt;/b&gt;&lt;b&gt;&lt;span&gt;.h&lt;/span&gt;&lt;/b&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1746783605115&quot; class=&quot;cpp&quot; data-ke-language=&quot;cpp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;#include &quot;CoreMinimal.h&quot;
#include &quot;AbilitySystem/Abilities/RPGGameplayAbility.h&quot;
#include &quot;WarriorHeroGameplayAbility.generated.h&quot;

class AWarriorHeroCharacter;
class AWarriorHeroController;
class UHeroCombatComponent;

UCLASS()
class RPG_API UWarriorHeroGameplayAbility : public URPGGameplayAbility
{
	GENERATED_BODY()
	
public:
	UFUNCTION(BlueprintPure, Category = &quot;RPGAbility&quot;)
	AWarriorHeroCharacter* GetHeroCharacterFromActorInfo();

	UFUNCTION(BlueprintPure, Category = &quot;RPGAbility&quot;)
	AWarriorHeroController* GetWarriorHeroControllerFromActorInfo();

	UFUNCTION(BlueprintPure, Category = &quot;RPGAbility&quot;)
	UHeroCombatComponent* GetHeroCombatComponentFromActorInfo();

	// GameplayEffectSpecHandle 반환 함수 추가
	UFUNCTION(BlueprintPure, Category = &quot;RPGAbility&quot;)
	FGameplayEffectSpecHandle MakeHeroDamageEffectSpecHandle(
		TSubclassOf&amp;lt;UGameplayEffect&amp;gt; EffectClass, float InWeaponBaseDamage,
		FGameplayTag InCurrentAttackTypeTag, int32 InUsedComboCount);

private:
	TWeakObjectPtr&amp;lt;AWarriorHeroCharacter&amp;gt; CachedWarriorHeroCharacter;
	TWeakObjectPtr&amp;lt;AWarriorHeroController&amp;gt; CachedWarriorHeroController;
};&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;b&gt; WarriorHeroGameplayAbility&lt;/b&gt;&lt;b&gt;&lt;span&gt;.cpp&lt;/span&gt;&lt;/b&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1746783659869&quot; class=&quot;cpp&quot; data-ke-language=&quot;cpp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;#include &quot;AbilitySystem/Abilities/WarriorHeroGameplayAbility.h&quot;
#include &quot;Characters/WarriorHeroCharacter.h&quot;
#include &quot;Controllers/WarriorHeroController.h&quot;
#include &quot;AbilitySystem/RPGAbilitySystemComponent.h&quot;
#include &quot;RPGGameplayTags.h&quot;

/** 생략 */

FGameplayEffectSpecHandle UWarriorHeroGameplayAbility::MakeHeroDamageEffectSpecHandle(
	TSubclassOf&amp;lt;UGameplayEffect&amp;gt; EffectClass, float InWeaponBaseDamage,
	FGameplayTag InCurrentAttackTypeTag, int32 InUsedComboCount)
{
	check(EffectClass);

	// 컨텍스트 생성: 누가, 어떤 상황에서 발동 했는지
	FGameplayEffectContextHandle ContextHandle = GetRPGAbilitySystemComponentFromActorInfo()-&amp;gt;MakeEffectContext();
	ContextHandle.SetAbility(this);
	ContextHandle.AddSourceObject(GetAvatarActorFromActorInfo());
	ContextHandle.AddInstigator(GetAvatarActorFromActorInfo(), GetAvatarActorFromActorInfo());

	// 스펙 생성: 
	FGameplayEffectSpecHandle EffectSpecHandle = GetRPGAbilitySystemComponentFromActorInfo()-&amp;gt;MakeOutgoingSpec(
		EffectClass,
		GetAbilityLevel(),
		ContextHandle
	);

	// 무기 기본 데미지 값 전달
	EffectSpecHandle.Data-&amp;gt;SetSetByCallerMagnitude(
		RPGGameplayTags::Shared_SetByCaller_BaseDamage,
		InWeaponBaseDamage
	);

	// 공격 유형 태그를 키로, 콤보 수를 값으로 전달
	if (InCurrentAttackTypeTag.IsValid())
	{
		EffectSpecHandle.Data-&amp;gt;SetSetByCallerMagnitude(InCurrentAttackTypeTag, InUsedComboCount);
	}

	return EffectSpecHandle;
}&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;&quot;FGameplayEffectSpecHandle&quot;은 적용할 GameplayEffect와 그 세부정보를 담은 객체이다.&lt;br /&gt;쉽게 말해 GE 클래스를 어떤 상황(Context)에, 어떤 수치(Magnitude)로 적용할지 담고 있는 객체를 반환하는 함수이다.&lt;br /&gt;또한 이 객체는 GE를 실질적으로 Taget에게 적용시키려면 반드시 필요하다.&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 style=&quot;color: #e8e6e3; text-align: start;&quot; data-ke-size=&quot;size23&quot; data-darkreader-inline-color=&quot;&quot;&gt;&lt;b&gt;✅ Make Hero Damage Effect Spec Handle에 할당할 데이터 생성&lt;/b&gt;&lt;/h3&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;291&quot; data-origin-height=&quot;218&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/be0xxE/btsNSu59g4b/iI2PmPgQDxZxO1KQbRpVo1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/be0xxE/btsNSu59g4b/iI2PmPgQDxZxO1KQbRpVo1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/be0xxE/btsNSu59g4b/iI2PmPgQDxZxO1KQbRpVo1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fbe0xxE%2FbtsNSu59g4b%2FiI2PmPgQDxZxO1KQbRpVo1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;291&quot; height=&quot;218&quot; data-origin-width=&quot;291&quot; data-origin-height=&quot;218&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;Effect Class:&lt;/b&gt; ExecutionCalculation에서 실제 데미지 계산&lt;/li&gt;
&lt;li&gt;&lt;b&gt;In Weapon Base Damage:&lt;/b&gt; 무기 기본 공격력&amp;nbsp;&amp;rarr; SetByCallerMagnitude()로 전달되어 데미지 계산에 사용&lt;/li&gt;
&lt;li&gt;&lt;b&gt;In Current Attack Type Tag:&lt;/b&gt; 강공격, 약공격 등 어떤 종류의 공격인지 ExecutionCalculation에서 분기 처리에 사용&lt;/li&gt;
&lt;li&gt;&lt;b&gt;In Used Combo Count:&lt;/b&gt; 콤보 수&amp;nbsp;&amp;rarr; 공격 데미지 증폭에 반영&lt;/li&gt;
&lt;/ul&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;에디터로 돌아와서 GameplayAbility에서 C++에서 구현했던&amp;nbsp; SpecHandle을 생성하는 함수를 BP로 사용할 수 있게 되었다.&lt;br /&gt;이제 각 데이터를 채워 넣기 위한 구현을 한다.&lt;/blockquote&gt;
&lt;p style=&quot;color: #e8e6e3; text-align: start;&quot; data-ke-size=&quot;size16&quot; data-darkreader-inline-color=&quot;&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 style=&quot;color: #e8e6e3; text-align: start;&quot; data-ke-size=&quot;size20&quot; data-darkreader-inline-color=&quot;&quot;&gt;&lt;b&gt;✔️ 데미지 계산 GameplayEffect 클래스 생성&lt;/b&gt;&lt;/h4&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1175&quot; data-origin-height=&quot;723&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/blo8oV/btsNQVDQmOJ/KGbk4yl8E4AAVgYQMrpcWK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/blo8oV/btsNQVDQmOJ/KGbk4yl8E4AAVgYQMrpcWK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/blo8oV/btsNQVDQmOJ/KGbk4yl8E4AAVgYQMrpcWK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fblo8oV%2FbtsNQVDQmOJ%2FKGbk4yl8E4AAVgYQMrpcWK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1175&quot; height=&quot;723&quot; data-origin-width=&quot;1175&quot; data-origin-height=&quot;723&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;&quot;GameplayEffectExecutionCalculation&quot;를 상속 받은 C++ 클래스를 생성해주고 &quot;GEExecCalc_Damage&quot;으로 명명한다.&lt;br /&gt;구현은 나중에 하고 우선 클래스만 먼저 생성하도록 한다.&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 style=&quot;color: #e8e6e3; text-align: start;&quot; data-ke-size=&quot;size20&quot; data-darkreader-inline-color=&quot;&quot;&gt;&lt;b&gt;✔️&lt;span&gt;&lt;span&gt;&lt;span&gt;&lt;span&gt;&lt;span&gt;&lt;span&gt;&lt;span&gt; 플레이어와 Enemy 모두에게 적용 가능한 Damage 관련 GE 생성&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/b&gt;&lt;/h4&gt;
&lt;p&gt;&lt;figure class=&quot;imagegridblock&quot;&gt;
  &lt;div class=&quot;image-container&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/Vnsge/btsNQXaBxdS/rYj5JOk3hEYnBraZB7WMuK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/Vnsge/btsNQXaBxdS/rYj5JOk3hEYnBraZB7WMuK/img.png&quot; data-origin-width=&quot;762&quot; data-origin-height=&quot;797&quot; data-is-animation=&quot;false&quot; style=&quot;width: 28.7204%; margin-right: 10px;&quot; data-widthpercent=&quot;29.4&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/Vnsge/btsNQXaBxdS/rYj5JOk3hEYnBraZB7WMuK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FVnsge%2FbtsNQXaBxdS%2FrYj5JOk3hEYnBraZB7WMuK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;762&quot; height=&quot;797&quot;/&gt;&lt;/span&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/H1ulk/btsNSHRKEjo/an547WWBxw2ap9Md1diSM1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/H1ulk/btsNSHRKEjo/an547WWBxw2ap9Md1diSM1/img.png&quot; data-origin-width=&quot;132&quot; data-origin-height=&quot;220&quot; data-is-animation=&quot;false&quot; style=&quot;width: 18.0238%; margin-right: 10px;&quot; data-widthpercent=&quot;18.45&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/H1ulk/btsNSHRKEjo/an547WWBxw2ap9Md1diSM1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FH1ulk%2FbtsNSHRKEjo%2Fan547WWBxw2ap9Md1diSM1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;132&quot; height=&quot;220&quot;/&gt;&lt;/span&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/NvvjK/btsNQ1RWGxK/86emPKLTi6J2K1FFLvtYpK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/NvvjK/btsNQ1RWGxK/86emPKLTi6J2K1FFLvtYpK/img.png&quot; data-origin-width=&quot;1375&quot; data-origin-height=&quot;811&quot; data-is-animation=&quot;false&quot; data-widthpercent=&quot;52.15&quot; style=&quot;width: 50.9303%;&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/NvvjK/btsNQ1RWGxK/86emPKLTi6J2K1FFLvtYpK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FNvvjK%2FbtsNQ1RWGxK%2F86emPKLTi6J2K1FFLvtYpK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1375&quot; height=&quot;811&quot;/&gt;&lt;/span&gt;&lt;/div&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;데미지 계산을 처리하는 GameplayEffect를 생성하고, 앞서 생성했던 &quot;GEExecCalc_Damage&quot;를 Calculation Class로 설정한다.&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 style=&quot;color: #e8e6e3; text-align: start;&quot; data-darkreader-inline-color=&quot;&quot; data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;✔️&lt;span&gt;&lt;span&gt;&lt;span&gt;&lt;span&gt;&lt;span&gt;&lt;span&gt; RPGStructTypes&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/b&gt;&lt;/h4&gt;
&lt;div data-ke-type=&quot;moreLess&quot; data-text-more=&quot;더보기&quot; data-text-less=&quot;닫기&quot;&gt;&lt;a class=&quot;btn-toggle-moreless&quot;&gt;더보기&lt;/a&gt;
&lt;div class=&quot;moreless-content&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt; RPGStructTypes&lt;/b&gt;&lt;b&gt;&lt;span&gt;.h&lt;/span&gt;&lt;/b&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1746789021115&quot; class=&quot;cpp&quot; data-ke-language=&quot;cpp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;USTRUCT(BlueprintType)
struct FRPGHeroWeaponData
{
	GENERATED_BODY()

	UPROPERTY(EditDefaultsOnly, BlueprintReadOnly)
	TSubclassOf&amp;lt;UWarriorHeroLinkedAnimLayer&amp;gt; WeaponAnimLayerToLink;

	UPROPERTY(EditDefaultsOnly, BlueprintReadOnly)
	UInputMappingContext* WeaponInputMappingContext;

	UPROPERTY(EditDefaultsOnly, BlueprintReadOnly, meta = (TitleProperty = &quot;InputTag&quot;))
	TArray&amp;lt;FWarriorHeroAbilitySet&amp;gt; DefaultWeaponAbilities;

	UPROPERTY(EditDefaultsOnly, BlueprintReadOnly)
	FScalableFloat WeaponBaseDamage; // BaseDamage 선언
};&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;무기 클래스가 가지고 있는 Data 구조체에 WeaponBaseDamage를 선언한다.&lt;br /&gt;FScalableFloat은 언리얼 GAS에서 데이터를 Curve Table 기반으로 동적으로 조정 가능한 수치로 만들기 위해 사용하는 구조체이다.&lt;br /&gt;단순한 float와 달리 레벨, 난이도, 상황에 따라 수치를 조정할 수 있는 기능을 제공한다.&lt;/blockquote&gt;
&lt;p&gt;&lt;figure class=&quot;imagegridblock&quot;&gt;
  &lt;div class=&quot;image-container&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/IALyB/btsNSuLSNJc/7VeeVG0uvO2wA3xKiLs8HK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/IALyB/btsNSuLSNJc/7VeeVG0uvO2wA3xKiLs8HK/img.png&quot; data-is-animation=&quot;false&quot; data-origin-height=&quot;198&quot; data-origin-width=&quot;723&quot; data-widthpercent=&quot;59.99&quot; style=&quot;width: 59.2929%; margin-right: 10px;&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/IALyB/btsNSuLSNJc/7VeeVG0uvO2wA3xKiLs8HK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FIALyB%2FbtsNSuLSNJc%2F7VeeVG0uvO2wA3xKiLs8HK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;723&quot; height=&quot;198&quot;/&gt;&lt;/span&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/mhHt1/btsNSIcaeFR/aEy8kOdBVuaf0FkhEKoLFK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/mhHt1/btsNSIcaeFR/aEy8kOdBVuaf0FkhEKoLFK/img.png&quot; data-origin-width=&quot;1807&quot; data-origin-height=&quot;742&quot; data-is-animation=&quot;false&quot; style=&quot;width: 39.5443%;&quot; data-widthpercent=&quot;40.01&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/mhHt1/btsNSIcaeFR/aEy8kOdBVuaf0FkhEKoLFK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FmhHt1%2FbtsNSIcaeFR%2FaEy8kOdBVuaf0FkhEKoLFK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1807&quot; height=&quot;742&quot;/&gt;&lt;/span&gt;&lt;/div&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;에디터에서 Curve Table을 생성해서 BaseDamage 데이터를 정의한다. 그리고 BP_Axe에 WeaponBaseDamage를 저장해준다.&lt;/blockquote&gt;
&lt;p style=&quot;color: #e8e6e3; text-align: start;&quot; data-ke-size=&quot;size16&quot; data-darkreader-inline-color=&quot;&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 style=&quot;color: #e8e6e3; text-align: start;&quot; data-ke-size=&quot;size20&quot; data-darkreader-inline-color=&quot;&quot;&gt;&lt;b&gt;✔️&lt;span&gt;&lt;span&gt;&lt;span&gt;&lt;span&gt;&lt;span&gt; HeroCombatComponent&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/b&gt;&lt;/h4&gt;
&lt;div style=&quot;background-color: #1b1d1e; color: #c8c3bc; text-align: start;&quot; data-darkreader-inline-color=&quot;&quot; data-darkreader-inline-bgcolor=&quot;&quot; data-ke-type=&quot;moreLess&quot; data-text-more=&quot;더보기&quot; data-text-less=&quot;닫기&quot;&gt;&lt;a class=&quot;btn-toggle-moreless&quot;&gt;더보기&lt;/a&gt;
&lt;div class=&quot;moreless-content&quot;&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;b&gt; HeroCombatComponent&lt;/b&gt;&lt;b&gt;&lt;span&gt;.h&lt;/span&gt;&lt;/b&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1746786309998&quot; class=&quot;cpp&quot; data-ke-language=&quot;cpp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;#pragma once

#include &quot;CoreMinimal.h&quot;
#include &quot;Components/Combat/PawnCombatComponent.h&quot;
#include &quot;HeroCombatComponent.generated.h&quot;

class AWarriorHeroWeapon;

UCLASS()
class RPG_API UHeroCombatComponent : public UPawnCombatComponent
{
	GENERATED_BODY()

public:
	UFUNCTION(BlueprintCallable, Category = &quot;RPG|Combat&quot;)
	AWarriorHeroWeapon* GetHeroCarriedWeaponByTag(FGameplayTag InWeaponTag) const;
	
    // 현재 장착 중인 무기 반환
	UFUNCTION(BlueprintCallable, Category = &quot;RPG|Combat&quot;)
	AWarriorHeroWeapon* GetHeroCurrentEquippedWeapon() const;
	
    // 현재 장착중인 무기 데미지 반환
	UFUNCTION(BlueprintCallable, Category = &quot;RPG|Combat&quot;)
	float GetHeroCurrentEquippedWeaponDamageAtLevel(float InLevel) const;

	virtual void OnHitTargetActor(AActor* HitActor) override;
	virtual void OnWeaponPulledFromTargetActor(AActor* InteractedActor) override;

};&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;b&gt; HeroCombatComponent&lt;/b&gt;&lt;b&gt;&lt;span&gt;.cpp&lt;/span&gt;&lt;/b&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1746786364835&quot; class=&quot;cpp&quot; data-ke-language=&quot;cpp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;#include &quot;Components/Combat/HeroCombatComponent.h&quot;
#include &quot;Items/Weapons/WarriorHeroWeapon.h&quot;
#include &quot;AbilitySystemBlueprintLibrary.h&quot;
#include &quot;RPGGameplayTags.h&quot;

#include &quot;WarriorDebugHelper.h&quot;

AWarriorHeroWeapon* UHeroCombatComponent::GetHeroCurrentEquippedWeapon() const
{
	return Cast&amp;lt;AWarriorHeroWeapon&amp;gt;(GetCharacterCurrentEquippedWeapon());
}

float UHeroCombatComponent::GetHeroCurrentEquippedWeaponDamageAtLevel(float InLevel) const
{
	return GetHeroCurrentEquippedWeapon()-&amp;gt;HeroWeaponData.WeaponBaseDamage.GetValueAtLevel(InLevel);
}&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;WeaponData를 하고 나서 현재 Level에 비례한 무기 데미지 반환 함수 구현한다.&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 style=&quot;color: #e8e6e3; text-align: start;&quot; data-darkreader-inline-color=&quot;&quot; data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;✔️&lt;span&gt;&lt;span&gt;&lt;span&gt;&lt;span&gt;&lt;span&gt;&lt;span&gt; RPGGameplayTags&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/b&gt;&lt;/h4&gt;
&lt;div data-ke-type=&quot;moreLess&quot; data-text-more=&quot;더보기&quot; data-text-less=&quot;닫기&quot;&gt;&lt;a class=&quot;btn-toggle-moreless&quot;&gt;더보기&lt;/a&gt;
&lt;div class=&quot;moreless-content&quot;&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;b&gt; RPGGameplayTags&lt;/b&gt;&lt;b&gt;&lt;span&gt;.h&lt;/span&gt;&lt;/b&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1746789511942&quot; class=&quot;cpp&quot; data-ke-language=&quot;cpp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;#pragma once

#include &quot;NativeGameplayTags.h&quot;

namespace RPGGameplayTags
{
	/** 생략 */
    
	/** Player Tags */

	RPG_API UE_DECLARE_GAMEPLAY_TAG_EXTERN(Player_SetByCaller_AttackType_Light);
	RPG_API UE_DECLARE_GAMEPLAY_TAG_EXTERN(Player_SetByCaller_AttackType_Heavy);
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt; RPGGameplayTags&lt;/b&gt;&lt;b&gt;&lt;span&gt;.cpp&lt;/span&gt;&lt;/b&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1746789564757&quot; class=&quot;cpp&quot; data-ke-language=&quot;cpp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;#include &quot;RPGGameplayTags.h&quot;

namespace RPGGameplayTags
{
	/** 생략 */

	/** Player Tags */
	UE_DEFINE_GAMEPLAY_TAG(Player_SetByCaller_AttackType_Light, &quot;Player.SetByCaller.AttackType.Light&quot;);
	UE_DEFINE_GAMEPLAY_TAG(Player_SetByCaller_AttackType_Heavy, &quot;Player.SetByCaller.AttackType.Heavy&quot;);
}&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;GameplayEffectSpec에 SetByCallerMagnitude 방식으로 공격 타입 정보를 담아 전달하기 위해, Tag를 선언한다.&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 style=&quot;color: #c8c3bc; text-align: start;&quot; data-ke-size=&quot;size20&quot; data-darkreader-inline-color=&quot;&quot;&gt;&lt;b&gt; &lt;span&gt; GE Spec Handle 반환 함수에 데이터 연결&lt;/span&gt;&lt;/b&gt;&lt;/h4&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1312&quot; data-origin-height=&quot;327&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bmyDxM/btsNQVKFjh6/7cLs5r2q2zFBl6E1PlkYYK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bmyDxM/btsNQVKFjh6/7cLs5r2q2zFBl6E1PlkYYK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bmyDxM/btsNQVKFjh6/7cLs5r2q2zFBl6E1PlkYYK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbmyDxM%2FbtsNQVKFjh6%2F7cLs5r2q2zFBl6E1PlkYYK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1312&quot; height=&quot;327&quot; data-origin-width=&quot;1312&quot; data-origin-height=&quot;327&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;pre id=&quot;code_1746791052680&quot; class=&quot;cpp&quot; data-ke-language=&quot;cpp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;FGameplayEffectSpecHandle UWarriorHeroGameplayAbility::MakeHeroDamageEffectSpecHandle(
	TSubclassOf&amp;lt;UGameplayEffect&amp;gt; EffectClass, float InWeaponBaseDamage,
	FGameplayTag InCurrentAttackTypeTag, int32 InUsedComboCount)
{
	check(EffectClass);

	// 컨텍스트 생성: 누가, 어떤 상황에서 발동 했는지
	FGameplayEffectContextHandle ContextHandle = GetRPGAbilitySystemComponentFromActorInfo()-&amp;gt;MakeEffectContext();
	ContextHandle.SetAbility(this);
	ContextHandle.AddSourceObject(GetAvatarActorFromActorInfo());
	ContextHandle.AddInstigator(GetAvatarActorFromActorInfo(), GetAvatarActorFromActorInfo());

	// 스펙 생성: 
	FGameplayEffectSpecHandle EffectSpecHandle = GetRPGAbilitySystemComponentFromActorInfo()-&amp;gt;MakeOutgoingSpec(
		EffectClass,
		GetAbilityLevel(),
		ContextHandle
	);

	// 무기 기본 데미지 값 전달
	EffectSpecHandle.Data-&amp;gt;SetSetByCallerMagnitude(
		RPGGameplayTags::Shared_SetByCaller_BaseDamage,
		InWeaponBaseDamage
	);

	// 공격 유형 태그를 키로, 콤보 수를 값으로 전달
	if (InCurrentAttackTypeTag.IsValid())
	{
		EffectSpecHandle.Data-&amp;gt;SetSetByCallerMagnitude(InCurrentAttackTypeTag, InUsedComboCount);
	}

	return EffectSpecHandle;
}&lt;/code&gt;&lt;/pre&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;EffectClass: 실행할 GE_Shared_DealDamage&lt;br /&gt;InWeaponBaseDamage: Curve Table에서 레벨에 매칭되는 Damage&lt;br /&gt;InCurrentAttackTypeTag: &quot;Player.SetByCaller.AttackType.Light&quot; Tag&lt;br /&gt;InUsedComboCount: GameplayAbility의 현재 Combo Count 변수 값&lt;br /&gt;&lt;br /&gt;위와 같이 데이터를 함수에 전달하였다.&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 style=&quot;color: #e8e6e3; text-align: start;&quot; data-darkreader-inline-color=&quot;&quot; data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;  FGameEffectSpecHandle로 대상에게 Damage 로직 구현&lt;/b&gt;&lt;/h2&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;앞에서 생성한 GameplayEffectHandle을 대상 Actor에게 적용 시키는 기능을 하는 함수를 구현한다.&lt;br /&gt;플레이어와 Enemy 모두 사용할 수 있도록&amp;nbsp; BaseGameplayAbility 클래스에 구현하도록 한다.&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 style=&quot;color: #e8e6e3; text-align: start;&quot; data-darkreader-inline-color=&quot;&quot; data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;✔️&lt;span&gt;&lt;span&gt;&lt;span&gt;&lt;span&gt;&lt;span&gt;&lt;span&gt;&lt;span&gt;&lt;span&gt; RPGEnumTypes.h&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/b&gt;&lt;/h4&gt;
&lt;pre id=&quot;code_1746795382480&quot; class=&quot;crystal&quot; style=&quot;background-color: #1c1e1f; color: #c1bcb4; text-align: start;&quot; data-ke-type=&quot;codeblock&quot; data-ke-language=&quot;cpp&quot; data-darkreader-inline-bgcolor=&quot;&quot; data-darkreader-inline-color=&quot;&quot;&gt;&lt;code&gt;#pragma once

/** 생략 */

// 성공 여부 EnumType 구현
UENUM(BlueprintType)
enum class ERPGSuccessType : uint8
{
	Successful,
	Failed
};&lt;/code&gt;&lt;/pre&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;Effect가 대상에게 실제로 적용되었는지 결과를 반환하기 위해 Enum 생성&lt;/blockquote&gt;
&lt;p style=&quot;color: #e8e6e3; text-align: start;&quot; data-ke-size=&quot;size16&quot; data-darkreader-inline-color=&quot;&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 style=&quot;color: #e8e6e3; text-align: start;&quot; data-ke-size=&quot;size20&quot; data-darkreader-inline-color=&quot;&quot;&gt;&lt;b&gt;✔️&lt;span&gt;&lt;span&gt;&lt;span&gt;&lt;span&gt;&lt;span&gt;&lt;span&gt;&lt;span&gt; RPGGameplayAbility&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/b&gt;&lt;/h4&gt;
&lt;div style=&quot;background-color: #1b1d1e; color: #c8c3bc; text-align: start;&quot; data-text-less=&quot;닫기&quot; data-text-more=&quot;더보기&quot; data-ke-type=&quot;moreLess&quot; data-darkreader-inline-bgcolor=&quot;&quot; data-darkreader-inline-color=&quot;&quot;&gt;&lt;a class=&quot;btn-toggle-moreless&quot;&gt;더보기&lt;/a&gt;
&lt;div class=&quot;moreless-content&quot;&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;b&gt; RPGGameplayAbility&lt;/b&gt;&lt;b&gt;&lt;span&gt;.h&lt;/span&gt;&lt;/b&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1746794817382&quot; class=&quot;cpp&quot; style=&quot;background-color: #1c1e1f; color: #c1bcb4; text-align: start;&quot; data-ke-type=&quot;codeblock&quot; data-ke-language=&quot;cpp&quot; data-darkreader-inline-bgcolor=&quot;&quot; data-darkreader-inline-color=&quot;&quot;&gt;&lt;code&gt;#pragma once

#include &quot;CoreMinimal.h&quot;
#include &quot;Abilities/GameplayAbility.h&quot;
#include &quot;RPGTypes/RPGEnumTypes.h&quot;
#include &quot;RPGGameplayAbility.generated.h&quot;

class UPawnCombatComponent;
class URPGAbilitySystemComponent;

UENUM(BlueprintType)
enum class ERPGAbilityActivationPolicy : uint8
{
	OnTriggered,
	OnGiven
};

UCLASS()
class RPG_API URPGGameplayAbility : public UGameplayAbility
{
	GENERATED_BODY()
	
protected:

	/** 생략 */
    
    // GameplayEffectSpecHandle을 대상 액터에게 적용하는 헬퍼 함수
	FActiveGameplayEffectHandle NativeApplyEffectSpecHandleToTarget(AActor* TargetActor, const FGameplayEffectSpecHandle&amp;amp; InSpecHandle);
	
    // 위와 기능 동일하고 Effect 적용 결과를 Enum으로 반환하는 BP 전용 함수
	UFUNCTION(BlueprintCallable, Category = &quot;RPGAbility&quot;, meta = (DisplayName = &quot;Apply Gameplay Effect Spec Handle To Target&quot;, ExpandEnumAsExecs = &quot;OutSuccessType&quot;))
	FActiveGameplayEffectHandle BP_ApplyEffectSpecHandleToTarget(AActor* TargetActor, const FGameplayEffectSpecHandle&amp;amp; InSpecHandle, ERPGSuccessType&amp;amp; OutSuccessType);

};&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;b&gt; RPGGameplayAbility&lt;/b&gt;&lt;b&gt;&lt;span&gt;.cpp&lt;/span&gt;&lt;/b&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1746794855195&quot; class=&quot;cpp&quot; data-ke-language=&quot;cpp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;#include &quot;AbilitySystem/Abilities/RPGGameplayAbility.h&quot;
#include &quot;AbilitySystem/RPGAbilitySystemComponent.h&quot;
#include &quot;Components/Combat/PawnCombatComponent.h&quot;
#include &quot;AbilitySystemBlueprintLibrary.h&quot;

/** 생략 */

FActiveGameplayEffectHandle URPGGameplayAbility::NativeApplyEffectSpecHandleToTarget(AActor* TargetActor, const FGameplayEffectSpecHandle&amp;amp; InSpecHandle)
{
	// Effect가 처리될 대상의 ASC
	UAbilitySystemComponent* TargetASC = UAbilitySystemBlueprintLibrary::GetAbilitySystemComponent(TargetActor);

	check(TargetASC &amp;amp;&amp;amp; InSpecHandle.IsValid());

	return GetRPGAbilitySystemComponentFromActorInfo()-&amp;gt;ApplyGameplayEffectSpecToTarget(
		*InSpecHandle.Data, // SpecHandle에서 실제 FGameplaySpecHandle 전달
		TargetASC			// Effect 적용 대상의 ASC 전달
	);
}

FActiveGameplayEffectHandle URPGGameplayAbility::BP_ApplyEffectSpecHandleToTarget(AActor* TargetActor, const FGameplayEffectSpecHandle&amp;amp; InSpecHandle, ERPGSuccessType&amp;amp; OutSuccessType)
{
	FActiveGameplayEffectHandle ActiveGameplayEffectHandle = NativeApplyEffectSpecHandleToTarget(TargetActor, InSpecHandle);

	OutSuccessType = ActiveGameplayEffectHandle.WasSuccessfullyApplied() ? ERPGSuccessType::Successful : ERPGSuccessType::Failed;

	return ActiveGameplayEffectHandle;
}&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;GameplayEffect를 대상 Actor의 ASC에 적용시키는 함수 구현&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock widthContent&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1536&quot; data-origin-height=&quot;576&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/SzzcJ/btsNRHSQVj1/UZaELWZkCfyWJNhp1bjzW1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/SzzcJ/btsNRHSQVj1/UZaELWZkCfyWJNhp1bjzW1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/SzzcJ/btsNRHSQVj1/UZaELWZkCfyWJNhp1bjzW1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FSzzcJ%2FbtsNRHSQVj1%2FUZaELWZkCfyWJNhp1bjzW1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1536&quot; height=&quot;576&quot; data-origin-width=&quot;1536&quot; data-origin-height=&quot;576&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;에디터로 돌아와 GA_LightAttack에서 방금 구현한 함수로 GameplayEffect를 적용시키는 로직을 추가한다.&lt;/blockquote&gt;
&lt;h4 style=&quot;color: #e8e6e3; text-align: start;&quot; data-ke-size=&quot;size20&quot; data-darkreader-inline-color=&quot;&quot;&gt;&lt;b&gt; 로직 흐름&lt;/b&gt;&lt;/h4&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;&lt;b&gt;Wait Gameplay Event 수신&lt;/b&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Shared.Event.MeleeHit 태그를 가진 이벤트를 수신&lt;/li&gt;
&lt;li&gt;적이 무기에 피격 되었을 때, SendGameplayEventToActor( ) 를 통해 호출&lt;/li&gt;
&lt;li&gt;Payload로 GameplayEventData를 전달&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;b&gt;Gameplay Event Data에서 Target 추출&lt;/b&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;여기서 추출한 Target은 피격 판정된 적 캐릭터(AActor)&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;b&gt;Make Hero Damage Effect Spec 호출&lt;/b&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;데미지 계산 수행하는 클래스 포함한 GameplayEffect&lt;/li&gt;
&lt;li&gt;Weapon클래스 데이터에 있는 BaseDamage&lt;/li&gt;
&lt;li&gt;데미지 계산을 위한 공격 Type Tag&amp;nbsp;&lt;/li&gt;
&lt;li&gt;현재 Combo Count&lt;/li&gt;
&lt;li&gt;위의 데이터를 SpecHandle에 저장하고 반환&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;b&gt;Apply Gameplay Effect Spec Handle To Target 호출&lt;/b&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;피격 처리할 대상(Target)에게 SpecHandle 적용&lt;/li&gt;
&lt;li&gt;Apply 시점에 SpecHandle에 저장된 정보를 바탕으로 데미지 계산&lt;/li&gt;
&lt;li&gt;Target의 ASC를 찾아 GameplayEffect 적용&lt;/li&gt;
&lt;li&gt;FActiveGameplayEffectHandle 반환 및 성공 여부 핀으로 전달&lt;br /&gt;&lt;br /&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;h2 style=&quot;color: #e8e6e3; text-align: start;&quot; data-darkreader-inline-color=&quot;&quot; data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;  Attribute 캡처 및 피해량 계산&lt;/b&gt;&lt;/h2&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;앞서 데미지 계산을 수행하는 클래스(Execution Calculation)를 포함한 GameEffect를 만들었고, 이 GE를 대상 Actor에게 적용시키는 로직을 구현하였다.&lt;br /&gt;하지만 클래스 생성만 해놓고, 데미지 계산 로직을 비워둔 상태로 GE 적용만 시켰기 때문에 현재는 피격당하더라도 데미지를 입을 수 없다.&lt;br /&gt;따라서 이제 데미지 계산 로직을 수행하는 클래스를 구현하도록 한다.&lt;br /&gt;&lt;br /&gt;GameplayEffectExecutionCalculation 클래스는 단순히 GameplayEffect에 설정된 Modifier만으로는 할 수 없는 동적인 계산(공격력 - 방어력 등)을 처리하기 위한 클래스이다.&lt;br /&gt;이 클래스는 실제 계산을 할 때, 지정된 Attribute를 &quot;캡처&quot;해서 값으로 사용할 수 있도록 설정해야한다.&lt;br /&gt;&lt;br /&gt;즉, Execute 함수 내부에서 값을 가져오려면, 사전에 어떤 값을 사용할 것인지 명시적으로 등록해야 하며, 이 등록이 RelevantAttributesToCapture 배열에 추가되는 &quot;Attribute Capture Definition&quot;이다.&lt;br /&gt;&lt;br /&gt;캡처하는 방법에는 &quot;직접 수동으로 캡처를 하는 방법&quot; 과 &quot;매크로 + 구조체를 사용한 방법&quot;이 있다.&lt;br /&gt;당연히 전자 쪽이 코드 작성이 더 많고 구현하는데 오래걸리므로 후자 쪽 방향으로 구현할 것이다.&amp;nbsp;&lt;/blockquote&gt;
&lt;h4 style=&quot;color: #e8e6e3; text-align: start; --darkreader-inline-color: var(--darkreader-text-e8e6e3, #d8d4cf);&quot; data-ke-size=&quot;size20&quot; data-darkreader-inline-color=&quot;&quot;&gt;&lt;b&gt; 캡처 정의 2가지 방법&lt;/b&gt;&lt;/h4&gt;
&lt;div data-ke-type=&quot;moreLess&quot; data-text-more=&quot;더보기&quot; data-text-less=&quot;닫기&quot;&gt;&lt;a class=&quot;btn-toggle-moreless&quot;&gt;더보기&lt;/a&gt;
&lt;div class=&quot;moreless-content&quot;&gt;
&lt;p style=&quot;color: #e8e6e3; text-align: start; --darkreader-inline-color: var(--darkreader-text-e8e6e3, #d8d4cf);&quot; data-ke-size=&quot;size18&quot; data-darkreader-inline-color=&quot;&quot;&gt;&lt;b&gt;  수동 캡쳐 정의&lt;/b&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1746845706004&quot; class=&quot;cpp&quot; data-ke-language=&quot;cpp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;UGEExecCalc_Damage::UGEExecCalc_Damage()
{
	/** 수동 캡처 */
	FProperty* AttackPowerProperty = FindFieldChecked&amp;lt;FProperty&amp;gt;(
		URPGAttributeSet::StaticClass(),
		GET_MEMBER_NAME_CHECKED(URPGAttributeSet, AttackPower)
	);

	FGameplayEffectAttributeCaptureDefinition AttackPowerCaptureDefinition(
		AttackPowerProperty,
		EGameplayEffectAttributeCaptureSource::Source,
		false
	);
    
    FProperty* DefensePowerProperty = FindFieldChecked&amp;lt;FProperty&amp;gt;(
		URPGAttributeSet::StaticClass(),
		GET_MEMBER_NAME_CHECKED(URPGAttributeSet, DefensePower)
	);

	FGameplayEffectAttributeCaptureDefinition DefensePowerCaptureDefinition(
		DefensePowerProperty,
		EGameplayEffectAttributeCaptureSource::Target,
		false
	);

	RelevantAttributesToCapture.Add(AttackPowerCaptureDefinition);
    RelevantAttributesToCapture.Add(DefensePowerCaptureDefinition);
}&lt;/code&gt;&lt;/pre&gt;
&lt;p style=&quot;color: #e8e6e3; text-align: start; --darkreader-inline-color: var(--darkreader-text-e8e6e3, #d8d4cf);&quot; data-ke-size=&quot;size18&quot; data-darkreader-inline-color=&quot;&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #e8e6e3; text-align: start; --darkreader-inline-color: var(--darkreader-text-e8e6e3, #d8d4cf);&quot; data-ke-size=&quot;size18&quot; data-darkreader-inline-color=&quot;&quot;&gt;&lt;b&gt;  매크로 + 구조체 사용한 정의&lt;/b&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1746845775338&quot; class=&quot;cpp&quot; data-ke-language=&quot;cpp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;#include &quot;AbilitySystem/GEExecCalc/GEExecCalc_Damage.h&quot;
#include &quot;AbilitySystem/RPGAttributeSet.h&quot;

struct FRPGDamageCaputre
{
	DECLARE_ATTRIBUTE_CAPTUREDEF(AttackPower)
	DECLARE_ATTRIBUTE_CAPTUREDEF(DefensePower)
    
	FRPGDamageCaputre()
	{
		DEFINE_ATTRIBUTE_CAPTUREDEF(URPGAttributeSet, AttackPower, Source, false)
		DEFINE_ATTRIBUTE_CAPTUREDEF(URPGAttributeSet, DefensePower, Target, false)
	}
};

static const FRPGDamageCaputre&amp;amp; GetRPGDamageCaputre()
{
	static FRPGDamageCaputre RPGDamageCaputre;
	return RPGDamageCaputre;
}

UGEExecCalc_Damage::UGEExecCalc_Damage()
{
	RelevantAttributesToCapture.Add(GetRPGDamageCaputre().AttackPowerDef);
    RelevantAttributesToCapture.Add(GetRPGDamageCaputre().DefensePowerDef);
}&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1173&quot; data-origin-height=&quot;330&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/x8X9w/btsNRStpJeK/EQZkE5QKlJRbAkASlOIjW1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/x8X9w/btsNRStpJeK/EQZkE5QKlJRbAkASlOIjW1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/x8X9w/btsNRStpJeK/EQZkE5QKlJRbAkASlOIjW1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fx8X9w%2FbtsNRStpJeK%2FEQZkE5QKlJRbAkASlOIjW1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1173&quot; height=&quot;330&quot; data-origin-width=&quot;1173&quot; data-origin-height=&quot;330&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;여러 값을 캡처한다고 했을 때, 매크로 + 구조체를 사용하는 것이 더 코드가 간결해질 것이라는 것을 확인할 수 있다.&lt;br /&gt;자세한 매크로 내부는 &quot;UGameplayEffectExecutionCalculation.h&quot;에 정의되어있다.&lt;b&gt;&lt;/b&gt;&lt;/blockquote&gt;
&lt;h3 style=&quot;color: #e8e6e3; text-align: start;&quot; data-darkreader-inline-color=&quot;&quot; data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;✅ 데미지 계산 클래스 기능 구현&lt;/b&gt;&lt;/h3&gt;
&lt;h4 style=&quot;color: #e8e6e3; text-align: start;&quot; data-darkreader-inline-color=&quot;&quot; data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;✔️&lt;span&gt; RPGAttributeSet&lt;/span&gt;&lt;/b&gt;&lt;/h4&gt;
&lt;div data-ke-type=&quot;moreLess&quot; data-text-more=&quot;더보기&quot; data-text-less=&quot;닫기&quot;&gt;&lt;a class=&quot;btn-toggle-moreless&quot;&gt;더보기&lt;/a&gt;
&lt;div class=&quot;moreless-content&quot;&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;b&gt; &lt;span&gt;&lt;span&gt; RPGAttributeSet&lt;/span&gt;.h&lt;/span&gt;&lt;/b&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1746854130592&quot; class=&quot;cpp&quot; data-ke-language=&quot;cpp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;#pragma once

#include &quot;CoreMinimal.h&quot;
#include &quot;AttributeSet.h&quot;
#include &quot;AbilitySystem/RPGAbilitySystemComponent.h&quot;
#include &quot;RPGAttributeSet.generated.h&quot;

#define ATTRIBUTE_ACCESSORS(ClassName, PropertyName) \
GAMEPLAYATTRIBUTE_PROPERTY_GETTER(ClassName, PropertyName) \
GAMEPLAYATTRIBUTE_VALUE_GETTER(PropertyName) \
GAMEPLAYATTRIBUTE_VALUE_SETTER(PropertyName) \
GAMEPLAYATTRIBUTE_VALUE_INITTER(PropertyName)

UCLASS()
class RPG_API URPGAttributeSet : public UAttributeSet
{
	GENERATED_BODY()
	
public:
	URPGAttributeSet();

	/** 생략 */

	UPROPERTY(BlueprintReadOnly, Category = &quot;Damage&quot;)
	FGameplayAttributeData AttackPower;
	ATTRIBUTE_ACCESSORS(URPGAttributeSet, AttackPower)

	UPROPERTY(BlueprintReadOnly, Category = &quot;Damage&quot;)
	FGameplayAttributeData DefensePower;
	ATTRIBUTE_ACCESSORS(URPGAttributeSet, DefensePower)

	UPROPERTY(BlueprintReadOnly, Category = &quot;Damage&quot;)
	FGameplayAttributeData DamageTaken;
	ATTRIBUTE_ACCESSORS(URPGAttributeSet, DamageTaken) // 최종 계산된 데미지 저장할 Attribute

};&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;ExecutionCalculation에서 계산된 최종 데미지를 저장하는 용도의 Attribute인 &quot;DamageTaken&quot; 선언한다.&lt;br /&gt;실제 체력 감소 처리 로직에서 이 속성 값이 사용된다.&lt;/blockquote&gt;
&lt;h4 style=&quot;color: #e8e6e3; text-align: start;&quot; data-ke-size=&quot;size20&quot; data-darkreader-inline-color=&quot;&quot;&gt;&lt;b&gt;✔️ GEExecCalc_Damage&lt;/b&gt;&lt;/h4&gt;
&lt;div data-ke-type=&quot;moreLess&quot; data-text-more=&quot;더보기&quot; data-text-less=&quot;닫기&quot;&gt;&lt;a class=&quot;btn-toggle-moreless&quot;&gt;더보기&lt;/a&gt;
&lt;div class=&quot;moreless-content&quot;&gt;
&lt;p style=&quot;color: #e8e6e3; text-align: start;&quot; data-ke-size=&quot;size18&quot; data-darkreader-inline-color=&quot;&quot;&gt;&lt;b&gt; &lt;span&gt; GEExecCalc_Damage.h&lt;/span&gt;&lt;/b&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1746853454029&quot; class=&quot;cpp&quot; data-ke-language=&quot;cpp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;#pragma once

#include &quot;CoreMinimal.h&quot;
#include &quot;GameplayEffectExecutionCalculation.h&quot;
#include &quot;GEExecCalc_Damage.generated.h&quot;

UCLASS()
class RPG_API UGEExecCalc_Damage : public UGameplayEffectExecutionCalculation
{
	GENERATED_BODY()
	
public:
	// 생성자에서 Attribute를 계산을 위해 캡처
	// 매크로 + 구조체 방식으로 캡처
	UGEExecCalc_Damage();
	
    // 계산 실행 함수
	virtual void Execute_Implementation(
		const FGameplayEffectCustomExecutionParameters&amp;amp; ExecutionParams,
		FGameplayEffectCustomExecutionOutput&amp;amp; OutExecutionOutput) const override;

};&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;b&gt; &lt;span&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;GEExecCalc_Damage.cpp&lt;/span&gt;&lt;/b&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1746853985767&quot; class=&quot;cpp&quot; data-ke-language=&quot;cpp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;#include &quot;AbilitySystem/GEExecCalc/GEExecCalc_Damage.h&quot;
#include &quot;AbilitySystem/RPGAttributeSet.h&quot;
#include &quot;RPGGameplayTags.h&quot;

#include &quot;WarriorDebugHelper.h&quot;

struct FRPGDamageCaputre
{
	DECLARE_ATTRIBUTE_CAPTUREDEF(AttackPower)
	DECLARE_ATTRIBUTE_CAPTUREDEF(DefensePower)
	DECLARE_ATTRIBUTE_CAPTUREDEF(DamageTaken)

	FRPGDamageCaputre()
	{
		DEFINE_ATTRIBUTE_CAPTUREDEF(URPGAttributeSet, AttackPower, Source, false)
		DEFINE_ATTRIBUTE_CAPTUREDEF(URPGAttributeSet, DefensePower, Target, false)
		DEFINE_ATTRIBUTE_CAPTUREDEF(URPGAttributeSet, DamageTaken, Target, false)
	}
};

static const FRPGDamageCaputre&amp;amp; GetRPGDamageCaputre()
{
	static FRPGDamageCaputre RPGDamageCaputre;
	return RPGDamageCaputre;
}

UGEExecCalc_Damage::UGEExecCalc_Damage()
{
	RelevantAttributesToCapture.Add(GetRPGDamageCaputre().AttackPowerDef);
	RelevantAttributesToCapture.Add(GetRPGDamageCaputre().DefensePowerDef);
	RelevantAttributesToCapture.Add(GetRPGDamageCaputre().DamageTakenDef);
}

void UGEExecCalc_Damage::Execute_Implementation(const FGameplayEffectCustomExecutionParameters&amp;amp; ExecutionParams, FGameplayEffectCustomExecutionOutput&amp;amp; OutExecutionOutput) const
{
	// 1.EffectSpec 준비
	const FGameplayEffectSpec&amp;amp; EffectSpec = ExecutionParams.GetOwningSpec();

	/** Effect Spec이 가지고 있는 ContextHandle에서 정보 가져올 수 있음 */
	/*EffectSpec.GetContext().GetSourceObject();
	EffectSpec.GetContext().GetAbility();
	EffectSpec.GetContext().GetInstigator();
	EffectSpec.GetContext().GetEffectCauser();*/

	// 2.EvaluateParameters 생성(속성 계산에 사용할 태그 정보)
	FAggregatorEvaluateParameters EvaluateParameters;
	EvaluateParameters.SourceTags = EffectSpec.CapturedSourceTags.GetAggregatedTags();
	EvaluateParameters.TargetTags = EffectSpec.CapturedTargetTags.GetAggregatedTags();

	// 3.Source(공격자)의 AttackPower 추출
	float SourceAttackPower = 0.0f;
	ExecutionParams.AttemptCalculateCapturedAttributeMagnitude(GetRPGDamageCaputre().AttackPowerDef, EvaluateParameters, SourceAttackPower);
	Debug::Print(TEXT(&quot;SourceAttackPower&quot;), SourceAttackPower);

	// 4.SetByCaller 값(BaseDamage, Light/Heavy Attack Combo Count) 추출
	//   Ability에서 SetSetByCallerMagnitude로 전달한 값을 TMap에서 추출하는 것
	float BaseDamage = 0.0f;
	int32 UsedLightAttackComboCount = 0;
	int32 UsedHeavyAttackComboCount = 0;

	for(const TPair&amp;lt;FGameplayTag, float&amp;gt;&amp;amp; TagMagnitude : EffectSpec.SetByCallerTagMagnitudes)
	{
		if (TagMagnitude.Key.MatchesTagExact(RPGGameplayTags::Shared_SetByCaller_BaseDamage))
		{
			BaseDamage = TagMagnitude.Value;
			Debug::Print(TEXT(&quot;BaseDamage&quot;), BaseDamage);
		}

		if (TagMagnitude.Key.MatchesTagExact(RPGGameplayTags::Player_SetByCaller_AttackType_Light))
		{
			UsedLightAttackComboCount = TagMagnitude.Value;
			Debug::Print(TEXT(&quot;UsedLightAttackComboCount&quot;), UsedLightAttackComboCount);
		}

		if (TagMagnitude.Key.MatchesTagExact(RPGGameplayTags::Player_SetByCaller_AttackType_Heavy))
		{
			UsedHeavyAttackComboCount = TagMagnitude.Value;
			Debug::Print(TEXT(&quot;UsedHeavyAttackComboCount&quot;), UsedHeavyAttackComboCount);
		}
	}

	// 5.Target(피격자)의 DefensePower 추출
	float TargetDefensePower = 0.0f;
	ExecutionParams.AttemptCalculateCapturedAttributeMagnitude(GetRPGDamageCaputre().DefensePowerDef, EvaluateParameters, TargetDefensePower);
	Debug::Print(TEXT(&quot;TargetDefensePower&quot;), TargetDefensePower);

	// 6.Combo Count에 따른 Damage 보정
	if (UsedLightAttackComboCount != 0)
	{
		const float DamageIncreasePercentLight = (UsedLightAttackComboCount - 1) * 0.05 + 1.0f;

		BaseDamage *= DamageIncreasePercentLight;
		Debug::Print(TEXT(&quot;ScaledBaseDamageLight&quot;), BaseDamage);
	}

	if (UsedHeavyAttackComboCount != 0)
	{
		const float DamageIncreasePercentHeavy = UsedHeavyAttackComboCount * 0.15f + 1.0f;

		BaseDamage *= DamageIncreasePercentHeavy;
		Debug::Print(TEXT(&quot;ScaledBaseDamageHeavy&quot;), BaseDamage);
	}

	// 7.최종 피해량 계산
	const float FinalDamageDone = BaseDamage * SourceAttackPower / TargetDefensePower;
	Debug::Print(TEXT(&quot;FinalDamageDone&quot;), FinalDamageDone);

	// 8.피해량 적용
	// 캡처한 DamageTaken Attribute에 Override 방식으로 최종 피해량 적용
	if (FinalDamageDone &amp;gt; 0.0f)
	{
		OutExecutionOutput.AddOutputModifier(
			FGameplayModifierEvaluatedData(
				GetRPGDamageCaputre().DamageTakenProperty,
				EGameplayModOp::Override,
				FinalDamageDone
			)
		);
	}
}&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;GameplaySpec에서 전달 받은 데이터(기본 데미지, 공격력, 방어력 등)을 캡처하여 최종 피해량 계산 후, &quot;DamageTaken&quot; 이라는 Attribute에 데이터 Override 하였다.&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 style=&quot;color: #c8c3bc; text-align: start;&quot; data-ke-size=&quot;size23&quot; data-darkreader-inline-color=&quot;&quot;&gt;&lt;b&gt; &lt;span&gt; 데미지 계산하여, 최종 데미지 도출 결과&lt;/span&gt;&lt;/b&gt;&lt;/h3&gt;
&lt;p&gt;&lt;figure class=&quot;imagegridblock&quot;&gt;
  &lt;div class=&quot;image-container&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bc7tZz/btsNSc50R9K/fqeNDGOpsekGWy2cVIetG1/img.gif&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bc7tZz/btsNSc50R9K/fqeNDGOpsekGWy2cVIetG1/img.gif&quot; data-origin-width=&quot;854&quot; data-origin-height=&quot;480&quot; data-is-animation=&quot;true&quot; data-filename=&quot;2025-05-10 14-19-44.mp4_20250510_142033.gif&quot; style=&quot;width: 59.1321%; margin-right: 10px;&quot; data-widthpercent=&quot;59.83&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bc7tZz/btsNSc50R9K/fqeNDGOpsekGWy2cVIetG1/img.gif&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fbc7tZz%2FbtsNSc50R9K%2FfqeNDGOpsekGWy2cVIetG1%2Fimg.gif&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;854&quot; height=&quot;480&quot;/&gt;&lt;/span&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/dqYbVl/btsNSm1Wl3g/Ee0RL0yMduiS3BDPKZzK40/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/dqYbVl/btsNSm1Wl3g/Ee0RL0yMduiS3BDPKZzK40/img.png&quot; data-origin-width=&quot;491&quot; data-origin-height=&quot;411&quot; data-is-animation=&quot;false&quot; style=&quot;width: 39.7051%;&quot; data-widthpercent=&quot;40.17&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/dqYbVl/btsNSm1Wl3g/Ee0RL0yMduiS3BDPKZzK40/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FdqYbVl%2FbtsNSm1Wl3g%2FEe0RL0yMduiS3BDPKZzK40%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;491&quot; height=&quot;411&quot;/&gt;&lt;/span&gt;&lt;/div&gt;
&lt;/figure&gt;
&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;413&quot; data-origin-height=&quot;76&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/c7CDtT/btsNSvYx1lw/SPF0iCvgscCydjjFqmEad1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/c7CDtT/btsNSvYx1lw/SPF0iCvgscCydjjFqmEad1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/c7CDtT/btsNSvYx1lw/SPF0iCvgscCydjjFqmEad1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fc7CDtT%2FbtsNSvYx1lw%2FSPF0iCvgscCydjjFqmEad1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;440&quot; height=&quot;81&quot; data-origin-width=&quot;413&quot; data-origin-height=&quot;76&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;내부적으로 GE가 Apply될 때, 정상적으로 데미지 계산이 이루어지는 것을 확인할 수 있었다.&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 style=&quot;color: #e8e6e3; text-align: start;&quot; data-ke-size=&quot;size26&quot; data-darkreader-inline-color=&quot;&quot;&gt;&lt;b&gt;  현재 체력에 데미지 적용&lt;/b&gt;&lt;/h2&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;앞서 계산된 최종 데미지를 현제 체력에 적용하고, 캐릭터가 데미지를 입고 죽는지 판단하는 로직을 &quot;Attribute Set&quot; 내부에서 처리하도록 할 것이다.&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 style=&quot;color: #e8e6e3; text-align: start;&quot; data-darkreader-inline-color=&quot;&quot; data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;✔️ RPGAttributeSet&lt;/b&gt;&lt;/h4&gt;
&lt;div data-ke-type=&quot;moreLess&quot; data-text-more=&quot;더보기&quot; data-text-less=&quot;닫기&quot;&gt;&lt;a class=&quot;btn-toggle-moreless&quot;&gt;더보기&lt;/a&gt;
&lt;div class=&quot;moreless-content&quot;&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;b&gt; &lt;span&gt;&lt;span&gt; RPGAttributeSet&lt;/span&gt;.h&lt;/span&gt;&lt;/b&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1746857806647&quot; class=&quot;cpp&quot; data-ke-language=&quot;cpp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;#pragma once

#include &quot;CoreMinimal.h&quot;
#include &quot;AttributeSet.h&quot;
#include &quot;AbilitySystem/RPGAbilitySystemComponent.h&quot;
#include &quot;RPGAttributeSet.generated.h&quot;

#define ATTRIBUTE_ACCESSORS(ClassName, PropertyName) \
GAMEPLAYATTRIBUTE_PROPERTY_GETTER(ClassName, PropertyName) \
GAMEPLAYATTRIBUTE_VALUE_GETTER(PropertyName) \
GAMEPLAYATTRIBUTE_VALUE_SETTER(PropertyName) \
GAMEPLAYATTRIBUTE_VALUE_INITTER(PropertyName)

UCLASS()
class RPG_API URPGAttributeSet : public UAttributeSet
{
	GENERATED_BODY()
	
public:
	URPGAttributeSet();

	// GE에 의해 Attribute 값에 Modifier가 적용되면, 자동으로 호출되는 콜백 함수
	virtual void PostGameplayEffectExecute(const struct FGameplayEffectModCallbackData&amp;amp; Data) override;

	/** 생략 */

	UPROPERTY(BlueprintReadOnly, Category = &quot;Damage&quot;)
	FGameplayAttributeData AttackPower;
	ATTRIBUTE_ACCESSORS(URPGAttributeSet, AttackPower)

	UPROPERTY(BlueprintReadOnly, Category = &quot;Damage&quot;)
	FGameplayAttributeData DefensePower;
	ATTRIBUTE_ACCESSORS(URPGAttributeSet, DefensePower)

	UPROPERTY(BlueprintReadOnly, Category = &quot;Damage&quot;)
	FGameplayAttributeData DamageTaken;
	ATTRIBUTE_ACCESSORS(URPGAttributeSet, DamageTaken)

};&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;b&gt; &lt;span&gt;&lt;span&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;RPGAttributeSet&lt;/span&gt;.cpp&lt;/span&gt;&lt;/b&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1746858003428&quot; class=&quot;cpp&quot; data-ke-language=&quot;cpp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;#include &quot;AbilitySystem/RPGAttributeSet.h&quot;
#include &quot;GameplayEffectExtension.h&quot;

#include &quot;WarriorDebugHelper.h&quot;

URPGAttributeSet::URPGAttributeSet()
{
	InitCurrentHealth(1.0f);
	InitMaxHealth(1.0f);
	InitCurrentRage(1.0f);
	InitMaxRage(1.0f);
	InitAttackPower(1.0f);
	InitDefensePower(1.0f);
}

void URPGAttributeSet::PostGameplayEffectExecute(const FGameplayEffectModCallbackData&amp;amp; Data)
{
	// GE 실행 결과로 Modifier가 적용된 Attribute가 CurrentHealth라면 해당 로직 실행
	if (Data.EvaluatedData.Attribute == GetCurrentHealthAttribute())
	{
		const float NewCurrentHealth = FMath::Clamp(GetCurrentHealth(), 0.0f, GetMaxHealth());

		SetCurrentHealth(NewCurrentHealth);
	}

	if (Data.EvaluatedData.Attribute == GetCurrentRageAttribute())
	{
		const float NewCurrentRage = FMath::Clamp(GetCurrentRage(), 0.0f, GetMaxRage());

		SetCurrentRage(NewCurrentRage);
	}

	if (Data.EvaluatedData.Attribute == GetDamageTakenAttribute())
	{
		const float OldHealth = GetCurrentHealth();
		const float DamageDone = GetDamageTaken();

		const float NewCurrentHealth = FMath::Clamp(OldHealth - DamageDone, 0.0f, GetMaxHealth());

		SetCurrentHealth(NewCurrentHealth);

		const FString DebugString = FString::Printf(
			TEXT(&quot;Old Health %f, Damage Done: %f, NewCurrentHealth: %f&quot;),
			OldHealth,
			DamageDone,
			NewCurrentHealth
		);

		Debug::Print(DebugString, FColor::Green);

		// TODO::사망 처리
		if (NewCurrentHealth == 0.0f)
		{
			// 사망 관련 태그 추가 -&amp;gt; DeathAbility 실행 등
		}
	}
}&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;GameplayEffect가 Modifier를 Attribute에 적용시키면 자동으로 호출되며 해당 로직들이 동작한다.&lt;br /&gt;추후 현재 체력이 0이라면 사망관련 Tag를 부여하고 ASC가 이를 감지해 DeathAbility를 실행시키는 구조로 간다면 자연스럽게 사망로직이 구현될 것이다.&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 style=&quot;color: #c8c3bc; text-align: start;&quot; data-ke-size=&quot;size20&quot; data-darkreader-inline-color=&quot;&quot;&gt; &lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;b&gt;데미지 적용 테스트 결과&lt;/b&gt;&lt;/h4&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock widthContent&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;2025-05-10 15-28-06.mp4_20250510_152848.gif&quot; data-origin-width=&quot;854&quot; data-origin-height=&quot;480&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bZ7ULM/btsNR9BOxoi/a1CjnIvo1J06G1SzhhQr8K/img.gif&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bZ7ULM/btsNR9BOxoi/a1CjnIvo1J06G1SzhhQr8K/img.gif&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bZ7ULM/btsNR9BOxoi/a1CjnIvo1J06G1SzhhQr8K/img.gif&quot; srcset=&quot;https://blog.kakaocdn.net/dn/bZ7ULM/btsNR9BOxoi/a1CjnIvo1J06G1SzhhQr8K/img.gif&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;854&quot; height=&quot;480&quot; data-filename=&quot;2025-05-10 15-28-06.mp4_20250510_152848.gif&quot; data-origin-width=&quot;854&quot; data-origin-height=&quot;480&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;pre id=&quot;code_1746858588009&quot; class=&quot;cpp&quot; data-ke-language=&quot;cpp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;LogTemp: Warning: FinalDamageDone: 16.0
LogTemp: Warning: Old Health 75.000000, Damage Done: 16.000000, NewCurrentHealth: 59.000000
LogTemp: Warning: FinalDamageDone: 16.799999
LogTemp: Warning: Old Health 59.000000, Damage Done: 16.799999, NewCurrentHealth: 42.200001
LogTemp: Warning: FinalDamageDone: 17.600002
LogTemp: Warning: Old Health 42.200001, Damage Done: 17.600002, NewCurrentHealth: 24.599998
LogTemp: Warning: FinalDamageDone: 20.799999
LogTemp: Warning: Old Health 24.599998, Damage Done: 20.799999, NewCurrentHealth: 3.799999
LogTemp: Warning: FinalDamageDone: 16.0
LogTemp: Warning: Old Health 3.799999, Damage Done: 16.000000, NewCurrentHealth: 0.000000
LogTemp: Warning: FinalDamageDone: 16.799999
LogTemp: Warning: Old Health 0.000000, Damage Done: 16.799999, NewCurrentHealth: 0.000000
LogTemp: Warning: FinalDamageDone: 18.4
LogTemp: Warning: Old Health 0.000000, Damage Done: 18.400000, NewCurrentHealth: 0.000000&lt;/code&gt;&lt;/pre&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;AI의 체력이 FinalDamage만큼 감소하고, 체력이 0이 되었을 때 그 이하로 떨어지지 않는 것을 확인할 수 있었다.&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>내배캠/GAS(Gameplay Ability System)</category>
      <author>동그래님</author>
      <guid isPermaLink="true">https://dong-grae.tistory.com/235</guid>
      <comments>https://dong-grae.tistory.com/235#entry235comment</comments>
      <pubDate>Fri, 9 May 2025 21:57:22 +0900</pubDate>
    </item>
    <item>
      <title>GAS_12_피격 판정</title>
      <link>https://dong-grae.tistory.com/234</link>
      <description>&lt;h2 style=&quot;color: #e8e6e3; text-align: start;&quot; data-ke-size=&quot;size26&quot; data-darkreader-inline-color=&quot;&quot;&gt;&lt;b&gt;  근접 무기 충돌 Toggle 시스템&lt;/b&gt;&lt;/h2&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock widthContent&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;2535&quot; data-origin-height=&quot;1086&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cYviK8/btsNPAM5J8a/Frkepi2xK4omFKbi3L7CqK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cYviK8/btsNPAM5J8a/Frkepi2xK4omFKbi3L7CqK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cYviK8/btsNPAM5J8a/Frkepi2xK4omFKbi3L7CqK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcYviK8%2FbtsNPAM5J8a%2FFrkepi2xK4omFKbi3L7CqK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;2535&quot; height=&quot;1086&quot; data-origin-width=&quot;2535&quot; data-origin-height=&quot;1086&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;무기 출돌 활성/ 비활성화를 애니메이션 타이밍에 맞춰 제어하기 위해 Toggle 시스템을 구현한다.&lt;br /&gt;공격 애니메이션의 특정 시점에 무기 충돌을 활성화 하고, 이후 다시 비활성화하여 정확한 타격 판정을 가능하게 하기 위함이다.&lt;br /&gt;&lt;br /&gt;Hero 무기의 Collision을 살펴보면 No Collision으로 되어있고, AnimMontage의 무기 휘두르는 특정 구간을 AnimNotifyState로 Collision 활성화할 것이다.&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 style=&quot;color: #e8e6e3; text-align: start;&quot; data-ke-size=&quot;size23&quot; data-darkreader-inline-color=&quot;&quot;&gt;&lt;b&gt;✅ PawnCombatInterface 클래스 생성&lt;/b&gt;&lt;/h3&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1172&quot; data-origin-height=&quot;707&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/upmJN/btsNPdZrEQY/e3S01o9kJPKJoPk4EhpF4k/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/upmJN/btsNPdZrEQY/e3S01o9kJPKJoPk4EhpF4k/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/upmJN/btsNPdZrEQY/e3S01o9kJPKJoPk4EhpF4k/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FupmJN%2FbtsNPdZrEQY%2Fe3S01o9kJPKJoPk4EhpF4k%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1172&quot; height=&quot;707&quot; data-origin-width=&quot;1172&quot; data-origin-height=&quot;707&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;&quot;Unreal Interface&quot;를 상속 받는 &quot;PawnCombatInterface&quot; C++ 클래스를 생성한다.&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 style=&quot;color: #e8e6e3; text-align: start;&quot; data-ke-size=&quot;size20&quot; data-darkreader-inline-color=&quot;&quot;&gt;&lt;b&gt; &lt;span&gt; PawnCombatInterface.h&lt;/span&gt;&lt;/b&gt;&lt;/h4&gt;
&lt;pre id=&quot;code_1746761757683&quot; class=&quot;cpp&quot; data-ke-language=&quot;cpp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;#pragma once

#include &quot;CoreMinimal.h&quot;
#include &quot;UObject/Interface.h&quot;
#include &quot;PawnCombatInterface.generated.h&quot;

class UPawnCombatComponent;

UINTERFACE(MinimalAPI)
class UPawnCombatInterface : public UInterface
{
	GENERATED_BODY()
};

class RPG_API IPawnCombatInterface
{
	GENERATED_BODY()

public:
	virtual UPawnCombatComponent* GetPawnCombatComponent() const = 0;
};&lt;/code&gt;&lt;/pre&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;PawnCombatComponent를 반환하는 순수가상함수를 선언한다.&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 style=&quot;color: #e8e6e3; text-align: start;&quot; data-darkreader-inline-color=&quot;&quot; data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;✅ PawnCombatInterface 상속 및 함수 구현&lt;/b&gt;&lt;/h3&gt;
&lt;h4 style=&quot;color: #e8e6e3; text-align: start;&quot; data-ke-size=&quot;size20&quot; data-darkreader-inline-color=&quot;&quot;&gt;&lt;b&gt;✔️ RPGBaseCharacter 클래스&lt;/b&gt;&lt;/h4&gt;
&lt;div data-ke-type=&quot;moreLess&quot; data-text-more=&quot;더보기&quot; data-text-less=&quot;닫기&quot;&gt;&lt;a class=&quot;btn-toggle-moreless&quot;&gt;더보기&lt;/a&gt;
&lt;div class=&quot;moreless-content&quot;&gt;
&lt;p style=&quot;color: #e8e6e3; text-align: start;&quot; data-darkreader-inline-color=&quot;&quot; data-ke-size=&quot;size18&quot;&gt;&lt;b&gt; &lt;span&gt;&lt;span&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;RPGBaseCharacter&lt;/span&gt;.h&lt;/span&gt;&lt;/b&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1746762350401&quot; class=&quot;cpp&quot; style=&quot;background-color: #1c1e1f; color: #c1bcb4;&quot; data-ke-type=&quot;codeblock&quot; data-ke-language=&quot;cpp&quot; data-darkreader-inline-bgcolor=&quot;&quot; data-darkreader-inline-color=&quot;&quot;&gt;&lt;code&gt;#pragma once

#include &quot;CoreMinimal.h&quot;
#include &quot;GameFramework/Character.h&quot;
#include &quot;AbilitySystemInterface.h&quot;
#include &quot;Interfaces/PawnCombatInterface.h&quot; // Interface 헤더 추가
#include &quot;RPGBaseCharacter.generated.h&quot;

class URPGAbilitySystemComponent;
class URPGAttributeSet;
class UDataAsset_StartUpDataBase;

UCLASS()
class RPG_API ARPGBaseCharacter : public ACharacter, public IAbilitySystemInterface, public IPawnCombatInterface // Interface 상속
{
	GENERATED_BODY()

public:
	ARPGBaseCharacter();

	//~ Begin IAbilitySystemInterface Interface
	virtual UAbilitySystemComponent* GetAbilitySystemComponent() const override;
	//~ End IAbilitySystemInterface Interface.

	//~ Begin IPawnCombatInterface Interface
	virtual UPawnCombatComponent* GetPawnCombatComponent() const override; // 순수 가상함수 Override
	//~ End IPawnCombatInterface Interface.
}&lt;/code&gt;&lt;/pre&gt;
&lt;p style=&quot;color: #e8e6e3; text-align: start;&quot; data-darkreader-inline-color=&quot;&quot; data-ke-size=&quot;size18&quot;&gt;&lt;b&gt; &lt;span&gt;&lt;span&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;RPGBaseCharacter&lt;/span&gt;.cpp&lt;/span&gt;&lt;/b&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1746762350402&quot; class=&quot;cpp&quot; style=&quot;background-color: #1c1e1f; color: #c1bcb4;&quot; data-ke-type=&quot;codeblock&quot; data-ke-language=&quot;cpp&quot; data-darkreader-inline-bgcolor=&quot;&quot; data-darkreader-inline-color=&quot;&quot;&gt;&lt;code&gt;UPawnCombatComponent* ARPGEnemyCharacter::GetPawnCombatComponent() const
{
	return EnemyCombatComponent;
}&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;BaseCharacter 클래스에서 &quot;PawnCombatInterface&quot;를 상속받고, &quot;GetPawnCombatComponent()&quot; 순수 가상함수를 선언 및 구현한다.&lt;/blockquote&gt;
&lt;h4 style=&quot;color: #e8e6e3; text-align: start;&quot; data-darkreader-inline-color=&quot;&quot; data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;✔️ Hero/Enemy Character클래스&lt;/b&gt;&lt;/h4&gt;
&lt;div data-ke-type=&quot;moreLess&quot; data-text-more=&quot;더보기&quot; data-text-less=&quot;닫기&quot;&gt;&lt;a class=&quot;btn-toggle-moreless&quot;&gt;더보기&lt;/a&gt;
&lt;div class=&quot;moreless-content&quot;&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;b&gt; &lt;span&gt;&lt;span&gt;&lt;span&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;WarriorHeroCharacter&lt;/span&gt;&lt;/span&gt;.h&lt;/span&gt;&lt;/b&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1746762602969&quot; class=&quot;angelscript&quot; style=&quot;background-color: #1c1e1f; color: #c1bcb4;&quot; data-ke-type=&quot;codeblock&quot; data-ke-language=&quot;cpp&quot; data-darkreader-inline-bgcolor=&quot;&quot; data-darkreader-inline-color=&quot;&quot;&gt;&lt;code&gt;UCLASS()
class RPG_API AWarriorHeroCharacter : public ARPGBaseCharacter
{
	GENERATED_BODY()
	
public:
	AWarriorHeroCharacter();

	//~ Begin IPawnCombatInterface Interface
	virtual UPawnCombatComponent* GetPawnCombatComponent() const override;
	//~ End IPawnCombatInterface Interface.
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;b&gt; &lt;span&gt;&lt;span&gt;&lt;span&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;WarriorHeroCharacter&lt;/span&gt;&lt;/span&gt;.cpp&lt;/span&gt;&lt;/b&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1746762602969&quot; class=&quot;cpp&quot; style=&quot;background-color: #1c1e1f; color: #c1bcb4;&quot; data-ke-type=&quot;codeblock&quot; data-ke-language=&quot;cpp&quot; data-darkreader-inline-bgcolor=&quot;&quot; data-darkreader-inline-color=&quot;&quot;&gt;&lt;code&gt;AWarriorHeroCharacter::AWarriorHeroCharacter()
{
	/** 생략 */

	HeroCombatComponent = CreateDefaultSubobject&amp;lt;UHeroCombatComponent&amp;gt;(TEXT(&quot;HeroCombatComponent&quot;));
}

UPawnCombatComponent* AWarriorHeroCharacter::GetPawnCombatComponent() const
{
	return HeroCombatComponent;
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;b&gt; &lt;span&gt;&lt;span&gt;&lt;span&gt;&lt;span&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;RPGEnemyCharacter&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;.h&lt;/span&gt;&lt;/b&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1746762602970&quot; class=&quot;angelscript&quot; style=&quot;background-color: #1c1e1f; color: #c1bcb4;&quot; data-ke-type=&quot;codeblock&quot; data-ke-language=&quot;cpp&quot; data-darkreader-inline-bgcolor=&quot;&quot; data-darkreader-inline-color=&quot;&quot;&gt;&lt;code&gt;UCLASS()
class RPG_API ARPGEnemyCharacter : public ARPGBaseCharacter
{
	GENERATED_BODY()
	
public:
	ARPGEnemyCharacter();

	//~ Begin IPawnCombatInterface Interface
	virtual UPawnCombatComponent* GetPawnCombatComponent() const override;
	//~ End IPawnCombatInterface Interface.
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;b&gt; &lt;span&gt;&lt;span&gt;&lt;span&gt;&lt;span&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;RPGEnemyCharacter&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;.cpp&lt;/span&gt;&lt;/b&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1746762602970&quot; class=&quot;cpp&quot; style=&quot;background-color: #1c1e1f; color: #c1bcb4;&quot; data-ke-type=&quot;codeblock&quot; data-ke-language=&quot;cpp&quot; data-darkreader-inline-bgcolor=&quot;&quot; data-darkreader-inline-color=&quot;&quot;&gt;&lt;code&gt;ARPGEnemyCharacter::ARPGEnemyCharacter()
{
	/** 생략 */

	EnemyCombatComponent = CreateDefaultSubobject&amp;lt;UEnemyCombatComponent&amp;gt;(TEXT(&quot;EnemyCombatComponent&quot;));
}

UPawnCombatComponent* ARPGEnemyCharacter::GetPawnCombatComponent() const
{
	return EnemyCombatComponent;
}&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;HeroCharacter, EnemyCharacter 모두 &quot;GetPawnCombatComponent&quot; 함수를 재정의 하여, 각각 가지고 있는 CombatComponent를 반환하도록 구현한다.&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 style=&quot;color: #e8e6e3; text-align: start;&quot; data-ke-size=&quot;size23&quot; data-darkreader-inline-color=&quot;&quot;&gt;&lt;b&gt;✅ PawnCombatComponent 반환 라이브러리 함수 구현&lt;/b&gt;&lt;/h3&gt;
&lt;h4 style=&quot;color: #e8e6e3; text-align: start;&quot; data-ke-size=&quot;size20&quot; data-darkreader-inline-color=&quot;&quot;&gt;&lt;b&gt;✔️ RPGFunctionLibrary 클래스&lt;/b&gt;&lt;/h4&gt;
&lt;div data-ke-type=&quot;moreLess&quot; data-text-more=&quot;더보기&quot; data-text-less=&quot;닫기&quot;&gt;&lt;a class=&quot;btn-toggle-moreless&quot;&gt;더보기&lt;/a&gt;
&lt;div class=&quot;moreless-content&quot;&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;b&gt; &lt;span&gt;&lt;span&gt;&lt;span&gt;&lt;span&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;RPGFunctionLibrary&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;.h&lt;/span&gt;&lt;/b&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1746766182933&quot; class=&quot;cpp&quot; style=&quot;background-color: #1c1e1f; color: #c1bcb4;&quot; data-ke-type=&quot;codeblock&quot; data-ke-language=&quot;cpp&quot; data-darkreader-inline-bgcolor=&quot;&quot; data-darkreader-inline-color=&quot;&quot;&gt;&lt;code&gt;#pragma once

#include &quot;CoreMinimal.h&quot;
#include &quot;Kismet/BlueprintFunctionLibrary.h&quot;
#include &quot;GameplayTagContainer.h&quot;
#include &quot;RPGTypes/RPGEnumTypes.h&quot;
#include &quot;RPGFunctionLibrary.generated.h&quot;

class URPGAbilitySystemComponent;
class UPawnCombatComponent;

UCLASS()
class RPG_API URPGFunctionLibrary : public UBlueprintFunctionLibrary
{
	GENERATED_BODY()
	
public:
	static URPGAbilitySystemComponent* NativeGetRPGASC_FormActor(AActor* InActor);

	static bool NativeDoesActorHaveTag(AActor* InActor, FGameplayTag TagToCheck);

	UFUNCTION(BlueprintCallable, Category = &quot;RPG|FunctionLibrary&quot;)
	static void AddGameplayTagToActorIfNone(AActor* InActor, FGameplayTag TagToAdd);

	UFUNCTION(BlueprintCallable, Category = &quot;RPG|FunctionLibrary&quot;)
	static void RemoveGameplayTagFromActorIfFound(AActor* InActor, FGameplayTag TagToRemove);

	UFUNCTION(BlueprintCallable, Category = &quot;RPG|FunctionLibrary&quot;, meta = (DisplayName = &quot;Does Actor Have Tag&quot;, ExpandEnumAsExecs = &quot;OutConfirmType&quot;))
	static void BP_DoesActorHaveTag(AActor* InActor, FGameplayTag TagToCheck, ERPGConfirmType&amp;amp; OutConfirmType);

	// PawnCombatComponent를 반환
	static UPawnCombatComponent* NativeGetPawnCombatComponentFromActor(AActor* InActor);

	// BP에서 PawnCombatComponent를 반환 + Enum 타입 반환
	UFUNCTION(BlueprintCallable, Category = &quot;RPG|FunctionLibrary&quot;, meta = (DisplayName = &quot;Get Pawn Combat Component From Actor&quot;, ExpandEnumAsExecs = &quot;OutVaildType&quot;))
	static UPawnCombatComponent* BP_GetPawnCombatComponentFromActor(AActor* InActor, ERPGValidType&amp;amp; OutVaildType);
	
};&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;b&gt; &lt;span&gt;&lt;span&gt;&lt;span&gt;&lt;span&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;RPGFunctionLibrary&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;.h&lt;/span&gt;&lt;/b&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1746766182935&quot; class=&quot;dts&quot; style=&quot;background-color: #1c1e1f; color: #c1bcb4;&quot; data-ke-type=&quot;codeblock&quot; data-ke-language=&quot;cpp&quot; data-darkreader-inline-bgcolor=&quot;&quot; data-darkreader-inline-color=&quot;&quot;&gt;&lt;code&gt;#include &quot;RPGFunctionLibrary.h&quot;
#include &quot;AbilitySystemBlueprintLibrary.h&quot;
#include &quot;AbilitySystem/RPGAbilitySystemComponent.h&quot;
#include &quot;Components/Combat/PawnCombatComponent.h&quot;
#include &quot;Interfaces/PawnCombatInterface.h&quot;

UPawnCombatComponent* URPGFunctionLibrary::NativeGetPawnCombatComponentFromActor(AActor* InActor)
{
	check(InActor);

	if (IPawnCombatInterface* PawnCombatInterface = Cast&amp;lt;IPawnCombatInterface&amp;gt;(InActor))
	{
		return PawnCombatInterface-&amp;gt;GetPawnCombatComponent();
	}

	return nullptr;
}

UPawnCombatComponent* URPGFunctionLibrary::BP_GetPawnCombatComponentFromActor(AActor* InActor, ERPGValidType&amp;amp; OutVaildType)
{
	UPawnCombatComponent* CombatComponent = NativeGetPawnCombatComponentFromActor(InActor);

	OutVaildType = CombatComponent ? ERPGValidType::Vaild : ERPGValidType::InVaild;

	return CombatComponent;
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;블루프린트(AnimNotifyState)내에서 쉽게 PawnCombatComponent를 가져오고 유효성 검사도 동시에 진행하는 Library 함수를 구현한다.&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 style=&quot;color: #e8e6e3; text-align: start;&quot; data-darkreader-inline-color=&quot;&quot; data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;✅ PawnCombatCompont에 ToggleCollision 기능 구현&lt;/b&gt;&lt;/h3&gt;
&lt;h4 style=&quot;color: #e8e6e3; text-align: start;&quot; data-ke-size=&quot;size20&quot; data-darkreader-inline-color=&quot;&quot;&gt;&lt;b&gt;✔️ PawnCombatComponent&amp;nbsp;&lt;/b&gt;&lt;/h4&gt;
&lt;div data-ke-type=&quot;moreLess&quot; data-text-more=&quot;더보기&quot; data-text-less=&quot;닫기&quot;&gt;&lt;a class=&quot;btn-toggle-moreless&quot;&gt;더보기&lt;/a&gt;
&lt;div class=&quot;moreless-content&quot;&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;b&gt; &lt;span&gt;&lt;span&gt;&lt;span&gt;&lt;span&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/b&gt;&lt;b&gt;PawnCombatComponent&lt;/b&gt;&lt;b&gt;&lt;span&gt;&lt;span&gt;&lt;span&gt;&lt;span&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;.h&lt;/span&gt;&lt;/b&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1746769229500&quot; class=&quot;cpp&quot; data-ke-language=&quot;cpp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;#pragma once

#include &quot;CoreMinimal.h&quot;
#include &quot;Components/PawnExtensionComponentBase.h&quot;
#include &quot;GameplayTagContainer.h&quot;
#include &quot;PawnCombatComponent.generated.h&quot;

class ARPGWeaponBase;

// 데미지 주체 타입 정의
UENUM(BlueprintType)
enum class EToggleDamageType : uint8
{
	CurrentEquippedWeapon,
	LeftHand,
	RightHand
};

UCLASS()
class RPG_API UPawnCombatComponent : public UPawnExtensionComponentBase
{
	GENERATED_BODY()
	
public:
	UFUNCTION(BlueprintCallable, Category = &quot;RPG|Combat&quot;)
	void RegisterSpawnedWeapon(FGameplayTag InWeaponTagToRegister, ARPGWeaponBase* InWeaponToRegister, bool bRegisterAsEquippedWeapon = false);

	UFUNCTION(BlueprintCallable, Category = &quot;RPG|Combat&quot;)
	ARPGWeaponBase* GetCharacterCarriedWeaponByTag(FGameplayTag InWeaponTagToGet) const;

	UFUNCTION(BlueprintCallable, Category = &quot;RPG|Combat&quot;)
	ARPGWeaponBase* GetCharacterCurrentEquippedWeapon() const;
    
	// 콜리전 (비)활성화 Toggle 기능
	UFUNCTION(BlueprintCallable, Category = &quot;RPG|Combat&quot;)
	void ToggleWeaponCollision(bool bShouldEnable, EToggleDamageType ToggleDamageType = EToggleDamageType::CurrentEquippedWeapon);

	UPROPERTY(BlueprintReadWrite, Category = &quot;RPG|Combat&quot;)
	FGameplayTag CurrentEquippedWeaponTag;

private:
	TMap&amp;lt;FGameplayTag, ARPGWeaponBase*&amp;gt; CharacterCarriedWeaponMap;
};&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;b&gt; &lt;span&gt;&lt;span&gt;&lt;span&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/b&gt;&lt;b&gt;PawnCombatComponent&lt;/b&gt;&lt;b&gt;&lt;span&gt;&lt;span&gt;&lt;span&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;.cpp&lt;/span&gt;&lt;/b&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1746769267122&quot; class=&quot;cpp&quot; data-ke-language=&quot;cpp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;#include &quot;Components/Combat/PawnCombatComponent.h&quot;
#include &quot;Items/Weapons/RPGWeaponBase.h&quot;
#include &quot;Components/BoxComponent.h&quot;

#include &quot;WarriorDebugHelper.h&quot;

/** 생략 */

void UPawnCombatComponent::ToggleWeaponCollision(bool bShouldEnable, EToggleDamageType ToggleDamageType)
{
	if (ToggleDamageType == EToggleDamageType::CurrentEquippedWeapon)
	{
		ARPGWeaponBase* WeaponToToggle = GetCharacterCurrentEquippedWeapon();

		check(WeaponToToggle);

		if (bShouldEnable)
		{
			WeaponToToggle-&amp;gt;GetWeaponCollisionBox()-&amp;gt;SetCollisionEnabled(ECollisionEnabled::QueryOnly);
			Debug::Print(WeaponToToggle-&amp;gt;GetName() + TEXT(&quot;Collision Enabled&quot;), FColor::Green);
		}
		else
		{
			WeaponToToggle-&amp;gt;GetWeaponCollisionBox()-&amp;gt;SetCollisionEnabled(ECollisionEnabled::NoCollision);
			Debug::Print(WeaponToToggle-&amp;gt;GetName() + TEXT(&quot;Collision Disabled&quot;), FColor::Red);
		}
		
	}
}&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;데미지 타입이 착용 중인 무기라면, bool 값에 따라 무기의 Collision을 설정하도록 한다.&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 style=&quot;color: #e8e6e3; text-align: start;&quot; data-darkreader-inline-color=&quot;&quot; data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;✅ 공격 시, 콜리전 (비)활성화 적용&lt;/b&gt;&lt;/h3&gt;
&lt;h4 style=&quot;color: #e8e6e3; text-align: start;&quot; data-darkreader-inline-color=&quot;&quot; data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;✔️ Anim Notify State_ToggleWeaponCollision 구현&lt;/b&gt;&lt;/h4&gt;
&lt;p&gt;&lt;figure class=&quot;imagegridblock&quot;&gt;
  &lt;div class=&quot;image-container&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/ZXhXo/btsNQclFTrI/kP37VCvgEFLP91BmbpZpbk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/ZXhXo/btsNQclFTrI/kP37VCvgEFLP91BmbpZpbk/img.png&quot; data-origin-width=&quot;767&quot; data-origin-height=&quot;781&quot; data-is-animation=&quot;false&quot; style=&quot;width: 32.3745%; margin-right: 10px;&quot; data-widthpercent=&quot;32.76&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/ZXhXo/btsNQclFTrI/kP37VCvgEFLP91BmbpZpbk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FZXhXo%2FbtsNQclFTrI%2FkP37VCvgEFLP91BmbpZpbk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;767&quot; height=&quot;781&quot;/&gt;&lt;/span&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/PmPxv/btsNRfIj4mp/mxw283KvBYoISIgc7nMH5k/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/PmPxv/btsNRfIj4mp/mxw283KvBYoISIgc7nMH5k/img.png&quot; data-origin-width=&quot;2000&quot; data-origin-height=&quot;992&quot; data-is-animation=&quot;false&quot; style=&quot;width: 66.4627%;&quot; data-widthpercent=&quot;67.24&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/PmPxv/btsNRfIj4mp/mxw283KvBYoISIgc7nMH5k/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FPmPxv%2FbtsNRfIj4mp%2Fmxw283KvBYoISIgc7nMH5k%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;2000&quot; height=&quot;992&quot;/&gt;&lt;/span&gt;&lt;/div&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;에디터로 돌아와 AnimNotifyState를 상속 받는 BP 클래스를 생성한다. &quot;ANS_ToggleWeaponCollision&quot;으로 명명한다.&lt;br /&gt;&lt;br /&gt;1. MeshComponent에서 &quot;GetOwner&quot;로 Actor를 가져온다.&lt;br /&gt;2. 앞서 Fuction Library에서 구현했던 &quot;GetPawnCombatComponentFromActor&quot; 함수에 Actor를 전달해서 &quot;PawnCombatComponent를 반환 받는다.&lt;br /&gt;3. &quot;PawnCombatComponent&quot;의 &quot;ToggleWeaponCollision&quot; 함수를 통해 해당 무기의 콜리전을 (비)활성화 한다.&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 style=&quot;color: #e8e6e3; text-align: start;&quot; data-ke-size=&quot;size20&quot; data-darkreader-inline-color=&quot;&quot;&gt;&lt;b&gt;✔️ 공격 Montage에 ANS 적용&lt;/b&gt;&lt;/h4&gt;
&lt;p&gt;&lt;figure class=&quot;imagegridblock&quot;&gt;
  &lt;div class=&quot;image-container&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/8i8jF/btsNPGm0E0v/DN1dTgOEMGWGwKQmqGiQB1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/8i8jF/btsNPGm0E0v/DN1dTgOEMGWGwKQmqGiQB1/img.png&quot; data-origin-width=&quot;1451&quot; data-origin-height=&quot;1218&quot; data-is-animation=&quot;false&quot; style=&quot;width: 55.1809%; margin-right: 10px;&quot; data-widthpercent=&quot;55.83&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/8i8jF/btsNPGm0E0v/DN1dTgOEMGWGwKQmqGiQB1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F8i8jF%2FbtsNPGm0E0v%2FDN1dTgOEMGWGwKQmqGiQB1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1451&quot; height=&quot;1218&quot;/&gt;&lt;/span&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/3mMbT/btsNRg1zqPb/iG1dyczaV2TAZoFizpGKqk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/3mMbT/btsNRg1zqPb/iG1dyczaV2TAZoFizpGKqk/img.png&quot; data-origin-width=&quot;590&quot; data-origin-height=&quot;626&quot; data-is-animation=&quot;false&quot; style=&quot;width: 43.6563%;&quot; data-widthpercent=&quot;44.17&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/3mMbT/btsNRg1zqPb/iG1dyczaV2TAZoFizpGKqk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F3mMbT%2FbtsNRg1zqPb%2FiG1dyczaV2TAZoFizpGKqk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;590&quot; height=&quot;626&quot;/&gt;&lt;/span&gt;&lt;/div&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;공격 판정이 필요한 지점에 &quot;ANS_ToggleWeaponCollision&quot; 설정을 진행한다.&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 style=&quot;color: #c8c3bc; text-align: start;&quot; data-ke-size=&quot;size23&quot; data-darkreader-inline-color=&quot;&quot;&gt;&lt;b&gt; &lt;span&gt; Collision 활성화 테스트&lt;/span&gt;&lt;/b&gt;&lt;/h3&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock widthContent&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;2025-05-09 15-05-52.mp4_20250509_150644.gif&quot; data-origin-width=&quot;854&quot; data-origin-height=&quot;480&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/plpzG/btsNR8vbdoj/DtptrkbiJglRBk6KbmaoYK/img.gif&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/plpzG/btsNR8vbdoj/DtptrkbiJglRBk6KbmaoYK/img.gif&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/plpzG/btsNR8vbdoj/DtptrkbiJglRBk6KbmaoYK/img.gif&quot; srcset=&quot;https://blog.kakaocdn.net/dn/plpzG/btsNR8vbdoj/DtptrkbiJglRBk6KbmaoYK/img.gif&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;854&quot; height=&quot;480&quot; data-filename=&quot;2025-05-09 15-05-52.mp4_20250509_150644.gif&quot; data-origin-width=&quot;854&quot; data-origin-height=&quot;480&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;무기를 휘두르기 시작한 지점에 Collision이 활성화되고, 휘두르는 동작 End지점에서 Collision이 다시 비활성화 되는 것을 확인할 수 있다.&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 style=&quot;color: #e8e6e3; text-align: start;&quot; data-darkreader-inline-color=&quot;&quot; data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;  콜리전으로 피격 대상 감지&lt;/b&gt;&lt;/h2&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;무기로 공격할 때, 적절한 시점에 Collision을 활성화 하고, 휘두르는 동작이 끝나면 Collision이 비활성화 되는 것까지 구현이 되었다.&lt;br /&gt;이제 실제로 Collision에 Overlap된 대상이 누구인지 감지하도록 한다.&lt;/blockquote&gt;
&lt;p style=&quot;color: #e8e6e3; text-align: start;&quot; data-ke-size=&quot;size16&quot; data-darkreader-inline-color=&quot;&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 style=&quot;color: #e8e6e3; text-align: start;&quot; data-ke-size=&quot;size20&quot; data-darkreader-inline-color=&quot;&quot;&gt;&lt;b&gt;✔️&lt;span&gt; RPGGameplayTags&lt;/span&gt;&lt;/b&gt;&lt;/h4&gt;
&lt;div style=&quot;background-color: #1b1d1e; color: #c8c3bc; text-align: start;&quot; data-darkreader-inline-color=&quot;&quot; data-darkreader-inline-bgcolor=&quot;&quot; data-ke-type=&quot;moreLess&quot; data-text-more=&quot;더보기&quot; data-text-less=&quot;닫기&quot;&gt;&lt;a class=&quot;btn-toggle-moreless&quot;&gt;더보기&lt;/a&gt;
&lt;div class=&quot;moreless-content&quot;&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;b&gt; RPGGameplayTags&lt;/b&gt;&lt;b&gt;&lt;span&gt;.h&lt;/span&gt;&lt;/b&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1746777748095&quot; class=&quot;cpp&quot; data-ke-language=&quot;cpp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;#include &quot;RPGGameplayTags.h&quot;

namespace RPGGameplayTags
{
	/** 생략 */

	/** Shared Tags */
	UE_DEFINE_GAMEPLAY_TAG(Shared_Event_MeleeHit, &quot;Shared.Event.MeleeHit&quot;);
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;b&gt; RPGGameplayTags&lt;/b&gt;&lt;b&gt;&lt;span&gt;.cpp&lt;/span&gt;&lt;/b&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1746777782487&quot; class=&quot;cpp&quot; data-ke-language=&quot;cpp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;#pragma once

#include &quot;NativeGameplayTags.h&quot;

namespace RPGGameplayTags
{
	/** 생략 */

	/** Shared Tags */
	RPG_API UE_DECLARE_GAMEPLAY_TAG_EXTERN(Shared_Event_MeleeHit);

}&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;피격 상황 발생 시, 해당 태그로 Ability에서 Event를 감지하도록 한다.&lt;/blockquote&gt;
&lt;p style=&quot;color: #e8e6e3; text-align: start;&quot; data-ke-size=&quot;size16&quot; data-darkreader-inline-color=&quot;&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 style=&quot;color: #e8e6e3; text-align: start;&quot; data-darkreader-inline-color=&quot;&quot; data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;✔️ &lt;b&gt;RPGWeaponBase&lt;/b&gt; &lt;/b&gt;&lt;/h4&gt;
&lt;div style=&quot;background-color: #1b1d1e; color: #c8c3bc; text-align: start;&quot; data-text-less=&quot;닫기&quot; data-text-more=&quot;더보기&quot; data-ke-type=&quot;moreLess&quot; data-darkreader-inline-bgcolor=&quot;&quot; data-darkreader-inline-color=&quot;&quot;&gt;&lt;a class=&quot;btn-toggle-moreless&quot;&gt;더보기&lt;/a&gt;
&lt;div class=&quot;moreless-content&quot;&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;b&gt; RPGWeaponBase&lt;/b&gt;&lt;b&gt;&lt;span&gt;.h&lt;/span&gt;&lt;/b&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1746776604939&quot; class=&quot;cpp&quot; data-ke-language=&quot;cpp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;#pragma once

#include &quot;CoreMinimal.h&quot;
#include &quot;GameFramework/Actor.h&quot;
#include &quot;RPGWeaponBase.generated.h&quot;

class UBoxComponent;

DECLARE_DELEGATE_OneParam(FOnTargetInterfacedDelegate, AActor*) // 인자 하나를 넘길 수 있는 델리게이트 선언

UCLASS()
class RPG_API ARPGWeaponBase : public AActor
{
	GENERATED_BODY()
	
public:	
	ARPGWeaponBase();

	FOnTargetInterfacedDelegate OnWeaponHitTarget;
	FOnTargetInterfacedDelegate OnWeaponPulledFromTarget;

protected:
	UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = &quot;Weapons&quot;)
	UStaticMeshComponent* WeaponMesh;

	UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = &quot;Weapons&quot;)
	UBoxComponent* WeaponCollisionBox;

	UFUNCTION()
	virtual void OnCollisionBoxBeginOverlap( // 오버랩 시작 시 호출 함수
		UPrimitiveComponent* OverlappedComponent, AActor* OtherActor,
		UPrimitiveComponent* OtherComp, int32 OtherBodyIndex,
		bool bFromSweep, const FHitResult&amp;amp; SweepResult);

	UFUNCTION()
	virtual void OnCollisionBoxEndOverlap( // 오버랩 종료 시 호출 함수
		UPrimitiveComponent* OverlappedComponent, AActor* OtherActor,
		UPrimitiveComponent* OtherComp, int32 OtherBodyIndex);

public:
	FORCEINLINE UBoxComponent* GetWeaponCollisionBox() const { return WeaponCollisionBox; }
};&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;b&gt; RPGWeaponBase&lt;/b&gt;&lt;b&gt;&lt;span&gt;.cpp&lt;/span&gt;&lt;/b&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1746776767478&quot; class=&quot;cpp&quot; data-ke-language=&quot;cpp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;#include &quot;Items/Weapons/RPGWeaponBase.h&quot;
#include &quot;Components/BoxComponent.h&quot;

#include &quot;WarriorDebugHelper.h&quot;

ARPGWeaponBase::ARPGWeaponBase()
{
	PrimaryActorTick.bCanEverTick = false;

	WeaponMesh = CreateDefaultSubobject&amp;lt;UStaticMeshComponent&amp;gt;(TEXT(&quot;WeaponMesh&quot;));
	SetRootComponent(WeaponMesh);

	WeaponCollisionBox = CreateDefaultSubobject&amp;lt;UBoxComponent&amp;gt;(TEXT(&quot;WeaponCollisionBox&quot;));
	WeaponCollisionBox-&amp;gt;SetupAttachment(GetRootComponent());
	WeaponCollisionBox-&amp;gt;SetBoxExtent(FVector(20.0f));
	WeaponCollisionBox-&amp;gt;SetCollisionEnabled(ECollisionEnabled::NoCollision);
	
    // Box Collision에 Overlap Event 발생 시, 호출될 함수 바인딩
	WeaponCollisionBox-&amp;gt;OnComponentBeginOverlap.AddUniqueDynamic(this, &amp;amp;ARPGWeaponBase::OnCollisionBoxBeginOverlap);
	WeaponCollisionBox-&amp;gt;OnComponentEndOverlap.AddUniqueDynamic(this, &amp;amp;ARPGWeaponBase::OnCollisionBoxEndOverlap);
}

void ARPGWeaponBase::OnCollisionBoxBeginOverlap(
	UPrimitiveComponent* OverlappedComponent, AActor* OtherActor,
	UPrimitiveComponent* OtherComp, int32 OtherBodyIndex,
	bool bFromSweep, const FHitResult&amp;amp; SweepResult)
{
	APawn* WeaponOwningPawn = GetInstigator&amp;lt;APawn&amp;gt;();

	checkf(WeaponOwningPawn, TEXT(&quot;Forgot to assign an instigator as the owning pawn for the weapon: %s&quot;), *GetName());

	if (APawn* HitPawn = Cast&amp;lt;APawn&amp;gt;(OtherActor))
	{
		if (WeaponOwningPawn != HitPawn)
		{
			OnWeaponHitTarget.ExecuteIfBound(OtherActor); // BroadCast 실행
		}
	}
}

void ARPGWeaponBase::OnCollisionBoxEndOverlap(
	UPrimitiveComponent* OverlappedComponent, AActor* OtherActor,
	UPrimitiveComponent* OtherComp, int32 OtherBodyIndex)
{
	APawn* WeaponOwningPawn = GetInstigator&amp;lt;APawn&amp;gt;();

	checkf(WeaponOwningPawn, TEXT(&quot;Forgot to assign an instigator as the owning pawn for the weapon: %s&quot;), *GetName());

	if (APawn* HitPawn = Cast&amp;lt;APawn&amp;gt;(OtherActor))
	{
		if (WeaponOwningPawn != HitPawn)
		{
			OnWeaponPulledFromTarget.ExecuteIfBound(OtherActor); // BroadCast 실행
		}
	}
}&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;BaseWeapon 클래스에 콜리전에 오버랩 이벤트 발생 시, 델리게이트 구독한 클래스 객체들에게 BroadCasting 하도록 구현한다.&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 style=&quot;color: #e8e6e3; text-align: start;&quot; data-ke-size=&quot;size20&quot; data-darkreader-inline-color=&quot;&quot;&gt;&lt;b&gt;✔️&lt;span&gt; PawnCombatComponent&lt;/span&gt;&lt;/b&gt;&lt;/h4&gt;
&lt;div style=&quot;background-color: #1b1d1e; color: #c8c3bc; text-align: start;&quot; data-darkreader-inline-color=&quot;&quot; data-darkreader-inline-bgcolor=&quot;&quot; data-ke-type=&quot;moreLess&quot; data-text-more=&quot;더보기&quot; data-text-less=&quot;닫기&quot;&gt;&lt;a class=&quot;btn-toggle-moreless&quot;&gt;더보기&lt;/a&gt;
&lt;div class=&quot;moreless-content&quot;&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;b&gt; PawnCombatComponent&lt;/b&gt;&lt;b&gt;&lt;span&gt;.h&lt;/span&gt;&lt;/b&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1746776870118&quot; class=&quot;cpp&quot; data-ke-language=&quot;cpp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;#pragma once

#include &quot;CoreMinimal.h&quot;
#include &quot;Components/PawnExtensionComponentBase.h&quot;
#include &quot;GameplayTagContainer.h&quot;
#include &quot;PawnCombatComponent.generated.h&quot;

class ARPGWeaponBase;

UENUM(BlueprintType)
enum class EToggleDamageType : uint8
{
	CurrentEquippedWeapon,
	LeftHand,
	RightHand
};

UCLASS()
class RPG_API UPawnCombatComponent : public UPawnExtensionComponentBase
{
	GENERATED_BODY()
	
public:
	// Tag 기반으로 무기를 Map에 저장, 현재 장착된 무기 정보 갱신
	UFUNCTION(BlueprintCallable, Category = &quot;RPG|Combat&quot;)
	void RegisterSpawnedWeapon(FGameplayTag InWeaponTagToRegister, ARPGWeaponBase* InWeaponToRegister, bool bRegisterAsEquippedWeapon = false);
	
    /** 생략 */
    
	// 콜리전 (비)활성화 Toggle 기능
	UFUNCTION(BlueprintCallable, Category = &quot;RPG|Combat&quot;)
	void ToggleWeaponCollision(bool bShouldEnable, EToggleDamageType ToggleDamageType = EToggleDamageType::CurrentEquippedWeapon);

	virtual void OnHitTargetActor(AActor* HitActor);
	virtual void OnWeaponPulledFromTargetActor(AActor* InteractedActor);
	
protected:
	TArray&amp;lt;AActor*&amp;gt; OverlappedActors;

private:
	TMap&amp;lt;FGameplayTag, ARPGWeaponBase*&amp;gt; CharacterCarriedWeaponMap;
};&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;b&gt; PawnCombatComponent&lt;/b&gt;&lt;b&gt;&lt;span&gt;.cpp&lt;/span&gt;&lt;/b&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1746776943859&quot; class=&quot;cpp&quot; data-ke-language=&quot;cpp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;#include &quot;Components/Combat/PawnCombatComponent.h&quot;
#include &quot;Items/Weapons/RPGWeaponBase.h&quot;
#include &quot;Components/BoxComponent.h&quot;

void UPawnCombatComponent::RegisterSpawnedWeapon(FGameplayTag InWeaponTagToRegister, ARPGWeaponBase* InWeaponToRegister, bool bRegisterAsEquippedWeapon)
{
	checkf(!CharacterCarriedWeaponMap.Contains(InWeaponTagToRegister), 
		TEXT(&quot;A named %s has already been added as carried weapon&quot;), 
		*InWeaponTagToRegister.ToString());
	check(InWeaponToRegister);

	CharacterCarriedWeaponMap.Emplace(InWeaponTagToRegister, InWeaponToRegister);

	// 무기에 Overlap Event 발생시 호출될 함수 바인딩
	InWeaponToRegister-&amp;gt;OnWeaponHitTarget.BindUObject(this, &amp;amp;ThisClass::OnHitTargetActor);
	InWeaponToRegister-&amp;gt;OnWeaponPulledFromTarget.BindUObject(this, &amp;amp;ThisClass::OnWeaponPulledFromTargetActor);

	if (bRegisterAsEquippedWeapon)
	{
		CurrentEquippedWeaponTag = InWeaponTagToRegister;
	}
}

void UPawnCombatComponent::ToggleWeaponCollision(bool bShouldEnable, EToggleDamageType ToggleDamageType)
{
	if (ToggleDamageType == EToggleDamageType::CurrentEquippedWeapon)
	{
		ARPGWeaponBase* WeaponToToggle = GetCharacterCurrentEquippedWeapon();

		check(WeaponToToggle);

		if (bShouldEnable)
		{
			WeaponToToggle-&amp;gt;GetWeaponCollisionBox()-&amp;gt;SetCollisionEnabled(ECollisionEnabled::QueryOnly);
		}
		else
		{
			WeaponToToggle-&amp;gt;GetWeaponCollisionBox()-&amp;gt;SetCollisionEnabled(ECollisionEnabled::NoCollision);

			OverlappedActors.Empty(); // 콜리전 비활성화 시, Overlap된 Actor 목록 비우기
		}
		
	}
}

// 자식 CombatComponent에서 구현
void UPawnCombatComponent::OnHitTargetActor(AActor* HitActor)
{
}

void UPawnCombatComponent::OnWeaponPulledFromTargetActor(AActor* InteractedActor)
{
}&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;델리게이트 BroadCasting 되었을 때 실행할 함수 바인딩을 하였고, 콜리전 비활성화 된 경우엔 Overlap된 Actor List 초기화하도록 구현하였다.&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 style=&quot;color: #e8e6e3; text-align: start;&quot; data-darkreader-inline-color=&quot;&quot; data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;✔️&lt;span&gt;&lt;span&gt; HeroCombatComponent&lt;/span&gt;&lt;/span&gt;&lt;/b&gt;&lt;/h4&gt;
&lt;div style=&quot;background-color: #1b1d1e; color: #c8c3bc; text-align: start;&quot; data-text-less=&quot;닫기&quot; data-text-more=&quot;더보기&quot; data-ke-type=&quot;moreLess&quot; data-darkreader-inline-bgcolor=&quot;&quot; data-darkreader-inline-color=&quot;&quot;&gt;&lt;a class=&quot;btn-toggle-moreless&quot;&gt;더보기&lt;/a&gt;
&lt;div class=&quot;moreless-content&quot;&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;b&gt; HeroCombatComponent&lt;/b&gt;&lt;b&gt;&lt;span&gt;.h&lt;/span&gt;&lt;/b&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1746777201648&quot; class=&quot;cpp&quot; data-ke-language=&quot;cpp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;#pragma once

#include &quot;CoreMinimal.h&quot;
#include &quot;Components/Combat/PawnCombatComponent.h&quot;
#include &quot;HeroCombatComponent.generated.h&quot;

class AWarriorHeroWeapon;

UCLASS()
class RPG_API UHeroCombatComponent : public UPawnCombatComponent
{
	GENERATED_BODY()

public:
	UFUNCTION(BlueprintCallable, Category = &quot;RPG|Combat&quot;)
	AWarriorHeroWeapon* GetHeroCarriedWeaponByTag(FGameplayTag InWeaponTag) const;
	
    // Delegate BroadCasting 되었을 때 호출되는 함수 선언 및 재정의
	virtual void OnHitTargetActor(AActor* HitActor) override;
	virtual void OnWeaponPulledFromTargetActor(AActor* InteractedActor) override;

};&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;b&gt; HeroCombatComponent&lt;/b&gt;&lt;b&gt;&lt;span&gt;.cpp&lt;/span&gt;&lt;/b&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1746777239407&quot; class=&quot;cpp&quot; data-ke-language=&quot;cpp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;#include &quot;Components/Combat/HeroCombatComponent.h&quot;
#include &quot;Items/Weapons/WarriorHeroWeapon.h&quot;
#include &quot;AbilitySystemBlueprintLibrary.h&quot;
#include &quot;RPGGameplayTags.h&quot;

AWarriorHeroWeapon* UHeroCombatComponent::GetHeroCarriedWeaponByTag(FGameplayTag InWeaponTag) const
{
   return Cast&amp;lt;AWarriorHeroWeapon&amp;gt;(GetCharacterCarriedWeaponByTag(InWeaponTag));
}

void UHeroCombatComponent::OnHitTargetActor(AActor* HitActor)
{
	// 이미 오버랩 된 객체라면 return
	if (OverlappedActors.Contains(HitActor))
	{
		return;
	}
	
    // 오버랩 액터 리스트에 추가
	OverlappedActors.AddUnique(HitActor); 
	
    // 공격자의 ASC로 GameplayEvent 전송
	FGameplayEventData Data;
	Data.Instigator = GetOwningPawn();
	Data.Target = HitActor;

	UAbilitySystemBlueprintLibrary::SendGameplayEventToActor(
		GetOwningPawn(),
		RPGGameplayTags::Shared_Event_MeleeHit,
		Data
	);
}

void UHeroCombatComponent::OnWeaponPulledFromTargetActor(AActor* InteractedActor)
{

}&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;피격된 대상의 정보를 공격자의 ASC로 전송하고, ASC에 등록된 Ability 중에서 해당 Event Tag를 트리거로 받을 수 있다.&lt;br /&gt;이때 GameplayEventData로 공격 주체 및 피격 대상을 확인할 수 있다.&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 style=&quot;color: #e8e6e3; text-align: start;&quot; data-ke-size=&quot;size20&quot; data-darkreader-inline-color=&quot;&quot;&gt;&lt;b&gt;✔️&lt;span&gt; Light / Heavy Attack Ability 로직 수정&lt;/span&gt;&lt;/b&gt;&lt;/h4&gt;
&lt;p&gt;&lt;figure class=&quot;imagegridblock&quot;&gt;
  &lt;div class=&quot;image-container&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/caC1EO/btsNQVRhlco/UpW37KZjvEzLeCCoAFlkm1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/caC1EO/btsNQVRhlco/UpW37KZjvEzLeCCoAFlkm1/img.png&quot; data-origin-width=&quot;1723&quot; data-origin-height=&quot;840&quot; data-is-animation=&quot;false&quot; style=&quot;width: 59.6135%; margin-right: 10px;&quot; data-widthpercent=&quot;60.31&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/caC1EO/btsNQVRhlco/UpW37KZjvEzLeCCoAFlkm1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcaC1EO%2FbtsNQVRhlco%2FUpW37KZjvEzLeCCoAFlkm1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1723&quot; height=&quot;840&quot;/&gt;&lt;/span&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/toNxs/btsNSatnhEM/QpbrzEkEloF8CxsHNdJ6M1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/toNxs/btsNSatnhEM/QpbrzEkEloF8CxsHNdJ6M1/img.png&quot; data-origin-width=&quot;1575&quot; data-origin-height=&quot;1167&quot; data-is-animation=&quot;false&quot; style=&quot;width: 39.2237%;&quot; data-widthpercent=&quot;39.69&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/toNxs/btsNSatnhEM/QpbrzEkEloF8CxsHNdJ6M1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FtoNxs%2FbtsNSatnhEM%2FQpbrzEkEloF8CxsHNdJ6M1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1575&quot; height=&quot;1167&quot;/&gt;&lt;/span&gt;&lt;/div&gt;
&lt;/figure&gt;
&lt;figure class=&quot;imagegridblock&quot;&gt;
  &lt;div class=&quot;image-container&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bDLgn7/btsNSdpN8Vp/wGoFfQFizgerwiQJ0ouKY1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bDLgn7/btsNSdpN8Vp/wGoFfQFizgerwiQJ0ouKY1/img.png&quot; data-origin-width=&quot;1622&quot; data-origin-height=&quot;690&quot; data-is-animation=&quot;false&quot; style=&quot;width: 53.7749%; margin-right: 10px;&quot; data-widthpercent=&quot;54.41&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bDLgn7/btsNSdpN8Vp/wGoFfQFizgerwiQJ0ouKY1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbDLgn7%2FbtsNSdpN8Vp%2FwGoFfQFizgerwiQJ0ouKY1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1622&quot; height=&quot;690&quot;/&gt;&lt;/span&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/b2sGvO/btsNQ3oq8EI/uK4kavQVYalBNRshAgB1E1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/b2sGvO/btsNQ3oq8EI/uK4kavQVYalBNRshAgB1E1/img.png&quot; data-origin-width=&quot;2157&quot; data-origin-height=&quot;1095&quot; data-is-animation=&quot;false&quot; style=&quot;width: 45.0623%;&quot; data-widthpercent=&quot;45.59&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/b2sGvO/btsNQ3oq8EI/uK4kavQVYalBNRshAgB1E1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fb2sGvO%2FbtsNQ3oq8EI%2FuK4kavQVYalBNRshAgB1E1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;2157&quot; height=&quot;1095&quot;/&gt;&lt;/span&gt;&lt;/div&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;1. 공격 Ability에서 PlayMontage 노드가 실행되기 직전에, 현재의 ComboCount를 캐싱&lt;br /&gt;2. 공격 Montage가 재생되었을 때, BoxCollision에 Hit된 객체가 있다면 &quot;Shared.Event.MeleeHit&quot; 태그로 이벤트 수신&lt;br /&gt;3. 이벤트를 수신 받았다면 Hit된 대상을 로그로 출력&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 style=&quot;color: #e8e6e3; text-align: start;&quot; data-darkreader-inline-color=&quot;&quot; data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;✔️&lt;span&gt;&lt;span&gt; Attack 관련 AnimMontage 설정&lt;/span&gt;&lt;/span&gt;&lt;/b&gt;&lt;/h4&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1378&quot; data-origin-height=&quot;556&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/lw3HN/btsNRJv28bm/JHnjsqUFJWmandy9Bx7Lp0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/lw3HN/btsNRJv28bm/JHnjsqUFJWmandy9Bx7Lp0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/lw3HN/btsNRJv28bm/JHnjsqUFJWmandy9Bx7Lp0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Flw3HN%2FbtsNRJv28bm%2FJHnjsqUFJWmandy9Bx7Lp0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1378&quot; height=&quot;556&quot; data-origin-width=&quot;1378&quot; data-origin-height=&quot;556&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;모든 Melee Attack 관련 Montage에 적절하게 ANS_ToggleWeaponCollision을 설정해준다.&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 style=&quot;color: #c8c3bc; text-align: start;&quot; data-ke-size=&quot;size23&quot; data-darkreader-inline-color=&quot;&quot;&gt;&lt;b&gt; &lt;span&gt; 피격 처리된 객체 정보 수신 테스트&lt;/span&gt;&lt;/b&gt;&lt;/h3&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock widthContent&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1316&quot; data-origin-height=&quot;753&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bgoZjQ/btsNREIurfY/uYG526eCYb7VsXcq52EUXK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bgoZjQ/btsNREIurfY/uYG526eCYb7VsXcq52EUXK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bgoZjQ/btsNREIurfY/uYG526eCYb7VsXcq52EUXK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbgoZjQ%2FbtsNREIurfY%2FuYG526eCYb7VsXcq52EUXK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1316&quot; height=&quot;753&quot; data-origin-width=&quot;1316&quot; data-origin-height=&quot;753&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;Heavy Attack_1에 피격 처리된 대상들을 확인할 수 있게 되었다.&lt;br /&gt;이제 이 피격 대상들에게 Gameplay Effect를 적용해서 데미지 처리를 진행하도록 한다.&lt;/blockquote&gt;</description>
      <category>내배캠/GAS(Gameplay Ability System)</category>
      <author>동그래님</author>
      <guid isPermaLink="true">https://dong-grae.tistory.com/234</guid>
      <comments>https://dong-grae.tistory.com/234#entry234comment</comments>
      <pubDate>Fri, 9 May 2025 12:50:22 +0900</pubDate>
    </item>
    <item>
      <title>GAS_11_Attribute와 GameplayEffect</title>
      <link>https://dong-grae.tistory.com/233</link>
      <description>&lt;h2 style=&quot;color: #e8e6e3; text-align: start;&quot; data-darkreader-inline-color=&quot;&quot; data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;  캐릭터에 Attribute 부여&lt;/b&gt;&lt;/h2&gt;
&lt;p&gt;&lt;figure class=&quot;imagegridblock&quot;&gt;
  &lt;div class=&quot;image-container&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cgVF73/btsNPhfGduJ/kyWHL7evWgvLFk1INI0If0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cgVF73/btsNPhfGduJ/kyWHL7evWgvLFk1INI0If0/img.png&quot; style=&quot;width: 40.7709%; margin-right: 10px;&quot; data-widthpercent=&quot;41.25&quot; data-is-animation=&quot;false&quot; data-origin-height=&quot;622&quot; data-origin-width=&quot;862&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cgVF73/btsNPhfGduJ/kyWHL7evWgvLFk1INI0If0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcgVF73%2FbtsNPhfGduJ%2FkyWHL7evWgvLFk1INI0If0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;862&quot; height=&quot;622&quot;/&gt;&lt;/span&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/Av2Zz/btsNQwC0nYB/H9ugcIPGZb3D0NECKn9XkK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/Av2Zz/btsNQwC0nYB/H9ugcIPGZb3D0NECKn9XkK/img.png&quot; style=&quot;width: 58.0663%;&quot; data-widthpercent=&quot;58.75&quot; data-is-animation=&quot;false&quot; data-origin-height=&quot;1295&quot; data-origin-width=&quot;2556&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/Av2Zz/btsNQwC0nYB/H9ugcIPGZb3D0NECKn9XkK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FAv2Zz%2FbtsNQwC0nYB%2FH9ugcIPGZb3D0NECKn9XkK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;2556&quot; height=&quot;1295&quot;/&gt;&lt;/span&gt;&lt;/div&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;blockquote style=&quot;background-color: #1a1c1d; color: #a8a095; text-align: left;&quot; data-ke-style=&quot;style3&quot; data-darkreader-inline-bgcolor=&quot;&quot; data-darkreader-inline-color=&quot;&quot;&gt;초기에 BaseCharacter 클래스를 구현할 때, AttributeSet을 만들어놨지만 아직 내부가 비어있는 상태이다.&lt;br /&gt;당연히 BaseCharacter를 상속 받은 플레이어 캐릭터와 Enemy 캐릭터는 모두 AttributeSet을 가지고 있다.&lt;br /&gt;이 Attribute Set은 플레이어나 적 캐릭터가 피해량, 체력, 분노, 쉴드 등을 수치적으로 계산하기 위해 필수적으로 필요하다.&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 style=&quot;color: #e8e6e3; text-align: start;&quot; data-darkreader-inline-color=&quot;&quot; data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;✅ Attribute Set 정의&lt;/b&gt;&lt;/h3&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock widthContent&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1408&quot; data-origin-height=&quot;552&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/ecNYlM/btsNQ2VJOcQ/cACY5JEg3akq4HoVzrXdOK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/ecNYlM/btsNQ2VJOcQ/cACY5JEg3akq4HoVzrXdOK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/ecNYlM/btsNQ2VJOcQ/cACY5JEg3akq4HoVzrXdOK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FecNYlM%2FbtsNQ2VJOcQ%2FcACY5JEg3akq4HoVzrXdOK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1408&quot; height=&quot;552&quot; data-origin-width=&quot;1408&quot; data-origin-height=&quot;552&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;blockquote style=&quot;background-color: #1a1c1d; color: #a8a095; text-align: left;&quot; data-ke-style=&quot;style3&quot; data-darkreader-inline-bgcolor=&quot;&quot; data-darkreader-inline-color=&quot;&quot;&gt;FGameplayAttributeData로 선언한 Attribute는 Getter/Setter/Init 함수가 있어야만 GAS 내부에서 올바르게 읽고 쓸 수 있다.&lt;br /&gt;이를 매번 수동으로 작성하면 중복 코드가 많아지고 실수할 수 있는 위험도가 높아지므로 Epic에서 매크로 헬퍼 함수를 제공한다.&lt;br /&gt;이를 사용해 RPGAttributeSet클래스를 구현하도록 한다.&lt;/blockquote&gt;
&lt;h4 style=&quot;color: #e8e6e3;&quot; data-ke-size=&quot;size20&quot; data-darkreader-inline-color=&quot;&quot;&gt;&lt;b&gt;✔️ RPGAttributeSet&amp;nbsp;&lt;/b&gt;&lt;/h4&gt;
&lt;div style=&quot;background-color: #1b1d1e; color: #c8c3bc;&quot; data-text-less=&quot;닫기&quot; data-text-more=&quot;더보기&quot; data-ke-type=&quot;moreLess&quot; data-darkreader-inline-bgcolor=&quot;&quot; data-darkreader-inline-color=&quot;&quot;&gt;&lt;a class=&quot;btn-toggle-moreless&quot;&gt;더보기&lt;/a&gt;
&lt;div class=&quot;moreless-content&quot;&gt;
&lt;p style=&quot;color: #e8e6e3;&quot; data-darkreader-inline-color=&quot;&quot; data-ke-size=&quot;size18&quot;&gt;&lt;b&gt; &lt;span&gt;&lt;span&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;RPGAttributeSet&lt;/span&gt;.h&lt;/span&gt;&lt;/b&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1746714516600&quot; class=&quot;reasonml&quot; style=&quot;background-color: #1c1e1f; color: #c1bcb4;&quot; data-darkreader-inline-color=&quot;&quot; data-darkreader-inline-bgcolor=&quot;&quot; data-ke-language=&quot;cpp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;#pragma once

#include &quot;CoreMinimal.h&quot;
#include &quot;AttributeSet.h&quot;
#include &quot;AbilitySystem/RPGAbilitySystemComponent.h&quot;
#include &quot;RPGAttributeSet.generated.h&quot;

#define ATTRIBUTE_ACCESSORS(ClassName, PropertyName) \
GAMEPLAYATTRIBUTE_PROPERTY_GETTER(ClassName, PropertyName) \
GAMEPLAYATTRIBUTE_VALUE_GETTER(PropertyName) \
GAMEPLAYATTRIBUTE_VALUE_SETTER(PropertyName) \
GAMEPLAYATTRIBUTE_VALUE_INITTER(PropertyName)

UCLASS()
class RPG_API URPGAttributeSet : public UAttributeSet
{
	GENERATED_BODY()
	
public:
	URPGAttributeSet();

	UPROPERTY(BlueprintReadOnly, Category = &quot;Health&quot;)
	FGameplayAttributeData CurrentHealth;
	ATTRIBUTE_ACCESSORS(URPGAttributeSet, CurrentHealth)

	UPROPERTY(BlueprintReadOnly, Category = &quot;Health&quot;)
	FGameplayAttributeData MaxHealth;
	ATTRIBUTE_ACCESSORS(URPGAttributeSet, MaxHealth)

	UPROPERTY(BlueprintReadOnly, Category = &quot;Rage&quot;)
	FGameplayAttributeData CurrentRage;
	ATTRIBUTE_ACCESSORS(URPGAttributeSet, CurrentRage)

	UPROPERTY(BlueprintReadOnly, Category = &quot;Rage&quot;)
	FGameplayAttributeData MaxRage;
	ATTRIBUTE_ACCESSORS(URPGAttributeSet, MaxRage)

	UPROPERTY(BlueprintReadOnly, Category = &quot;Damage&quot;)
	FGameplayAttributeData AttackPower;
	ATTRIBUTE_ACCESSORS(URPGAttributeSet, AttackPower)

	UPROPERTY(BlueprintReadOnly, Category = &quot;Damage&quot;)
	FGameplayAttributeData DefensePower;
	ATTRIBUTE_ACCESSORS(URPGAttributeSet, DefensePower)
};&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;b&gt; &lt;span&gt;&lt;span&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;RPGAttributeSet&lt;/span&gt;.cpp&lt;/span&gt;&lt;/b&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1746714516600&quot; class=&quot;reasonml&quot; style=&quot;background-color: #1c1e1f; color: #c1bcb4;&quot; data-ke-type=&quot;codeblock&quot; data-ke-language=&quot;cpp&quot; data-darkreader-inline-bgcolor=&quot;&quot; data-darkreader-inline-color=&quot;&quot;&gt;&lt;code&gt;#include &quot;AbilitySystem/RPGAttributeSet.h&quot;

URPGAttributeSet::URPGAttributeSet()
{
	InitCurrentHealth(1.0f);
	InitMaxHealth(1.0f);
	InitCurrentRage(1.0f);
	InitMaxRage(1.0f);
	InitAttackPower(1.0f);
	InitDefensePower(1.0f);
}&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;p style=&quot;color: #c8c3bc; text-align: start;&quot; data-darkreader-inline-color=&quot;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 style=&quot;color: #c8c3bc; text-align: start;&quot; data-darkreader-inline-color=&quot;&quot; data-ke-size=&quot;size20&quot;&gt; &lt;span&gt;&amp;nbsp;&lt;b&gt;Attribute 설정 확인&lt;/b&gt;&lt;/span&gt;&lt;/h4&gt;
&lt;p&gt;&lt;figure class=&quot;imagegridblock&quot;&gt;
  &lt;div class=&quot;image-container&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bJ5dz0/btsNQVJbxYY/WFYp0fyAElGuxAtZxUTPlK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bJ5dz0/btsNQVJbxYY/WFYp0fyAElGuxAtZxUTPlK/img.png&quot; style=&quot;width: 36.5319%; margin-right: 10px;&quot; data-widthpercent=&quot;36.96&quot; data-is-animation=&quot;false&quot; data-origin-height=&quot;355&quot; data-origin-width=&quot;487&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bJ5dz0/btsNQVJbxYY/WFYp0fyAElGuxAtZxUTPlK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbJ5dz0%2FbtsNQVJbxYY%2FWFYp0fyAElGuxAtZxUTPlK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;487&quot; height=&quot;355&quot;/&gt;&lt;/span&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cLn4ys/btsNQYsnEDz/TZtDCWm4ZnQphu1OCvXM8k/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cLn4ys/btsNQYsnEDz/TZtDCWm4ZnQphu1OCvXM8k/img.png&quot; style=&quot;width: 62.3053%;&quot; data-widthpercent=&quot;63.04&quot; data-is-animation=&quot;false&quot; data-origin-height=&quot;842&quot; data-origin-width=&quot;1970&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cLn4ys/btsNQYsnEDz/TZtDCWm4ZnQphu1OCvXM8k/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcLn4ys%2FbtsNQYsnEDz%2FTZtDCWm4ZnQphu1OCvXM8k%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1970&quot; height=&quot;842&quot;/&gt;&lt;/span&gt;&lt;/div&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;blockquote style=&quot;background-color: #1a1c1d; color: #a8a095; text-align: left;&quot; data-ke-style=&quot;style3&quot; data-darkreader-inline-bgcolor=&quot;&quot; data-darkreader-inline-color=&quot;&quot;&gt;'~' 물결 키 버튼을 눌러 커멘드 창에 &quot;showdebug AbilitySystem&quot;을 입력하면, 다음과 같이 Ability System 디버그 UI가 보여진다.&lt;br /&gt;앞서 설정했던 속성들이 보여지고, 생성자에서 Default값으로 1을 준 것 그대로 적용되어있는 것을 확인할 수 있다.&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 style=&quot;color: #e8e6e3; text-align: start;&quot; data-ke-size=&quot;size26&quot; data-darkreader-inline-color=&quot;&quot;&gt;&lt;b&gt;  Gameplay Effect 생성&lt;/b&gt;&lt;/h2&gt;
&lt;p&gt;&lt;figure class=&quot;imagegridblock&quot;&gt;
  &lt;div class=&quot;image-container&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cGgnrw/btsNPyID4Ma/jcKgcbIDSh7GrR5oPdJLFK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cGgnrw/btsNPyID4Ma/jcKgcbIDSh7GrR5oPdJLFK/img.png&quot; style=&quot;width: 50.5939%; margin-right: 10px;&quot; data-widthpercent=&quot;51.19&quot; data-is-animation=&quot;false&quot; data-origin-height=&quot;475&quot; data-origin-width=&quot;477&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cGgnrw/btsNPyID4Ma/jcKgcbIDSh7GrR5oPdJLFK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcGgnrw%2FbtsNPyID4Ma%2FjcKgcbIDSh7GrR5oPdJLFK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;477&quot; height=&quot;475&quot;/&gt;&lt;/span&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bnjIqZ/btsNPcFSOit/AJqK8RqkH10nfUpSkk1Uw1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bnjIqZ/btsNPcFSOit/AJqK8RqkH10nfUpSkk1Uw1/img.png&quot; style=&quot;width: 48.2433%;&quot; data-widthpercent=&quot;48.81&quot; data-is-animation=&quot;false&quot; data-origin-height=&quot;801&quot; data-origin-width=&quot;767&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bnjIqZ/btsNPcFSOit/AJqK8RqkH10nfUpSkk1Uw1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbnjIqZ%2FbtsNPcFSOit%2FAJqK8RqkH10nfUpSkk1Uw1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;767&quot; height=&quot;801&quot;/&gt;&lt;/span&gt;&lt;/div&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;blockquote style=&quot;background-color: #1a1c1d; color: #a8a095; text-align: left;&quot; data-ke-style=&quot;style3&quot; data-darkreader-inline-bgcolor=&quot;&quot; data-darkreader-inline-color=&quot;&quot;&gt;에디터에서 &quot;GameplayEffect&quot;를 상속 받는 BP 클래스를 생성하고 &quot;GE_Hero_StartUp&quot;이라고 명명한다.&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 style=&quot;color: #e8e6e3; text-align: start;&quot; data-darkreader-inline-color=&quot;&quot; data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;✅ GameEffect_StartUp 데이터 설정&lt;/b&gt;&lt;/h3&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;2148&quot; data-origin-height=&quot;1015&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/t5QeW/btsNQaggMlw/zVWzm00pI0wOaRiSBJQhK0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/t5QeW/btsNQaggMlw/zVWzm00pI0wOaRiSBJQhK0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/t5QeW/btsNQaggMlw/zVWzm00pI0wOaRiSBJQhK0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Ft5QeW%2FbtsNQaggMlw%2FzVWzm00pI0wOaRiSBJQhK0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;2148&quot; height=&quot;1015&quot; data-origin-width=&quot;2148&quot; data-origin-height=&quot;1015&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;h4 style=&quot;color: #e8e6e3; text-align: start;&quot; data-darkreader-inline-color=&quot;&quot; data-ke-size=&quot;size20&quot;&gt; &lt;span&gt;&amp;nbsp;&lt;b&gt;Duration Policy&lt;/b&gt;&lt;/span&gt;&lt;/h4&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock widthContent&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1125&quot; data-origin-height=&quot;223&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/tawiF/btsNON7lRqX/YRIRsbHL2n50ncrH8Pp4Kk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/tawiF/btsNON7lRqX/YRIRsbHL2n50ncrH8Pp4Kk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/tawiF/btsNON7lRqX/YRIRsbHL2n50ncrH8Pp4Kk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FtawiF%2FbtsNON7lRqX%2FYRIRsbHL2n50ncrH8Pp4Kk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1125&quot; height=&quot;223&quot; data-origin-width=&quot;1125&quot; data-origin-height=&quot;223&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Instant: 즉시 적용되고 바로 사라짐(초기 체력 100 세팅 등)&lt;/li&gt;
&lt;li&gt;Infinite: 지속 효과, 수동으로 제거 전까지 유지됨(버프, 디버프 등)&lt;/li&gt;
&lt;li&gt;Has Duration: 일정 시간 동안 유지되며 시간이 지나면 자동 종료(10초 동안 공격력 증가 등)&lt;/li&gt;
&lt;/ul&gt;
&lt;blockquote style=&quot;background-color: #1a1c1d; color: #a8a095; text-align: left;&quot; data-ke-style=&quot;style3&quot; data-darkreader-inline-bgcolor=&quot;&quot; data-darkreader-inline-color=&quot;&quot;&gt;지속시간 정책을 의미하며, 현재는 초기 데이터 셋업이기 때문에 Instant로 적용한다.&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 style=&quot;color: #e8e6e3; text-align: start;&quot; data-ke-size=&quot;size20&quot; data-darkreader-inline-color=&quot;&quot;&gt;&lt;b&gt; &lt;span&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;Modifier Operation&lt;/span&gt;&lt;/b&gt;&lt;/h4&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock widthContent&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1713&quot; data-origin-height=&quot;377&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/dAgqPo/btsNPA0pOTT/hQaQ6B4xmaPOFrswkdETJk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/dAgqPo/btsNPA0pOTT/hQaQ6B4xmaPOFrswkdETJk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/dAgqPo/btsNPA0pOTT/hQaQ6B4xmaPOFrswkdETJk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FdAgqPo%2FbtsNPA0pOTT%2FhQaQ6B4xmaPOFrswkdETJk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1713&quot; height=&quot;377&quot; data-origin-width=&quot;1713&quot; data-origin-height=&quot;377&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;pre id=&quot;code_1746714516601&quot; class=&quot;armasm&quot; style=&quot;background-color: #1c1e1f; color: #c1bcb4;&quot; data-ke-type=&quot;codeblock&quot; data-ke-language=&quot;cpp&quot; data-darkreader-inline-bgcolor=&quot;&quot; data-darkreader-inline-color=&quot;&quot;&gt;&lt;code&gt;FinalValue = ((BaseValue + AdditiveModifiers) * MultiplicativeModifiers) + PostMultiplicativeModifiers&lt;/code&gt;&lt;/pre&gt;
&lt;blockquote style=&quot;background-color: #1a1c1d; color: #a8a095; text-align: left;&quot; data-ke-style=&quot;style3&quot; data-darkreader-inline-bgcolor=&quot;&quot; data-darkreader-inline-color=&quot;&quot;&gt;GAS의 Attribute는 내부적으로 위와 같이 계산된다.&lt;/blockquote&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Add(Base): BaseValue에 더하는 값(기본 스텟 보정 등)&lt;/li&gt;
&lt;li&gt;Multiply(Additive): BaseValue + Additive 값에 곱해지는 값(버프/디버프 등)&lt;/li&gt;
&lt;li&gt;Divide(Additive): BaseValue + Additive 값에 나눠지는 값&lt;/li&gt;
&lt;li&gt;Multiply(Compound): 누적 곱셈(여러 개의 계수가 각각 순차적으로 곱셈을 적용, 여러 버프 누적해서 적용 등)&lt;/li&gt;
&lt;li&gt;Add(Final): PostMultiplicativeModifiers 단계 즉, 최종 결과에 더하는 값(회복량 보정 등)&lt;/li&gt;
&lt;li&gt;Override: BaseValue 교체(기존 값 완전히 덮어씌움)&lt;/li&gt;
&lt;/ul&gt;
&lt;blockquote style=&quot;background-color: #1a1c1d; color: #a8a095; text-align: left;&quot; data-ke-style=&quot;style3&quot; data-darkreader-inline-bgcolor=&quot;&quot; data-darkreader-inline-color=&quot;&quot;&gt;초기 값을 설정하는 것이기 때문에, Override로 설정한다.&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 style=&quot;color: #e8e6e3; text-align: start;&quot; data-darkreader-inline-color=&quot;&quot; data-ke-size=&quot;size20&quot;&gt;&lt;b&gt; &lt;span&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;Magnitude Calculation Type&lt;/span&gt;&lt;/b&gt;&lt;/h4&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1717&quot; data-origin-height=&quot;376&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/YHPes/btsNPWCBeWU/wsMcB0LxfSEKq8cYs5bQk1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/YHPes/btsNPWCBeWU/wsMcB0LxfSEKq8cYs5bQk1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/YHPes/btsNPWCBeWU/wsMcB0LxfSEKq8cYs5bQk1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FYHPes%2FbtsNPWCBeWU%2FwsMcB0LxfSEKq8cYs5bQk1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1717&quot; height=&quot;376&quot; data-origin-width=&quot;1717&quot; data-origin-height=&quot;376&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Scalable Float: 하드코딩된 값 또는 Curve Table 기반으로 값을 계산&lt;/li&gt;
&lt;li&gt;Attribute Based: 다른 속성 값 기반으로 계산(MaxHealth 기준으로 CurrentHealth 설정 등)&lt;/li&gt;
&lt;li&gt;Custom Calculation Class: C++ 또는 Blueprint로 만든 커스텀 계산 로직 사용&lt;/li&gt;
&lt;li&gt;SetByCaller: 능력 사용자가 값을 실시간으로 전달(탄환 피해량을 발사 시점에 결정 등)&lt;/li&gt;
&lt;/ul&gt;
&lt;blockquote style=&quot;background-color: #1a1c1d; color: #a8a095; text-align: left;&quot; data-ke-style=&quot;style3&quot; data-darkreader-inline-bgcolor=&quot;&quot; data-darkreader-inline-color=&quot;&quot;&gt;Scalable Float로 설정 후, Curve Table 기반으로 값을 계산할 것이다.&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 style=&quot;color: #e8e6e3; text-align: start;&quot; data-ke-size=&quot;size20&quot; data-darkreader-inline-color=&quot;&quot;&gt;&lt;b&gt; &lt;span&gt;&lt;span&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;Curve Table 생성 및 설정&lt;/span&gt;&lt;/span&gt;&lt;/b&gt;&lt;/h4&gt;
&lt;p&gt;&lt;figure class=&quot;imagegridblock&quot;&gt;
  &lt;div class=&quot;image-container&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/z5wyH/btsNPEPkwyY/rBwFSiVW00KzX3ag7bsHs1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/z5wyH/btsNPEPkwyY/rBwFSiVW00KzX3ag7bsHs1/img.png&quot; style=&quot;width: 24.4706%; margin-right: 10px;&quot; data-widthpercent=&quot;24.76&quot; data-is-animation=&quot;false&quot; data-origin-height=&quot;1098&quot; data-origin-width=&quot;880&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/z5wyH/btsNPEPkwyY/rBwFSiVW00KzX3ag7bsHs1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fz5wyH%2FbtsNPEPkwyY%2FrBwFSiVW00KzX3ag7bsHs1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;880&quot; height=&quot;1098&quot;/&gt;&lt;/span&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/brCInW/btsNPGl33lh/D9hAoBgKTv65FhiiSPq4QK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/brCInW/btsNPGl33lh/D9hAoBgKTv65FhiiSPq4QK/img.png&quot; style=&quot;width: 74.3666%;&quot; data-widthpercent=&quot;75.24&quot; data-is-animation=&quot;false&quot; data-origin-height=&quot;202&quot; data-origin-width=&quot;492&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/brCInW/btsNPGl33lh/D9hAoBgKTv65FhiiSPq4QK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbrCInW%2FbtsNPGl33lh%2FD9hAoBgKTv65FhiiSPq4QK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;492&quot; height=&quot;202&quot;/&gt;&lt;/span&gt;&lt;/div&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Linear: 두 키 사이를 직선(선형)으로 보간한다. 부드럽고 자연스러운 증가곡선으로 가장 일반적이다.&lt;/li&gt;
&lt;li&gt;Constant: 정수 간 값 사이의 보간 없이 해당 키의 값만 사용한다(1.0~2.9까지는 Level 1 값 그대로, 3.0이되면 Level 3 값으로 점프하는 등)&lt;/li&gt;
&lt;li&gt;Cubic: 삼차 곡선(Cubic Hermite) 보간으로, 더 부드럽고 곡선 형태가 강하다(캐릭터 성장곡선, 이동속도 감속,가속 등 부드러운 곡선 형태가 중요한 경우 등)&lt;/li&gt;
&lt;/ul&gt;
&lt;blockquote style=&quot;background-color: #1a1c1d; color: #a8a095; text-align: left;&quot; data-ke-style=&quot;style3&quot; data-darkreader-inline-bgcolor=&quot;&quot; data-darkreader-inline-color=&quot;&quot;&gt;GAS의 ScalableFloat에서 Curve Table을 사용할 때, 레벨 기반 스텟 스케일링을 하기 위해 Linear가 가장 많이 사용된다.&lt;br /&gt;따라서 Linear로 설정해준다.&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imagegridblock&quot;&gt;
  &lt;div class=&quot;image-container&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/RAJJ0/btsNPBSy4xv/I2zoFN44okStgnLKuSGikK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/RAJJ0/btsNPBSy4xv/I2zoFN44okStgnLKuSGikK/img.png&quot; style=&quot;width: 60.7972%; margin-right: 10px;&quot; data-widthpercent=&quot;61.51&quot; data-is-animation=&quot;false&quot; data-origin-height=&quot;296&quot; data-origin-width=&quot;757&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/RAJJ0/btsNPBSy4xv/I2zoFN44okStgnLKuSGikK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FRAJJ0%2FbtsNPBSy4xv%2FI2zoFN44okStgnLKuSGikK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;757&quot; height=&quot;296&quot;/&gt;&lt;/span&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/CDUhV/btsNQHxEST3/7UHwWXVTTjkxOZGGxrMqCk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/CDUhV/btsNQHxEST3/7UHwWXVTTjkxOZGGxrMqCk/img.png&quot; style=&quot;width: 38.04%;&quot; data-widthpercent=&quot;38.49&quot; data-is-animation=&quot;false&quot; data-origin-height=&quot;1328&quot; data-origin-width=&quot;2125&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/CDUhV/btsNQHxEST3/7UHwWXVTTjkxOZGGxrMqCk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FCDUhV%2FbtsNQHxEST3%2F7UHwWXVTTjkxOZGGxrMqCk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;2125&quot; height=&quot;1328&quot;/&gt;&lt;/span&gt;&lt;/div&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;blockquote style=&quot;background-color: #1a1c1d; color: #a8a095; text-align: left;&quot; data-ke-style=&quot;style3&quot; data-darkreader-inline-bgcolor=&quot;&quot; data-darkreader-inline-color=&quot;&quot;&gt;다음과 같이 Curve Table를 작성해주고, &quot;GE_StartUp&quot;에서 바인딩 해준다.&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;2542&quot; data-origin-height=&quot;1310&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/rehCq/btsNPyaQIrE/PG2LyJkgO2Z5TeIWt5yu50/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/rehCq/btsNPyaQIrE/PG2LyJkgO2Z5TeIWt5yu50/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/rehCq/btsNPyaQIrE/PG2LyJkgO2Z5TeIWt5yu50/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FrehCq%2FbtsNPyaQIrE%2FPG2LyJkgO2Z5TeIWt5yu50%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;2542&quot; height=&quot;1310&quot; data-origin-width=&quot;2542&quot; data-origin-height=&quot;1310&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;blockquote style=&quot;background-color: #1a1c1d; color: #a8a095; text-align: left;&quot; data-ke-style=&quot;style3&quot; data-darkreader-inline-bgcolor=&quot;&quot; data-darkreader-inline-color=&quot;&quot;&gt;MaxHealth, MaxRage, AttackPower, DefensePower 속성 설정을 한다.&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 style=&quot;color: #e8e6e3; text-align: start;&quot; data-ke-size=&quot;size23&quot; data-darkreader-inline-color=&quot;&quot;&gt;&lt;b&gt;✅ GameEffect_Static 데이터 설정&lt;/b&gt;&lt;/h3&gt;
&lt;blockquote style=&quot;background-color: #1a1c1d; color: #a8a095; text-align: left;&quot; data-ke-style=&quot;style3&quot; data-darkreader-inline-bgcolor=&quot;&quot; data-darkreader-inline-color=&quot;&quot;&gt;&quot;GE_Hero_StartUp&quot;에서 캐릭터의 MaxHealth, MaxRange, Attack, Defense 등을 Curve Table 기반으로 설정하였다.&lt;br /&gt;&quot;GE_Static&quot; 클래스를 만들어서 설정된 StartUp 데이터를 기반으로 현재 값을 계산해, CurrentHealth, CurrentRage등에 설정한다.&lt;/blockquote&gt;
&lt;p&gt;&lt;figure class=&quot;imagegridblock&quot;&gt;
  &lt;div class=&quot;image-container&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/nXLVQ/btsNOMtXbwc/LntnKL3yLfK1gTP6jZEYW1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/nXLVQ/btsNOMtXbwc/LntnKL3yLfK1gTP6jZEYW1/img.png&quot; style=&quot;width: 60.3683%; margin-right: 10px;&quot; data-widthpercent=&quot;61.08&quot; data-is-animation=&quot;false&quot; data-origin-height=&quot;810&quot; data-origin-width=&quot;770&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/nXLVQ/btsNOMtXbwc/LntnKL3yLfK1gTP6jZEYW1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FnXLVQ%2FbtsNOMtXbwc%2FLntnKL3yLfK1gTP6jZEYW1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;770&quot; height=&quot;810&quot;/&gt;&lt;/span&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/chJwXF/btsNQZdQunA/uwdkxCgVuJueGKJ1VMKZs1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/chJwXF/btsNQZdQunA/uwdkxCgVuJueGKJ1VMKZs1/img.png&quot; style=&quot;width: 38.4689%;&quot; data-widthpercent=&quot;38.92&quot; data-is-animation=&quot;false&quot; data-origin-height=&quot;208&quot; data-origin-width=&quot;126&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/chJwXF/btsNQZdQunA/uwdkxCgVuJueGKJ1VMKZs1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FchJwXF%2FbtsNQZdQunA%2FuwdkxCgVuJueGKJ1VMKZs1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;126&quot; height=&quot;208&quot;/&gt;&lt;/span&gt;&lt;/div&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;blockquote style=&quot;background-color: #1a1c1d; color: #a8a095; text-align: left;&quot; data-ke-style=&quot;style3&quot; data-darkreader-inline-bgcolor=&quot;&quot; data-darkreader-inline-color=&quot;&quot;&gt;이번에도 &quot;GameplayEffect&quot;를 상속받는 &quot;GE_Hero_Static&quot; 이름의 클래스를 생성한다.&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock widthContent&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;2551&quot; data-origin-height=&quot;1322&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/vMTDJ/btsNOM8yhzN/RSQ7v6gJPzdtoSnkElMtv1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/vMTDJ/btsNOM8yhzN/RSQ7v6gJPzdtoSnkElMtv1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/vMTDJ/btsNOM8yhzN/RSQ7v6gJPzdtoSnkElMtv1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FvMTDJ%2FbtsNOM8yhzN%2FRSQ7v6gJPzdtoSnkElMtv1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;2551&quot; height=&quot;1322&quot; data-origin-width=&quot;2551&quot; data-origin-height=&quot;1322&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;blockquote style=&quot;background-color: #1a1c1d; color: #a8a095; text-align: left;&quot; data-ke-style=&quot;style3&quot; data-darkreader-inline-bgcolor=&quot;&quot; data-darkreader-inline-color=&quot;&quot;&gt;다음과 같이 CurrentHealth와 CurrentRage를 설정해준다.&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 style=&quot;color: #e8e6e3; text-align: start;&quot; data-darkreader-inline-color=&quot;&quot; data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;  Hero에 Gameplay Effect로 초기 Attribute 값 설정&lt;/b&gt;&lt;/h2&gt;
&lt;blockquote style=&quot;background-color: #1a1c1d; color: #a8a095; text-align: left;&quot; data-ke-style=&quot;style3&quot; data-darkreader-inline-bgcolor=&quot;&quot; data-darkreader-inline-color=&quot;&quot;&gt;Gameplay Effect를 적용하기에 가장 적합한 지점은 플레이어와 적 모두 시작 데이터를 구성하기 때문에 DataAsset_StartUpDataBase 클래스이다.&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 style=&quot;color: #e8e6e3; text-align: start;&quot; data-darkreader-inline-color=&quot;&quot; data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;✅ 시작 데이터에 Gameplay Effect 저장&lt;/b&gt;&lt;/h3&gt;
&lt;h4 style=&quot;color: #e8e6e3; text-align: start;&quot; data-ke-size=&quot;size20&quot; data-darkreader-inline-color=&quot;&quot;&gt;&lt;b&gt; DataAsset_StartUpDataBase.h&lt;/b&gt;&lt;/h4&gt;
&lt;pre id=&quot;code_1746714516602&quot; class=&quot;cpp&quot; style=&quot;background-color: #1c1e1f; color: #c1bcb4;&quot; data-ke-type=&quot;codeblock&quot; data-ke-language=&quot;cpp&quot; data-darkreader-inline-bgcolor=&quot;&quot; data-darkreader-inline-color=&quot;&quot;&gt;&lt;code&gt;#pragma once

#include &quot;CoreMinimal.h&quot;
#include &quot;Engine/DataAsset.h&quot;
#include &quot;DataAsset_StartUpDataBase.generated.h&quot;

class URPGGameplayAbility;
class URPGAbilitySystemComponent;
class UGameplayEffect;

UCLASS()
class RPG_API UDataAsset_StartUpDataBase : public UDataAsset
{
	GENERATED_BODY()
	
public:
	// 캐릭터(플레이어/적)의 ASC에 초기 능력을 부여
	virtual void GiveToAbilitySystemComponent(URPGAbilitySystemComponent* InRPG_ASC, int32 ApplyLevel = 1);

protected:
	UPROPERTY(EditDefaultsOnly, Category = &quot;StartUpData&quot;)
	TArray&amp;lt;TSubclassOf&amp;lt;URPGGameplayAbility&amp;gt;&amp;gt; ActivateOnGivenAbilites;

	UPROPERTY(EditDefaultsOnly, Category = &quot;StartUpData&quot;)
	TArray&amp;lt;TSubclassOf&amp;lt;URPGGameplayAbility&amp;gt;&amp;gt; ReactiveAbilites;

	// GameplayEffect 데이터들
	UPROPERTY(EditDefaultsOnly, Category = &quot;StartUpData&quot;)
	TArray&amp;lt;TSubclassOf&amp;lt;UGameplayEffect&amp;gt;&amp;gt; StartUpGameplayEffects;

	void GrantAbilites(
		const TArray&amp;lt;TSubclassOf&amp;lt;URPGGameplayAbility&amp;gt;&amp;gt;&amp;amp; InAbilitesToGive,
		URPGAbilitySystemComponent* InRPG_ASC, int32 ApplyLevel = 1
	);
};&lt;/code&gt;&lt;/pre&gt;
&lt;h4 style=&quot;color: #e8e6e3; text-align: start;&quot; data-darkreader-inline-color=&quot;&quot; data-ke-size=&quot;size20&quot;&gt;&lt;b&gt; DataAsset_StartUpDataBase.cpp&lt;/b&gt;&lt;/h4&gt;
&lt;pre id=&quot;code_1746714516603&quot; class=&quot;cpp&quot; style=&quot;background-color: #1c1e1f; color: #c1bcb4;&quot; data-ke-type=&quot;codeblock&quot; data-ke-language=&quot;cpp&quot; data-darkreader-inline-bgcolor=&quot;&quot; data-darkreader-inline-color=&quot;&quot;&gt;&lt;code&gt;#include &quot;DataAssets/StartUpData/DataAsset_StartUpDataBase.h&quot;
#include &quot;AbilitySystem/RPGAbilitySystemComponent.h&quot;
#include &quot;AbilitySystem/Abilities/RPGGameplayAbility.h&quot;

void UDataAsset_StartUpDataBase::GiveToAbilitySystemComponent(URPGAbilitySystemComponent* InRPG_ASC, int32 ApplyLevel)
{
	check(InRPG_ASC);

	GrantAbilites(ActivateOnGivenAbilites, InRPG_ASC, ApplyLevel);
	GrantAbilites(ReactiveAbilites, InRPG_ASC, ApplyLevel);

	// GameplayEffect들을 자기 자신에게 적용
	if (!StartUpGameplayEffects.IsEmpty())
	{
		for (const TSubclassOf&amp;lt;UGameplayEffect&amp;gt;&amp;amp; EffectClass : StartUpGameplayEffects)
		{
			if (!EffectClass) continue;

			UGameplayEffect* EffectCDL = EffectClass-&amp;gt;GetDefaultObject&amp;lt;UGameplayEffect&amp;gt;();
			InRPG_ASC-&amp;gt;ApplyGameplayEffectToSelf(
				EffectCDL,
				ApplyLevel,
				InRPG_ASC-&amp;gt;MakeEffectContext()
			);
		}
	}
}

void UDataAsset_StartUpDataBase::GrantAbilites(const TArray&amp;lt;TSubclassOf&amp;lt;URPGGameplayAbility&amp;gt;&amp;gt;&amp;amp; InAbilitesToGive, URPGAbilitySystemComponent* InRPG_ASC, int32 ApplyLevel)
{
	if (InAbilitesToGive.IsEmpty())
	{
		return;
	}

	for (const TSubclassOf&amp;lt;URPGGameplayAbility&amp;gt;&amp;amp; Ability : InAbilitesToGive)
	{
		if (!Ability) continue;

		FGameplayAbilitySpec AbilitySpec(Ability); // 어빌리티 클래스 지정
		AbilitySpec.SourceObject = InRPG_ASC-&amp;gt;GetAvatarActor(); // 출처 지정
		AbilitySpec.Level = ApplyLevel; // 레벨 설정

		InRPG_ASC-&amp;gt;GiveAbility(AbilitySpec); // ASC에 등록
	}
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 style=&quot;color: #e8e6e3; text-align: start;&quot; data-ke-size=&quot;size23&quot; data-darkreader-inline-color=&quot;&quot;&gt;&lt;b&gt;✅ DA_Hero에 GE 할당&lt;/b&gt;&lt;/h3&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1400&quot; data-origin-height=&quot;606&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cYndq6/btsNP50ctfS/UqK32WZ69uGBMqnsy5A3I0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cYndq6/btsNP50ctfS/UqK32WZ69uGBMqnsy5A3I0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cYndq6/btsNP50ctfS/UqK32WZ69uGBMqnsy5A3I0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcYndq6%2FbtsNP50ctfS%2FUqK32WZ69uGBMqnsy5A3I0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1400&quot; height=&quot;606&quot; data-origin-width=&quot;1400&quot; data-origin-height=&quot;606&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;blockquote style=&quot;background-color: #1a1c1d; color: #a8a095; text-align: left;&quot; data-ke-style=&quot;style3&quot; data-darkreader-inline-bgcolor=&quot;&quot; data-darkreader-inline-color=&quot;&quot;&gt;에디터로 돌아와 &quot;DA_Hero&quot;를 살펴보면 방금 추가했던 GameEffect 저장할 배열이 생겨난 것을 확인할 수 있다.&lt;br /&gt;앞서 만들었던 &quot;GE_Hero_StartUp&quot;과 &quot;GE_Hero_Static&quot; Effect를 할당해준다.&lt;br /&gt;이때 중요한 것은&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;span style=&quot;background-color: #781b33;&quot; data-darkreader-inline-bgcolor=&quot;&quot;&gt;&quot;StartUp&quot;이 앞 인덱스에 와있어야 최대 체력을 기준으로 &quot;Static&quot;에 현재 체력을 저장할 수 있기 때문에 순서를 유의&lt;/span&gt;해야한다.&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 style=&quot;color: #c8c3bc; text-align: start;&quot; data-darkreader-inline-color=&quot;&quot; data-ke-size=&quot;size23&quot;&gt;&lt;b&gt; &lt;span&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;GameplayEffect로 Attribute 값 초기화 결과&lt;/span&gt;&lt;/b&gt;&lt;/h3&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock widthContent&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1980&quot; data-origin-height=&quot;1306&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bmaKgL/btsNPWim68H/F2R9YkPlEnLkcd1Ekxusu1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bmaKgL/btsNPWim68H/F2R9YkPlEnLkcd1Ekxusu1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bmaKgL/btsNPWim68H/F2R9YkPlEnLkcd1Ekxusu1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbmaKgL%2FbtsNPWim68H%2FF2R9YkPlEnLkcd1Ekxusu1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1980&quot; height=&quot;1306&quot; data-origin-width=&quot;1980&quot; data-origin-height=&quot;1306&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;blockquote style=&quot;background-color: #1a1c1d; color: #a8a095; text-align: left;&quot; data-ke-style=&quot;style3&quot; data-darkreader-inline-bgcolor=&quot;&quot; data-darkreader-inline-color=&quot;&quot;&gt;콘솔에 &quot;showdebug AbilitySystem&quot;을 입력하고 디버그 창을 띄워서 살펴보면 Curve Table의 Level1에서 설정한 초기 값들이 정상적으로 입력되어있는 것을 확인할 수 있다.&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 style=&quot;color: #e8e6e3; text-align: start;&quot; data-ke-size=&quot;size26&quot; data-darkreader-inline-color=&quot;&quot;&gt;&lt;b&gt;  Enemy에 &lt;/b&gt;&lt;b&gt;Gameplay Effect로 초기 Attribute 값 설정&lt;/b&gt;&lt;/h2&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;플레이어에게 했던 것처럼 Enemy의 Attribute에 GE로 초기 데이터 셋업을 진행한다.&lt;/blockquote&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;723&quot; data-origin-height=&quot;265&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/2q759/btsNPEIMgtr/kQQp0E7RWZPKFxzNduoDb0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/2q759/btsNPEIMgtr/kQQp0E7RWZPKFxzNduoDb0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/2q759/btsNPEIMgtr/kQQp0E7RWZPKFxzNduoDb0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F2q759%2FbtsNPEIMgtr%2FkQQp0E7RWZPKFxzNduoDb0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;723&quot; height=&quot;265&quot; data-origin-width=&quot;723&quot; data-origin-height=&quot;265&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;플레이어 CurveTable을 복사해서 &quot;CT_GuardianStats&quot;로 명명하고, Rage Attribute 만 빼주었다.&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1822&quot; data-origin-height=&quot;1312&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/xCfZe/btsNPyIWOzS/bKFGLHKORiDK8Drha83BIK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/xCfZe/btsNPyIWOzS/bKFGLHKORiDK8Drha83BIK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/xCfZe/btsNPyIWOzS/bKFGLHKORiDK8Drha83BIK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FxCfZe%2FbtsNPyIWOzS%2FbKFGLHKORiDK8Drha83BIK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1822&quot; height=&quot;1312&quot; data-origin-width=&quot;1822&quot; data-origin-height=&quot;1312&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;Guardian Enemy 전용 GE_StartUp을 생성하고 다음과 같이 설정한다.&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1812&quot; data-origin-height=&quot;1195&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cp3NqL/btsNPEvcAvZ/AHkZQHjQpOFntlRjvFa9v0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cp3NqL/btsNPEvcAvZ/AHkZQHjQpOFntlRjvFa9v0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cp3NqL/btsNPEvcAvZ/AHkZQHjQpOFntlRjvFa9v0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fcp3NqL%2FbtsNPEvcAvZ%2FAHkZQHjQpOFntlRjvFa9v0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1812&quot; height=&quot;1195&quot; data-origin-width=&quot;1812&quot; data-origin-height=&quot;1195&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;GE_StartUp에서 설정한 MaxHealth 값을 가져와 현재 CurrentHealth값을 덮어씌우는 GameplayEffect를 만들었다.&lt;br /&gt;모든 적이 공통으로 최대 체력 값으로 현재 체력으로 설정하는 것을 반복해야되기 때문에, &quot;GE_Enemy_Static&quot;으로 명명하여 공통으로 사용하는 GamplayEffect임을 명시해준다.&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1435&quot; data-origin-height=&quot;485&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bqF7u3/btsNPvZQSZv/yLqBsdpm2oHKwEGxJRvkQ0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bqF7u3/btsNPvZQSZv/yLqBsdpm2oHKwEGxJRvkQ0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bqF7u3/btsNPvZQSZv/yLqBsdpm2oHKwEGxJRvkQ0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbqF7u3%2FbtsNPvZQSZv%2FyLqBsdpm2oHKwEGxJRvkQ0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1435&quot; height=&quot;485&quot; data-origin-width=&quot;1435&quot; data-origin-height=&quot;485&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;Guardian의 StartUp DataAsset에서 GameplayEffect를 할당해준다.&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size23&quot; data-darkreader-inline-color=&quot;&quot;&gt;&lt;b&gt; &lt;span&gt; 테스트 결과 / 디버그 HUD 오류&lt;/span&gt;&lt;/b&gt;&lt;/h3&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock widthContent&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;blob&quot; data-origin-width=&quot;1566&quot; data-origin-height=&quot;765&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bccDnn/btsNPyvuvyh/xnDUvMxcRZt9bdKG5sacHK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bccDnn/btsNPyvuvyh/xnDUvMxcRZt9bdKG5sacHK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bccDnn/btsNPyvuvyh/xnDUvMxcRZt9bdKG5sacHK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbccDnn%2FbtsNPyvuvyh%2FxnDUvMxcRZt9bdKG5sacHK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1566&quot; height=&quot;765&quot; data-filename=&quot;blob&quot; data-origin-width=&quot;1566&quot; data-origin-height=&quot;765&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;&quot;showdebug AbilitySystem&quot;으로 디버깅 UI를 생성하고 (PageUp/PageDown)을 입력하면 다른 대상의 정보를 확인할 수 있다.&lt;br /&gt;Enemy의 정보를 확인해본 결과, CT에 있는 데이터와 디버그에 표시되는 Enemy의 스탯이 다르게 보여지는 문제가 생겼다.&lt;br /&gt;&lt;br /&gt;Enemy의 올바른 속성 값을 표시하려면 DefaultGame.ini에서 설정을 해줘야한다.&lt;br /&gt;그렇지 않으면 디버그 카드의 모든 속성에 대해 동일한 속성값만 표시될 것이다.&lt;/blockquote&gt;
&lt;h4 style=&quot;color: #e8e6e3; text-align: start;&quot; data-ke-size=&quot;size20&quot; data-darkreader-inline-color=&quot;&quot;&gt;&lt;b&gt; &lt;span&gt; DefaultGame.ini 파일 수정&lt;/span&gt;&lt;/b&gt;&lt;/h4&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;497&quot; data-origin-height=&quot;292&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cFJg4x/btsNRPB0s0D/SkQ2K2yAFvbIKjjDYn6Vsk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cFJg4x/btsNRPB0s0D/SkQ2K2yAFvbIKjjDYn6Vsk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cFJg4x/btsNRPB0s0D/SkQ2K2yAFvbIKjjDYn6Vsk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcFJg4x%2FbtsNRPB0s0D%2FSkQ2K2yAFvbIKjjDYn6Vsk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;497&quot; height=&quot;292&quot; data-origin-width=&quot;497&quot; data-origin-height=&quot;292&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imagegridblock&quot;&gt;
  &lt;div class=&quot;image-container&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/3tyaA/btsNPFHJrEU/lL9K12VvEZfDQ4k1OKT34K/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/3tyaA/btsNPFHJrEU/lL9K12VvEZfDQ4k1OKT34K/img.png&quot; data-origin-width=&quot;1196&quot; data-origin-height=&quot;732&quot; data-is-animation=&quot;false&quot; style=&quot;width: 49.9928%; margin-right: 10px;&quot; data-widthpercent=&quot;50.58&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/3tyaA/btsNPFHJrEU/lL9K12VvEZfDQ4k1OKT34K/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F3tyaA%2FbtsNPFHJrEU%2FlL9K12VvEZfDQ4k1OKT34K%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1196&quot; height=&quot;732&quot;/&gt;&lt;/span&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/PQsht/btsNQXgbIbF/sk2QvvkUhpZE7wvwJjvFt1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/PQsht/btsNQXgbIbF/sk2QvvkUhpZE7wvwJjvFt1/img.png&quot; data-origin-width=&quot;961&quot; data-origin-height=&quot;602&quot; data-is-animation=&quot;false&quot; style=&quot;width: 48.8444%;&quot; data-widthpercent=&quot;49.42&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/PQsht/btsNQXgbIbF/sk2QvvkUhpZE7wvwJjvFt1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FPQsht%2FbtsNQXgbIbF%2Fsk2QvvkUhpZE7wvwJjvFt1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;961&quot; height=&quot;602&quot;/&gt;&lt;/span&gt;&lt;/div&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;[/Script/GameplayAbilities.AbilitySystemGlobals]&lt;br /&gt;bUseDebugTargetFromHud = true&lt;br /&gt;우측과 같이 위의 두줄을 포함해준다.&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 style=&quot;color: #c8c3bc; text-align: start;&quot; data-ke-size=&quot;size20&quot; data-darkreader-inline-color=&quot;&quot;&gt;&lt;b&gt; &lt;span&gt; 테스트 최종 결과&lt;/span&gt;&lt;/b&gt;&lt;/h4&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;blob&quot; data-origin-width=&quot;1416&quot; data-origin-height=&quot;761&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bo3joR/btsNQeJPMeQ/Ydzdz1dK86PVrFeyE7DHK0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bo3joR/btsNQeJPMeQ/Ydzdz1dK86PVrFeyE7DHK0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bo3joR/btsNQeJPMeQ/Ydzdz1dK86PVrFeyE7DHK0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fbo3joR%2FbtsNQeJPMeQ%2FYdzdz1dK86PVrFeyE7DHK0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1416&quot; height=&quot;761&quot; data-filename=&quot;blob&quot; data-origin-width=&quot;1416&quot; data-origin-height=&quot;761&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;ini 파일을 수정한 후 다시 테스트를 해보면 정상적으로 Enemy의 Stat이 올바르게 표시되는 것을 확인할 수 있다.&lt;/blockquote&gt;</description>
      <category>내배캠/GAS(Gameplay Ability System)</category>
      <author>동그래님</author>
      <guid isPermaLink="true">https://dong-grae.tistory.com/233</guid>
      <comments>https://dong-grae.tistory.com/233#entry233comment</comments>
      <pubDate>Thu, 8 May 2025 23:29:23 +0900</pubDate>
    </item>
    <item>
      <title>GAS_10_Enemy 클래스 생성</title>
      <link>https://dong-grae.tistory.com/232</link>
      <description>&lt;h2 style=&quot;color: #e8e6e3; text-align: start;&quot; data-ke-size=&quot;size26&quot; data-darkreader-inline-color=&quot;&quot;&gt;&lt;b&gt;  적 캐릭터 생성&lt;/b&gt;&lt;/h2&gt;
&lt;h3 style=&quot;color: #e8e6e3; text-align: start;&quot; data-ke-size=&quot;size23&quot; data-darkreader-inline-color=&quot;&quot;&gt;&lt;b&gt;✅ Enemy 관련 클래스 생성&lt;/b&gt;&lt;/h3&gt;
&lt;p&gt;&lt;figure class=&quot;imagegridblock&quot;&gt;
  &lt;div class=&quot;image-container&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bRv94Q/btsNOnmyEX4/IlSfodALUMDc2fNngGlwX1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bRv94Q/btsNOnmyEX4/IlSfodALUMDc2fNngGlwX1/img.png&quot; data-origin-width=&quot;603&quot; data-origin-height=&quot;674&quot; data-is-animation=&quot;false&quot; style=&quot;width: 43.9681%; margin-right: 10px;&quot; data-widthpercent=&quot;44.49&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bRv94Q/btsNOnmyEX4/IlSfodALUMDc2fNngGlwX1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbRv94Q%2FbtsNOnmyEX4%2FIlSfodALUMDc2fNngGlwX1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;603&quot; height=&quot;674&quot;/&gt;&lt;/span&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cW6xJi/btsNN7R61FG/7MjmH6JcCrGKUufEE8N2Dk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cW6xJi/btsNN7R61FG/7MjmH6JcCrGKUufEE8N2Dk/img.png&quot; data-origin-width=&quot;671&quot; data-origin-height=&quot;601&quot; data-is-animation=&quot;false&quot; style=&quot;width: 54.8691%;&quot; data-widthpercent=&quot;55.51&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cW6xJi/btsNN7R61FG/7MjmH6JcCrGKUufEE8N2Dk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcW6xJi%2FbtsNN7R61FG%2F7MjmH6JcCrGKUufEE8N2Dk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;671&quot; height=&quot;601&quot;/&gt;&lt;/span&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/wlejW/btsNOfvlzhL/XXt7SKHOWhpGGqwHATVgs0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/wlejW/btsNOfvlzhL/XXt7SKHOWhpGGqwHATVgs0/img.png&quot; data-origin-width=&quot;612&quot; data-origin-height=&quot;575&quot; data-is-animation=&quot;false&quot; style=&quot;width: 49.9749%; margin-right: 10px; margin-top: 10px;&quot; data-widthpercent=&quot;50.56&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/wlejW/btsNOfvlzhL/XXt7SKHOWhpGGqwHATVgs0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FwlejW%2FbtsNOfvlzhL%2FXXt7SKHOWhpGGqwHATVgs0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;612&quot; height=&quot;575&quot;/&gt;&lt;/span&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/DvTHc/btsNN2pq1qs/gvOCabzLkBokf7GkSUCSP1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/DvTHc/btsNN2pq1qs/gvOCabzLkBokf7GkSUCSP1/img.png&quot; data-origin-width=&quot;640&quot; data-origin-height=&quot;615&quot; data-is-animation=&quot;false&quot; style=&quot;width: 48.8623%; margin-top: 10px;&quot; data-widthpercent=&quot;49.44&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/DvTHc/btsNN2pq1qs/gvOCabzLkBokf7GkSUCSP1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FDvTHc%2FbtsNN2pq1qs%2FgvOCabzLkBokf7GkSUCSP1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;640&quot; height=&quot;615&quot;/&gt;&lt;/span&gt;&lt;/div&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;&lt;b&gt;Enemy&lt;/b&gt; 관련 (&lt;b&gt;Character, Start Up Data, Gameplay Ability, Combat Component&lt;/b&gt;) 클래스를 생성해준다.&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 style=&quot;color: #e8e6e3; text-align: start;&quot; data-ke-size=&quot;size23&quot; data-darkreader-inline-color=&quot;&quot;&gt;&lt;b&gt;✅ Enemy 관련 클래스 구현&lt;/b&gt;&lt;/h3&gt;
&lt;h4 style=&quot;color: #e8e6e3; text-align: start;&quot; data-ke-size=&quot;size20&quot; data-darkreader-inline-color=&quot;&quot;&gt;&lt;b&gt;  EnemyCharacter 클래스&lt;/b&gt;&lt;/h4&gt;
&lt;div data-ke-type=&quot;moreLess&quot; data-text-more=&quot;더보기&quot; data-text-less=&quot;닫기&quot;&gt;&lt;a class=&quot;btn-toggle-moreless&quot;&gt;더보기&lt;/a&gt;
&lt;div class=&quot;moreless-content&quot;&gt;
&lt;p style=&quot;color: #e8e6e3; text-align: start;&quot; data-darkreader-inline-color=&quot;&quot; data-ke-size=&quot;size18&quot;&gt;&lt;b&gt; &lt;span&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;EnemyCharacter.h&lt;/span&gt;&lt;/b&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1746688412772&quot; class=&quot;cpp&quot; style=&quot;background-color: #1c1e1f; color: #c1bcb4; text-align: start;&quot; data-ke-type=&quot;codeblock&quot; data-ke-language=&quot;cpp&quot; data-darkreader-inline-bgcolor=&quot;&quot; data-darkreader-inline-color=&quot;&quot;&gt;&lt;code&gt;#pragma once

#include &quot;CoreMinimal.h&quot;
#include &quot;Characters/RPGBaseCharacter.h&quot;
#include &quot;RPGEnemyCharacter.generated.h&quot;

class UEnemyCombatComponent;

UCLASS()
class RPG_API ARPGEnemyCharacter : public ARPGBaseCharacter
{
	GENERATED_BODY()
	
public:
	ARPGEnemyCharacter();

protected:
	UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = &quot;Combat&quot;)
	TObjectPtr&amp;lt;UEnemyCombatComponent&amp;gt; EnemyCombatComponent;

public:
	FORCEINLINE UEnemyCombatComponent* GetEnemyCombatComponent() const { return EnemyCombatComponent; }
};&lt;/code&gt;&lt;/pre&gt;
&lt;p style=&quot;color: #e8e6e3; text-align: start;&quot; data-darkreader-inline-color=&quot;&quot; data-ke-size=&quot;size18&quot;&gt;&lt;b&gt; &lt;span&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;EnemyCharacter.cpp&lt;/span&gt;&lt;/b&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1746688412772&quot; class=&quot;livescript&quot; style=&quot;background-color: #1c1e1f; color: #c1bcb4; text-align: start;&quot; data-ke-type=&quot;codeblock&quot; data-ke-language=&quot;cpp&quot; data-darkreader-inline-bgcolor=&quot;&quot; data-darkreader-inline-color=&quot;&quot;&gt;&lt;code&gt;#include &quot;Characters/RPGEnemyCharacter.h&quot;
#include &quot;GameFramework/CharacterMovementComponent.h&quot;
#include &quot;Components/Combat/EnemyCombatComponent.h&quot;

ARPGEnemyCharacter::ARPGEnemyCharacter()
{
	AutoPossessAI = EAutoPossessAI::PlacedInWorldOrSpawned;

	bUseControllerRotationPitch = false;
	bUseControllerRotationRoll = false;
	bUseControllerRotationYaw = false;

	GetCharacterMovement()-&amp;gt;bUseControllerDesiredRotation = false;
	GetCharacterMovement()-&amp;gt;bOrientRotationToMovement = true;
	GetCharacterMovement()-&amp;gt;RotationRate = FRotator(0.0f, 180.0f, 0.0f);
	GetCharacterMovement()-&amp;gt;MaxWalkSpeed = 300.0f;
	GetCharacterMovement()-&amp;gt;BrakingDecelerationWalking = 1000.0f;

	EnemyCombatComponent = CreateDefaultSubobject&amp;lt;UEnemyCombatComponent&amp;gt;(TEXT(&quot;EnemyCombatComponent&quot;));
}&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;h4 style=&quot;color: #e8e6e3; text-align: start;&quot; data-darkreader-inline-color=&quot;&quot; data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;  EnemyGameplayAbility&amp;nbsp; 클래스&lt;/b&gt;&lt;/h4&gt;
&lt;div data-ke-type=&quot;moreLess&quot; data-text-more=&quot;더보기&quot; data-text-less=&quot;닫기&quot;&gt;&lt;a class=&quot;btn-toggle-moreless&quot;&gt;더보기&lt;/a&gt;
&lt;div class=&quot;moreless-content&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt; &lt;span&gt;&lt;span&gt; EnemyGameplayAbility.h&lt;/span&gt;&lt;/span&gt;&lt;/b&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1746688455875&quot; class=&quot;cpp&quot; data-ke-language=&quot;cpp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;#pragma once

#include &quot;CoreMinimal.h&quot;
#include &quot;AbilitySystem/Abilities/RPGGameplayAbility.h&quot;
#include &quot;RPGEnemyGameplayAbility.generated.h&quot;

class ARPGEnemyCharacter;
class UEnemyCombatComponent;

UCLASS()
class RPG_API URPGEnemyGameplayAbility : public URPGGameplayAbility
{
	GENERATED_BODY()
	
public:
	// Enemy 캐릭터 반환
	UFUNCTION(BlueprintPure, Category = &quot;RPG|Ability&quot;)
	ARPGEnemyCharacter* GetEnemyCharacterFromActorInfo();

	// Enemy CombatComponent 반환
	UFUNCTION(BlueprintPure, Category = &quot;RPG|Ability&quot;)
	UEnemyCombatComponent* GetEnemyCombatComponentFromActorInfo();

private:
	TWeakObjectPtr&amp;lt;ARPGEnemyCharacter&amp;gt; CachedRPGEnemyCharacter;
};&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt; &lt;span&gt;&lt;span&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;EnemyGameplayAbility.cpp&lt;/span&gt;&lt;/span&gt;&lt;/b&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1746688488490&quot; class=&quot;cpp&quot; data-ke-language=&quot;cpp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;#include &quot;AbilitySystem/Abilities/RPGEnemyGameplayAbility.h&quot;
#include &quot;Characters/RPGEnemyCharacter.h&quot;

ARPGEnemyCharacter* URPGEnemyGameplayAbility::GetEnemyCharacterFromActorInfo()
{
	if (!CachedRPGEnemyCharacter.IsValid())
	{
		CachedRPGEnemyCharacter = Cast&amp;lt;ARPGEnemyCharacter&amp;gt;(CurrentActorInfo-&amp;gt;AvatarActor);
	}

	return CachedRPGEnemyCharacter.IsValid() ? CachedRPGEnemyCharacter.Get() : nullptr;
}

UEnemyCombatComponent* URPGEnemyGameplayAbility::GetEnemyCombatComponentFromActorInfo()
{
	return GetEnemyCharacterFromActorInfo()-&amp;gt;GetEnemyCombatComponent();
}&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;우선 EnemyCharacter 클래스와 EnemyGameplayAbility 클래스에 기본적인 구현을 진행한다.&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imagegridblock&quot;&gt;
  &lt;div class=&quot;image-container&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cs185Q/btsNPz1cb0B/9HMx96jxTtgEFKfpQPxzEK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cs185Q/btsNPz1cb0B/9HMx96jxTtgEFKfpQPxzEK/img.png&quot; data-origin-width=&quot;617&quot; data-origin-height=&quot;633&quot; data-is-animation=&quot;false&quot; style=&quot;width: 61.5419%; margin-right: 10px;&quot; data-widthpercent=&quot;62.27&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cs185Q/btsNPz1cb0B/9HMx96jxTtgEFKfpQPxzEK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fcs185Q%2FbtsNPz1cb0B%2F9HMx96jxTtgEFKfpQPxzEK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;617&quot; height=&quot;633&quot;/&gt;&lt;/span&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/baTgDM/btsNN6rWSjn/3RGhPBlQALyOcApVt1r780/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/baTgDM/btsNN6rWSjn/3RGhPBlQALyOcApVt1r780/img.png&quot; data-origin-width=&quot;127&quot; data-origin-height=&quot;215&quot; data-is-animation=&quot;false&quot; style=&quot;width: 37.2953%;&quot; data-widthpercent=&quot;37.73&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/baTgDM/btsNN6rWSjn/3RGhPBlQALyOcApVt1r780/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbaTgDM%2FbtsNN6rWSjn%2F3RGhPBlQALyOcApVt1r780%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;127&quot; height=&quot;215&quot;/&gt;&lt;/span&gt;&lt;/div&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;에디터로 돌아와 &quot;RPGEnemyCharacter&quot; C++ 클래스를 상속 받는 BP클래스를 생성한다.&lt;br /&gt;&quot;BP_EnemyCharacter_Base&quot;라 명명하고, 이 클래스는 모든 Enemy 클래스의 최상위 부모 클래스가 된다.&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;888&quot; data-origin-height=&quot;390&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/ctAkCF/btsNQqh3eEi/RA0v5uOpkAty7OYXs4PY90/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/ctAkCF/btsNQqh3eEi/RA0v5uOpkAty7OYXs4PY90/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/ctAkCF/btsNQqh3eEi/RA0v5uOpkAty7OYXs4PY90/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FctAkCF%2FbtsNQqh3eEi%2FRA0v5uOpkAty7OYXs4PY90%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;888&quot; height=&quot;390&quot; data-origin-width=&quot;888&quot; data-origin-height=&quot;390&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;다음과 같은 상속 구조로 Enemy Character BP 클래스를 생성한다.&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 style=&quot;color: #e8e6e3; text-align: start;&quot; data-ke-size=&quot;size23&quot; data-darkreader-inline-color=&quot;&quot;&gt;&lt;b&gt;✅ Enemy / ABP 세팅&lt;/b&gt;&lt;/h3&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;2557&quot; data-origin-height=&quot;1107&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/pzScq/btsNP56kINm/APuKlj6MXBpoZlY0lACBh1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/pzScq/btsNP56kINm/APuKlj6MXBpoZlY0lACBh1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/pzScq/btsNP56kINm/APuKlj6MXBpoZlY0lACBh1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FpzScq%2FbtsNP56kINm%2FAPuKlj6MXBpoZlY0lACBh1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;2557&quot; height=&quot;1107&quot; data-origin-width=&quot;2557&quot; data-origin-height=&quot;1107&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;최하단 자식 클래스인 &quot;BP_Gruntling_Guardian&quot; 클래스에 Mesh와 Collision 설정을 적절히 해준다.&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imagegridblock&quot;&gt;
  &lt;div class=&quot;image-container&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bLEoxe/btsNPW9I2Sq/h1PRyDHMdhOlKtxcydrJQ0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bLEoxe/btsNPW9I2Sq/h1PRyDHMdhOlKtxcydrJQ0/img.png&quot; data-origin-width=&quot;702&quot; data-origin-height=&quot;1189&quot; data-is-animation=&quot;false&quot; style=&quot;width: 19.8223%; margin-right: 10px;&quot; data-widthpercent=&quot;20.29&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bLEoxe/btsNPW9I2Sq/h1PRyDHMdhOlKtxcydrJQ0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbLEoxe%2FbtsNPW9I2Sq%2Fh1PRyDHMdhOlKtxcydrJQ0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;702&quot; height=&quot;1189&quot;/&gt;&lt;/span&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/E3gX2/btsNNGm07fQ/bJhEHUvVzu0jBMVFwC3K0k/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/E3gX2/btsNNGm07fQ/bJhEHUvVzu0jBMVFwC3K0k/img.png&quot; data-origin-width=&quot;527&quot; data-origin-height=&quot;887&quot; data-is-animation=&quot;false&quot; style=&quot;width: 19.9474%; margin-right: 10px;&quot; data-widthpercent=&quot;20.42&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/E3gX2/btsNNGm07fQ/bJhEHUvVzu0jBMVFwC3K0k/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FE3gX2%2FbtsNNGm07fQ%2FbJhEHUvVzu0jBMVFwC3K0k%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;527&quot; height=&quot;887&quot;/&gt;&lt;/span&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/dpoLR0/btsNOULfBkJ/Qgw8l36tgDJ3tfv1azlMV0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/dpoLR0/btsNOULfBkJ/Qgw8l36tgDJ3tfv1azlMV0/img.png&quot; data-origin-width=&quot;1892&quot; data-origin-height=&quot;1097&quot; data-is-animation=&quot;false&quot; style=&quot;width: 57.9047%;&quot; data-widthpercent=&quot;59.29&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/dpoLR0/btsNOULfBkJ/Qgw8l36tgDJ3tfv1azlMV0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FdpoLR0%2FbtsNOULfBkJ%2FQgw8l36tgDJ3tfv1azlMV0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1892&quot; height=&quot;1097&quot;/&gt;&lt;/span&gt;&lt;/div&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;Enemy의 애니메이션의 경우 대부분 다 비슷하기 때문에 각각 개별적으로 만들어 주지 않고, Template을 만들어준다.&lt;br /&gt;Template ABP의 경우 스켈레톤을 할당할 필요는 없지만 부모 클래스는 지정해야한다. 이전에 만들어두었던 Character AnimIntacne클래스로 설정한다.&lt;br /&gt;이전에 부모 클래스 Character Anim Instance에 만들어 두었던 Data들을 확인할 수 있다.&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock widthContent&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1897&quot; data-origin-height=&quot;1132&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bwdLjq/btsNP0j2Ghv/3lpK7YkAv9ps6lvSNJJTBk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bwdLjq/btsNP0j2Ghv/3lpK7YkAv9ps6lvSNJJTBk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bwdLjq/btsNP0j2Ghv/3lpK7YkAv9ps6lvSNJJTBk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbwdLjq%2FbtsNP0j2Ghv%2F3lpK7YkAv9ps6lvSNJJTBk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1897&quot; height=&quot;1132&quot; data-origin-width=&quot;1897&quot; data-origin-height=&quot;1132&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;다음과 같이 Enemy 전용 ABP Template클래스에 기본적인 로직을 구현한다.&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imagegridblock&quot;&gt;
  &lt;div class=&quot;image-container&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cP32uf/btsNPDWCysj/3Nqc5Bk7vVhlzpBHZDrg7k/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cP32uf/btsNPDWCysj/3Nqc5Bk7vVhlzpBHZDrg7k/img.png&quot; data-origin-width=&quot;527&quot; data-origin-height=&quot;873&quot; data-is-animation=&quot;false&quot; style=&quot;width: 34.336%; margin-right: 10px;&quot; data-widthpercent=&quot;34.74&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cP32uf/btsNPDWCysj/3Nqc5Bk7vVhlzpBHZDrg7k/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcP32uf%2FbtsNPDWCysj%2F3Nqc5Bk7vVhlzpBHZDrg7k%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;527&quot; height=&quot;873&quot;/&gt;&lt;/span&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/c4Tg7l/btsNOK3jtOp/OfBO7dR1T5DC8DWdKOiXGk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/c4Tg7l/btsNOK3jtOp/OfBO7dR1T5DC8DWdKOiXGk/img.png&quot; data-origin-width=&quot;677&quot; data-origin-height=&quot;597&quot; data-is-animation=&quot;false&quot; data-widthpercent=&quot;65.26&quot; style=&quot;width: 64.5012%;&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/c4Tg7l/btsNOK3jtOp/OfBO7dR1T5DC8DWdKOiXGk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fc4Tg7l%2FbtsNOK3jtOp%2FOfBO7dR1T5DC8DWdKOiXGk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;677&quot; height=&quot;597&quot;/&gt;&lt;/span&gt;&lt;/div&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;해당 ABP의 자식 클래스를 생성하고 만들고자 하는 Enemy Skeleton을 지정한다.&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imagegridblock&quot;&gt;
  &lt;div class=&quot;image-container&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/Bf9FZ/btsNPeW9fRK/jkSD5LTm1E8yKsKUt4YRK1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/Bf9FZ/btsNPeW9fRK/jkSD5LTm1E8yKsKUt4YRK1/img.png&quot; data-origin-width=&quot;2557&quot; data-origin-height=&quot;1366&quot; data-is-animation=&quot;false&quot; style=&quot;width: 44.3846%; margin-right: 10px;&quot; data-widthpercent=&quot;44.91&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/Bf9FZ/btsNPeW9fRK/jkSD5LTm1E8yKsKUt4YRK1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FBf9FZ%2FbtsNPeW9fRK%2FjkSD5LTm1E8yKsKUt4YRK1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;2557&quot; height=&quot;1366&quot;/&gt;&lt;/span&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/LUuSj/btsNOVp0yOn/4nOmmM4BicftSaOPqOXgG1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/LUuSj/btsNOVp0yOn/4nOmmM4BicftSaOPqOXgG1/img.png&quot; data-origin-width=&quot;2556&quot; data-origin-height=&quot;1113&quot; data-is-animation=&quot;false&quot; style=&quot;width: 54.4526%;&quot; data-widthpercent=&quot;55.09&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/LUuSj/btsNOVp0yOn/4nOmmM4BicftSaOPqOXgG1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FLUuSj%2FbtsNOVp0yOn%2F4nOmmM4BicftSaOPqOXgG1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;2556&quot; height=&quot;1113&quot;/&gt;&lt;/span&gt;&lt;/div&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;자식 클래스에서 BlendSpace 1D를 생성해 할당하고, Enemy Character 클래스에 해당 ABP를 할당해준다.&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 style=&quot;color: #e8e6e3; text-align: start;&quot; data-darkreader-inline-color=&quot;&quot; data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;  Enemy에 Ability 부여&lt;/b&gt;&lt;/h2&gt;
&lt;h3 style=&quot;color: #e8e6e3; text-align: start;&quot; data-ke-size=&quot;size23&quot; data-darkreader-inline-color=&quot;&quot;&gt;&lt;b&gt;✅ Enemy / StartUpData 세팅&lt;/b&gt;&lt;/h3&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1201&quot; data-origin-height=&quot;478&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/t7a1z/btsNPCwHIyE/MMborumx6GkBUpMqhJuAJ1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/t7a1z/btsNPCwHIyE/MMborumx6GkBUpMqhJuAJ1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/t7a1z/btsNPCwHIyE/MMborumx6GkBUpMqhJuAJ1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Ft7a1z%2FbtsNPCwHIyE%2FMMborumx6GkBUpMqhJuAJ1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1201&quot; height=&quot;478&quot; data-origin-width=&quot;1201&quot; data-origin-height=&quot;478&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;기존에&amp;nbsp; 플레이어 캐릭터도 &quot;DataAsset_HeroStartUpData&quot; 클래스를 상속 받은 &quot;DA_Hero&quot; 클래스를 만들어 Ability들을 저장하고 부여했었다.&lt;br /&gt;Enemy 캐릭터도 마찬가지로 StartUpData를 구성해준다.&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 style=&quot;color: #e8e6e3; text-align: start;&quot; data-ke-size=&quot;size20&quot; data-darkreader-inline-color=&quot;&quot;&gt;&lt;b&gt; &lt;span&gt; DataAsset_EnemyStartUpData.h&lt;/span&gt;&lt;/b&gt;&lt;/h4&gt;
&lt;pre id=&quot;code_1746692062236&quot; class=&quot;cpp&quot; data-ke-language=&quot;cpp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;#pragma once

#include &quot;CoreMinimal.h&quot;
#include &quot;DataAssets/StartUpData/DataAsset_StartUpDataBase.h&quot;
#include &quot;DataAsset_EnemyStartUpData.generated.h&quot;

class URPGEnemyGameplayAbility;

UCLASS()
class RPG_API UDataAsset_EnemyStartUpData : public UDataAsset_StartUpDataBase
{
	GENERATED_BODY()

public:
	// Enemy 캐릭터의 ASC에 초기 능력을 부여
	virtual void GiveToAbilitySystemComponent(URPGAbilitySystemComponent* InRPG_ASC, int32 ApplyLevel = 1) override;

private:
	UPROPERTY(EditDefaultsOnly, Category = &quot;StartUpData&quot;)
	TArray&amp;lt;TSubclassOf&amp;lt;URPGEnemyGameplayAbility&amp;gt;&amp;gt; EnemyCombatAbilities;
};&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;Enemy의 경우 Input이 따로 없기 때문에, 바로 TArray&amp;lt;TSubclassOf&amp;lt;URPGGameplayAbility&amp;gt;&amp;gt; 타입으로 배열을 선언해주었다.&amp;nbsp;&lt;/blockquote&gt;
&lt;h4 style=&quot;color: #e8e6e3; text-align: start;&quot; data-darkreader-inline-color=&quot;&quot; data-ke-size=&quot;size20&quot;&gt;&lt;b&gt; &lt;span&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;DataAsset_EnemyStartUpData.cpp&lt;/span&gt;&lt;/b&gt;&lt;/h4&gt;
&lt;pre id=&quot;code_1746692092304&quot; class=&quot;cpp&quot; data-ke-language=&quot;cpp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;#include &quot;DataAssets/StartUpData/DataAsset_EnemyStartUpData.h&quot;
#include &quot;AbilitySystem/RPGAbilitySystemComponent.h&quot;
#include &quot;AbilitySystem/Abilities/RPGEnemyGameplayAbility.h&quot;

void UDataAsset_EnemyStartUpData::GiveToAbilitySystemComponent(URPGAbilitySystemComponent* InRPG_ASC, int32 ApplyLevel)
{
	Super::GiveToAbilitySystemComponent(InRPG_ASC, ApplyLevel);

	if (!EnemyCombatAbilities.IsEmpty())
	{
		for (const TSubclassOf&amp;lt;URPGEnemyGameplayAbility&amp;gt;&amp;amp; AbilityClass : EnemyCombatAbilities)
		{
			if (!AbilityClass) continue;

			FGameplayAbilitySpec AbilitySpec(AbilityClass);
			AbilitySpec.SourceObject = InRPG_ASC-&amp;gt;GetAvatarActor();
			AbilitySpec.Level = ApplyLevel;

			InRPG_ASC-&amp;gt;GiveAbility(AbilitySpec);
		}
	}
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imagegridblock&quot;&gt;
  &lt;div class=&quot;image-container&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cPHz2r/btsNOhtlB93/kl0qvgykGUnWp4GyrqY890/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cPHz2r/btsNOhtlB93/kl0qvgykGUnWp4GyrqY890/img.png&quot; data-origin-width=&quot;842&quot; data-origin-height=&quot;1322&quot; data-is-animation=&quot;false&quot; style=&quot;width: 10.6538%; margin-right: 10px;&quot; data-widthpercent=&quot;10.91&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cPHz2r/btsNOhtlB93/kl0qvgykGUnWp4GyrqY890/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcPHz2r%2FbtsNOhtlB93%2Fkl0qvgykGUnWp4GyrqY890%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;842&quot; height=&quot;1322&quot;/&gt;&lt;/span&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bos8eH/btsNQgUjU6v/KxpMwzhy8SrmZhlSowOfJK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bos8eH/btsNQgUjU6v/KxpMwzhy8SrmZhlSowOfJK/img.png&quot; data-origin-width=&quot;770&quot; data-origin-height=&quot;493&quot; data-is-animation=&quot;false&quot; style=&quot;width: 26.1258%; margin-right: 10px;&quot; data-widthpercent=&quot;26.75&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bos8eH/btsNQgUjU6v/KxpMwzhy8SrmZhlSowOfJK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fbos8eH%2FbtsNQgUjU6v%2FKxpMwzhy8SrmZhlSowOfJK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;770&quot; height=&quot;493&quot;/&gt;&lt;/span&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bx4eyT/btsNQcLtOLk/VENPlBb9GFfISb6uzvq2Z1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bx4eyT/btsNQcLtOLk/VENPlBb9GFfISb6uzvq2Z1/img.png&quot; data-origin-width=&quot;1296&quot; data-origin-height=&quot;356&quot; data-is-animation=&quot;false&quot; style=&quot;width: 60.8948%;&quot; data-widthpercent=&quot;62.34&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bx4eyT/btsNQcLtOLk/VENPlBb9GFfISb6uzvq2Z1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fbx4eyT%2FbtsNQcLtOLk%2FVENPlBb9GFfISb6uzvq2Z1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1296&quot; height=&quot;356&quot;/&gt;&lt;/span&gt;&lt;/div&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;에디터에서 해당 클래스를 상속 받은 &quot;DA_Guardian&quot; 클래스를 생성해준다.&lt;br /&gt;내부를 보면 Enemy Combat Abilities 배열이 추가되어있는 것을 확인할 수 있다.&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 style=&quot;color: #e8e6e3; text-align: start;&quot; data-ke-size=&quot;size23&quot; data-darkreader-inline-color=&quot;&quot;&gt;&lt;b&gt;✅ Enemy / StartUpData 로딩&lt;/b&gt;&lt;/h3&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock widthContent&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;942&quot; data-origin-height=&quot;297&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bxnxhe/btsNOgH2qvS/Zr7Wh78jKg5ldBnf25Lnz1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bxnxhe/btsNOgH2qvS/Zr7Wh78jKg5ldBnf25Lnz1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bxnxhe/btsNOgH2qvS/Zr7Wh78jKg5ldBnf25Lnz1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fbxnxhe%2FbtsNOgH2qvS%2FZr7Wh78jKg5ldBnf25Lnz1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;942&quot; height=&quot;297&quot; data-origin-width=&quot;942&quot; data-origin-height=&quot;297&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;이전에 플레이어 캐릭터는 PossessedBy 시점에, 데이터를 동기 로딩하였었다.&lt;br /&gt;현재 이 프로젝트는 싱글 플레이로, 플레이어 캐릭터 인스턴스가 1개임이 확정적이기 때문에 즉시 사용가능하도록 하기 위함이었다.&lt;br /&gt;하지만 AI의 경우 어느정도의 인스턴스가 생성될지 모르고, 대량의 로딩이 한꺼번에 이루어질 경우 hitch(게임이 멈추고나 끊김 현상)가 발생할 수 있어 비동기 로딩으로 진행하는 것이 안정적이다.&lt;br /&gt;따라서 특정 시점에 BackGround에서 병렬적으로 로딩할 수 있도록 한다.&lt;/blockquote&gt;
&lt;h4 style=&quot;color: #e8e6e3; text-align: start;&quot; data-ke-size=&quot;size20&quot; data-darkreader-inline-color=&quot;&quot;&gt;&lt;b&gt; &lt;span&gt;&lt;span&gt; RPGEnemyCharacter&lt;/span&gt;.h&lt;/span&gt;&lt;/b&gt;&lt;/h4&gt;
&lt;pre id=&quot;code_1746693830797&quot; class=&quot;cpp&quot; data-ke-language=&quot;cpp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;#pragma once

#include &quot;CoreMinimal.h&quot;
#include &quot;Characters/RPGBaseCharacter.h&quot;
#include &quot;RPGEnemyCharacter.generated.h&quot;

class UEnemyCombatComponent;

UCLASS()
class RPG_API ARPGEnemyCharacter : public ARPGBaseCharacter
{
	GENERATED_BODY()
	
public:
	ARPGEnemyCharacter();

protected:
	//~ Begin APawn Interface
	virtual void PossessedBy(AController* NewController) override;
	//~ End APawn Interface

	UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = &quot;Combat&quot;)
	TObjectPtr&amp;lt;UEnemyCombatComponent&amp;gt; EnemyCombatComponent;
	
private:
	void InitEnemyStartUpData();

public:
	FORCEINLINE UEnemyCombatComponent* GetEnemyCombatComponent() const { return EnemyCombatComponent; }
};&lt;/code&gt;&lt;/pre&gt;
&lt;h4 style=&quot;color: #e8e6e3; text-align: start;&quot; data-darkreader-inline-color=&quot;&quot; data-ke-size=&quot;size20&quot;&gt;&lt;b&gt; &lt;span&gt;&lt;span&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;RPGEnemyCharacter&lt;/span&gt;.cpp&lt;/span&gt;&lt;/b&gt;&lt;/h4&gt;
&lt;pre id=&quot;code_1746693866401&quot; class=&quot;cpp&quot; data-ke-language=&quot;cpp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;#include &quot;Characters/RPGEnemyCharacter.h&quot;
#include &quot;GameFramework/CharacterMovementComponent.h&quot;
#include &quot;Components/Combat/EnemyCombatComponent.h&quot;
#include &quot;Engine/AssetManager.h&quot;
#include &quot;DataAssets/StartUpData/DataAsset_EnemyStartUpData.h&quot;

#include &quot;WarriorDebugHelper.h&quot;

ARPGEnemyCharacter::ARPGEnemyCharacter()
{
	AutoPossessAI = EAutoPossessAI::PlacedInWorldOrSpawned;

	bUseControllerRotationPitch = false;
	bUseControllerRotationRoll = false;
	bUseControllerRotationYaw = false;

	GetCharacterMovement()-&amp;gt;bUseControllerDesiredRotation = false;
	GetCharacterMovement()-&amp;gt;bOrientRotationToMovement = true;
	GetCharacterMovement()-&amp;gt;RotationRate = FRotator(0.0f, 180.0f, 0.0f);
	GetCharacterMovement()-&amp;gt;MaxWalkSpeed = 300.0f;
	GetCharacterMovement()-&amp;gt;BrakingDecelerationWalking = 1000.0f;

	EnemyCombatComponent = CreateDefaultSubobject&amp;lt;UEnemyCombatComponent&amp;gt;(TEXT(&quot;EnemyCombatComponent&quot;));
}

void ARPGEnemyCharacter::PossessedBy(AController* NewController)
{
	Super::PossessedBy(NewController);

	InitEnemyStartUpData();
}

void ARPGEnemyCharacter::InitEnemyStartUpData()
{
	if (CharacterStartUpData.IsNull()) return;

	UAssetManager::GetStreamableManager().RequestAsyncLoad( // AssetManager를 통해 비동기 로딩 요청
		CharacterStartUpData.ToSoftObjectPath(),			// 불러올 자산의 경로
		FStreamableDelegate::CreateLambda(					// 로딩 완료 시 실행할 콜백 함수
			[this]()
			{
				if (UDataAsset_StartUpDataBase* LoadedData = CharacterStartUpData.Get())
				{
					LoadedData-&amp;gt;GiveToAbilitySystemComponent(RPGAbilitySystemComponent);

					Debug::Print(TEXT(&quot;Enemy Start up Data Loaded&quot;), FColor::Green);
				}
			}
		)
	);
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imagegridblock&quot;&gt;
  &lt;div class=&quot;image-container&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bFIkPJ/btsNNRaY18d/kWb7KaRQSEzNbFkz0N2r00/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bFIkPJ/btsNNRaY18d/kWb7KaRQSEzNbFkz0N2r00/img.png&quot; data-origin-width=&quot;2543&quot; data-origin-height=&quot;1377&quot; data-is-animation=&quot;false&quot; style=&quot;width: 45.9443%; margin-right: 10px;&quot; data-widthpercent=&quot;46.48&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bFIkPJ/btsNNRaY18d/kWb7KaRQSEzNbFkz0N2r00/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbFIkPJ%2FbtsNNRaY18d%2FkWb7KaRQSEzNbFkz0N2r00%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;2543&quot; height=&quot;1377&quot;/&gt;&lt;/span&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/r0z4j/btsNN4gPOqT/h222FUaqvc46zUC0SezCW0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/r0z4j/btsNN4gPOqT/h222FUaqvc46zUC0SezCW0/img.png&quot; data-origin-width=&quot;1973&quot; data-origin-height=&quot;928&quot; data-is-animation=&quot;false&quot; style=&quot;width: 52.893%;&quot; data-widthpercent=&quot;53.52&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/r0z4j/btsNN4gPOqT/h222FUaqvc46zUC0SezCW0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fr0z4j%2FbtsNN4gPOqT%2Fh222FUaqvc46zUC0SezCW0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1973&quot; height=&quot;928&quot;/&gt;&lt;/span&gt;&lt;/div&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;컴파일 후, 에디터로 돌아와서 Enemy Character 클래스에 StartUpData를 할당해주었다.&lt;br /&gt;플레이 했을 때 정상적으로 LoadData가 연결된 것을 확인할 수 있었다.&lt;br /&gt;&lt;br /&gt;다만 현재 Possessed By 시점에 비동기 로딩을 하게끔 구현하였는데, Editor 환경에서 월드에 미리 배치 되어있는 적의 경우 StartUpData가 미리 로딩되어있을 수 있어서 정상적으로 비동기 로딩이 되었을 수 있다.&lt;br /&gt;캐릭터 클래스에서 Soft Reference로 StartUpData를 가지고 있기 때문에 런타임에 Spawn된 적의 경우나 패키징된 상황에서 Possessed By 시점에 Data가 메모리에 올라와 있지 않아, 정상적으로 Ability가 등록 되지 않을 경우가 발생할 수 있다.&lt;br /&gt;&lt;br /&gt;따라서 테스트를 해보고 이상이 있다면, BeginPlay에서 ㅁ&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 style=&quot;color: #e8e6e3; text-align: start;&quot; data-ke-size=&quot;size23&quot; data-darkreader-inline-color=&quot;&quot;&gt;&lt;b&gt;✅ Enemy / StartUpData 데이터 할당&lt;/b&gt;&lt;/h3&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;✔️ Enemy Weapon Tag 추가&lt;/b&gt;&lt;/h4&gt;
&lt;div data-ke-type=&quot;moreLess&quot; data-text-more=&quot;더보기&quot; data-text-less=&quot;닫기&quot;&gt;&lt;a class=&quot;btn-toggle-moreless&quot;&gt;더보기&lt;/a&gt;
&lt;div class=&quot;moreless-content&quot;&gt;
&lt;p style=&quot;color: #e8e6e3; text-align: start;&quot; data-ke-size=&quot;size18&quot; data-darkreader-inline-color=&quot;&quot;&gt;&lt;b&gt; &lt;span&gt; RPGGameplayTags.h&lt;/span&gt;&lt;/b&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1746699198657&quot; class=&quot;cpp&quot; style=&quot;background-color: #1c1e1f; color: #c1bcb4; text-align: start;&quot; data-ke-type=&quot;codeblock&quot; data-ke-language=&quot;cpp&quot; data-darkreader-inline-bgcolor=&quot;&quot; data-darkreader-inline-color=&quot;&quot;&gt;&lt;code&gt;#pragma once

#include &quot;NativeGameplayTags.h&quot;

namespace RPGGameplayTags
{
	/** Input Tags */
	RPG_API UE_DECLARE_GAMEPLAY_TAG_EXTERN(InputTag_Move);
	RPG_API UE_DECLARE_GAMEPLAY_TAG_EXTERN(InputTag_Look);
	RPG_API UE_DECLARE_GAMEPLAY_TAG_EXTERN(InputTag_EquipAxe);
	RPG_API UE_DECLARE_GAMEPLAY_TAG_EXTERN(InputTag_UnequipAxe);
	RPG_API UE_DECLARE_GAMEPLAY_TAG_EXTERN(InputTag_LightAttack_Axe);
	RPG_API UE_DECLARE_GAMEPLAY_TAG_EXTERN(InputTag_HeavyAttack_Axe);

	/** Player Tags */
	RPG_API UE_DECLARE_GAMEPLAY_TAG_EXTERN(Player_Ability_Equip_Axe);
	RPG_API UE_DECLARE_GAMEPLAY_TAG_EXTERN(Player_Ability_Unequip_Axe);

	RPG_API UE_DECLARE_GAMEPLAY_TAG_EXTERN(Player_Ability_Attack_Light_Axe);
	RPG_API UE_DECLARE_GAMEPLAY_TAG_EXTERN(Player_Ability_Attack_Heavy_Axe);

	RPG_API UE_DECLARE_GAMEPLAY_TAG_EXTERN(Player_Weapon_Axe);

	RPG_API UE_DECLARE_GAMEPLAY_TAG_EXTERN(Player_Event_Equip_Axe);
	RPG_API UE_DECLARE_GAMEPLAY_TAG_EXTERN(Player_Event_Unequip_Axe);

	RPG_API UE_DECLARE_GAMEPLAY_TAG_EXTERN(Player_Status_JumpToFinisher);

	/** Enemy Tags */
	RPG_API UE_DECLARE_GAMEPLAY_TAG_EXTERN(Enemy_Weapon); // Enemy 무기 태그 추가
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt; &lt;span&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;RPGGameplayTags.CPP&lt;/span&gt;&lt;/b&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1746699221090&quot; class=&quot;cpp&quot; data-ke-language=&quot;cpp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;#include &quot;RPGGameplayTags.h&quot;

namespace RPGGameplayTags
{
	/** Input Tags */
	UE_DEFINE_GAMEPLAY_TAG(InputTag_Move, &quot;InputTag.Move&quot;);
	UE_DEFINE_GAMEPLAY_TAG(InputTag_Look, &quot;InputTag.Look&quot;);
	UE_DEFINE_GAMEPLAY_TAG(InputTag_EquipAxe, &quot;InputTag.EquipAxe&quot;);
	UE_DEFINE_GAMEPLAY_TAG(InputTag_UnequipAxe, &quot;InputTag.UnequipAxe&quot;);
	UE_DEFINE_GAMEPLAY_TAG(InputTag_LightAttack_Axe, &quot;InputTag.LightAttack.Axe&quot;);
	UE_DEFINE_GAMEPLAY_TAG(InputTag_HeavyAttack_Axe, &quot;InputTag.HeavyAttack.Axe&quot;);

	/** Player Tags */
	UE_DEFINE_GAMEPLAY_TAG(Player_Ability_Equip_Axe, &quot;Player.Ability.Equip.Axe&quot;);
	UE_DEFINE_GAMEPLAY_TAG(Player_Ability_Unequip_Axe, &quot;Player.Ability.Unequip.Axe&quot;);

	UE_DEFINE_GAMEPLAY_TAG(Player_Ability_Attack_Light_Axe, &quot;Player.Ability.Attack.Light.Axe&quot;);
	UE_DEFINE_GAMEPLAY_TAG(Player_Ability_Attack_Heavy_Axe, &quot;Player.Ability.Attack.Heavy.Axe&quot;);

	UE_DEFINE_GAMEPLAY_TAG(Player_Weapon_Axe, &quot;Player.Weapon.Axe&quot;);

	UE_DEFINE_GAMEPLAY_TAG(Player_Event_Equip_Axe, &quot;Player.Event.Equip.Axe&quot;);
	UE_DEFINE_GAMEPLAY_TAG(Player_Event_Unequip_Axe, &quot;Player.Event.Unequip.Axe&quot;);

	UE_DEFINE_GAMEPLAY_TAG(Player_Status_JumpToFinisher, &quot;Player.Status.JumpToFinisher&quot;);

	/** Enemy Tags */
	UE_DEFINE_GAMEPLAY_TAG(Enemy_Weapon, &quot;Enemy.Weapon&quot;); // Enemy 무기 태그 추가

}&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;✔️ Enemy 무기 클래스 생성&lt;/b&gt;&lt;/h4&gt;
&lt;p&gt;&lt;figure class=&quot;imagegridblock&quot;&gt;
  &lt;div class=&quot;image-container&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/vVTqR/btsNPHE6kFD/V9zQnKf9vE3i1dStUZqGZk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/vVTqR/btsNPHE6kFD/V9zQnKf9vE3i1dStUZqGZk/img.png&quot; data-origin-width=&quot;763&quot; data-origin-height=&quot;672&quot; data-is-animation=&quot;false&quot; style=&quot;width: 24.6417%; margin-right: 10px;&quot; data-widthpercent=&quot;25.23&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/vVTqR/btsNPHE6kFD/V9zQnKf9vE3i1dStUZqGZk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FvVTqR%2FbtsNPHE6kFD%2FV9zQnKf9vE3i1dStUZqGZk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;763&quot; height=&quot;672&quot;/&gt;&lt;/span&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bJ8zCz/btsNPWoRXPJ/5Xo5Pka4SCt2yHcrUTFfl1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bJ8zCz/btsNPWoRXPJ/5Xo5Pka4SCt2yHcrUTFfl1/img.png&quot; data-origin-width=&quot;364&quot; data-origin-height=&quot;342&quot; data-is-animation=&quot;false&quot; style=&quot;width: 23.0988%; margin-right: 10px;&quot; data-widthpercent=&quot;23.65&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bJ8zCz/btsNPWoRXPJ/5Xo5Pka4SCt2yHcrUTFfl1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbJ8zCz%2FbtsNPWoRXPJ%2F5Xo5Pka4SCt2yHcrUTFfl1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;364&quot; height=&quot;342&quot;/&gt;&lt;/span&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bqtW30/btsNPGTHRMA/CnSMnw3I8zduq0rekqGPdK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bqtW30/btsNPGTHRMA/CnSMnw3I8zduq0rekqGPdK/img.png&quot; data-origin-width=&quot;2547&quot; data-origin-height=&quot;1107&quot; data-is-animation=&quot;false&quot; style=&quot;width: 49.9339%;&quot; data-widthpercent=&quot;51.12&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bqtW30/btsNPGTHRMA/CnSMnw3I8zduq0rekqGPdK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbqtW30%2FbtsNPGTHRMA%2FCnSMnw3I8zduq0rekqGPdK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;2547&quot; height=&quot;1107&quot;/&gt;&lt;/span&gt;&lt;/div&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;&quot;RPGWeaponBase&quot;를 상속 받아 &quot;BP_Enemy_Weapon_Base&quot; 클래스를 생성한 뒤, 이를 상속 받는 가디언 전용 무기 클래스를 생성한다.&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;✔️ Skeletal Mesh에서 Weapon Socket 생성&lt;/b&gt;&lt;/h4&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;2547&quot; data-origin-height=&quot;1368&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/nvDjQ/btsNQYMAWVK/zbRdqk8hIQJMzPoFvihe8k/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/nvDjQ/btsNQYMAWVK/zbRdqk8hIQJMzPoFvihe8k/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/nvDjQ/btsNQYMAWVK/zbRdqk8hIQJMzPoFvihe8k/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FnvDjQ%2FbtsNQYMAWVK%2FzbRdqk8hIQJMzPoFvihe8k%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;2547&quot; height=&quot;1368&quot; data-origin-width=&quot;2547&quot; data-origin-height=&quot;1368&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;✔️ 무기 스폰 Ability 생성 및 데이터 할당&lt;/b&gt;&lt;/h4&gt;
&lt;p&gt;&lt;figure class=&quot;imagegridblock&quot;&gt;
  &lt;div class=&quot;image-container&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bDvI6W/btsNPHyicTk/bfFWplf73AHKedbhI8uPP1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bDvI6W/btsNPHyicTk/bfFWplf73AHKedbhI8uPP1/img.png&quot; width=&quot;382&quot; height=&quot;304&quot; data-is-animation=&quot;false&quot; data-origin-height=&quot;400&quot; data-origin-width=&quot;502&quot; style=&quot;width: 38.9476%; margin-right: 10px;&quot; data-widthpercent=&quot;39.41&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bDvI6W/btsNPHyicTk/bfFWplf73AHKedbhI8uPP1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbDvI6W%2FbtsNPHyicTk%2FbfFWplf73AHKedbhI8uPP1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;502&quot; height=&quot;400&quot;/&gt;&lt;/span&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/WZoqQ/btsNPiyTUXW/PCYRIo2OlbuKloNIjkVKH0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/WZoqQ/btsNPiyTUXW/PCYRIo2OlbuKloNIjkVKH0/img.png&quot; data-origin-width=&quot;2557&quot; data-origin-height=&quot;1325&quot; data-is-animation=&quot;false&quot; style=&quot;width: 59.8896%;&quot; data-widthpercent=&quot;60.59&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/WZoqQ/btsNPiyTUXW/PCYRIo2OlbuKloNIjkVKH0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FWZoqQ%2FbtsNPiyTUXW%2FPCYRIo2OlbuKloNIjkVKH0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;2557&quot; height=&quot;1325&quot;/&gt;&lt;/span&gt;&lt;/div&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;이전에 캐릭터 스폰 시, 무기를 스폰해 지정한 Mesh Socket에 부착하는 GameplayAbility를 만들었었다.&lt;br /&gt;이를 상속받는 &quot;GA_Guardian_SpawnWeapon&quot; 어빌리티를 생성한다.&lt;br /&gt;앞서 만들었던 무기 클래스, Socket Name, Weapon Tag, 장착 무기로 등록(true) 설정을 진행한다.&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;✔️ 캐릭터가 가지고 있는 StartUpData에 무기 스폰 Ability 등록&lt;/b&gt;&lt;/h4&gt;
&lt;p&gt;&lt;figure class=&quot;imagegridblock&quot;&gt;
  &lt;div class=&quot;image-container&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/efvKKX/btsNQgtCX23/nGDkQjQebRg40sixQ8lO80/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/efvKKX/btsNQgtCX23/nGDkQjQebRg40sixQ8lO80/img.png&quot; data-origin-width=&quot;2552&quot; data-origin-height=&quot;1332&quot; data-is-animation=&quot;false&quot; style=&quot;width: 47.9241%; margin-right: 10px;&quot; data-widthpercent=&quot;48.49&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/efvKKX/btsNQgtCX23/nGDkQjQebRg40sixQ8lO80/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FefvKKX%2FbtsNQgtCX23%2FnGDkQjQebRg40sixQ8lO80%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;2552&quot; height=&quot;1332&quot;/&gt;&lt;/span&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/mxbCe/btsNPeczRzc/F8xfeIuQ3SKInC8xKkalk0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/mxbCe/btsNPeczRzc/F8xfeIuQ3SKInC8xKkalk0/img.png&quot; data-origin-width=&quot;1437&quot; data-origin-height=&quot;706&quot; data-is-animation=&quot;false&quot; data-widthpercent=&quot;51.51&quot; data-filename=&quot;blob&quot; style=&quot;width: 50.9131%;&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/mxbCe/btsNPeczRzc/F8xfeIuQ3SKInC8xKkalk0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FmxbCe%2FbtsNPeczRzc%2FF8xfeIuQ3SKInC8xKkalk0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1437&quot; height=&quot;706&quot;/&gt;&lt;/span&gt;&lt;/div&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;캐릭터 클래스에 할당되어있는 &quot;DA_Guardian&quot;을 더블클릭 후, 방금 만든 무기 스폰 Ability를 할당해준다.&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 style=&quot;color: #c8c3bc; text-align: start;&quot; data-ke-size=&quot;size23&quot; data-darkreader-inline-color=&quot;&quot;&gt;&lt;b&gt; &lt;span&gt; Enemy 무기 스폰 Ability 동작 테스트&lt;/span&gt;&lt;/b&gt;&lt;/h3&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1976&quot; data-origin-height=&quot;797&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bMZWJ8/btsNOTe3mYI/kWygyYHNVhKUEsuo8KKk51/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bMZWJ8/btsNOTe3mYI/kWygyYHNVhKUEsuo8KKk51/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bMZWJ8/btsNOTe3mYI/kWygyYHNVhKUEsuo8KKk51/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbMZWJ8%2FbtsNOTe3mYI%2FkWygyYHNVhKUEsuo8KKk51%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1976&quot; height=&quot;797&quot; data-origin-width=&quot;1976&quot; data-origin-height=&quot;797&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;게임을 시작하면, 성공적으로 Enemy의 StartUpData가 비동기 로딩 되고, 내부 데이터에 GameAbility가 등록되며 자동으로 실행되어 무기가 정상적으로 스폰되었다.&lt;/blockquote&gt;</description>
      <category>내배캠/GAS(Gameplay Ability System)</category>
      <author>동그래님</author>
      <guid isPermaLink="true">https://dong-grae.tistory.com/232</guid>
      <comments>https://dong-grae.tistory.com/232#entry232comment</comments>
      <pubDate>Thu, 8 May 2025 16:17:54 +0900</pubDate>
    </item>
    <item>
      <title>GAS_09_무기 공격 애니메이션 구현</title>
      <link>https://dong-grae.tistory.com/231</link>
      <description>&lt;h2 style=&quot;color: #e8e6e3; text-align: start;&quot; data-ke-size=&quot;size26&quot; data-darkreader-inline-color=&quot;&quot;&gt;&lt;b&gt;  Light Attack 콤보 구현&lt;/b&gt;&lt;/h2&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;도끼 장착시 강공격/약공격 어빌리티를 추가하여 콤보 시스템 구현을 준비한다.&lt;br /&gt;&lt;br /&gt;1. InputTag / AbilityTag 설정&lt;br /&gt;2. GA_LightAttack 능력 생성 및 Tag Setting&lt;br /&gt;3. InputAction 생성하고 IMC_Axe에 바인딩&lt;br /&gt;4. DA_InputConfig에 InputAction과 Tag 바인딩&lt;br /&gt;5. 무기 클래스에 GA와 InputTag 바인딩&lt;br /&gt;6. 무기 장착 / 해제 Ability에 Attack Tag 블로킹 처리&lt;/blockquote&gt;
&lt;h3 style=&quot;color: #e8e6e3; text-align: start;&quot; data-ke-size=&quot;size23&quot; data-darkreader-inline-color=&quot;&quot;&gt;&lt;b&gt;✅ Light Attack_Input 및 Ability 세팅&lt;/b&gt;&lt;/h3&gt;
&lt;h4 style=&quot;color: #e8e6e3; text-align: start;&quot; data-ke-size=&quot;size20&quot; data-darkreader-inline-color=&quot;&quot;&gt; &lt;span&gt;&amp;nbsp;&lt;b&gt;RPGGameplayTags.h&lt;/b&gt;&lt;/span&gt;&lt;/h4&gt;
&lt;pre id=&quot;code_1746618269578&quot; class=&quot;cpp&quot; style=&quot;background-color: #1c1e1f; color: #c1bcb4; text-align: start;&quot; data-ke-type=&quot;codeblock&quot; data-ke-language=&quot;cpp&quot; data-darkreader-inline-bgcolor=&quot;&quot; data-darkreader-inline-color=&quot;&quot;&gt;&lt;code&gt;#pragma once

#include &quot;NativeGameplayTags.h&quot;

namespace RPGGameplayTags
{
	/** Input Tags */
	RPG_API UE_DECLARE_GAMEPLAY_TAG_EXTERN(InputTag_Move);
	RPG_API UE_DECLARE_GAMEPLAY_TAG_EXTERN(InputTag_Look);
	RPG_API UE_DECLARE_GAMEPLAY_TAG_EXTERN(InputTag_EquipAxe);
	RPG_API UE_DECLARE_GAMEPLAY_TAG_EXTERN(InputTag_UnequipAxe);
	RPG_API UE_DECLARE_GAMEPLAY_TAG_EXTERN(InputTag_LightAttack_Axe); // 공격 Input 추가
	RPG_API UE_DECLARE_GAMEPLAY_TAG_EXTERN(InputTag_HeavyAttack_Axe); // 공격 Input 추가

	/** Player Tags */
	RPG_API UE_DECLARE_GAMEPLAY_TAG_EXTERN(Player_Ability_Equip_Axe);
	RPG_API UE_DECLARE_GAMEPLAY_TAG_EXTERN(Player_Ability_Unequip_Axe);

	RPG_API UE_DECLARE_GAMEPLAY_TAG_EXTERN(Player_Ability_Attack_Light_Axe); // 공격 Ability 추가
	RPG_API UE_DECLARE_GAMEPLAY_TAG_EXTERN(Player_Ability_Attack_Heavy_Axe); // 공격 Ability 추가

	RPG_API UE_DECLARE_GAMEPLAY_TAG_EXTERN(Player_Weapon_Axe);

	RPG_API UE_DECLARE_GAMEPLAY_TAG_EXTERN(Player_Event_Equip_Axe);
	RPG_API UE_DECLARE_GAMEPLAY_TAG_EXTERN(Player_Event_Unequip_Axe);
}&lt;/code&gt;&lt;/pre&gt;
&lt;h4 style=&quot;color: #e8e6e3; text-align: start;&quot; data-darkreader-inline-color=&quot;&quot; data-ke-size=&quot;size20&quot;&gt; &lt;span&gt;&amp;nbsp;&lt;b&gt;RPGGameplayTags.cpp&lt;/b&gt;&lt;/span&gt;&lt;/h4&gt;
&lt;pre id=&quot;code_1746618272354&quot; class=&quot;cpp&quot; style=&quot;background-color: #1c1e1f; color: #c1bcb4; text-align: start;&quot; data-ke-type=&quot;codeblock&quot; data-ke-language=&quot;cpp&quot; data-darkreader-inline-bgcolor=&quot;&quot; data-darkreader-inline-color=&quot;&quot;&gt;&lt;code&gt;#include &quot;RPGGameplayTags.h&quot;

namespace RPGGameplayTags
{
	/** Input Tags */
	UE_DEFINE_GAMEPLAY_TAG(InputTag_Move, &quot;InputTag.Move&quot;);
	UE_DEFINE_GAMEPLAY_TAG(InputTag_Look, &quot;InputTag.Look&quot;);
	UE_DEFINE_GAMEPLAY_TAG(InputTag_EquipAxe, &quot;InputTag.EquipAxe&quot;);
	UE_DEFINE_GAMEPLAY_TAG(InputTag_UnequipAxe, &quot;InputTag.UnequipAxe&quot;);
	UE_DEFINE_GAMEPLAY_TAG(InputTag_LightAttack_Axe, &quot;InputTag.LightAttack.Axe&quot;); // 공격 Input 추가
	UE_DEFINE_GAMEPLAY_TAG(InputTag_HeavyAttack_Axe, &quot;InputTag.HeavyAttack.Axe&quot;); // 공격 Input 추가

	/** Player Tags */
	UE_DEFINE_GAMEPLAY_TAG(Player_Ability_Equip_Axe, &quot;Player.Ability.Equip.Axe&quot;);
	UE_DEFINE_GAMEPLAY_TAG(Player_Ability_Unequip_Axe, &quot;Player.Ability.Unequip.Axe&quot;);

	UE_DEFINE_GAMEPLAY_TAG(Player_Ability_Attack_Light_Axe, &quot;Player.Ability.Attack.Light.Axe&quot;); // 공격 Ability 추가
	UE_DEFINE_GAMEPLAY_TAG(Player_Ability_Attack_Heavy_Axe, &quot;Player.Ability.Attack.Heavy.Axe&quot;); // 공격 Ability 추가

	UE_DEFINE_GAMEPLAY_TAG(Player_Weapon_Axe, &quot;Player.Weapon.Axe&quot;);

	UE_DEFINE_GAMEPLAY_TAG(Player_Event_Equip_Axe, &quot;Player.Event.Equip.Axe&quot;);
	UE_DEFINE_GAMEPLAY_TAG(Player_Event_Unequip_Axe, &quot;Player.Event.Unequip.Axe&quot;);
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imagegridblock&quot;&gt;
  &lt;div class=&quot;image-container&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/ckrjnN/btsNN7wm6Fn/eO5BPNq5lqnlFdtSp3LX2K/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/ckrjnN/btsNN7wm6Fn/eO5BPNq5lqnlFdtSp3LX2K/img.png&quot; data-origin-width=&quot;874&quot; data-origin-height=&quot;1117&quot; data-is-animation=&quot;false&quot; style=&quot;width: 29.8913%; margin-right: 10px;&quot; data-widthpercent=&quot;30.6&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/ckrjnN/btsNN7wm6Fn/eO5BPNq5lqnlFdtSp3LX2K/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FckrjnN%2FbtsNN7wm6Fn%2FeO5BPNq5lqnlFdtSp3LX2K%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;874&quot; height=&quot;1117&quot;/&gt;&lt;/span&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cqgtFf/btsNNjK9OA7/YJCaithVk4uu9BgaTFtFFK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cqgtFf/btsNNjK9OA7/YJCaithVk4uu9BgaTFtFFK/img.png&quot; data-origin-width=&quot;503&quot; data-origin-height=&quot;922&quot; data-is-animation=&quot;false&quot; style=&quot;width: 20.8412%; margin-right: 10px;&quot; data-widthpercent=&quot;21.34&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cqgtFf/btsNNjK9OA7/YJCaithVk4uu9BgaTFtFFK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcqgtFf%2FbtsNNjK9OA7%2FYJCaithVk4uu9BgaTFtFFK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;503&quot; height=&quot;922&quot;/&gt;&lt;/span&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/b039cB/btsNMbtLzpO/hCNB9LJvQV7O3hp6CFhw21/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/b039cB/btsNMbtLzpO/hCNB9LJvQV7O3hp6CFhw21/img.png&quot; data-origin-width=&quot;333&quot; data-origin-height=&quot;271&quot; data-is-animation=&quot;false&quot; style=&quot;width: 46.9419%;&quot; data-widthpercent=&quot;48.06&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/b039cB/btsNMbtLzpO/hCNB9LJvQV7O3hp6CFhw21/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fb039cB%2FbtsNMbtLzpO%2FhCNB9LJvQV7O3hp6CFhw21%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;333&quot; height=&quot;271&quot;/&gt;&lt;/span&gt;&lt;/div&gt;
&lt;/figure&gt;
&lt;figure class=&quot;imageblock widthContent&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1960&quot; data-origin-height=&quot;772&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/rWarr/btsNN8ozXWT/RQ2J6Ujpzz4AoroGKWUrAk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/rWarr/btsNN8ozXWT/RQ2J6Ujpzz4AoroGKWUrAk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/rWarr/btsNN8ozXWT/RQ2J6Ujpzz4AoroGKWUrAk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FrWarr%2FbtsNN8ozXWT%2FRQ2J6Ujpzz4AoroGKWUrAk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1960&quot; height=&quot;772&quot; data-origin-width=&quot;1960&quot; data-origin-height=&quot;772&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;WarriorHeroGameplayAbility를 상속 받는 &quot;GA_Hero_LightAttackMaster&quot; 이름으로 약공격 Ability 부모클래스를 만들고, 이를 상속 받는 &quot;GA_Hero_LightAttack_Axe&quot; 클래스를 생성해주었다.&lt;br /&gt;LightAttackMaster에 Tag Setting 진행.&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imagegridblock&quot;&gt;
  &lt;div class=&quot;image-container&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/J0cab/btsNMyPyGRu/ec91kRykKn83MJtvUvsDMK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/J0cab/btsNMyPyGRu/ec91kRykKn83MJtvUvsDMK/img.png&quot; data-origin-width=&quot;282&quot; data-origin-height=&quot;230&quot; data-is-animation=&quot;false&quot; style=&quot;width: 33.2973%; margin-right: 10px;&quot; data-widthpercent=&quot;33.69&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/J0cab/btsNMyPyGRu/ec91kRykKn83MJtvUvsDMK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FJ0cab%2FbtsNMyPyGRu%2Fec91kRykKn83MJtvUvsDMK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;282&quot; height=&quot;230&quot;/&gt;&lt;/span&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/rWXbQ/btsNNfII5QS/kN9zTBKTjs5N5aTvn7hKb1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/rWXbQ/btsNNfII5QS/kN9zTBKTjs5N5aTvn7hKb1/img.png&quot; data-is-animation=&quot;false&quot; data-origin-height=&quot;525&quot; data-origin-width=&quot;1267&quot; data-widthpercent=&quot;66.31&quot; style=&quot;width: 65.5399%;&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/rWXbQ/btsNNfII5QS/kN9zTBKTjs5N5aTvn7hKb1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FrWXbQ%2FbtsNNfII5QS%2FkN9zTBKTjs5N5aTvn7hKb1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1267&quot; height=&quot;525&quot;/&gt;&lt;/span&gt;&lt;/div&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;InputAction_LightAttackAxe를 생성하고, Axe 무기 전용 IMC에 바인딩&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1398&quot; data-origin-height=&quot;1097&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cgivFI/btsNM9hFElg/rGdKFJkSH16jRgvjkFHGL1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cgivFI/btsNM9hFElg/rGdKFJkSH16jRgvjkFHGL1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cgivFI/btsNM9hFElg/rGdKFJkSH16jRgvjkFHGL1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcgivFI%2FbtsNM9hFElg%2FrGdKFJkSH16jRgvjkFHGL1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1398&quot; height=&quot;1097&quot; data-origin-width=&quot;1398&quot; data-origin-height=&quot;1097&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;InputConfig에 &quot;Light Attack&quot; 관련 InputTag - InputAction 바인딩&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1151&quot; data-origin-height=&quot;604&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cFxPds/btsNNP3RSfG/hlq0IkPfbmYibBnvNB4SY0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cFxPds/btsNNP3RSfG/hlq0IkPfbmYibBnvNB4SY0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cFxPds/btsNNP3RSfG/hlq0IkPfbmYibBnvNB4SY0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcFxPds%2FbtsNNP3RSfG%2Fhlq0IkPfbmYibBnvNB4SY0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1151&quot; height=&quot;604&quot; data-origin-width=&quot;1151&quot; data-origin-height=&quot;604&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;무기 클래스에 InputTag와 GA 바인딩&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imagegridblock&quot;&gt;
  &lt;div class=&quot;image-container&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cSwhJx/btsNNielsiL/yp2kQAOrjOuIMeMGdGQoAK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cSwhJx/btsNNielsiL/yp2kQAOrjOuIMeMGdGQoAK/img.png&quot; data-origin-width=&quot;588&quot; data-origin-height=&quot;465&quot; data-is-animation=&quot;false&quot; style=&quot;width: 49.3851%; margin-right: 10px;&quot; data-widthpercent=&quot;49.97&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cSwhJx/btsNNielsiL/yp2kQAOrjOuIMeMGdGQoAK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcSwhJx%2FbtsNNielsiL%2Fyp2kQAOrjOuIMeMGdGQoAK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;588&quot; height=&quot;465&quot;/&gt;&lt;/span&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bbFrlW/btsNMybaHZq/KPYngckiXECIIHONVAlRIk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bbFrlW/btsNMybaHZq/KPYngckiXECIIHONVAlRIk/img.png&quot; data-origin-width=&quot;585&quot; data-origin-height=&quot;462&quot; data-is-animation=&quot;false&quot; style=&quot;width: 49.4521%;&quot; data-widthpercent=&quot;50.03&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bbFrlW/btsNMybaHZq/KPYngckiXECIIHONVAlRIk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbbFrlW%2FbtsNMybaHZq%2FKPYngckiXECIIHONVAlRIk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;585&quot; height=&quot;462&quot;/&gt;&lt;/span&gt;&lt;/div&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;&quot;GA_Hero_EquipAxe&quot;, &quot;GA_Hero_UnequipAxe&quot; Ability 클래스에서 장비 착용 및 장비 해제 Ability가 활성화 되는 동안 공격을 하지 못하도록 Block 처리 해주었다.&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 style=&quot;color: #e8e6e3; text-align: start;&quot; data-darkreader-inline-color=&quot;&quot; data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;✅ Combo Attack 로직&lt;/b&gt;&lt;/h3&gt;
&lt;h4 style=&quot;color: #e8e6e3; text-align: start;&quot; data-ke-size=&quot;size20&quot; data-darkreader-inline-color=&quot;&quot;&gt;&lt;b&gt;✔️ Instancing Policy&lt;/b&gt;&lt;/h4&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;blob&quot; data-origin-width=&quot;723&quot; data-origin-height=&quot;461&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/LY2bb/btsNNeiSlSr/lZh87Onx7ZopNzUfEeyeQk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/LY2bb/btsNNeiSlSr/lZh87Onx7ZopNzUfEeyeQk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/LY2bb/btsNNeiSlSr/lZh87Onx7ZopNzUfEeyeQk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FLY2bb%2FbtsNNeiSlSr%2FlZh87Onx7ZopNzUfEeyeQk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;723&quot; height=&quot;461&quot; data-filename=&quot;blob&quot; data-origin-width=&quot;723&quot; data-origin-height=&quot;461&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;본격적으로 콤보 로직을 구현하기&amp;nbsp; GameAbility의 Instancing Policy(인스턴스화 정책)에 대해 알고 넘어가야 한다.&lt;br /&gt;콤보 시스템에서 애니메이션을 독립적으로 만들려면 GA에 Instancing Policy 설정을 해줘야한다.&lt;/blockquote&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;Instanced Per Execution:&lt;/b&gt;
&lt;ul style=&quot;list-style-type: circle;&quot; data-ke-list-type=&quot;circle&quot;&gt;
&lt;li&gt;어빌리티 실행 시, 항상 새 인스턴스 생성&lt;/li&gt;
&lt;li&gt;변수는 항상 기본값으로 초기화됨&lt;/li&gt;
&lt;li&gt;재사용 안 함 &amp;rarr; 메모리/성능 손해&lt;/li&gt;
&lt;li&gt;Fire / Dash / 짧은 효과 등 매번 초기화가 필요한 Ability에 적합&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;b&gt;Instanced Per Actor:&lt;/b&gt;
&lt;ul style=&quot;list-style-type: circle;&quot; data-ke-list-type=&quot;circle&quot;&gt;
&lt;li&gt;어빌리티 인스턴스를 처음 1회만 생성&lt;/li&gt;
&lt;li&gt;이후 계속 재사용&lt;/li&gt;
&lt;li&gt;내부 상태 저장 가능&lt;/li&gt;
&lt;li&gt;Combo / Charge / 지속 효과 등 상태 저장이 필요한 Ability에 적합&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;b&gt;Non Instanced:&lt;/b&gt;
&lt;ul style=&quot;list-style-type: circle;&quot; data-ke-list-type=&quot;circle&quot;&gt;
&lt;li&gt;인스턴스 생성 X&lt;/li&gt;
&lt;li&gt;로직은 전부 C++에서만 구현 가능&lt;/li&gt;
&lt;li&gt;Blueprint에서는 변수 저장 불가&lt;/li&gt;
&lt;li&gt;성능이 제일 좋지만 유연성은 적음&lt;/li&gt;
&lt;li&gt;단순 / 빠름/ 최적화 우선이며 Blueprint 사용하지 않아도 될 때 적합&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;546&quot; data-origin-height=&quot;101&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bmTRgu/btsNOiR1KPG/gv1LgdjlCxqL0uhVfVp5KK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bmTRgu/btsNOiR1KPG/gv1LgdjlCxqL0uhVfVp5KK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bmTRgu/btsNOiR1KPG/gv1LgdjlCxqL0uhVfVp5KK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbmTRgu%2FbtsNOiR1KPG%2Fgv1LgdjlCxqL0uhVfVp5KK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;546&quot; height=&quot;101&quot; data-origin-width=&quot;546&quot; data-origin-height=&quot;101&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;현재 콤보 어택을 구현하고 있는 LightAttack 부모 클래스에 Instancing Policy를 &lt;b&gt;&quot;Instanced Per Actor&quot;&lt;/b&gt;로 설정 해준다.&lt;br /&gt;이유는 내부에 Combo Attack Count라는 int 변수를 두고 공격을 실행 했을 때마다 Count 값을 증가시키고 현재 Count 값에 맞는 Anim Montage를 재생시켜야하기 때문이다.&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imagegridblock&quot;&gt;
  &lt;div class=&quot;image-container&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/ba9oKO/btsNOaNsu5S/CDankhAI0VWXD6phm67uP0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/ba9oKO/btsNOaNsu5S/CDankhAI0VWXD6phm67uP0/img.png&quot; data-origin-width=&quot;2242&quot; data-origin-height=&quot;827&quot; data-is-animation=&quot;false&quot; style=&quot;width: 77.1485%; margin-right: 10px;&quot; data-widthpercent=&quot;78.06&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/ba9oKO/btsNOaNsu5S/CDankhAI0VWXD6phm67uP0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fba9oKO%2FbtsNOaNsu5S%2FCDankhAI0VWXD6phm67uP0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;2242&quot; height=&quot;827&quot;/&gt;&lt;/span&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/d2DfO2/btsNOoxXHBO/uJNAMwul64K2Kes0LVmns1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/d2DfO2/btsNOoxXHBO/uJNAMwul64K2Kes0LVmns1/img.png&quot; data-origin-width=&quot;455&quot; data-origin-height=&quot;597&quot; data-is-animation=&quot;false&quot; style=&quot;width: 21.6887%;&quot; data-widthpercent=&quot;21.94&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/d2DfO2/btsNOoxXHBO/uJNAMwul64K2Kes0LVmns1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fd2DfO2%2FbtsNOoxXHBO%2FuJNAMwul64K2Kes0LVmns1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;455&quot; height=&quot;597&quot;/&gt;&lt;/span&gt;&lt;/div&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;&quot;GA_Hero_LightAttackMontage&quot;에 콤보 어택 로직을 다음과 같이 작성한다.&lt;br /&gt;TMap을 사용해서 각 인덱스마다 Anim Montage를 저장해놓고 현재 Combo Count에 맞게 애니메이션을 재생시켜준다.&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;2553&quot; data-origin-height=&quot;1327&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/dIYX0g/btsNOr2xxUd/cnaMSGTZ8UryhOfiPz0OM0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/dIYX0g/btsNOr2xxUd/cnaMSGTZ8UryhOfiPz0OM0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/dIYX0g/btsNOr2xxUd/cnaMSGTZ8UryhOfiPz0OM0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FdIYX0g%2FbtsNOr2xxUd%2FcnaMSGTZ8UryhOfiPz0OM0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;2553&quot; height=&quot;1327&quot; data-origin-width=&quot;2553&quot; data-origin-height=&quot;1327&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;Combo 로직을 구현한 &quot;GA_Hero_LightAttackMater&quot; 클래스를 상속 받은 &quot;GA_Hero_LightAttack_Axe&quot;클래스에서 4개의 애니메이션을 저장할 준비를 한다.&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;2559&quot; data-origin-height=&quot;1342&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/du1avX/btsNNPJFIGX/Ei2uotzvXFmlMHe8HE2Q61/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/du1avX/btsNNPJFIGX/Ei2uotzvXFmlMHe8HE2Q61/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/du1avX/btsNNPJFIGX/Ei2uotzvXFmlMHe8HE2Q61/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fdu1avX%2FbtsNNPJFIGX%2FEi2uotzvXFmlMHe8HE2Q61%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;2559&quot; height=&quot;1342&quot; data-origin-width=&quot;2559&quot; data-origin-height=&quot;1342&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;이전에 장비를 장착/탈착 했던 애니메이션들은 이동하면서 해당 동작이 가능했어야 해서 UpperBody 슬롯에서만 재생되도록 구현하다보니 Root Motion을 비활성화 했어야 했다.&lt;br /&gt;하지만 현재는 하체까지 공격하는 애니메이션이 적용되도록 할 것이기 때문에, 4가지 애니메이션 모두 Root Motion 활성화를 체크하도록 한다.&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;2534&quot; data-origin-height=&quot;1371&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/JiL1d/btsNOSZSygA/nkYob33hW6dNwQObo0Tm7K/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/JiL1d/btsNOSZSygA/nkYob33hW6dNwQObo0Tm7K/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/JiL1d/btsNOSZSygA/nkYob33hW6dNwQObo0Tm7K/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FJiL1d%2FbtsNOSZSygA%2FnkYob33hW6dNwQObo0Tm7K%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;2534&quot; height=&quot;1371&quot; data-origin-width=&quot;2534&quot; data-origin-height=&quot;1371&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;4개의 애니메이션 몽타주를 생성하고, 콤보 어택에 사용되기에 재생속도가 느려서 Rate Scale을 2.0으로 빠르게 설정해주었고, &quot;FullBody&quot;slot을 생성하여 적용시켜주었다.&amp;nbsp;&lt;br /&gt;1~3번 애니메이션은 2.0의 속도, 마지막 4번 애니메이션은 1.5로 앞 동작보다는 느리게 해주었다.&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;700&quot; data-origin-height=&quot;562&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/I3Znp/btsNMTlG7r9/3SMmAEExkZZkpRFlkCWc7k/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/I3Znp/btsNMTlG7r9/3SMmAEExkZZkpRFlkCWc7k/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/I3Znp/btsNMTlG7r9/3SMmAEExkZZkpRFlkCWc7k/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FI3Znp%2FbtsNMTlG7r9%2F3SMmAEExkZZkpRFlkCWc7k%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;700&quot; height=&quot;562&quot; data-origin-width=&quot;700&quot; data-origin-height=&quot;562&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;다시 &quot;GA_Hero_LightAttack&quot;에 돌아와 Map컨테이너에 몽타주들을 바인딩한다.&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1455&quot; data-origin-height=&quot;997&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bTomTq/btsNOg00YD4/gH2kisuJKopJ123td5Zob1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bTomTq/btsNOg00YD4/gH2kisuJKopJ123td5Zob1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bTomTq/btsNOg00YD4/gH2kisuJKopJ123td5Zob1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbTomTq%2FbtsNOg00YD4%2FgH2kisuJKopJ123td5Zob1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1455&quot; height=&quot;997&quot; data-origin-width=&quot;1455&quot; data-origin-height=&quot;997&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;메인 ABP클래스인 &quot;ABP_Hero&quot; AnimGraph에서 방금 생성했던 FullBody Slot을 최종 Output Pose 핀 앞에 연결해준다.&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 style=&quot;color: #c8c3bc; text-align: start;&quot; data-ke-size=&quot;size23&quot; data-darkreader-inline-color=&quot;&quot;&gt;&lt;b&gt; &lt;span&gt; Light 콤보 어택 테스트&lt;/span&gt;&lt;/b&gt;&lt;/h3&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock widthContent&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;2025-05-07 23-15-20.mp4_20250507_231644.gif&quot; data-origin-width=&quot;854&quot; data-origin-height=&quot;480&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/oRlwa/btsNNFgbLV8/nRCb3nKC14VGGWc4jDAjK1/img.gif&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/oRlwa/btsNNFgbLV8/nRCb3nKC14VGGWc4jDAjK1/img.gif&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/oRlwa/btsNNFgbLV8/nRCb3nKC14VGGWc4jDAjK1/img.gif&quot; srcset=&quot;https://blog.kakaocdn.net/dn/oRlwa/btsNNFgbLV8/nRCb3nKC14VGGWc4jDAjK1/img.gif&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;854&quot; height=&quot;480&quot; data-filename=&quot;2025-05-07 23-15-20.mp4_20250507_231644.gif&quot; data-origin-width=&quot;854&quot; data-origin-height=&quot;480&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;마우스 클릭을 천천히 하면 Montage_1만 나오는 모습을 볼 수 있고, 연속적으로 빠르게 누르면 Montage_1~4번까지 정상적으로 잘 나오는 모습을 확인할 수 있었다.&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 style=&quot;color: #e8e6e3; text-align: start;&quot; data-darkreader-inline-color=&quot;&quot; data-ke-size=&quot;size26&quot;&gt;&lt;b&gt; &amp;nbsp; Heavy Attack 구현&lt;/b&gt;&lt;/h2&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;Light Attack을 구현하면서 Heavy Attack에 대한 GameplayTag도 선언하였었다.&lt;br /&gt;앞서 Light Attack Ability를 인풋을 등록하고 캐릭터에 능력을 부여하는 것까지의 과정을 똑같이 Heavy Attack Ability 빠르게 적용시킨다.&lt;br /&gt;&lt;br /&gt;1. GameplayAbility 생성 후 Tag Setting&lt;br /&gt;2. InputAction 생성 후 IMC에 바인딩&lt;br /&gt;3. DA_InputConfig에 InputTag - InputAction 바인딩&lt;br /&gt;4. Weapon Class에 InputTag - GA 바인딩&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 style=&quot;color: #e8e6e3; text-align: start;&quot; data-ke-size=&quot;size23&quot; data-darkreader-inline-color=&quot;&quot;&gt;&lt;b&gt;✅ Heavy Attack Ability 생성 및 등록&lt;/b&gt;&lt;/h3&gt;
&lt;p&gt;&lt;figure class=&quot;imagegridblock&quot;&gt;
  &lt;div class=&quot;image-container&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/ycyWd/btsNOUjeZ2M/Ltar1QNr9Y25XQy09q3l00/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/ycyWd/btsNOUjeZ2M/Ltar1QNr9Y25XQy09q3l00/img.png&quot; data-origin-width=&quot;339&quot; data-origin-height=&quot;271&quot; data-is-animation=&quot;false&quot; style=&quot;width: 39.6705%; margin-right: 10px;&quot; data-widthpercent=&quot;40.14&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/ycyWd/btsNOUjeZ2M/Ltar1QNr9Y25XQy09q3l00/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FycyWd%2FbtsNOUjeZ2M%2FLtar1QNr9Y25XQy09q3l00%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;339&quot; height=&quot;271&quot;/&gt;&lt;/span&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cbd3Xv/btsNNfIWeQV/RclGFWT3zYKzZDqq7HcxNK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cbd3Xv/btsNNfIWeQV/RclGFWT3zYKzZDqq7HcxNK/img.png&quot; data-origin-width=&quot;2556&quot; data-origin-height=&quot;1370&quot; data-is-animation=&quot;false&quot; data-widthpercent=&quot;59.86&quot; style=&quot;width: 59.1667%;&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cbd3Xv/btsNNfIWeQV/RclGFWT3zYKzZDqq7HcxNK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fcbd3Xv%2FbtsNNfIWeQV%2FRclGFWT3zYKzZDqq7HcxNK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;2556&quot; height=&quot;1370&quot;/&gt;&lt;/span&gt;&lt;/div&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;GameplayAbility 생성 후 Tag Setting&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock widthContent&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1275&quot; data-origin-height=&quot;983&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bhHxhs/btsNMyPNikO/OKJyCKwji8MOIEFKYe7voK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bhHxhs/btsNMyPNikO/OKJyCKwji8MOIEFKYe7voK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bhHxhs/btsNMyPNikO/OKJyCKwji8MOIEFKYe7voK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbhHxhs%2FbtsNMyPNikO%2FOKJyCKwji8MOIEFKYe7voK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1275&quot; height=&quot;983&quot; data-origin-width=&quot;1275&quot; data-origin-height=&quot;983&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;InputAction 생성 후 IMC에 바인딩&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock widthContent&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1397&quot; data-origin-height=&quot;950&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bOoyxr/btsNOgfMyAL/wda2bNaAZx02fPKUoS9AC0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bOoyxr/btsNOgfMyAL/wda2bNaAZx02fPKUoS9AC0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bOoyxr/btsNOgfMyAL/wda2bNaAZx02fPKUoS9AC0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbOoyxr%2FbtsNOgfMyAL%2Fwda2bNaAZx02fPKUoS9AC0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1397&quot; height=&quot;950&quot; data-origin-width=&quot;1397&quot; data-origin-height=&quot;950&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;DA_InputConfig에 InputTag - InputAction 바인딩&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock widthContent&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;2083&quot; data-origin-height=&quot;1093&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/clLaZl/btsNNUYFzcG/QXokHAVOnLgDlApdWC3xX1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/clLaZl/btsNNUYFzcG/QXokHAVOnLgDlApdWC3xX1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/clLaZl/btsNNUYFzcG/QXokHAVOnLgDlApdWC3xX1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FclLaZl%2FbtsNNUYFzcG%2FQXokHAVOnLgDlApdWC3xX1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;2083&quot; height=&quot;1093&quot; data-origin-width=&quot;2083&quot; data-origin-height=&quot;1093&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;Weapon Class에 InputTag - GA 바인딩&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 style=&quot;color: #e8e6e3; text-align: start;&quot; data-darkreader-inline-color=&quot;&quot; data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;✅ Heavy Attack Ability 기능 구현&lt;/b&gt;&lt;/h3&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;앞서 Light Attack Ability 로직도 연속적으로 공격을 입력하면 Combo Attack이 나오도록 구현하였다.&lt;br /&gt;Heavy Attack Ability도 마찬가지로 연속적으로 빠르게 입력을 하면 Combo Attack이 되도록 구현하도록 한다.&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imagegridblock&quot;&gt;
  &lt;div class=&quot;image-container&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bwVkOD/btsNOLtakXn/9aSQZfcYV3slZ2Q9J3HS7K/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bwVkOD/btsNOLtakXn/9aSQZfcYV3slZ2Q9J3HS7K/img.png&quot; data-origin-width=&quot;495&quot; data-origin-height=&quot;523&quot; data-is-animation=&quot;false&quot; style=&quot;width: 25.0736%; margin-right: 10px;&quot; data-widthpercent=&quot;25.37&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bwVkOD/btsNOLtakXn/9aSQZfcYV3slZ2Q9J3HS7K/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbwVkOD%2FbtsNOLtakXn%2F9aSQZfcYV3slZ2Q9J3HS7K%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;495&quot; height=&quot;523&quot;/&gt;&lt;/span&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/8nxAa/btsNN3uffxo/guS5DHPhbgSuwBnD1T4t7K/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/8nxAa/btsNN3uffxo/guS5DHPhbgSuwBnD1T4t7K/img.png&quot; data-origin-width=&quot;2247&quot; data-origin-height=&quot;807&quot; data-is-animation=&quot;false&quot; data-widthpercent=&quot;74.63&quot; style=&quot;width: 73.7636%;&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/8nxAa/btsNN3uffxo/guS5DHPhbgSuwBnD1T4t7K/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F8nxAa%2FbtsNN3uffxo%2FguS5DHPhbgSuwBnD1T4t7K%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;2247&quot; height=&quot;807&quot;/&gt;&lt;/span&gt;&lt;/div&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;&quot;GA_Hero_HeavyAttackMaster&quot; 부모 클래스에 해당 로직을 작성한다. 로직은 LightAttack과 동일하다.&lt;br /&gt;&lt;br /&gt;&quot;AttackMontagesMap&quot; - Integer를 Key로, AnimMonstage Object Class를 Value로 가지고 있다.&lt;br /&gt;&quot;HeavyAttackComboCount&quot; - Default Value로 1을 가지고 있고 연타하면 최대 Map의 크기 만큼 1씩 증가한다.&lt;br /&gt;&quot;ComboCountResetTimerHandle&quot; - 0.3초 이내에 추가 입력이 들어오지 않으면 ComboCount를 Reset 시켜주는데, 이때 사용한 TimerHandle 캐싱&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;699&quot; data-origin-height=&quot;1065&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/b2dVz5/btsNNjdzhdV/tpt9NNKK9nv2XHo2jfgbKk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/b2dVz5/btsNNjdzhdV/tpt9NNKK9nv2XHo2jfgbKk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/b2dVz5/btsNNjdzhdV/tpt9NNKK9nv2XHo2jfgbKk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fb2dVz5%2FbtsNNjdzhdV%2Ftpt9NNKK9nv2XHo2jfgbKk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;699&quot; height=&quot;1065&quot; data-origin-width=&quot;699&quot; data-origin-height=&quot;1065&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;Heavy Attack 부모 클래스에선 Asset Tag를 &quot;Player.Ability.Attack.Heavy&quot; 까지만 설정하고 자식 클래스에서 세부 무기까지 포함해서 &quot;Player.Ability.Attack.Heavy.Axe&quot;로 설정할 것이다.&lt;br /&gt;Combo Attack Count 변수가 계속 지속적으로 관리되어야 하기 때문에, &quot;Instanced Per Actor&quot; 정책으로 설정해주었다.&lt;br /&gt;해당 설정들은 &quot;GA_Hero_LightAttackMaster&quot; 클래스와 동일하다.&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;2533&quot; data-origin-height=&quot;1263&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bkQwMr/btsNN4moyWj/K0SbgKsfBGOod3jqBQLj7k/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bkQwMr/btsNN4moyWj/K0SbgKsfBGOod3jqBQLj7k/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bkQwMr/btsNN4moyWj/K0SbgKsfBGOod3jqBQLj7k/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbkQwMr%2FbtsNN4moyWj%2FK0SbgKsfBGOod3jqBQLj7k%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;2533&quot; height=&quot;1263&quot; data-origin-width=&quot;2533&quot; data-origin-height=&quot;1263&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;&quot;GA_Hero_HeavyAttackMaster&quot;클래스를 상속받은 Ability 클래스에서 Map 컨테이너에 해당하는 몽타주를 넣어준다.&lt;br /&gt;앞서 말했듯이 자식클래스에선 AssetTag를 Axe까지 붙혀주었고 Instancing Policy도 부모와 동일한지 확인해준다.&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 style=&quot;color: #c8c3bc; text-align: start;&quot; data-darkreader-inline-color=&quot;&quot; data-ke-size=&quot;size23&quot;&gt;&lt;b&gt; &lt;span&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;Light / Heavy 콤보 어택 테스트&lt;/span&gt;&lt;/b&gt;&lt;/h3&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock widthContent&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;2025-05-08 11-27-44.mp4_20250508_112831.gif&quot; data-origin-width=&quot;854&quot; data-origin-height=&quot;480&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/IRYI0/btsNNST1w7I/I2xVgnKZFTrMsuIE6PtXK0/img.gif&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/IRYI0/btsNNST1w7I/I2xVgnKZFTrMsuIE6PtXK0/img.gif&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/IRYI0/btsNNST1w7I/I2xVgnKZFTrMsuIE6PtXK0/img.gif&quot; srcset=&quot;https://blog.kakaocdn.net/dn/IRYI0/btsNNST1w7I/I2xVgnKZFTrMsuIE6PtXK0/img.gif&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;854&quot; height=&quot;480&quot; data-filename=&quot;2025-05-08 11-27-44.mp4_20250508_112831.gif&quot; data-origin-width=&quot;854&quot; data-origin-height=&quot;480&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;무기를 장착하고, Light Attack(1~4) 그리고 Heavy Attack(1~2) 몽타주가 정상적으로 재생되는것을 확인할 수있다.&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 style=&quot;color: #e8e6e3; text-align: start;&quot; data-ke-size=&quot;size26&quot; data-darkreader-inline-color=&quot;&quot;&gt;&lt;b&gt; &amp;nbsp; Light &amp;rarr; Heavy(마지막 공격)으로 전환하는 Combo Attack 구현&lt;/b&gt;&lt;/h2&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock widthContent&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1454&quot; data-origin-height=&quot;1020&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bGgDzo/btsNMCkqKts/8E74rsQDzNu3i2dS85dBm0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bGgDzo/btsNMCkqKts/8E74rsQDzNu3i2dS85dBm0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bGgDzo/btsNMCkqKts/8E74rsQDzNu3i2dS85dBm0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbGgDzo%2FbtsNMCkqKts%2F8E74rsQDzNu3i2dS85dBm0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1454&quot; height=&quot;1020&quot; data-origin-width=&quot;1454&quot; data-origin-height=&quot;1020&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;현재 약공격은 4단계로 이루어져있고, 강공격은 2단계로 이루어져 있다.&lt;br /&gt;약공격 3단계까지 수행한 후에 바로 강공격 2단계로 넘어갈 수 있도록 해서, 플레이어가 다른 공격 콤보 패턴을 사용할 수 있도록 해보려한다.&lt;br /&gt;&lt;br /&gt;이를 위해선 LightAttack의 ComboCnt가 3단계 이후 라면 &quot;JumpToFinisher&quot;라는 GameplayTag를 캐릭터에 부여하고,&lt;br /&gt;HeavyAttack Ability는 해당 태그가 존재한다면 현재 ComboCnt를 무시하고 즉시 HeavyAttack 2단계인 피니셔 동작을 실행할 수 있어야한다.&lt;br /&gt;이후 피니셔 동작이 끝나면 태그를 제거하고 ComboCnt를 초기화 하도록 한다.&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 style=&quot;color: #e8e6e3; text-align: start;&quot; data-ke-size=&quot;size23&quot; data-darkreader-inline-color=&quot;&quot;&gt;&lt;b&gt;✅ GAS 기반 시스템 전용 Function Library 구현&lt;/b&gt;&lt;/h3&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;GAS 시스템을 사용하다보면 ASC를 여러 클래스(Character, Ability 등)에서 가져와야 하는 경우가 있다.&lt;br /&gt;그리고 현재 구현하는 상황처럼, 특정 Ability 활성화 중에 Tag를 부여하거나 제거해야할 일이 많이 생길 것이다.&lt;br /&gt;&lt;br /&gt;이를 위해 코드 재사용성 증가와 로직 통일성, 블루프린트 로직 간결화 등의 이점을 가져가기 위해 Blueprint Function Library 클래스를 만들어 자주 사용되는 기능들을 구현할 것이다.&lt;/blockquote&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1172&quot; data-origin-height=&quot;716&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/A9mty/btsNPdwhOTE/M831dQY48ed4piwKk9yFvk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/A9mty/btsNPdwhOTE/M831dQY48ed4piwKk9yFvk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/A9mty/btsNPdwhOTE/M831dQY48ed4piwKk9yFvk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FA9mty%2FbtsNPdwhOTE%2FM831dQY48ed4piwKk9yFvk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1172&quot; height=&quot;716&quot; data-origin-width=&quot;1172&quot; data-origin-height=&quot;716&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;&quot;Blueprint Function Library&quot;를 상속 받는 C++클래스를 생성한다.&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 style=&quot;color: #e8e6e3; text-align: start;&quot; data-ke-size=&quot;size20&quot; data-darkreader-inline-color=&quot;&quot;&gt;&lt;b&gt; &lt;span&gt; RPGFunctionLibrary.h&lt;/span&gt;&lt;/b&gt;&lt;/h4&gt;
&lt;pre id=&quot;code_1746675716083&quot; class=&quot;cpp&quot; data-ke-language=&quot;cpp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;#pragma once

#include &quot;CoreMinimal.h&quot;
#include &quot;Kismet/BlueprintFunctionLibrary.h&quot;
#include &quot;GameplayTagContainer.h&quot;
#include &quot;RPGFunctionLibrary.generated.h&quot;

class URPGAbilitySystemComponent;

UENUM()
enum class ERPGConfirmType : uint8
{
	Yes,
	No
};

UCLASS()
class RPG_API URPGFunctionLibrary : public UBlueprintFunctionLibrary
{
	GENERATED_BODY()
	
public:
	// Actor로부터 ASC 반환 함수
	static URPGAbilitySystemComponent* NativeGetRPGASC_FormActor(AActor* InActor);

	// ASC가 특정 Tag를 가지고 있는지 확인
	static bool NativeDoesActorHaveTag(AActor* InActor, FGameplayTag TagToCheck);

	// Actor에 Tag 추가
	UFUNCTION(BlueprintCallable, Category = &quot;RPG|FunctionLibrary&quot;)
	static void AddGameplayTagToActorIfNone(AActor* InActor, FGameplayTag TagToAdd);

	// Actor에 Tag 제거
	UFUNCTION(BlueprintCallable, Category = &quot;RPG|FunctionLibrary&quot;)
	static void RemoveGameplayTagFromActorIfFound(AActor* InActor, FGameplayTag TagToRemove);

	// Native 함수 사용해서 BP에서 Tag 가지고 있는지 여부를 Enum으로 반환(Branch 노드 사용 줄이기 위함)
	UFUNCTION(BlueprintCallable, Category = &quot;RPG|FunctionLibrary&quot;, meta = (DisplayName = &quot;Does Actor Have Tag&quot;, ExpandEnumAsExecs = &quot;OutConfirmType&quot;))
	static void BP_DoesActorHaveTag(AActor* InActor, FGameplayTag TagToCheck, ERPGConfirmType&amp;amp; OutConfirmType);

};&lt;/code&gt;&lt;/pre&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;C++ 클래스 내부에서만 사용되는 함수 네이밍은 접두어로 Native로 구분해주었다.&lt;br /&gt;&quot;BP_DoesActorHaveTag&quot; 함수의 경우 Enum을 BP에서 분기 OutPin으로 설정하여 Branch 노드를 사용하지 않아도 되어 블루프린트 로직을 간결하게 한다.&lt;/blockquote&gt;
&lt;h4 style=&quot;color: #e8e6e3; text-align: start;&quot; data-darkreader-inline-color=&quot;&quot; data-ke-size=&quot;size20&quot;&gt;&lt;b&gt; &lt;span&gt;&lt;span&gt; &lt;/span&gt;RPGFunctionLibrary.cpp&lt;/span&gt;&lt;/b&gt;&lt;/h4&gt;
&lt;pre id=&quot;code_1746675774189&quot; class=&quot;cpp&quot; data-ke-language=&quot;cpp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;#include &quot;RPGFunctionLibrary.h&quot;
#include &quot;AbilitySystemBlueprintLibrary.h&quot;
#include &quot;AbilitySystem/RPGAbilitySystemComponent.h&quot;

URPGAbilitySystemComponent* URPGFunctionLibrary::NativeGetRPGASC_FormActor(AActor* InActor)
{
	check(InActor);
	
	return CastChecked&amp;lt;URPGAbilitySystemComponent&amp;gt;(UAbilitySystemBlueprintLibrary::GetAbilitySystemComponent(InActor));
}

bool URPGFunctionLibrary::NativeDoesActorHaveTag(AActor* InActor, FGameplayTag TagToCheck)
{
	URPGAbilitySystemComponent* ASC = NativeGetRPGASC_FormActor(InActor);

	return ASC-&amp;gt;HasMatchingGameplayTag(TagToCheck);
}

void URPGFunctionLibrary::AddGameplayTagToActorIfNone(AActor* InActor, FGameplayTag TagToAdd)
{
	URPGAbilitySystemComponent* ASC = NativeGetRPGASC_FormActor(InActor);

	if (!ASC-&amp;gt;HasMatchingGameplayTag(TagToAdd))
	{
		ASC-&amp;gt;AddLooseGameplayTag(TagToAdd);
	}
}

void URPGFunctionLibrary::RemoveGameplayTagFromActorIfFound(AActor* InActor, FGameplayTag TagToRemove)
{
	URPGAbilitySystemComponent* ASC = NativeGetRPGASC_FormActor(InActor);

	if (ASC-&amp;gt;HasMatchingGameplayTag(TagToRemove))
	{
		ASC-&amp;gt;RemoveLooseGameplayTag(TagToRemove);
	}
}

void URPGFunctionLibrary::BP_DoesActorHaveTag(AActor* InActor, FGameplayTag TagToCheck, ERPGConfirmType&amp;amp; OutConfirmType)
{
	OutConfirmType = NativeDoesActorHaveTag(InActor, TagToCheck) ? ERPGConfirmType::Yes : ERPGConfirmType::No;
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock widthContent&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1052&quot; data-origin-height=&quot;581&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/nDaPY/btsNOodZQWM/JK57KfBZRPV4W2wh9IZPu1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/nDaPY/btsNOodZQWM/JK57KfBZRPV4W2wh9IZPu1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/nDaPY/btsNOodZQWM/JK57KfBZRPV4W2wh9IZPu1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FnDaPY%2FbtsNOodZQWM%2FJK57KfBZRPV4W2wh9IZPu1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1052&quot; height=&quot;581&quot; data-origin-width=&quot;1052&quot; data-origin-height=&quot;581&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;앞서 만든 함수들을 블루프린트 전역에서 사용할 수 있게 되었다.&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 style=&quot;color: #e8e6e3; text-align: start;&quot; data-darkreader-inline-color=&quot;&quot; data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;✅ GAS 기반 시스템 전용 Function Library 구현&lt;/b&gt;&lt;/h3&gt;
&lt;h4 style=&quot;color: #e8e6e3; text-align: start;&quot; data-ke-size=&quot;size20&quot; data-darkreader-inline-color=&quot;&quot;&gt;&lt;b&gt;✔️ Gameplay Tag 추가&lt;/b&gt;&lt;/h4&gt;
&lt;div data-ke-type=&quot;moreLess&quot; data-text-more=&quot;더보기&quot; data-text-less=&quot;닫기&quot;&gt;&lt;a class=&quot;btn-toggle-moreless&quot;&gt;더보기&lt;/a&gt;
&lt;div class=&quot;moreless-content&quot;&gt;
&lt;h4 style=&quot;color: #e8e6e3; text-align: start;&quot; data-darkreader-inline-color=&quot;&quot; data-ke-size=&quot;size20&quot;&gt;&lt;b&gt; &lt;span&gt;&lt;span&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;RPGGameplayTags&lt;/span&gt;.h&lt;/span&gt;&lt;/b&gt;&lt;/h4&gt;
&lt;pre id=&quot;code_1746676592521&quot; class=&quot;cpp&quot; style=&quot;background-color: #1c1e1f; color: #c1bcb4;&quot; data-ke-type=&quot;codeblock&quot; data-ke-language=&quot;cpp&quot; data-darkreader-inline-bgcolor=&quot;&quot; data-darkreader-inline-color=&quot;&quot;&gt;&lt;code&gt;#pragma once

#include &quot;NativeGameplayTags.h&quot;

namespace RPGGameplayTags
{
	/** Input Tags */
	RPG_API UE_DECLARE_GAMEPLAY_TAG_EXTERN(InputTag_Move);
	RPG_API UE_DECLARE_GAMEPLAY_TAG_EXTERN(InputTag_Look);
	RPG_API UE_DECLARE_GAMEPLAY_TAG_EXTERN(InputTag_EquipAxe);
	RPG_API UE_DECLARE_GAMEPLAY_TAG_EXTERN(InputTag_UnequipAxe);
	RPG_API UE_DECLARE_GAMEPLAY_TAG_EXTERN(InputTag_LightAttack_Axe);
	RPG_API UE_DECLARE_GAMEPLAY_TAG_EXTERN(InputTag_HeavyAttack_Axe);

	/** Player Tags */
	RPG_API UE_DECLARE_GAMEPLAY_TAG_EXTERN(Player_Ability_Equip_Axe);
	RPG_API UE_DECLARE_GAMEPLAY_TAG_EXTERN(Player_Ability_Unequip_Axe);

	RPG_API UE_DECLARE_GAMEPLAY_TAG_EXTERN(Player_Ability_Attack_Light_Axe);
	RPG_API UE_DECLARE_GAMEPLAY_TAG_EXTERN(Player_Ability_Attack_Heavy_Axe);

	RPG_API UE_DECLARE_GAMEPLAY_TAG_EXTERN(Player_Weapon_Axe);

	RPG_API UE_DECLARE_GAMEPLAY_TAG_EXTERN(Player_Event_Equip_Axe);
	RPG_API UE_DECLARE_GAMEPLAY_TAG_EXTERN(Player_Event_Unequip_Axe);

	RPG_API UE_DECLARE_GAMEPLAY_TAG_EXTERN(Player_Status_JumpToFinisher); // 마지막 공격 전환 Tag 추가
}&lt;/code&gt;&lt;/pre&gt;
&lt;h4 style=&quot;color: #e8e6e3; text-align: start;&quot; data-ke-size=&quot;size20&quot; data-darkreader-inline-color=&quot;&quot;&gt;&lt;b&gt; &lt;span&gt;&lt;span&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;RPGGameplayTags&lt;/span&gt;.cpp&lt;/span&gt;&lt;/b&gt;&lt;/h4&gt;
&lt;pre id=&quot;code_1746676592522&quot; class=&quot;reasonml&quot; style=&quot;background-color: #1c1e1f; color: #c1bcb4;&quot; data-ke-type=&quot;codeblock&quot; data-ke-language=&quot;cpp&quot; data-darkreader-inline-bgcolor=&quot;&quot; data-darkreader-inline-color=&quot;&quot;&gt;&lt;code&gt;#include &quot;RPGGameplayTags.h&quot;

namespace RPGGameplayTags
{
	/** Input Tags */
	UE_DEFINE_GAMEPLAY_TAG(InputTag_Move, &quot;InputTag.Move&quot;);
	UE_DEFINE_GAMEPLAY_TAG(InputTag_Look, &quot;InputTag.Look&quot;);
	UE_DEFINE_GAMEPLAY_TAG(InputTag_EquipAxe, &quot;InputTag.EquipAxe&quot;);
	UE_DEFINE_GAMEPLAY_TAG(InputTag_UnequipAxe, &quot;InputTag.UnequipAxe&quot;);
	UE_DEFINE_GAMEPLAY_TAG(InputTag_LightAttack_Axe, &quot;InputTag.LightAttack.Axe&quot;);
	UE_DEFINE_GAMEPLAY_TAG(InputTag_HeavyAttack_Axe, &quot;InputTag.HeavyAttack.Axe&quot;);

	/** Player Tags */
	UE_DEFINE_GAMEPLAY_TAG(Player_Ability_Equip_Axe, &quot;Player.Ability.Equip.Axe&quot;);
	UE_DEFINE_GAMEPLAY_TAG(Player_Ability_Unequip_Axe, &quot;Player.Ability.Unequip.Axe&quot;);

	UE_DEFINE_GAMEPLAY_TAG(Player_Ability_Attack_Light_Axe, &quot;Player.Ability.Attack.Light.Axe&quot;);
	UE_DEFINE_GAMEPLAY_TAG(Player_Ability_Attack_Heavy_Axe, &quot;Player.Ability.Attack.Heavy.Axe&quot;);

	UE_DEFINE_GAMEPLAY_TAG(Player_Weapon_Axe, &quot;Player.Weapon.Axe&quot;);

	UE_DEFINE_GAMEPLAY_TAG(Player_Event_Equip_Axe, &quot;Player.Event.Equip.Axe&quot;);
	UE_DEFINE_GAMEPLAY_TAG(Player_Event_Unequip_Axe, &quot;Player.Event.Unequip.Axe&quot;);

	UE_DEFINE_GAMEPLAY_TAG(Player_Status_JumpToFinisher, &quot;Player.Status.JumpToFinisher&quot;); // 마지막 공격 전환 Tag 추가

}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;h4 style=&quot;color: #e8e6e3; text-align: start;&quot; data-darkreader-inline-color=&quot;&quot; data-ke-size=&quot;size20&quot;&gt;&lt;b&gt; &lt;span&gt;&lt;span&gt; GA_Hero_LightAttackMaster&lt;/span&gt;&lt;/span&gt;&lt;/b&gt;&lt;/h4&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock widthContent&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;2466&quot; data-origin-height=&quot;732&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/466wn/btsNONSO1s5/C0FMp10diCyhioFHFGUIl0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/466wn/btsNONSO1s5/C0FMp10diCyhioFHFGUIl0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/466wn/btsNONSO1s5/C0FMp10diCyhioFHFGUIl0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F466wn%2FbtsNONSO1s5%2FC0FMp10diCyhioFHFGUIl0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;2466&quot; height=&quot;732&quot; data-origin-width=&quot;2466&quot; data-origin-height=&quot;732&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;figure class=&quot;imagegridblock&quot;&gt;
  &lt;div class=&quot;image-container&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bEF3Ux/btsNNhHukGJ/mQeDr8ovq4qrVK82Mt97Q0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bEF3Ux/btsNNhHukGJ/mQeDr8ovq4qrVK82Mt97Q0/img.png&quot; data-origin-width=&quot;2470&quot; data-origin-height=&quot;570&quot; data-is-animation=&quot;false&quot; style=&quot;width: 64.8808%; margin-right: 10px;&quot; data-widthpercent=&quot;65.64&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bEF3Ux/btsNNhHukGJ/mQeDr8ovq4qrVK82Mt97Q0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbEF3Ux%2FbtsNNhHukGJ%2FmQeDr8ovq4qrVK82Mt97Q0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;2470&quot; height=&quot;570&quot;/&gt;&lt;/span&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/KvQXM/btsNN5GfRhI/Vk9ZwsgxQr35NRnflVO7c1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/KvQXM/btsNN5GfRhI/Vk9ZwsgxQr35NRnflVO7c1/img.png&quot; data-origin-width=&quot;1202&quot; data-origin-height=&quot;530&quot; data-is-animation=&quot;false&quot; style=&quot;width: 33.9565%;&quot; data-widthpercent=&quot;34.36&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/KvQXM/btsNN5GfRhI/Vk9ZwsgxQr35NRnflVO7c1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FKvQXM%2FbtsNN5GfRhI%2FVk9ZwsgxQr35NRnflVO7c1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1202&quot; height=&quot;530&quot;/&gt;&lt;/span&gt;&lt;/div&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;Light Attack Ability 로직을 다음과 같이 수정한다.&lt;br /&gt;&lt;br /&gt;1. 현재 Combo Count가 마지막 전이라면(총 4개의 공격 콤보 중 3번째 공격까지 한 상황), 라이브러리 함수를 사용해서 Character에 Tag를 부여한다.&lt;br /&gt;2. Combo Count가 Reset 될 때, 부여했던 Tag를 삭제한다.&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 style=&quot;color: #e8e6e3; text-align: start;&quot; data-ke-size=&quot;size20&quot; data-darkreader-inline-color=&quot;&quot;&gt;&lt;b&gt; &lt;span&gt;&lt;span&gt;&lt;span&gt; GA_Hero_HeavyAttackMaster&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/b&gt;&lt;/h4&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock widthContent&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;2326&quot; data-origin-height=&quot;797&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cxKVSt/btsNNISu2rT/1noVxkSPVrUghqNVZbQvK1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cxKVSt/btsNNISu2rT/1noVxkSPVrUghqNVZbQvK1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cxKVSt/btsNNISu2rT/1noVxkSPVrUghqNVZbQvK1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcxKVSt%2FbtsNNISu2rT%2F1noVxkSPVrUghqNVZbQvK1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;2326&quot; height=&quot;797&quot; data-origin-width=&quot;2326&quot; data-origin-height=&quot;797&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;figure class=&quot;imagegridblock&quot;&gt;
  &lt;div class=&quot;image-container&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/KBFRK/btsNN6Flsra/cBPkz8oYwNXzmagGDQ8Op1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/KBFRK/btsNN6Flsra/cBPkz8oYwNXzmagGDQ8Op1/img.png&quot; data-origin-width=&quot;1696&quot; data-origin-height=&quot;692&quot; data-is-animation=&quot;false&quot; style=&quot;width: 55.4004%; margin-right: 10px;&quot; data-widthpercent=&quot;56.05&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/KBFRK/btsNN6Flsra/cBPkz8oYwNXzmagGDQ8Op1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FKBFRK%2FbtsNN6Flsra%2FcBPkz8oYwNXzmagGDQ8Op1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1696&quot; height=&quot;692&quot;/&gt;&lt;/span&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/peWzH/btsNNQo0xcc/fKT4Yq74uC85ZEXi66fu4K/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/peWzH/btsNNQo0xcc/fKT4Yq74uC85ZEXi66fu4K/img.png&quot; data-origin-width=&quot;1005&quot; data-origin-height=&quot;523&quot; data-is-animation=&quot;false&quot; style=&quot;width: 43.4368%;&quot; data-widthpercent=&quot;43.95&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/peWzH/btsNNQo0xcc/fKT4Yq74uC85ZEXi66fu4K/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FpeWzH%2FbtsNNQo0xcc%2FfKT4Yq74uC85ZEXi66fu4K%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1005&quot; height=&quot;523&quot;/&gt;&lt;/span&gt;&lt;/div&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;1. Heavy Attack Ability가 활성화 되었을 때, 현재 Character에 마지막 공격으로 전환 Tag가 있는지 확인한다.&lt;br /&gt;2. 만약 태그가 있다면 현재 Combo Count를 마지막 단계로 설정하고 해당 몽타주를 재생한다.&lt;br /&gt;3. 태그가 없다면 현재 Combo Count에 맞는 몽타주를 재생한다.&lt;br /&gt;4. Combo Count를 초기화 하고 이때도 마찬가지로 마지막 공격 전환 Tag를 캐릭터에게서 제거한다.&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 style=&quot;color: #c8c3bc; text-align: start;&quot; data-ke-size=&quot;size23&quot; data-darkreader-inline-color=&quot;&quot;&gt;&lt;b&gt; &lt;span&gt; Jump To Finisher 공격 콤보 테스트&lt;/span&gt;&lt;/b&gt;&lt;/h3&gt;
&lt;p&gt;&lt;figure class=&quot;imagegridblock&quot;&gt;
  &lt;div class=&quot;image-container&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/dH56in/btsNOfProaF/HkqPXkpWnBky4J7JuGWw10/img.gif&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/dH56in/btsNOfProaF/HkqPXkpWnBky4J7JuGWw10/img.gif&quot; data-origin-width=&quot;854&quot; data-origin-height=&quot;480&quot; data-is-animation=&quot;true&quot; data-filename=&quot;2025-05-08 14-28-31.mp4_20250508_142906.gif&quot; style=&quot;width: 49.4186%; margin-right: 10px;&quot; data-widthpercent=&quot;50&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/dH56in/btsNOfProaF/HkqPXkpWnBky4J7JuGWw10/img.gif&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FdH56in%2FbtsNOfProaF%2FHkqPXkpWnBky4J7JuGWw10%2Fimg.gif&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;854&quot; height=&quot;480&quot;/&gt;&lt;/span&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/GFOnK/btsNNIrtshg/mYSoGfmjiwlN6Fs1kakCa1/img.gif&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/GFOnK/btsNNIrtshg/mYSoGfmjiwlN6Fs1kakCa1/img.gif&quot; data-origin-width=&quot;854&quot; data-origin-height=&quot;480&quot; data-is-animation=&quot;true&quot; data-filename=&quot;2025-05-08 14-31-10.mp4_20250508_143141.gif&quot; style=&quot;width: 49.4186%;&quot; data-widthpercent=&quot;50&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/GFOnK/btsNNIrtshg/mYSoGfmjiwlN6Fs1kakCa1/img.gif&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FGFOnK%2FbtsNNIrtshg%2FmYSoGfmjiwlN6Fs1kakCa1%2Fimg.gif&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;854&quot; height=&quot;480&quot;/&gt;&lt;/span&gt;&lt;/div&gt;
  &lt;figcaption&gt;좌(Light_1~3 &amp;amp; Heavy_2) / 우(Heavy_1~2)&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;Light Attack_1~3 이후 바로 Heavy Attack을 시전하면 바로 Heavy Attack의 마무리 동작인 위에서 내려찍는 애니메이션이 정상적으로 재생된다.&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 style=&quot;color: #e8e6e3; text-align: start;&quot; data-darkreader-inline-color=&quot;&quot; data-ke-size=&quot;size26&quot;&gt;&lt;b&gt; &amp;nbsp; Attack 몽타주에 효과 주기&lt;/b&gt;&lt;/h2&gt;
&lt;h3 style=&quot;color: #e8e6e3; text-align: start;&quot; data-ke-size=&quot;size23&quot; data-darkreader-inline-color=&quot;&quot;&gt;&lt;b&gt;✅ 슬로우 모션 효과&lt;/b&gt;&lt;/h3&gt;
&lt;p&gt;&lt;figure class=&quot;imagegridblock&quot;&gt;
  &lt;div class=&quot;image-container&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bHhMdN/btsNPEnq2aH/BYIXpAhhRxNJ1s5cLpWpKk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bHhMdN/btsNPEnq2aH/BYIXpAhhRxNJ1s5cLpWpKk/img.png&quot; data-origin-width=&quot;768&quot; data-origin-height=&quot;776&quot; data-is-animation=&quot;false&quot; style=&quot;width: 61.5694%; margin-right: 10px;&quot; data-widthpercent=&quot;62.29&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bHhMdN/btsNPEnq2aH/BYIXpAhhRxNJ1s5cLpWpKk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbHhMdN%2FbtsNPEnq2aH%2FBYIXpAhhRxNJ1s5cLpWpKk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;768&quot; height=&quot;776&quot;/&gt;&lt;/span&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bqU4ax/btsNPhF3JV3/kdlkUNVrgmSHwvTlzzPru1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bqU4ax/btsNPhF3JV3/kdlkUNVrgmSHwvTlzzPru1/img.png&quot; data-origin-width=&quot;127&quot; data-origin-height=&quot;212&quot; data-is-animation=&quot;false&quot; style=&quot;width: 37.2678%;&quot; data-widthpercent=&quot;37.71&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bqU4ax/btsNPhF3JV3/kdlkUNVrgmSHwvTlzzPru1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbqU4ax%2FbtsNPhF3JV3%2FkdlkUNVrgmSHwvTlzzPru1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;127&quot; height=&quot;212&quot;/&gt;&lt;/span&gt;&lt;/div&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;에디터에서 &quot;AnimNotifyState&quot;를 상속받는 블루프린트 클래스를 생성해준다.&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;750&quot; data-origin-height=&quot;306&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/DmRhD/btsNNPjvfWh/ZhkKIR5knmx8lfd5Y75PF0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/DmRhD/btsNNPjvfWh/ZhkKIR5knmx8lfd5Y75PF0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/DmRhD/btsNNPjvfWh/ZhkKIR5knmx8lfd5Y75PF0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FDmRhD%2FbtsNNPjvfWh%2FZhkKIR5knmx8lfd5Y75PF0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;750&quot; height=&quot;306&quot; data-origin-width=&quot;750&quot; data-origin-height=&quot;306&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;좌측 상단에서 두 개의 함수를 Override 해준다.&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imagegridblock&quot;&gt;
  &lt;div class=&quot;image-container&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/dB0EYZ/btsNOUxyIi9/7IxK4n5vmNCjTg6KrBeeUK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/dB0EYZ/btsNOUxyIi9/7IxK4n5vmNCjTg6KrBeeUK/img.png&quot; data-origin-width=&quot;1097&quot; data-origin-height=&quot;256&quot; data-is-animation=&quot;false&quot; style=&quot;width: 85.8383%; margin-right: 10px;&quot; data-widthpercent=&quot;86.85&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/dB0EYZ/btsNOUxyIi9/7IxK4n5vmNCjTg6KrBeeUK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FdB0EYZ%2FbtsNOUxyIi9%2F7IxK4n5vmNCjTg6KrBeeUK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1097&quot; height=&quot;256&quot;/&gt;&lt;/span&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/vqyqZ/btsNQeV6IgU/kz0EzG8lCSsxORxrQaYNk1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/vqyqZ/btsNQeV6IgU/kz0EzG8lCSsxORxrQaYNk1/img.png&quot; data-origin-width=&quot;451&quot; data-origin-height=&quot;695&quot; data-is-animation=&quot;false&quot; style=&quot;width: 12.9989%;&quot; data-widthpercent=&quot;13.15&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/vqyqZ/btsNQeV6IgU/kz0EzG8lCSsxORxrQaYNk1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FvqyqZ%2FbtsNQeV6IgU%2Fkz0EzG8lCSsxORxrQaYNk1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;451&quot; height=&quot;695&quot;/&gt;&lt;/span&gt;&lt;/div&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;Begin 함수에서 MeshComponent에 Dilation 설정을 해준다.&lt;br /&gt;TimeDilation을 변수로 승격시키고, Default 값을 0.2로 준다.&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;932&quot; data-origin-height=&quot;231&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/E4rh0/btsNPikExhe/yflSTr20UpPkkWiyktl82k/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/E4rh0/btsNPikExhe/yflSTr20UpPkkWiyktl82k/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/E4rh0/btsNPikExhe/yflSTr20UpPkkWiyktl82k/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FE4rh0%2FbtsNPikExhe%2FyflSTr20UpPkkWiyktl82k%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;932&quot; height=&quot;231&quot; data-origin-width=&quot;932&quot; data-origin-height=&quot;231&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;End 함수에서 Time Dilation을 1.0으로 다시 기본 값으로 설정해준다.&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imagegridblock&quot;&gt;
  &lt;div class=&quot;image-container&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bLHUAZ/btsNOlIWhyG/FloRskRH6UfAIA14lhqaW0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bLHUAZ/btsNOlIWhyG/FloRskRH6UfAIA14lhqaW0/img.png&quot; data-origin-width=&quot;1208&quot; data-origin-height=&quot;1255&quot; data-is-animation=&quot;false&quot; style=&quot;width: 51.3377%; margin-right: 10px;&quot; data-widthpercent=&quot;51.94&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bLHUAZ/btsNOlIWhyG/FloRskRH6UfAIA14lhqaW0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbLHUAZ%2FbtsNOlIWhyG%2FFloRskRH6UfAIA14lhqaW0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1208&quot; height=&quot;1255&quot;/&gt;&lt;/span&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/ce2S8d/btsNNhU5oFD/CaphqasZyKU1Q6S9WL8sZk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/ce2S8d/btsNNhU5oFD/CaphqasZyKU1Q6S9WL8sZk/img.png&quot; data-origin-width=&quot;1107&quot; data-origin-height=&quot;1243&quot; data-is-animation=&quot;false&quot; style=&quot;width: 47.4995%;&quot; data-widthpercent=&quot;48.06&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/ce2S8d/btsNNhU5oFD/CaphqasZyKU1Q6S9WL8sZk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fce2S8d%2FbtsNNhU5oFD%2FCaphqasZyKU1Q6S9WL8sZk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1107&quot; height=&quot;1243&quot;/&gt;&lt;/span&gt;&lt;/div&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;Light / Heavy Attack의 마지막 콤보 애니메이션에 방금 만든 AnimNotifyState_SlowMotion을 적절한 위치에 배치해준다.&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 style=&quot;color: #c8c3bc; text-align: start;&quot; data-ke-size=&quot;size20&quot; data-darkreader-inline-color=&quot;&quot;&gt;&lt;b&gt; &lt;span&gt; 슬로우 모션 적용 테스트&lt;/span&gt;&lt;/b&gt;&lt;/h4&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock widthContent&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;2025-05-08 15-01-22.mp4_20250508_150200.gif&quot; data-origin-width=&quot;854&quot; data-origin-height=&quot;480&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/XhIb5/btsNN6kXaPE/g903cUmh4SKyPIPXQiZuM0/img.gif&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/XhIb5/btsNN6kXaPE/g903cUmh4SKyPIPXQiZuM0/img.gif&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/XhIb5/btsNN6kXaPE/g903cUmh4SKyPIPXQiZuM0/img.gif&quot; srcset=&quot;https://blog.kakaocdn.net/dn/XhIb5/btsNN6kXaPE/g903cUmh4SKyPIPXQiZuM0/img.gif&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;854&quot; height=&quot;480&quot; data-filename=&quot;2025-05-08 15-01-22.mp4_20250508_150200.gif&quot; data-origin-width=&quot;854&quot; data-origin-height=&quot;480&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;Light / Heavy Attack 모두, 마지막 공격에 슬로우 모션이 걸리면서 강하게 힘을 모아 공격하는 듯한 연출이 가능해졌다.&lt;/blockquote&gt;</description>
      <category>내배캠/GAS(Gameplay Ability System)</category>
      <author>동그래님</author>
      <guid isPermaLink="true">https://dong-grae.tistory.com/231</guid>
      <comments>https://dong-grae.tistory.com/231#entry231comment</comments>
      <pubDate>Wed, 7 May 2025 20:44:46 +0900</pubDate>
    </item>
    <item>
      <title>GAS_08_무장 상태에 따른 Ability + IMC 설계</title>
      <link>https://dong-grae.tistory.com/230</link>
      <description>&lt;h2 style=&quot;color: #e8e6e3; text-align: start;&quot; data-ke-size=&quot;size26&quot; data-darkreader-inline-color=&quot;&quot;&gt;&lt;b&gt;  Unequip Ability 기본 세팅&lt;/b&gt;&lt;/h2&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;현재까지 시스템을 고려할 때, 새로운 플레이어(Hero) 능력을 추가한다면 아래와 같은 순서로 적용시킬 가능성이 높다.&lt;br /&gt;&lt;br /&gt;1. Ability Tag 생성&lt;br /&gt;2. 새로운 몽타주 생성&lt;br /&gt;3. Ability BP 생성&lt;br /&gt;4. 이 능력을 부여하기 위한 Ability Input Action 처리&lt;br /&gt;&lt;br /&gt;이 순서대로 빠르게 Unequip Ability를 세팅해보자.&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;1. Ability Tag 생성&lt;/b&gt;&lt;b&gt;&lt;/b&gt;&lt;/h4&gt;
&lt;pre id=&quot;code_1746541902124&quot; class=&quot;cpp&quot; data-ke-language=&quot;cpp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;#pragma once

#include &quot;NativeGameplayTags.h&quot;

namespace RPGGameplayTags
{
	/** Input Tags */
	RPG_API UE_DECLARE_GAMEPLAY_TAG_EXTERN(InputTag_Move);
	RPG_API UE_DECLARE_GAMEPLAY_TAG_EXTERN(InputTag_Look);
	RPG_API UE_DECLARE_GAMEPLAY_TAG_EXTERN(InputTag_EquipAxe);
	RPG_API UE_DECLARE_GAMEPLAY_TAG_EXTERN(InputTag_UnequipAxe);

	/** Player Tags */
	RPG_API UE_DECLARE_GAMEPLAY_TAG_EXTERN(Player_Ability_Equip_Axe); // 실제 도끼 장(탈)착 Ability Tag
	RPG_API UE_DECLARE_GAMEPLAY_TAG_EXTERN(Player_Ability_Unequip_Axe);

	RPG_API UE_DECLARE_GAMEPLAY_TAG_EXTERN(Player_Weapon_Axe);

	RPG_API UE_DECLARE_GAMEPLAY_TAG_EXTERN(Player_Event_Equip_Axe);   // 해당 Anim에서 사용했던 Ability 활성화 Event Tag
	RPG_API UE_DECLARE_GAMEPLAY_TAG_EXTERN(Player_Event_Unequip_Axe);
}&lt;/code&gt;&lt;/pre&gt;
&lt;pre id=&quot;code_1746541912618&quot; class=&quot;cpp&quot; data-ke-language=&quot;cpp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;#include &quot;RPGGameplayTags.h&quot;

namespace RPGGameplayTags
{
	/** Input Tags */
	UE_DEFINE_GAMEPLAY_TAG(InputTag_Move, &quot;InputTag.Move&quot;);
	UE_DEFINE_GAMEPLAY_TAG(InputTag_Look, &quot;InputTag.Look&quot;);
	UE_DEFINE_GAMEPLAY_TAG(InputTag_EquipAxe, &quot;InputTag.EquipAxe&quot;);
	UE_DEFINE_GAMEPLAY_TAG(InputTag_UnequipAxe, &quot;InputTag.UnequipAxe&quot;);

	/** Player Tags */
	UE_DEFINE_GAMEPLAY_TAG(Player_Ability_Equip_Axe, &quot;Player.Ability.Equip.Axe&quot;);
	UE_DEFINE_GAMEPLAY_TAG(Player_Ability_Unequip_Axe, &quot;Player.Ability.Unequip.Axe&quot;);

	UE_DEFINE_GAMEPLAY_TAG(Player_Weapon_Axe, &quot;Player.Weapon.Axe&quot;);

	UE_DEFINE_GAMEPLAY_TAG(Player_Event_Equip_Axe, &quot;Player.Event.Equip.Axe&quot;);
	UE_DEFINE_GAMEPLAY_TAG(Player_Event_Unequip_Axe, &quot;Player.Event.Unequip.Axe&quot;);
}&lt;/code&gt;&lt;/pre&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;앞서 AnimNotify로 도끼 장착 Ability가 발생해야되는 Event를 알려주는 Tag를 사용했었는데,&amp;nbsp;&lt;br /&gt;이제 실제로 해당 Ability에 관련한 Tag를 작성해준다.&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;2. Ability 관련 애니메이션 몽타주 생성&lt;/b&gt;&lt;/h4&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock widthContent&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;2557&quot; data-origin-height=&quot;1341&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cJpKUs/btsNLo6R2gX/JQdD4aZ2rwpMhtrAlBp5kK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cJpKUs/btsNLo6R2gX/JQdD4aZ2rwpMhtrAlBp5kK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cJpKUs/btsNLo6R2gX/JQdD4aZ2rwpMhtrAlBp5kK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcJpKUs%2FbtsNLo6R2gX%2FJQdD4aZ2rwpMhtrAlBp5kK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;2557&quot; height=&quot;1341&quot; data-origin-width=&quot;2557&quot; data-origin-height=&quot;1341&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;AnimNotify를 적절한 프레임 위치에 배치하고, Event Tag 설정&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 style=&quot;color: #e8e6e3; text-align: start;&quot; data-darkreader-inline-color=&quot;&quot; data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;3. Ability BP 생성&lt;/b&gt;&lt;/h4&gt;
&lt;p&gt;&lt;figure class=&quot;imagegridblock&quot;&gt;
  &lt;div class=&quot;image-container&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/4WzLh/btsNMbTi8yr/3aoMhygzgMo2ckXkK7hV9k/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/4WzLh/btsNMbTi8yr/3aoMhygzgMo2ckXkK7hV9k/img.png&quot; style=&quot;width: 37.3121%; margin-right: 10px;&quot; data-widthpercent=&quot;38.2&quot; data-is-animation=&quot;false&quot; data-origin-height=&quot;1088&quot; data-origin-width=&quot;782&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/4WzLh/btsNMbTi8yr/3aoMhygzgMo2ckXkK7hV9k/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F4WzLh%2FbtsNMbTi8yr%2F3aoMhygzgMo2ckXkK7hV9k%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;782&quot; height=&quot;1088&quot;/&gt;&lt;/span&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/WpQym/btsNKg2VRqi/3gzyifLtzXXU4enoZOWbv0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/WpQym/btsNKg2VRqi/3gzyifLtzXXU4enoZOWbv0/img.png&quot; style=&quot;width: 28.3262%; margin-right: 10px;&quot; data-widthpercent=&quot;29&quot; data-is-animation=&quot;false&quot; data-origin-height=&quot;920&quot; data-origin-width=&quot;502&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/WpQym/btsNKg2VRqi/3gzyifLtzXXU4enoZOWbv0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FWpQym%2FbtsNKg2VRqi%2F3gzyifLtzXXU4enoZOWbv0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;502&quot; height=&quot;920&quot;/&gt;&lt;/span&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/8RhrM/btsNMxV8uPg/iT4U9KdSJ6mq6hTUwajh7k/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/8RhrM/btsNMxV8uPg/iT4U9KdSJ6mq6hTUwajh7k/img.png&quot; style=&quot;width: 32.0361%;&quot; data-widthpercent=&quot;32.8&quot; data-is-animation=&quot;false&quot; data-origin-height=&quot;222&quot; data-origin-width=&quot;137&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/8RhrM/btsNMxV8uPg/iT4U9KdSJ6mq6hTUwajh7k/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F8RhrM%2FbtsNMxV8uPg%2FiT4U9KdSJ6mq6hTUwajh7k%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;137&quot; height=&quot;222&quot;/&gt;&lt;/span&gt;&lt;/div&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;무기 해제 Ability인 &quot;GA_Hero_UnequipAxe&quot; 생성&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock widthContent&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1376&quot; data-origin-height=&quot;551&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bJatS6/btsNMRz1QYQ/LXv4QDarCSmNFwdptpeGl1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bJatS6/btsNMRz1QYQ/LXv4QDarCSmNFwdptpeGl1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bJatS6/btsNMRz1QYQ/LXv4QDarCSmNFwdptpeGl1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbJatS6%2FbtsNMRz1QYQ%2FLXv4QDarCSmNFwdptpeGl1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1376&quot; height=&quot;551&quot; data-origin-width=&quot;1376&quot; data-origin-height=&quot;551&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;애니메이션 몽타주를 연결해주고 Wait Gameplay Event로 Event 수신&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;773&quot; data-origin-height=&quot;586&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cnYKLN/btsNLBLHN3F/c8aURQITlPdvybuD8alGXk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cnYKLN/btsNLBLHN3F/c8aURQITlPdvybuD8alGXk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cnYKLN/btsNLBLHN3F/c8aURQITlPdvybuD8alGXk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcnYKLN%2FbtsNLBLHN3F%2Fc8aURQITlPdvybuD8alGXk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;773&quot; height=&quot;586&quot; data-origin-width=&quot;773&quot; data-origin-height=&quot;586&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;앞서 Ability Tag를 선언했었는데, 이를 바탕으로 Tag Setting을 진행한다.&lt;/blockquote&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;AssetTags:
&lt;ul style=&quot;list-style-type: circle;&quot; data-ke-list-type=&quot;circle&quot;&gt;
&lt;li&gt;이 태그는 해당 GameplayAbility의 정체성&lt;/li&gt;
&lt;li&gt;이 능력이 어떤 능력인지 명시하는 태그&lt;/li&gt;
&lt;li&gt;다른 능력이나 조건에서 현재 어떤 능력이 활성화 되었는지 확인할 때 사용&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Cancel Abilities with Tag:
&lt;ul style=&quot;list-style-type: circle;&quot; data-ke-list-type=&quot;circle&quot;&gt;
&lt;li&gt;현재 GA가 발동될 때, 이 카테고리에 있는 Tag가 붙은 Ability는 즉시 취소됨&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Block Abilities with Tag:
&lt;ul style=&quot;list-style-type: circle;&quot; data-ke-list-type=&quot;circle&quot;&gt;
&lt;li&gt;이 Ability가 활성화 되어 있는 동안, 이 카테고리에 있는 Tag가 붙은 Ability는 발동할 수 없음&lt;/li&gt;
&lt;li&gt;즉 현재는 도끼를 해제하는 Ability가 활성화 되었다면, 다른 장착/탈착 Ability는 Block처리&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock widthContent&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;2565&quot; data-origin-height=&quot;1187&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bQEptj/btsNLaHBbRl/vGtbbaolwBL7tFcdBlMCx0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bQEptj/btsNLaHBbRl/vGtbbaolwBL7tFcdBlMCx0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bQEptj/btsNLaHBbRl/vGtbbaolwBL7tFcdBlMCx0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbQEptj%2FbtsNLaHBbRl%2FvGtbbaolwBL7tFcdBlMCx0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;2565&quot; height=&quot;1187&quot; data-origin-width=&quot;2565&quot; data-origin-height=&quot;1187&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;앞서 구현했었던 무기를 장착하는 &quot;GA_Hero_EquipAxe&quot;에도 Tag 설정을 해준다.&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 style=&quot;color: #e8e6e3; text-align: start;&quot; data-ke-size=&quot;size20&quot; data-darkreader-inline-color=&quot;&quot;&gt;&lt;b&gt;4. Ability Input Action 세팅&lt;/b&gt;&lt;/h4&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1416&quot; data-origin-height=&quot;954&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/UqpeP/btsNMdKlFLA/1Khm5J40IFUKDeuxqBxCX0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/UqpeP/btsNMdKlFLA/1Khm5J40IFUKDeuxqBxCX0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/UqpeP/btsNMdKlFLA/1Khm5J40IFUKDeuxqBxCX0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FUqpeP%2FbtsNMdKlFLA%2F1Khm5J40IFUKDeuxqBxCX0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1416&quot; height=&quot;954&quot; data-origin-width=&quot;1416&quot; data-origin-height=&quot;954&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;IA_UniqueAxe 클래스를 DA_InputConfig에 할당&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 style=&quot;color: #e8e6e3; text-align: start;&quot; data-darkreader-inline-color=&quot;&quot; data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;  Equip/Unequip에 따른 Ability 설계&lt;/b&gt;&lt;/h2&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock widthContent&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;967&quot; data-origin-height=&quot;475&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/JEkzO/btsNK4t5eq7/kUlCYDmzR5QkKVQxhFxW9k/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/JEkzO/btsNK4t5eq7/kUlCYDmzR5QkKVQxhFxW9k/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/JEkzO/btsNK4t5eq7/kUlCYDmzR5QkKVQxhFxW9k/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FJEkzO%2FbtsNK4t5eq7%2FkUlCYDmzR5QkKVQxhFxW9k%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;967&quot; height=&quot;475&quot; data-origin-width=&quot;967&quot; data-origin-height=&quot;475&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;Unequip Ability(장비 해제 능력)는 장비를 착용하고 있는 상태에서만 부여하고, 그때만 능력을 활성화하는 것이 자연스러울 것이다.&lt;br /&gt;반대로 Equip Ability(장비 장착 능력)은 장비를 착용하고 있지 않은 상태에서 부여하고, 장비 착용 능력을 활성화 하면 해당 무기에 특화된 능력과 입력 키를 자동으로 부여하고, 해제 시 해당 능력과 입력 키를 제거하는 구조가 가장 이상적이다.&lt;br /&gt;&lt;br /&gt;기존에 Weapon 클래스에 AnimLayer 데이터를 보관했는데 앞선 구조를 적용시키려면, 더 많은 데이터(IMC, Ability Set 등)를 무기가 가지고 있어야 한다.&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 style=&quot;color: #e8e6e3; text-align: start;&quot; data-ke-size=&quot;size20&quot; data-darkreader-inline-color=&quot;&quot;&gt;&lt;b&gt; &lt;span&gt; DataAsset_HeroStartUpData.h 수정 전&lt;/span&gt;&lt;/b&gt;&lt;/h4&gt;
&lt;div data-ke-type=&quot;moreLess&quot; data-text-more=&quot;더보기&quot; data-text-less=&quot;닫기&quot;&gt;&lt;a class=&quot;btn-toggle-moreless&quot;&gt;더보기&lt;/a&gt;
&lt;div class=&quot;moreless-content&quot;&gt;
&lt;pre id=&quot;code_1746544992354&quot; class=&quot;cpp&quot; style=&quot;background-color: #1c1e1f; color: #c1bcb4; text-align: start;&quot; data-ke-type=&quot;codeblock&quot; data-ke-language=&quot;cpp&quot; data-darkreader-inline-bgcolor=&quot;&quot; data-darkreader-inline-color=&quot;&quot;&gt;&lt;code&gt;#pragma once

#include &quot;CoreMinimal.h&quot;
#include &quot;DataAssets/StartUpData/DataAsset_StartUpDataBase.h&quot;
#include &quot;GameplayTagContainer.h&quot; // FGameplayTag 헤더
#include &quot;DataAsset_HeroStartUpData.generated.h&quot;

USTRUCT(BlueprintType)
struct FWarriorHeroAbilitySet
{
	GENERATED_BODY()

	UPROPERTY(EditDefaultsOnly, BlueprintReadOnly, meta = (Category = &quot;InputTag&quot;))
	FGameplayTag InputTag;

	UPROPERTY(EditDefaultsOnly, BlueprintReadOnly)
	TSubclassOf&amp;lt;URPGGameplayAbility&amp;gt; AbilityToGrant;

	bool IsVaild() const;
};

UCLASS()
class RPG_API UDataAsset_HeroStartUpData : public UDataAsset_StartUpDataBase
{
	GENERATED_BODY()
	
public:
	// 플레이어 캐릭터의 ASC에 초기 능력을 부여
	virtual void GiveToAbilitySystemComponent(URPGAbilitySystemComponent* InRPG_ASC, int32 ApplyLevel = 1) override;

private:
	UPROPERTY(EditDefaultsOnly, Category = &quot;StartUpData&quot;, meta = (TitleProperty = &quot;InputTag&quot;))
	TArray&amp;lt;FWarriorHeroAbilitySet&amp;gt; HeroStartUpAbilitySets;
};&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;h4 style=&quot;color: #e8e6e3; text-align: start;&quot; data-darkreader-inline-color=&quot;&quot; data-ke-size=&quot;size20&quot;&gt;&lt;b&gt; &lt;span&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;DataAsset_HeroStartUpData.h 수정 후&lt;/span&gt;&lt;/b&gt;&lt;/h4&gt;
&lt;div data-ke-type=&quot;moreLess&quot; data-text-more=&quot;더보기&quot; data-text-less=&quot;닫기&quot;&gt;&lt;a class=&quot;btn-toggle-moreless&quot;&gt;더보기&lt;/a&gt;
&lt;div class=&quot;moreless-content&quot;&gt;
&lt;pre id=&quot;code_1746545015608&quot; class=&quot;cpp&quot; data-ke-language=&quot;cpp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;#pragma once

#include &quot;CoreMinimal.h&quot;
#include &quot;DataAssets/StartUpData/DataAsset_StartUpDataBase.h&quot;
#include &quot;RPGTypes/RPGStructTypes.h&quot;
#include &quot;DataAsset_HeroStartUpData.generated.h&quot;

UCLASS()
class RPG_API UDataAsset_HeroStartUpData : public UDataAsset_StartUpDataBase
{
	GENERATED_BODY()
	
public:
	// 플레이어 캐릭터의 ASC에 초기 능력을 부여
	virtual void GiveToAbilitySystemComponent(URPGAbilitySystemComponent* InRPG_ASC, int32 ApplyLevel = 1) override;

private:
	UPROPERTY(EditDefaultsOnly, Category = &quot;StartUpData&quot;, meta = (TitleProperty = &quot;InputTag&quot;))
	TArray&amp;lt;FWarriorHeroAbilitySet&amp;gt; HeroStartUpAbilitySets;
};&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;기존 &quot;DataAsset_HeroStartUpData&quot;에 정의되어있던 &quot;FWarriorHeroAbilitySet&quot; 구조체를 구조체 전용 클래스인 &quot;RPGStructTypes&quot; 클래스로 옮겨준다.&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 style=&quot;color: #e8e6e3; text-align: start;&quot; data-ke-size=&quot;size20&quot; data-darkreader-inline-color=&quot;&quot;&gt;&lt;b&gt; &lt;span&gt;&lt;span&gt; RPGStructTypes.h&lt;/span&gt;&lt;/span&gt;&lt;/b&gt;&lt;/h4&gt;
&lt;pre id=&quot;code_1746544902735&quot; class=&quot;cpp&quot; data-ke-language=&quot;cpp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;#pragma once

#include &quot;GameplayTagContainer.h&quot;
#include &quot;RPGStructTypes.generated.h&quot;

class UWarriorHeroLinkedAnimLayer;
class URPGGameplayAbility;
class UInputMappingContext;

USTRUCT(BlueprintType)
struct FWarriorHeroAbilitySet
{
	GENERATED_BODY()

	UPROPERTY(EditDefaultsOnly, BlueprintReadOnly, meta = (Category = &quot;InputTag&quot;))
	FGameplayTag InputTag;

	UPROPERTY(EditDefaultsOnly, BlueprintReadOnly)
	TSubclassOf&amp;lt;URPGGameplayAbility&amp;gt; AbilityToGrant;

	bool IsVaild() const;
};

USTRUCT(BlueprintType)
struct FRPGHeroWeaponData
{
	GENERATED_BODY()

	UPROPERTY(EditDefaultsOnly, BlueprintReadOnly)
	TSubclassOf&amp;lt;UWarriorHeroLinkedAnimLayer&amp;gt; WeaponAnimLayerToLink;

	UPROPERTY(EditDefaultsOnly, BlueprintReadOnly)
	UInputMappingContext* WeaponInputMappingContext;

	UPROPERTY(EditDefaultsOnly, BlueprintReadOnly, meta = (TitleProperty = &quot;InputTag&quot;))
	TArray&amp;lt;FWarriorHeroAbilitySet&amp;gt; DefaultWeaponAbilities;
};&lt;/code&gt;&lt;/pre&gt;
&lt;h4 style=&quot;color: #e8e6e3; text-align: start;&quot; data-darkreader-inline-color=&quot;&quot; data-ke-size=&quot;size20&quot;&gt;&lt;b&gt; &lt;span&gt;&lt;span&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;RPGStructTypes.cpp&lt;/span&gt;&lt;/span&gt;&lt;/b&gt;&lt;/h4&gt;
&lt;pre id=&quot;code_1746545152435&quot; class=&quot;cpp&quot; data-ke-language=&quot;cpp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;#include &quot;RPGTypes/RPGStructTypes.h&quot;
#include &quot;AbilitySystem/Abilities/RPGGameplayAbility.h&quot;

bool FWarriorHeroAbilitySet::IsVaild() const
{
	return InputTag.IsValid() &amp;amp;&amp;amp; AbilityToGrant;
}&lt;/code&gt;&lt;/pre&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;무기 전용 키 설정을 위한 InputMappingContext와 Tag-Ability를 가지고 있는 FWarriorHeroAbilitySet 구조체 타입의 TArray를 선언해준다.&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock widthContent&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1212&quot; data-origin-height=&quot;412&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bggzE5/btsNMgNSBAv/0bALX7OD21epoH0ErLKKvK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bggzE5/btsNMgNSBAv/0bALX7OD21epoH0ErLKKvK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bggzE5/btsNMgNSBAv/0bALX7OD21epoH0ErLKKvK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbggzE5%2FbtsNMgNSBAv%2F0bALX7OD21epoH0ErLKKvK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1212&quot; height=&quot;412&quot; data-origin-width=&quot;1212&quot; data-origin-height=&quot;412&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;에디터로 돌아와서 IMC_Axe 이름의 InputMappingContext를 생성해준다.&lt;br /&gt;&quot;IA_UnequipAxe&quot;를 바인딩 하고, 앞서 무기를 장착했던 키 1번과 동일하게 세팅해주었다.&amp;nbsp;&lt;br /&gt;빠른 테스트를 위해 기본 이동관련 맵핑 컨텍스트 재정의는 추후에 진행하고 무기 해제 InputAction만 바인딩 해주었다.&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imagegridblock&quot;&gt;
  &lt;div class=&quot;image-container&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/ErBV5/btsNMdXTnEj/wpsZcsg2JemJ6TkzPnCgY0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/ErBV5/btsNMdXTnEj/wpsZcsg2JemJ6TkzPnCgY0/img.png&quot; data-origin-width=&quot;2552&quot; data-origin-height=&quot;1113&quot; data-is-animation=&quot;false&quot; style=&quot;width: 63.0371%; margin-right: 10px;&quot; data-widthpercent=&quot;63.78&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/ErBV5/btsNMdXTnEj/wpsZcsg2JemJ6TkzPnCgY0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FErBV5%2FbtsNMdXTnEj%2FwpsZcsg2JemJ6TkzPnCgY0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;2552&quot; height=&quot;1113&quot;/&gt;&lt;/span&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bb9l69/btsNLat3BKi/92eeM0ToRVBEvBPhtd7FAk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bb9l69/btsNLat3BKi/92eeM0ToRVBEvBPhtd7FAk/img.png&quot; data-origin-width=&quot;892&quot; data-origin-height=&quot;685&quot; data-is-animation=&quot;false&quot; style=&quot;width: 35.8001%;&quot; data-widthpercent=&quot;36.22&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bb9l69/btsNLat3BKi/92eeM0ToRVBEvBPhtd7FAk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fbb9l69%2FbtsNLat3BKi%2F92eeM0ToRVBEvBPhtd7FAk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;892&quot; height=&quot;685&quot;/&gt;&lt;/span&gt;&lt;/div&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;플레이어가 사용하는 BP_Axe에서 앞서 WeaponData에 추가했던 InputMappingText와 Input Tag - Ability를 바인딩해준다.&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 style=&quot;color: #e8e6e3; text-align: start;&quot; data-ke-size=&quot;size23&quot; data-darkreader-inline-color=&quot;&quot;&gt;&lt;b&gt;✅ 무기 장착(GA_Hero_EquipAxe) 후, 로직 수정&lt;/b&gt;&lt;/h3&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock widthContent&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;2023&quot; data-origin-height=&quot;440&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/pTwFy/btsNMcTcAu9/KzhWhAyShKj4o9Dw7owJKk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/pTwFy/btsNMcTcAu9/KzhWhAyShKj4o9Dw7owJKk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/pTwFy/btsNMcTcAu9/KzhWhAyShKj4o9Dw7owJKk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FpTwFy%2FbtsNMcTcAu9%2FKzhWhAyShKj4o9Dw7owJKk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;2023&quot; height=&quot;440&quot; data-origin-width=&quot;2023&quot; data-origin-height=&quot;440&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;무기를 장착하는 Ability인 &quot;GA_Hero_EquipAxe&quot;에서 기존엔 무기가 가지고 있는 AnimLayer 정보를 가져와 SkeletalMesh에 Link 해주는 로직 하나였지만,&lt;br /&gt;이에 더해 무기가 가지고 있는 IMC와 무기가 가지고 있는 Ability를 적용시키는 로직을 추가하도록 한다.&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 style=&quot;color: #e8e6e3; text-align: start;&quot; data-ke-size=&quot;size20&quot; data-darkreader-inline-color=&quot;&quot;&gt;&lt;b&gt; &lt;span&gt; RPGAbilitySystemComponent.h&lt;/span&gt;&lt;/b&gt;&lt;/h4&gt;
&lt;pre id=&quot;code_1746605662407&quot; class=&quot;cpp&quot; data-ke-language=&quot;cpp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;#pragma once

#include &quot;CoreMinimal.h&quot;
#include &quot;AbilitySystemComponent.h&quot;
#include &quot;RPGTypes/RPGStructTypes.h&quot;
#include &quot;RPGAbilitySystemComponent.generated.h&quot;

UCLASS()
class RPG_API URPGAbilitySystemComponent : public UAbilitySystemComponent
{
	GENERATED_BODY()
	
public:
	void OnAbilityInputPressed(const FGameplayTag&amp;amp; InInputTag);
	void OnAbilityInputReleased(const FGameplayTag&amp;amp; InInputTag);
	
    // 무기 전용 Ability 등록
	UFUNCTION(BlueprintCallable, Category = &quot;RPG|Ability&quot;, meta = (ApplyLevel = &quot;1&quot;))
	void GrantHeroWeaponAbilities(
		const TArray&amp;lt;FWarriorHeroAbilitySet&amp;gt;&amp;amp; InDefaultWeaponAbilities,
		int32 ApplyLevel,
		TArray&amp;lt;FGameplayAbilitySpecHandle&amp;gt;&amp;amp; OutGrantAbilitySpecHandles);
	
    // 무기 전용 Ability 등록 해제
	UFUNCTION(BlueprintCallable, Category = &quot;RPG|Ability&quot;)
	void RemovedGrantedHeroWeaponAbilities(UPARAM(ref) TArray&amp;lt;FGameplayAbilitySpecHandle&amp;gt;&amp;amp; InSpecHandlesToRemove);
};&lt;/code&gt;&lt;/pre&gt;
&lt;h4 style=&quot;color: #e8e6e3; text-align: start;&quot; data-darkreader-inline-color=&quot;&quot; data-ke-size=&quot;size20&quot;&gt;&lt;b&gt; &lt;span&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;RPGAbilitySystemComponent.cpp&lt;/span&gt;&lt;/b&gt;&lt;/h4&gt;
&lt;pre id=&quot;code_1746605698419&quot; class=&quot;cpp&quot; data-ke-language=&quot;cpp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;#include &quot;AbilitySystem/RPGAbilitySystemComponent.h&quot;
#include &quot;AbilitySystem/Abilities/RPGGameplayAbility.h&quot;

void URPGAbilitySystemComponent::OnAbilityInputPressed(const FGameplayTag&amp;amp; InInputTag)
{
	if (!InInputTag.IsValid()) return;

	for (const FGameplayAbilitySpec&amp;amp; AbilitySpec : GetActivatableAbilities())
	{
		if (!AbilitySpec.DynamicAbilityTags.HasTagExact(InInputTag)) continue;

		TryActivateAbility(AbilitySpec.Handle);
	}
}

void URPGAbilitySystemComponent::OnAbilityInputReleased(const FGameplayTag&amp;amp; InInputTag)
{
}

void URPGAbilitySystemComponent::GrantHeroWeaponAbilities(const TArray&amp;lt;FWarriorHeroAbilitySet&amp;gt;&amp;amp; InDefaultWeaponAbilities, int32 ApplyLevel, TArray&amp;lt;FGameplayAbilitySpecHandle&amp;gt;&amp;amp; OutGrantAbilitySpecHandles)
{
	if (InDefaultWeaponAbilities.IsEmpty()) return;

	for (const FWarriorHeroAbilitySet&amp;amp; AbilitySet : InDefaultWeaponAbilities)
	{
		if (!AbilitySet.IsVaild()) continue;

		FGameplayAbilitySpec AbilitySpec(AbilitySet.AbilityToGrant);
		AbilitySpec.SourceObject = GetAvatarActor();
		AbilitySpec.Level = ApplyLevel;
		AbilitySpec.DynamicAbilityTags.AddTag(AbilitySet.InputTag);

		OutGrantAbilitySpecHandles.AddUnique(GiveAbility(AbilitySpec));
	}
}

void URPGAbilitySystemComponent::RemovedGrantedHeroWeaponAbilities(UPARAM(ref)TArray&amp;lt;FGameplayAbilitySpecHandle&amp;gt;&amp;amp; InSpecHandlesToRemove)
{
	if (InSpecHandlesToRemove.IsEmpty()) return;

	for (const FGameplayAbilitySpecHandle&amp;amp; SpecHandle : InSpecHandlesToRemove)
	{
		if (SpecHandle.IsValid())
		{
			ClearAbility(SpecHandle);
		}
	}

	InSpecHandlesToRemove.Empty();
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;ASC에 무기가 가지고 있는 전용 Ability들을 &quot;GiveAbility()&quot;로 능력을 부여하고, 나중에 무기가 해제되었을 때 무기 전용 Ability 역시 해제해야하기 때문에 SpecHandle을 캐싱해둘 수 있도록 한다.&lt;/blockquote&gt;
&lt;h4 style=&quot;color: #e8e6e3; text-align: start;&quot; data-darkreader-inline-color=&quot;&quot; data-ke-size=&quot;size20&quot;&gt;&lt;b&gt; &lt;span&gt;&lt;span&gt; WarriorHeroWeapon&lt;/span&gt;.h&lt;/span&gt;&lt;/b&gt;&lt;/h4&gt;
&lt;pre id=&quot;code_1746613123597&quot; class=&quot;cpp&quot; data-ke-language=&quot;cpp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;#pragma once

#include &quot;CoreMinimal.h&quot;
#include &quot;Items/Weapons/RPGWeaponBase.h&quot;
#include &quot;RPGTypes/RPGStructTypes.h&quot;
#include &quot;GameplayAbilitySpecHandle.h&quot;
#include &quot;WarriorHeroWeapon.generated.h&quot;

UCLASS()
class RPG_API AWarriorHeroWeapon : public ARPGWeaponBase
{
	GENERATED_BODY()
	
public:
	UPROPERTY(EditDefaultsOnly, BlueprintReadOnly, Category = &quot;WeaponData&quot;)
	FRPGHeroWeaponData HeroWeaponData;

	UFUNCTION(BlueprintCallable)
	void AssignGrantedAbilitySpecHandles(const TArray&amp;lt;FGameplayAbilitySpecHandle&amp;gt;&amp;amp; InSpecHandles);

	UFUNCTION(BlueprintPure)
	TArray&amp;lt;FGameplayAbilitySpecHandle&amp;gt; GetGrantedAbilitySpecHandles() const;

private:
	TArray&amp;lt;FGameplayAbilitySpecHandle&amp;gt; GrantedAbilitySpecHandles;
};&lt;/code&gt;&lt;/pre&gt;
&lt;h4 style=&quot;color: #e8e6e3; text-align: start;&quot; data-ke-size=&quot;size20&quot; data-darkreader-inline-color=&quot;&quot;&gt;&lt;b&gt; &lt;span&gt;&lt;span&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;WarriorHeroWeapon&lt;/span&gt;.cpp&lt;/span&gt;&lt;/b&gt;&lt;/h4&gt;
&lt;pre id=&quot;code_1746613144722&quot; class=&quot;cpp&quot; data-ke-language=&quot;cpp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;#include &quot;Items/Weapons/WarriorHeroWeapon.h&quot;

void AWarriorHeroWeapon::AssignGrantedAbilitySpecHandles(const TArray&amp;lt;FGameplayAbilitySpecHandle&amp;gt;&amp;amp; InSpecHandles)
{
	GrantedAbilitySpecHandles = InSpecHandles;
}

TArray&amp;lt;FGameplayAbilitySpecHandle&amp;gt; AWarriorHeroWeapon::GetGrantedAbilitySpecHandles() const
{
	return GrantedAbilitySpecHandles;
}&lt;/code&gt;&lt;/pre&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;ASC에서 캐싱해둔 SpecHandle 데이터는 무기 클래스에 보존해 놓는다.&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imagegridblock&quot;&gt;
  &lt;div class=&quot;image-container&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/MQK9F/btsNMxXh4wX/mW0CvaqNNLrFP2qsNTKSq1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/MQK9F/btsNMxXh4wX/mW0CvaqNNLrFP2qsNTKSq1/img.png&quot; data-origin-width=&quot;398&quot; data-origin-height=&quot;510&quot; data-is-animation=&quot;false&quot; data-widthpercent=&quot;41.28&quot; style=&quot;width: 40.799%; margin-right: 10px;&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/MQK9F/btsNMxXh4wX/mW0CvaqNNLrFP2qsNTKSq1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FMQK9F%2FbtsNMxXh4wX%2FmW0CvaqNNLrFP2qsNTKSq1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;398&quot; height=&quot;510&quot;/&gt;&lt;/span&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/SiKNk/btsNNcrqXxM/V62u1FmkXK0xI6ppdlJbe0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/SiKNk/btsNNcrqXxM/V62u1FmkXK0xI6ppdlJbe0/img.png&quot; data-origin-width=&quot;635&quot; data-origin-height=&quot;572&quot; data-is-animation=&quot;false&quot; style=&quot;width: 58.0382%;&quot; data-widthpercent=&quot;58.72&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/SiKNk/btsNNcrqXxM/V62u1FmkXK0xI6ppdlJbe0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FSiKNk%2FbtsNNcrqXxM%2FV62u1FmkXK0xI6ppdlJbe0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;635&quot; height=&quot;572&quot;/&gt;&lt;/span&gt;&lt;/div&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;에디터로 돌아와서 무기 장착 Ability에서 Function을 하나 추가해주고, &quot;HandleEquipWeapon&quot;이라 명명한다.&lt;br /&gt;이름 그대로 무기를 장착하고 나서의 로직을 핸들링 하는 함수이다.&lt;br /&gt;함수 인자로 플레이어 무기 클래스인 &quot;Warrior Hero Weapon&quot; 클래스 Object Reference를 받도록 한다.&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;2552&quot; data-origin-height=&quot;1185&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/u6V2E/btsNMuzJgCU/gPN2OH1U1Z4uh4J87WAlKK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/u6V2E/btsNMuzJgCU/gPN2OH1U1Z4uh4J87WAlKK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/u6V2E/btsNMuzJgCU/gPN2OH1U1Z4uh4J87WAlKK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fu6V2E%2FbtsNMuzJgCU%2FgPN2OH1U1Z4uh4J87WAlKK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;2552&quot; height=&quot;1185&quot; data-origin-width=&quot;2552&quot; data-origin-height=&quot;1185&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;무기 클래스를 로컬 변수로 캐싱 해두고, 무기 클래스가 가지고 있는 정보로 아래와 같은 로직을 작성한다.&lt;br /&gt;1. 무기 클래스에서 Anim Layer 클래스를 SkeletalMesh에 Link 해준다.&lt;br /&gt;2. 무기 전용 IMC를 Add Mapping Context로 등록해주고 우선순위를 1로 높여준다.(기존 Mapping 덮어 쓰기 위함)&lt;br /&gt;3. 앞서 ASC에 구현했던 무기가 가지고 있는 전용 Ability를 ASC에 등록해주고, 캐싱된 데이터(SpecHandle)은 무기 클래스에 보존한다.&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock widthContent&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;2125&quot; data-origin-height=&quot;463&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cpvgIF/btsNLAG3mYJ/ECN8xoeZvTVnUZFOTjoVMK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cpvgIF/btsNLAG3mYJ/ECN8xoeZvTVnUZFOTjoVMK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cpvgIF/btsNLAG3mYJ/ECN8xoeZvTVnUZFOTjoVMK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcpvgIF%2FbtsNLAG3mYJ%2FECN8xoeZvTVnUZFOTjoVMK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;2125&quot; height=&quot;463&quot; data-origin-width=&quot;2125&quot; data-origin-height=&quot;463&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;최종적으로 무기를 Attach하고 해당 무기를 방금 구현한 &quot;HandleEquipWeapon&quot; 함수로 넘겨주어서,&lt;br /&gt;애니메이션, IMC, Ability를 적용 시켜주고, 마지막으로 HeroCombatComponent에 &quot;Current Equipped Weapon Tag&quot; 변수에 현재 장착 중인 무기 Tag를 저장해준다.&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 style=&quot;color: #e8e6e3; text-align: start;&quot; data-darkreader-inline-color=&quot;&quot; data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;✅ 무기 해제(GA_Hero_UnequipAxe) 후, 로직 수정&lt;/b&gt;&lt;/h3&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;앞서 무기를 장착했을 때, 애니메이션과 IMC, Ability를 등록해주었는데 반대로 무기를 해제 했을 경우엔 앞서 추가된 목록을 제거해주면 된다.&lt;/blockquote&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;2227&quot; data-origin-height=&quot;1157&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/ooCqj/btsNOodu0iF/vTElmapC0z1Kam4VT5gPwK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/ooCqj/btsNOodu0iF/vTElmapC0z1Kam4VT5gPwK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/ooCqj/btsNOodu0iF/vTElmapC0z1Kam4VT5gPwK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FooCqj%2FbtsNOodu0iF%2FvTElmapC0z1Kam4VT5gPwK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;2227&quot; height=&quot;1157&quot; data-origin-width=&quot;2227&quot; data-origin-height=&quot;1157&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;앞서 무기를 장착할 때와 같이, 무기 클래스를 전달 받는 함수를 선언한다.&lt;br /&gt;해당 함수에서 AnimLayer 제거, Weapon 전용 IMC, Weapon 전용 Ability를 해제하는 로직을 구현한다.&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;2536&quot; data-origin-height=&quot;585&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bc5PHE/btsNMBMfhDy/3ru23wqERY7lvxMHYJakU1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bc5PHE/btsNMBMfhDy/3ru23wqERY7lvxMHYJakU1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bc5PHE/btsNMBMfhDy/3ru23wqERY7lvxMHYJakU1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fbc5PHE%2FbtsNMBMfhDy%2F3ru23wqERY7lvxMHYJakU1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;2536&quot; height=&quot;585&quot; data-origin-width=&quot;2536&quot; data-origin-height=&quot;585&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;최종적으로 무기를 해제하는 GameplayAbility가 활성화 되면 다음과 같은 순서로 로직이 실행된다.&lt;br /&gt;&lt;br /&gt;1. 무기를 해제하는 Montage 재생&lt;br /&gt;2. CombatComponent에 현재 장착 중인 무기 Tag 해제&lt;br /&gt;3. 몽타주에서 무기를 등에 장착하는 시점을 AnimNotify로 EventTag 수신&lt;br /&gt;4. 무기를 등 위치 소켓에 부착&lt;br /&gt;5. 무기 클래스를 &quot;Handle Unequip Weapon&quot; 전달&lt;br /&gt;6. 해당 함수 내에서 AnimLayer, 무기 전용 IMC, 무기 전용 Ability 제거&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 style=&quot;color: #c8c3bc; text-align: start;&quot; data-ke-size=&quot;size23&quot; data-darkreader-inline-color=&quot;&quot;&gt;&lt;b&gt; &lt;span&gt; 테스트 결과&lt;/span&gt;&lt;/b&gt;&lt;/h3&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock widthContent&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;2025-05-07 19-38-01.mp4_20250507_193903.gif&quot; data-origin-width=&quot;854&quot; data-origin-height=&quot;480&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/kQeRF/btsNMaVPGDS/d94kGHMYboK9UkJIt92sc1/img.gif&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/kQeRF/btsNMaVPGDS/d94kGHMYboK9UkJIt92sc1/img.gif&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/kQeRF/btsNMaVPGDS/d94kGHMYboK9UkJIt92sc1/img.gif&quot; srcset=&quot;https://blog.kakaocdn.net/dn/kQeRF/btsNMaVPGDS/d94kGHMYboK9UkJIt92sc1/img.gif&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;854&quot; height=&quot;480&quot; data-filename=&quot;2025-05-07 19-38-01.mp4_20250507_193903.gif&quot; data-origin-width=&quot;854&quot; data-origin-height=&quot;480&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;무기를 장착하지 않았을 때는 GA_Hero_EquipAxe 능력만 활성화 되어 1개의 Ability를 가지고 있는 것을 볼 수 있고, 해당 Ability를 활성화해 무기를 장착하면 GA_Hero_UnequipAxe 능력이 활성화 되어 2개의 Ability를 가지고 있는 것을 볼 수 있다.&lt;br /&gt;또한 움직이면서 무기를 장착하면 Weapon에 저장되어있는 AnimLayer로 애니메이션이 정상적으로 변경되고, 무기를 탈착하면 다시 기존의 애니메이션으로 돌아가는 것을 확인할 수 있었다.&lt;/blockquote&gt;</description>
      <category>내배캠/GAS(Gameplay Ability System)</category>
      <author>동그래님</author>
      <guid isPermaLink="true">https://dong-grae.tistory.com/230</guid>
      <comments>https://dong-grae.tistory.com/230#entry230comment</comments>
      <pubDate>Wed, 7 May 2025 00:37:02 +0900</pubDate>
    </item>
    <item>
      <title>GAS_07_무장 상태 애니메이션 전환 시스템 설계</title>
      <link>https://dong-grae.tistory.com/229</link>
      <description>&lt;h2 style=&quot;color: #e8e6e3; text-align: start;&quot; data-ke-size=&quot;size26&quot; data-darkreader-inline-color=&quot;&quot;&gt;&lt;b&gt;  애니메이션 전환 시스템&lt;/b&gt;&lt;/h2&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;한 캐릭터가 여러 종류의 무기(권총, 소총, 저격총 등)를 사용한다고 하면, 각 무기 타입마다 애니메이션이 다를 것이다.&lt;br /&gt;이것을 각 무기마다 Anim Graph에 직접 노드를 추가한다면 유지보수가 매우 어려워지게 된다.&lt;br /&gt;&lt;br /&gt;언리얼의 Lyra 프로젝트의 경우도 무기가 여러 종류가 있는데, &quot;Anim Layer Interface&quot;를 사용해서 각 무기별 애니메이션 로직을 레이어별로 분리하여 처리하였다.&lt;br /&gt;이 설계 방향은 무기나 상태 확장에 유연하고 애니메이션 그래프의 복잡도가 크게 감소하게 된다.&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 style=&quot;color: #e8e6e3; text-align: start;&quot; data-ke-size=&quot;size23&quot; data-darkreader-inline-color=&quot;&quot;&gt;&lt;b&gt;✅ Anim Layer Interface 생성 및 적용&lt;/b&gt;&lt;/h3&gt;
&lt;p&gt;&lt;figure class=&quot;imagegridblock&quot;&gt;
  &lt;div class=&quot;image-container&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/b61oJj/btsNKeKv9RA/3F3cn8tILk1VHmIMeiEle0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/b61oJj/btsNKeKv9RA/3F3cn8tILk1VHmIMeiEle0/img.png&quot; data-origin-width=&quot;766&quot; data-origin-height=&quot;1182&quot; data-is-animation=&quot;false&quot; style=&quot;width: 25.6556%; margin-right: 10px;&quot; data-widthpercent=&quot;25.96&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/b61oJj/btsNKeKv9RA/3F3cn8tILk1VHmIMeiEle0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fb61oJj%2FbtsNKeKv9RA%2F3F3cn8tILk1VHmIMeiEle0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;766&quot; height=&quot;1182&quot;/&gt;&lt;/span&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/BOfMS/btsNKbAg1By/SRzrF66Qi1CxZQfcqctND0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/BOfMS/btsNKbAg1By/SRzrF66Qi1CxZQfcqctND0/img.png&quot; data-origin-width=&quot;2551&quot; data-origin-height=&quot;1380&quot; data-is-animation=&quot;false&quot; style=&quot;width: 73.1816%;&quot; data-widthpercent=&quot;74.04&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/BOfMS/btsNKbAg1By/SRzrF66Qi1CxZQfcqctND0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FBOfMS%2FbtsNKbAg1By%2FSRzrF66Qi1CxZQfcqctND0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;2551&quot; height=&quot;1380&quot;/&gt;&lt;/span&gt;&lt;/div&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;에디터에서 Animation Layer Interface를 생성하고 클래스 이름을 &quot;ALI_Hero&quot;로 명명하였다.&lt;br /&gt;기본 Animation Layer 이름을 &quot;ArmedLocomotionState&quot;로 변경해주었고, 이게 설정 끝이다.&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imagegridblock&quot;&gt;
  &lt;div class=&quot;image-container&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bdIelk/btsNKhHi5UV/1S9F7FpDJ94kC7oBgXMypK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bdIelk/btsNKhHi5UV/1S9F7FpDJ94kC7oBgXMypK/img.png&quot; data-origin-width=&quot;515&quot; data-origin-height=&quot;516&quot; data-is-animation=&quot;false&quot; style=&quot;width: 46.3331%; margin-right: 10px;&quot; data-widthpercent=&quot;46.88&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bdIelk/btsNKhHi5UV/1S9F7FpDJ94kC7oBgXMypK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbdIelk%2FbtsNKhHi5UV%2F1S9F7FpDJ94kC7oBgXMypK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;515&quot; height=&quot;516&quot;/&gt;&lt;/span&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cDOHqU/btsNK0ygs5D/tNOrWeIwluxNGGVnKLHkV0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cDOHqU/btsNK0ygs5D/tNOrWeIwluxNGGVnKLHkV0/img.png&quot; data-origin-width=&quot;1416&quot; data-origin-height=&quot;1252&quot; data-is-animation=&quot;false&quot; style=&quot;width: 52.5041%;&quot; data-widthpercent=&quot;53.12&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cDOHqU/btsNK0ygs5D/tNOrWeIwluxNGGVnKLHkV0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcDOHqU%2FbtsNK0ygs5D%2FtNOrWeIwluxNGGVnKLHkV0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1416&quot; height=&quot;1252&quot;/&gt;&lt;/span&gt;&lt;/div&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;기본적으로 설정되어있는 ABP_Hero에서 Property Access 노드를 추가한다.&lt;br /&gt;OwningHeroCharacter&amp;rarr;HeroCombatComponent&amp;rarr;CurrentEquippedWeaponTag를 설정한다.&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imagegridblock&quot;&gt;
  &lt;div class=&quot;image-container&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/b2MfIX/btsNJ51osgd/XVa6dhEeccCzwesfUJMzS0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/b2MfIX/btsNJ51osgd/XVa6dhEeccCzwesfUJMzS0/img.png&quot; data-origin-width=&quot;1202&quot; data-origin-height=&quot;508&quot; data-is-animation=&quot;false&quot; style=&quot;width: 61.1125%; margin-right: 10px;&quot; data-widthpercent=&quot;61.83&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/b2MfIX/btsNJ51osgd/XVa6dhEeccCzwesfUJMzS0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fb2MfIX%2FbtsNJ51osgd%2FXVa6dhEeccCzwesfUJMzS0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1202&quot; height=&quot;508&quot;/&gt;&lt;/span&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/eVIeqG/btsNL05jV64/V1eDea5P0VlAGWpgvafIZK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/eVIeqG/btsNL05jV64/V1eDea5P0VlAGWpgvafIZK/img.png&quot; data-origin-width=&quot;1465&quot; data-origin-height=&quot;1003&quot; data-is-animation=&quot;false&quot; style=&quot;width: 37.7247%;&quot; data-widthpercent=&quot;38.17&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/eVIeqG/btsNL05jV64/V1eDea5P0VlAGWpgvafIZK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FeVIeqG%2FbtsNL05jV64%2FV1eDea5P0VlAGWpgvafIZK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1465&quot; height=&quot;1003&quot;/&gt;&lt;/span&gt;&lt;/div&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;Locomotion을 다음과 같이 정리한다.&lt;br /&gt;현재 이 AnimInstance를 소유하고 있는 캐릭터의 CombatComponent에 접근하여, 현재 장착중인 무기 Tag를 확인한다.&lt;br /&gt;유효하다면 ArmedLocomotionState를 사용하고 현재 장착 중인 무기가 없다면, 비무장 Locomotion으로 설정한다.&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;2553&quot; data-origin-height=&quot;1372&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/pKLIR/btsNMu51jWV/FpJAkDKMpkKYatMnjAADw0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/pKLIR/btsNMu51jWV/FpJAkDKMpkKYatMnjAADw0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/pKLIR/btsNMu51jWV/FpJAkDKMpkKYatMnjAADw0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FpKLIR%2FbtsNMu51jWV%2FFpJAkDKMpkKYatMnjAADw0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;2553&quot; height=&quot;1372&quot; data-origin-width=&quot;2553&quot; data-origin-height=&quot;1372&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;마지막으로 앞서 만들었던 Animation Layer Interface인 ALI_Hero를 설정해준다&lt;br /&gt;이 인터페이스로 무기별로 AnimInstance가 달라도 하나의 인터페이스로 공통 동작을 호출할 수 있게 된다.&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 style=&quot;color: #e8e6e3; text-align: start;&quot; data-darkreader-inline-color=&quot;&quot; data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;✅ Anim Layer Interface를 상속 받은 ABP_Layer 구현&lt;/b&gt;&lt;/h3&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;이전에 AnimInstacne 구조 설계할 때 생성했던 &quot;WarriorHeroLinkedAnimLayer&quot; 클래스로 각 무기 전용 Anim Layer(ABP)를 만들 것이다.&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 style=&quot;color: #e8e6e3; text-align: start;&quot; data-ke-size=&quot;size20&quot; data-darkreader-inline-color=&quot;&quot;&gt;&lt;b&gt; &lt;span&gt; WarriorHeroLinkedAnimLayer.h&lt;/span&gt;&lt;/b&gt;&lt;/h4&gt;
&lt;pre id=&quot;code_1746515446992&quot; class=&quot;cpp&quot; data-ke-language=&quot;cpp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;#pragma once

#include &quot;CoreMinimal.h&quot;
#include &quot;AnimIstances/RPGBaseAnimInstance.h&quot;
#include &quot;WarriorHeroLinkedAnimLayer.generated.h&quot;

class UWarriorHeroAnimInstance;

UCLASS()
class RPG_API UWarriorHeroLinkedAnimLayer : public URPGBaseAnimInstance
{
	GENERATED_BODY()
	
public:
	UFUNCTION(BlueprintPure, meta = (BlueprintThreadSafe))
	UWarriorHeroAnimInstance* GetHeroAnimInstance() const;
};&lt;/code&gt;&lt;/pre&gt;
&lt;h4 style=&quot;color: #e8e6e3; text-align: start;&quot; data-darkreader-inline-color=&quot;&quot; data-ke-size=&quot;size20&quot;&gt;&lt;b&gt; &lt;span&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;WarriorHeroLinkedAnimLayer.h&lt;/span&gt;&lt;/b&gt;&lt;/h4&gt;
&lt;pre id=&quot;code_1746515496280&quot; class=&quot;cpp&quot; data-ke-language=&quot;cpp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;#include &quot;AnimIstances/Hero/WarriorHeroLinkedAnimLayer.h&quot;
#include &quot;AnimIstances/Hero/WarriorHeroAnimInstance.h&quot;

UWarriorHeroAnimInstance* UWarriorHeroLinkedAnimLayer::GetHeroAnimInstance() const
{
    return Cast&amp;lt;UWarriorHeroAnimInstance&amp;gt;(GetOwningComponent()-&amp;gt;GetAnimInstance());
}&lt;/code&gt;&lt;/pre&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;메인으로 사용되는 AnimInstance에서 GroundSpeed를 가져와야 하므로, Instance 반환 함수를 구현하였다.&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imagegridblock&quot;&gt;
  &lt;div class=&quot;image-container&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/Efa5S/btsNL2PAHBi/jqfIHsNqUuEuqx5z4v4w0K/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/Efa5S/btsNL2PAHBi/jqfIHsNqUuEuqx5z4v4w0K/img.png&quot; data-origin-width=&quot;767&quot; data-origin-height=&quot;1171&quot; data-is-animation=&quot;false&quot; style=&quot;width: 34.5016%; margin-right: 10px;&quot; data-widthpercent=&quot;35.32&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/Efa5S/btsNL2PAHBi/jqfIHsNqUuEuqx5z4v4w0K/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FEfa5S%2FbtsNL2PAHBi%2FjqfIHsNqUuEuqx5z4v4w0K%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;767&quot; height=&quot;1171&quot;/&gt;&lt;/span&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/kDUlQ/btsNL0ROLo0/YXCNH9WrpM3aAkPf7UEtxk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/kDUlQ/btsNL0ROLo0/YXCNH9WrpM3aAkPf7UEtxk/img.png&quot; data-origin-width=&quot;533&quot; data-origin-height=&quot;888&quot; data-is-animation=&quot;false&quot; style=&quot;width: 31.6166%; margin-right: 10px;&quot; data-widthpercent=&quot;32.37&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/kDUlQ/btsNL0ROLo0/YXCNH9WrpM3aAkPf7UEtxk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FkDUlQ%2FbtsNL0ROLo0%2FYXCNH9WrpM3aAkPf7UEtxk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;533&quot; height=&quot;888&quot;/&gt;&lt;/span&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/dkxBUl/btsNMgtkT5z/q4D3n5ZksdjZ70X3z3gsE0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/dkxBUl/btsNMgtkT5z/q4D3n5ZksdjZ70X3z3gsE0/img.png&quot; data-origin-width=&quot;130&quot; data-origin-height=&quot;217&quot; data-is-animation=&quot;false&quot; style=&quot;width: 31.5562%;&quot; data-widthpercent=&quot;32.31&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/dkxBUl/btsNMgtkT5z/q4D3n5ZksdjZ70X3z3gsE0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FdkxBUl%2FbtsNMgtkT5z%2Fq4D3n5ZksdjZ70X3z3gsE0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;130&quot; height=&quot;217&quot;/&gt;&lt;/span&gt;&lt;/div&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;에디터로 돌아와, &quot;WarriorHeroLinkedAnimLayer&quot;를 상속받는 ABP를 생성해준다.&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imagegridblock&quot;&gt;
  &lt;div class=&quot;image-container&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/HrAN9/btsNK4HmEeh/ZZDkF04zu6tavMrWir3gKK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/HrAN9/btsNK4HmEeh/ZZDkF04zu6tavMrWir3gKK/img.png&quot; data-origin-width=&quot;2557&quot; data-origin-height=&quot;1336&quot; data-is-animation=&quot;false&quot; style=&quot;width: 52.0822%; margin-right: 10px;&quot; data-widthpercent=&quot;52.69&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/HrAN9/btsNK4HmEeh/ZZDkF04zu6tavMrWir3gKK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FHrAN9%2FbtsNK4HmEeh%2FZZDkF04zu6tavMrWir3gKK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;2557&quot; height=&quot;1336&quot;/&gt;&lt;/span&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cagSnT/btsNKeDNUEW/fyHEEjDalOhbt6bK4IKQa1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cagSnT/btsNKeDNUEW/fyHEEjDalOhbt6bK4IKQa1/img.png&quot; data-origin-width=&quot;1902&quot; data-origin-height=&quot;1107&quot; data-is-animation=&quot;false&quot; style=&quot;width: 46.755%;&quot; data-widthpercent=&quot;47.31&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cagSnT/btsNKeDNUEW/fyHEEjDalOhbt6bK4IKQa1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcagSnT%2FbtsNKeDNUEW%2FfyHEEjDalOhbt6bK4IKQa1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1902&quot; height=&quot;1107&quot;/&gt;&lt;/span&gt;&lt;/div&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;이 ABP도 마찬가지로 앞서 생성했던 Animation Layer Interface를 구현한다.&lt;br /&gt;좌측 &quot;ArmedLocomotionState&quot;를 더블클릭 해서 Animation Layer 세부 구현을 진행한다.&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imagegridblock&quot;&gt;
  &lt;div class=&quot;image-container&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/c6elHT/btsNMcR2yyh/3ARWVIY8rT7r2yBmjo5mm0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/c6elHT/btsNMcR2yyh/3ARWVIY8rT7r2yBmjo5mm0/img.png&quot; data-origin-width=&quot;926&quot; data-origin-height=&quot;747&quot; data-is-animation=&quot;false&quot; style=&quot;width: 44.3605%; margin-right: 10px;&quot; data-widthpercent=&quot;44.88&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/c6elHT/btsNMcR2yyh/3ARWVIY8rT7r2yBmjo5mm0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fc6elHT%2FbtsNMcR2yyh%2F3ARWVIY8rT7r2yBmjo5mm0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;926&quot; height=&quot;747&quot;/&gt;&lt;/span&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/zRP27/btsNMevw5IO/srCa8ksm5DgqwBuBkkefH1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/zRP27/btsNMevw5IO/srCa8ksm5DgqwBuBkkefH1/img.png&quot; data-origin-width=&quot;921&quot; data-origin-height=&quot;605&quot; data-is-animation=&quot;false&quot; style=&quot;width: 54.4767%;&quot; data-widthpercent=&quot;55.12&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/zRP27/btsNMevw5IO/srCa8ksm5DgqwBuBkkefH1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FzRP27%2FbtsNMevw5IO%2FsrCa8ksm5DgqwBuBkkefH1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;921&quot; height=&quot;605&quot;/&gt;&lt;/span&gt;&lt;/div&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;&quot;Blendspace Player&quot; 노드를 생성한 뒤 클릭해서 Detail 창에서 몇가지 설정을 해준다.&lt;br /&gt;Y값은 사용하지 않기 때문에 &quot;Expose As Pin&quot;을 눌러 핀을 없애주었고, Blend Space에 &quot;Expose As Pin&quot;을 눌러 핀을 활성화 해주었다.&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imagegridblock&quot;&gt;
  &lt;div class=&quot;image-container&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bUCfJl/btsNJZtlwDM/xIEVKTfg0tgK80W0XUmj50/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bUCfJl/btsNJZtlwDM/xIEVKTfg0tgK80W0XUmj50/img.png&quot; style=&quot;width: 43.2716%; margin-right: 10px;&quot; data-widthpercent=&quot;43.78&quot; data-is-animation=&quot;false&quot; data-origin-height=&quot;511&quot; data-origin-width=&quot;502&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bUCfJl/btsNJZtlwDM/xIEVKTfg0tgK80W0XUmj50/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbUCfJl%2FbtsNJZtlwDM%2FxIEVKTfg0tgK80W0XUmj50%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;502&quot; height=&quot;511&quot;/&gt;&lt;/span&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/WA9CR/btsNMUi3JVx/KEWGQSqHd22tfFhzyxUHc1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/WA9CR/btsNMUi3JVx/KEWGQSqHd22tfFhzyxUHc1/img.png&quot; style=&quot;width: 55.5656%;&quot; data-widthpercent=&quot;56.22&quot; data-is-animation=&quot;false&quot; data-origin-height=&quot;696&quot; data-origin-width=&quot;878&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/WA9CR/btsNMUi3JVx/KEWGQSqHd22tfFhzyxUHc1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FWA9CR%2FbtsNMUi3JVx%2FKEWGQSqHd22tfFhzyxUHc1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;878&quot; height=&quot;696&quot;/&gt;&lt;/span&gt;&lt;/div&gt;
&lt;/figure&gt;
&lt;figure class=&quot;imageblock widthContent&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;845&quot; data-origin-height=&quot;172&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bIU0vF/btsNKfQggdt/oUe6oIldgsU3LcFpjEiHB0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bIU0vF/btsNKfQggdt/oUe6oIldgsU3LcFpjEiHB0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bIU0vF/btsNKfQggdt/oUe6oIldgsU3LcFpjEiHB0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbIU0vF%2FbtsNKfQggdt%2FoUe6oIldgsU3LcFpjEiHB0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;845&quot; height=&quot;172&quot; data-origin-width=&quot;845&quot; data-origin-height=&quot;172&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;PropertyAcess 노드를 만든 뒤, 앞서 구현했던 &quot;GetHeroAnimInstance&quot; 함수를 통해 AnimInstance에서 GroundSpeed값을 가져온다.&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock widthContent&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;2553&quot; data-origin-height=&quot;1328&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bqtw5k/btsNMze9hIy/lzrwoE11B7Lz5qMKFHyWAK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bqtw5k/btsNMze9hIy/lzrwoE11B7Lz5qMKFHyWAK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bqtw5k/btsNMze9hIy/lzrwoE11B7Lz5qMKFHyWAK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fbqtw5k%2FbtsNMze9hIy%2FlzrwoE11B7Lz5qMKFHyWAK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;2553&quot; height=&quot;1328&quot; data-origin-width=&quot;2553&quot; data-origin-height=&quot;1328&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;&quot;Blendspace Player&quot; 노드의 X축 핀에 GroundSpeed 값을 연결해주고, Blend Space는 변수로 승격시켜준다.&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imagegridblock&quot;&gt;
  &lt;div class=&quot;image-container&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/dXRJF7/btsNMQVgSQl/iYAptwwz1Kl1y1jjFPLnvk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/dXRJF7/btsNMQVgSQl/iYAptwwz1Kl1y1jjFPLnvk/img.png&quot; data-origin-width=&quot;498&quot; data-origin-height=&quot;1043&quot; data-is-animation=&quot;false&quot; style=&quot;width: 21.1562%; margin-right: 10px;&quot; data-widthpercent=&quot;21.66&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/dXRJF7/btsNMQVgSQl/iYAptwwz1Kl1y1jjFPLnvk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FdXRJF7%2FbtsNMQVgSQl%2FiYAptwwz1Kl1y1jjFPLnvk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;498&quot; height=&quot;1043&quot;/&gt;&lt;/span&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/n9hJ3/btsNMJPrbVE/ESW6kbr2FrIkczAfQL5Xvk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/n9hJ3/btsNMJPrbVE/ESW6kbr2FrIkczAfQL5Xvk/img.png&quot; data-origin-width=&quot;676&quot; data-origin-height=&quot;597&quot; data-is-animation=&quot;false&quot; style=&quot;width: 50.1723%; margin-right: 10px;&quot; data-widthpercent=&quot;51.37&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/n9hJ3/btsNMJPrbVE/ESW6kbr2FrIkczAfQL5Xvk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fn9hJ3%2FbtsNMJPrbVE%2FESW6kbr2FrIkczAfQL5Xvk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;676&quot; height=&quot;597&quot;/&gt;&lt;/span&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/NFuWq/btsNLUjRgGX/WEf2fQCAkuQkkQnWDWPc20/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/NFuWq/btsNLUjRgGX/WEf2fQCAkuQkkQnWDWPc20/img.png&quot; data-origin-width=&quot;132&quot; data-origin-height=&quot;222&quot; data-is-animation=&quot;false&quot; style=&quot;width: 26.3459%;&quot; data-widthpercent=&quot;26.97&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/NFuWq/btsNLUjRgGX/WEf2fQCAkuQkkQnWDWPc20/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FNFuWq%2FbtsNLUjRgGX%2FWEf2fQCAkuQkkQnWDWPc20%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;132&quot; height=&quot;222&quot;/&gt;&lt;/span&gt;&lt;/div&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;지금 만든 &quot;MasterAnimLayer_Hero&quot; 클래스를 상속받는 ABP 클래스를 생성한다.&lt;br /&gt;이 클래스를 상속받는 ABP가 각 무기마다 전용 Animation을 담는 클래스가 될 것이다.&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock widthContent&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;2563&quot; data-origin-height=&quot;1366&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bdXfik/btsNMAZpNoi/3wooDAASqXJlqzcyfvdUvK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bdXfik/btsNMAZpNoi/3wooDAASqXJlqzcyfvdUvK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bdXfik/btsNMAZpNoi/3wooDAASqXJlqzcyfvdUvK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbdXfik%2FbtsNMAZpNoi%2F3wooDAASqXJlqzcyfvdUvK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;2563&quot; height=&quot;1366&quot; data-origin-width=&quot;2563&quot; data-origin-height=&quot;1366&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;이 Axe 무기 전용 Layer에 BlendSpace만 설정해준다. 앞선 포스팅에서 BlendSpace 1D 만드는 과정을 서술했으므로 BS를 만드는 과정은 따로 정리하지 않는다.&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 style=&quot;color: #e8e6e3; text-align: start;&quot; data-ke-size=&quot;size23&quot; data-darkreader-inline-color=&quot;&quot;&gt;&lt;b&gt;✅ 각 무기 클래스에 AnimLayer 정보 저장&lt;/b&gt;&lt;/h3&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;각 무기마다 AnimLayer(ABP 클래스)를 저장해놓고, 특정 무기를 들었을 때 해당 ABP를 캐릭터에 동적으로 연결해줌으로써, 자동으로 무기별 전용 애니메이션이 동작하게 만드는 구조이다.&lt;br /&gt;&lt;br /&gt;먼저 프로젝트 전체 구조체를 정리할 클래스를 만들고 그 안에 AnimLayer를 저장할 구조체를 선언한 뒤, 무기에 해당 구조체를 멤버변수로 가지고 있게할 것이다.&lt;br /&gt;이후 GA_EquipAxe와 같은 Ability에서 무기를 장착하면 SkeletalMesh에 AnimLayer를 연결해주도록 한다.&lt;/blockquote&gt;
&lt;h4 style=&quot;color: #e8e6e3; text-align: start;&quot; data-ke-size=&quot;size20&quot; data-darkreader-inline-color=&quot;&quot;&gt; &lt;span&gt;&amp;nbsp;&lt;b&gt;RPGStructTypes.h&lt;/b&gt;&lt;/span&gt;&lt;/h4&gt;
&lt;pre id=&quot;code_1746517609021&quot; class=&quot;cpp&quot; data-ke-language=&quot;cpp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;#pragma once

#include &quot;RPGStructTypes.generated.h&quot;

class UWarriorHeroLinkedAnimLayer;

USTRUCT(BlueprintType)
struct FRPGHeroWeaponData
{
	GENERATED_BODY()

	UPROPERTY(EditDefaultsOnly, BlueprintReadOnly)
	TSubclassOf&amp;lt;UWarriorHeroLinkedAnimLayer&amp;gt; WeaponAnimLayerToLink;
};&lt;/code&gt;&lt;/pre&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;방금 만들었던 도끼 전용 AnimLayer와 같이 무기별 AnimLayer를 담을 수 있는 구조체를 선언하였다.&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 style=&quot;color: #e8e6e3; text-align: start;&quot; data-darkreader-inline-color=&quot;&quot; data-ke-size=&quot;size20&quot;&gt;&lt;b&gt; &lt;span&gt; WarriorHeroWeapon.h&lt;/span&gt;&lt;/b&gt;&lt;/h4&gt;
&lt;pre id=&quot;code_1746517756387&quot; class=&quot;cpp&quot; data-ke-language=&quot;cpp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;#pragma once

#include &quot;CoreMinimal.h&quot;
#include &quot;Items/Weapons/RPGWeaponBase.h&quot;
#include &quot;RPGTypes/RPGStructTypes.h&quot;
#include &quot;WarriorHeroWeapon.generated.h&quot;

UCLASS()
class RPG_API AWarriorHeroWeapon : public ARPGWeaponBase
{
	GENERATED_BODY()
	
public:
	UPROPERTY(EditDefaultsOnly, BlueprintReadOnly, Category = &quot;WeaponData&quot;)
	FRPGHeroWeaponData HeroWeaponData;
};&lt;/code&gt;&lt;/pre&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;플레이어 무기 클래스에 앞서 만든 구조체 타입 변수를 선언해준다.&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 style=&quot;color: #e8e6e3; text-align: start;&quot; data-ke-size=&quot;size20&quot; data-darkreader-inline-color=&quot;&quot;&gt;&lt;b&gt; &lt;span&gt;&lt;span&gt; HeroCombatComponent&lt;/span&gt;.h&lt;/span&gt;&lt;/b&gt;&lt;/h4&gt;
&lt;pre id=&quot;code_1746517945931&quot; class=&quot;cpp&quot; data-ke-language=&quot;cpp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;#pragma once

#include &quot;CoreMinimal.h&quot;
#include &quot;Components/Combat/PawnCombatComponent.h&quot;
#include &quot;HeroCombatComponent.generated.h&quot;

class AWarriorHeroWeapon;

UCLASS()
class RPG_API UHeroCombatComponent : public UPawnCombatComponent
{
	GENERATED_BODY()
	
public:
	UFUNCTION(BlueprintCallable, Category = &quot;RPG|Combat&quot;)
	AWarriorHeroWeapon* GetHeroCarriedWeaponByTag(FGameplayTag InWeaponTag) const;
};&lt;/code&gt;&lt;/pre&gt;
&lt;h4 style=&quot;color: #e8e6e3; text-align: start;&quot; data-darkreader-inline-color=&quot;&quot; data-ke-size=&quot;size20&quot;&gt;&lt;b&gt; &lt;span&gt;&lt;span&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;HeroCombatComponent&lt;/span&gt;.cpp&lt;/span&gt;&lt;/b&gt;&lt;/h4&gt;
&lt;pre id=&quot;code_1746517954278&quot; class=&quot;cpp&quot; data-ke-language=&quot;cpp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;#include &quot;Components/Combat/HeroCombatComponent.h&quot;
#include &quot;Items/Weapons/WarriorHeroWeapon.h&quot;

AWarriorHeroWeapon* UHeroCombatComponent::GetHeroCarriedWeaponByTag(FGameplayTag InWeaponTag) const
{
   return Cast&amp;lt;AWarriorHeroWeapon&amp;gt;(GetCharacterCarriedWeaponByTag(InWeaponTag));
}&lt;/code&gt;&lt;/pre&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;GA에서 HeroCombatComponet를 가져와서 Tag기반으로 바로 무기 클래스를 가져와, 무기 클래스 안의 AnimLayer 데이터를 가져올텐데 조금 더 BP에서 코드를 단순화 하기 위해, HeroWeapon타입으로 캐스팅해 반환하는 헬퍼 함수를 작성해주었다.&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock widthContent&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;2258&quot; data-origin-height=&quot;653&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/QF63V/btsNK0LKRy9/kxkkANt5JYBxU8Cazck7f0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/QF63V/btsNK0LKRy9/kxkkANt5JYBxU8Cazck7f0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/QF63V/btsNK0LKRy9/kxkkANt5JYBxU8Cazck7f0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FQF63V%2FbtsNK0LKRy9%2FkxkkANt5JYBxU8Cazck7f0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;2258&quot; height=&quot;653&quot; data-origin-width=&quot;2258&quot; data-origin-height=&quot;653&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;figure class=&quot;imageblock widthContent&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1995&quot; data-origin-height=&quot;576&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/boXoIa/btsNLVbYJKp/PR9CYsfwQNUcxbcWu6Vtp0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/boXoIa/btsNLVbYJKp/PR9CYsfwQNUcxbcWu6Vtp0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/boXoIa/btsNLVbYJKp/PR9CYsfwQNUcxbcWu6Vtp0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FboXoIa%2FbtsNLVbYJKp%2FPR9CYsfwQNUcxbcWu6Vtp0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1995&quot; height=&quot;576&quot; data-origin-width=&quot;1995&quot; data-origin-height=&quot;576&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;컴파일 후, 에디터로 돌아와 &quot;GA_Hero_EquipAxe&quot;에 해당 무기안에 AnimLayer 데이터를 가져와서 연결시켜주었다.&lt;br /&gt;그리고 Hero Combat Component에 현재 장착중인 무기의 Tag를 설정해주었다.&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock widthContent&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;2554&quot; data-origin-height=&quot;1245&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cJSVEM/btsNKxC8pK0/JmyqzdToeHoDosKzTqk6h1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cJSVEM/btsNKxC8pK0/JmyqzdToeHoDosKzTqk6h1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cJSVEM/btsNKxC8pK0/JmyqzdToeHoDosKzTqk6h1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcJSVEM%2FbtsNKxC8pK0%2FJmyqzdToeHoDosKzTqk6h1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;2554&quot; height=&quot;1245&quot; data-origin-width=&quot;2554&quot; data-origin-height=&quot;1245&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;마지막으로 해당 무기에 AnimLayer 데이터를 할당해준다.&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 style=&quot;color: #c8c3bc; text-align: start;&quot; data-ke-size=&quot;size23&quot; data-darkreader-inline-color=&quot;&quot;&gt;&lt;b&gt; &lt;span&gt; 테스트 결과&lt;/span&gt;&lt;/b&gt;&lt;/h3&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock widthContent&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;2025-05-06 17-03-25.mp4_20250506_170408.gif&quot; data-origin-width=&quot;854&quot; data-origin-height=&quot;480&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/6dWKx/btsNLuZXneu/nv8bQh5brIFjLnMWq9ja1K/img.gif&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/6dWKx/btsNLuZXneu/nv8bQh5brIFjLnMWq9ja1K/img.gif&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/6dWKx/btsNLuZXneu/nv8bQh5brIFjLnMWq9ja1K/img.gif&quot; srcset=&quot;https://blog.kakaocdn.net/dn/6dWKx/btsNLuZXneu/nv8bQh5brIFjLnMWq9ja1K/img.gif&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;854&quot; height=&quot;480&quot; data-filename=&quot;2025-05-06 17-03-25.mp4_20250506_170408.gif&quot; data-origin-width=&quot;854&quot; data-origin-height=&quot;480&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;무기가 계속 추가되더라도 기존 메인으로 사용되는 &quot;ABP_Hero&quot;에서 수정하는 것이 아닌,&lt;br /&gt;&quot;MasterAnimLayer_Hero&quot; 클래스를 상속 받은 &quot;AnimLayer_HeroAxe&quot;와 같은 무기 전용 ABP를 만들어서 무기에 넣어주기만 하면 자동으로 해당 애니메이션이 재생되는 구조가 되었다.&lt;br /&gt;이로써 슈팅 게임에서 권총 혹은 소총, 저격총 등 여러 무기를 장착했을 때 개별적인 애니메이션을 보다 더 쉽게 적용시킬 수 있을 것 같다.&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>내배캠/GAS(Gameplay Ability System)</category>
      <author>동그래님</author>
      <guid isPermaLink="true">https://dong-grae.tistory.com/229</guid>
      <comments>https://dong-grae.tistory.com/229#entry229comment</comments>
      <pubDate>Tue, 6 May 2025 17:10:06 +0900</pubDate>
    </item>
  </channel>
</rss>