<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0">
  <channel>
    <title>공부 정리 블로그</title>
    <link>https://gasick.tistory.com/</link>
    <description>공부한것을 잊어버리지 않게 정리할 목적인 블로그</description>
    <language>ko</language>
    <pubDate>Fri, 12 Jun 2026 13:59:33 +0900</pubDate>
    <generator>TISTORY</generator>
    <ttl>100</ttl>
    <managingEditor>gasick</managingEditor>
    <image>
      <title>공부 정리 블로그</title>
      <url>https://tistory1.daumcdn.net/tistory/7736396/attach/a6c6dda0f2b44e6f932a183c9d5a98b9</url>
      <link>https://gasick.tistory.com</link>
    </image>
    <item>
      <title>아이템 시스템(트리거)</title>
      <link>https://gasick.tistory.com/22</link>
      <description>&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;537&quot; data-origin-height=&quot;352&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/dgl2k8/btsNt1jv29Y/b79ZBP3oKAmUUK6kYu46Q0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/dgl2k8/btsNt1jv29Y/b79ZBP3oKAmUUK6kYu46Q0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/dgl2k8/btsNt1jv29Y/b79ZBP3oKAmUUK6kYu46Q0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fdgl2k8%2FbtsNt1jv29Y%2Fb79ZBP3oKAmUUK6kYu46Q0%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;537&quot; height=&quot;352&quot; data-origin-width=&quot;537&quot; data-origin-height=&quot;352&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;데이터 애셋 생성&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;&amp;lt;.h&amp;gt;&lt;/b&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1745223834175&quot; class=&quot;cpp&quot; data-ke-language=&quot;cpp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;// Fill out your copyright notice in the Description page of Project Settings.

#pragma once

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

//아이템 종류 열거형.
//블루프린트와 호환되도록 BlueprintType 지정.
UENUM(BlueprintType)
enum class EItemType : uint8
{
	Weapon = 0,
	Potion,
	Scroll
};

/**
 * 
 */
UCLASS()
class ARENABATTLEDEMO_API UABItemData : public UPrimaryDataAsset
{
	GENERATED_BODY()

public:
	//아이템 타입을 지정하는 열거형 변수.
	UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = Type)
	EItemType Type;
	
};&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;ItemData를 상속 받는 WeaponItemData 애셋 생성&lt;/b&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1745223904832&quot; class=&quot;cpp&quot; data-ke-language=&quot;cpp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;// Fill out your copyright notice in the Description page of Project Settings.

#pragma once

#include &quot;CoreMinimal.h&quot;
#include &quot;Item/ABItemData.h&quot;
#include &quot;ABWeaponItemData.generated.h&quot;

/**
 * 
 */
UCLASS()
class ARENABATTLEDEMO_API UABWeaponItemData : public UABItemData
{
	GENERATED_BODY()

public:
	//제공할 무기에 대한 스켈레탈 메시.
	UPROPERTY(EditAnywhere, Category = Weapon)
	TSoftObjectPtr&amp;lt;class USkeletalMesh&amp;gt; WeaponMesh;
	
};&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;&lt;b&gt;아이템 박스 객체 클래스 생성&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;&amp;lt;.h&amp;gt;&lt;/b&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1745223999799&quot; class=&quot;cpp&quot; data-ke-language=&quot;cpp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;// Fill out your copyright notice in the Description page of Project Settings.

#pragma once

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

UCLASS()
class ARENABATTLEDEMO_API AABItemBox : public AActor
{
	GENERATED_BODY()
	
public:	
	// Sets default values for this actor's properties
	AABItemBox();

protected:
	//박스 컴포넌트의 오버랩 델리게이트에 등록할 함수.
	//OnComponentBeginOverlap 델리게이트는 다이나믹으로 지정되어 있기대문에
	//UFUNCTION() 매크로를 지정해야 한다.
	UFUNCTION()
	void OnOverlapBegin(UPrimitiveComponent* OverlappedComponent,
		AActor* OtherActor,
		UPrimitiveComponent* OtherComp,
		int32 OtherBodyIndex,
		bool bFromSweep,
		const FHitResult&amp;amp; SweepResult);

	//파티클 재생 종료 시 발행되는 델리게이트에 등록 할 함수.
	UFUNCTION()
	void OnEffectFinished(class UParticleSystemComponent* PSystem );

protected:
	//액터의 충돌을 담당할 박스 컴포넌트.
	UPROPERTY(VisibleAnywhere, Category = Box)
	TObjectPtr&amp;lt;class UBoxComponent&amp;gt; TriggerBox;

	//아이템 박스를 보여줄 메시 컴포넌트.
	UPROPERTY(VisibleAnywhere, Category = Box)
	TObjectPtr&amp;lt;class UStaticMeshComponent&amp;gt; Mesh;

	//박스와 상호작용 했을때 보여줄 파티클 효과 컴포넌트.(나이아가라 X)
	UPROPERTY(VisibleAnywhere, Category = Box)
	TObjectPtr&amp;lt;class UParticleSystemComponent&amp;gt; Effect;

	//아이템 정보.
	UPROPERTY(EditAnywhere, Category = Item)
	TObjectPtr&amp;lt;class UABItemData&amp;gt; Item;
	
};&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;&amp;lt;.cpp&amp;gt;&lt;/b&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1745224021311&quot; class=&quot;cpp&quot; data-ke-language=&quot;cpp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;// Fill out your copyright notice in the Description page of Project Settings.


#include &quot;Item/ABItemBox.h&quot;
#include &quot;Components/BoxComponent.h&quot;
#include &quot;Components/StaticMeshComponent.h&quot;
#include &quot;Particles/ParticleSystemComponent.h&quot;
#include &quot;Physics/ABCollision.h&quot;
#include &quot;Interface/ABCharacterItemInterface.h&quot;
#include &quot;ABWeaponItemData.h&quot;

// Sets default values
AABItemBox::AABItemBox()
{
	TriggerBox = CreateDefaultSubobject&amp;lt;UBoxComponent&amp;gt;(TEXT(&quot;TriggerBox&quot;));
	Mesh = CreateDefaultSubobject&amp;lt;UStaticMeshComponent&amp;gt;(TEXT(&quot;Mesh&quot;));
	Effect = CreateDefaultSubobject&amp;lt;UParticleSystemComponent&amp;gt;(TEXT(&quot;Effect&quot;));

	//계층설정.
	RootComponent = TriggerBox;
	Mesh-&amp;gt;SetupAttachment(TriggerBox);
	Effect-&amp;gt;SetupAttachment(TriggerBox);

	//콜리전 프로파일 설정.
	TriggerBox-&amp;gt;SetCollisionProfileName(CPROFILE_ABTRIGGER);
	TriggerBox-&amp;gt;SetBoxExtent(FVector(40.0f, 42.0f, 30.0f));
	//트리거가 발생하는 다이나믹 델리게이트에 함수 등록.
	TriggerBox-&amp;gt;OnComponentBeginOverlap.AddDynamic(this, &amp;amp;AABItemBox::OnOverlapBegin);
	
	Mesh-&amp;gt;SetCollisionProfileName(TEXT(&quot;NoCollision&quot;));

	//애셋 로드.
	static ConstructorHelpers::FObjectFinder&amp;lt;UStaticMesh&amp;gt; BoxMeshRef(TEXT(&quot;/Game/ArenaBattle/Environment/Props/SM_Env_Breakables_Box1.SM_Env_Breakables_Box1&quot;));
	if (BoxMeshRef.Object)
	{
		Mesh-&amp;gt;SetStaticMesh(BoxMeshRef.Object);
	}
	//메시 컴포넌트의 위치 조정.
	Mesh-&amp;gt;AddRelativeLocation(FVector(0.0f, -3.5f, -30.0f));

	//파티클 애셋 로드.
	static ConstructorHelpers::FObjectFinder&amp;lt;UParticleSystem&amp;gt; EffectRef(TEXT(&quot;/Game/ArenaBattle/Effect/P_TreasureChest_Open_Mesh.P_TreasureChest_Open_Mesh&quot;));
	if (EffectRef.Object)
	{
		//파티클 애셋 설정.
		Effect-&amp;gt;SetTemplate(EffectRef.Object);

		//바로 재생되지 않도록 설정.
		Effect-&amp;gt;bAutoActivate = false;
	}

	//static ConstructorHelpers::FObjectFinder&amp;lt;UABItemData&amp;gt; ItemDataRef(TEXT(&quot;/Game/ArenaBattle/Item/Weapon/ABIW_Weapon.ABIW_Weapon&quot;));
	static ConstructorHelpers::FObjectFinder&amp;lt;UABItemData&amp;gt; ItemDataRef(TEXT(&quot;/Game/ArenaBattle/Item/Weapon/ABIW_Weapon.ABIW_Weapon&quot;));
	if (ItemDataRef.Object)
	{
		Item = ItemDataRef.Object;
	}
	
}

void AABItemBox::OnOverlapBegin(UPrimitiveComponent* OverlappedComponent, AActor* OtherActor,
	UPrimitiveComponent* OtherComp, int32 OtherBodyIndex, bool bFromSweep, const FHitResult&amp;amp; SweepResult)
{
	//꽝 상자도 있다고 가정.
	//Item이 Nullptr이면 꽝.
	if (!Item)
	{
		Destroy();
		return;
	}

	//아이템이 있으면 캐릭터에 아이템 획득 메시지 전달.
	IABCharacterItemInterface* OverlappedPawn = Cast&amp;lt;IABCharacterItemInterface&amp;gt;(OtherActor);
	if (OverlappedPawn)
	{
		OverlappedPawn-&amp;gt;TakeItem(Item);
	}
	
	//파티클 재생.
	Effect-&amp;gt;Activate();

	//매시는 안보이도록 처리(비활성화).
	Mesh-&amp;gt;SetHiddenInGame(true);

	//액터의 콜리전 끄기.
	SetActorEnableCollision(false);

	//파티클 재생 종료시 발행되는 델리게이트에 함수 등록.
	Effect-&amp;gt;OnSystemFinished.AddDynamic(this, &amp;amp;AABItemBox::OnEffectFinished);
}

void AABItemBox::OnEffectFinished(class UParticleSystemComponent* PSystem)
{
	//파티클 재생이 완료되면 액터 삭제.
	Destroy();
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;캐릭터Base클래스에 Item에 관한 코드 추가&lt;/b&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1745224091711&quot; class=&quot;cpp&quot; data-ke-language=&quot;cpp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;	//ItemSection.
protected:

	//래퍼 구조체를 관리할수 있는 배열.
	UPROPERTY()
	TArray&amp;lt;FTakeItemDelegateWrapper&amp;gt; TakeItemActions;

	//아이템 획득시 호출될 함수.
	virtual void TakeItem(class UABItemData* InItemData) override;

	//아이템 종류마다 처리될 함수 선언.
	virtual void DrinkPortion(class UABItemData* InItemData);
	virtual void EquipWeapon(class UABItemData* InItemData);
	virtual void ReadScroll(class UABItemData* InItemData);

	//무기 아이템을 획득했을 때 사용할 스켈레탈 메시 컴포넌트.
	UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = Equipent, meta = (AllowPrivateAccess = &quot;true&quot;))
	TObjectPtr&amp;lt;class USkeletalMeshComponent&amp;gt; Weapon;&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;생성자에서 델리게이트 함수등록 및 스켈레탈 메시 생성.&lt;/b&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1745224231649&quot; class=&quot;cpp&quot; data-ke-language=&quot;cpp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;	//Item Section.
	TakeItemActions.Add(FTakeItemDelegateWrapper(FOnTakeItemDelegate::CreateUObject(this, &amp;amp;AABCharacterBase::EquipWeapon)));
	TakeItemActions.Add(FTakeItemDelegateWrapper(FOnTakeItemDelegate::CreateUObject(this, &amp;amp;AABCharacterBase::DrinkPortion)));
	TakeItemActions.Add(FTakeItemDelegateWrapper(FOnTakeItemDelegate::CreateUObject(this, &amp;amp;AABCharacterBase::ReadScroll)));

	//무기를 보여줄 스켈레탈 메쉬 생성.
	Weapon = CreateDefaultSubobject&amp;lt;USkeletalMeshComponent&amp;gt;(TEXT(&quot;Weapon&quot;));
	//메시 컴포넌트 하위로 계층을 설정하고, 이때 hand_rSocket 소켓에 부착.
	Weapon-&amp;gt;SetupAttachment(GetMesh(), TEXT(&quot;hand_rSocket&quot;));&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;아이템의 트리거 충돌시 TakeItem()함수 호출.&lt;/p&gt;
&lt;pre id=&quot;code_1745224347752&quot; class=&quot;cpp&quot; data-ke-language=&quot;cpp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;void AABCharacterBase::TakeItem(class UABItemData* InItemData)
{
	//아이템 정보가 넘어오면 처리.
	if (InItemData)
	{
		TakeItemActions[(uint8)InItemData-&amp;gt;Type].ItemDelegate.ExecuteIfBound(InItemData);//C# : ?.Invoke()랑 같은 함수이다.
	}
}

void AABCharacterBase::DrinkPortion(class UABItemData* InItemData)
{
	UE_LOG(LogTemp, Log, TEXT(&quot;DrinkPortion&quot;));
}

void AABCharacterBase::EquipWeapon(class UABItemData* InItemData)
{
	//UE_LOG(LogTemp, Log, TEXT(&quot;EquipWeapon&quot;));
	//함수에 전달된 아이템 데이터 애셋을 무기 데이터로 변환.
	UABWeaponItemData* WeaponItemData = Cast&amp;lt;UABWeaponItemData&amp;gt;(InItemData);
	if (WeaponItemData)
	{
		//무기 메시가 아직 로딩 안된 경우 로드 처리.
		if (WeaponItemData-&amp;gt;WeaponMesh.IsPending())
		{
			WeaponItemData-&amp;gt;WeaponMesh.LoadSynchronous();	
		}
		//무기 컴포넌트에 로드가 완료된 스켈레탈 메시 설정.
		Weapon-&amp;gt;SetSkeletalMesh(WeaponItemData-&amp;gt;WeaponMesh.Get());
	}
}

void AABCharacterBase::ReadScroll(class UABItemData* InItemData)
{
	UE_LOG(LogTemp, Log, TEXT(&quot;ReadScroll&quot;));
}&lt;/code&gt;&lt;/pre&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt; 소프트 레퍼런싱 vs 하드 레퍼런싱&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;bull; 액터 로딩시 TObjectPtr로 선언한 언리얼 오브젝트도 따라서 메모리에 로딩됨.&lt;br /&gt;&amp;bull; 이를 하드 레퍼런싱이라고 함.&lt;br /&gt;&amp;bull; 게임 진행에 필수적인 언리얼 오브젝트는 이렇게 선언해도 되지만 아이템의 경우? &lt;br /&gt;&amp;bull; 데이터 라이브러리에 1000종의 아이템 목록이 있을 때 이를 모두 다 로딩할 것인가? &lt;br /&gt;&amp;bull; 필요한 데이터만 로딩하도록 TSoftObjectPtr로 선언하고 대신 애셋 주소 문자열을 지정함&lt;br /&gt;&amp;bull; 필요시에 애셋을 로딩하도록 구현을 변경할 수 있으나 애셋 로딩 시간이 소요됨. &lt;br /&gt;&amp;bull; 현재 게임에서 로딩되어 있는 스켈레탈 메시의 목록을 살펴보기&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock widthContent&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;952&quot; data-origin-height=&quot;183&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bKS90s/btsNtrXufJa/H3WdHWqUcx2K3loLwYK4EK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bKS90s/btsNtrXufJa/H3WdHWqUcx2K3loLwYK4EK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bKS90s/btsNtrXufJa/H3WdHWqUcx2K3loLwYK4EK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbKS90s%2FbtsNtrXufJa%2FH3WdHWqUcx2K3loLwYK4EK%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;952&quot; height=&quot;183&quot; data-origin-width=&quot;952&quot; data-origin-height=&quot;183&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;</description>
      <category>언리얼 엔진 공부/언리얼C++</category>
      <author>gasick</author>
      <guid isPermaLink="true">https://gasick.tistory.com/22</guid>
      <comments>https://gasick.tistory.com/22#entry22comment</comments>
      <pubDate>Mon, 21 Apr 2025 17:34:31 +0900</pubDate>
    </item>
    <item>
      <title>캐릭터 공격 판정</title>
      <link>https://gasick.tistory.com/21</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;월드가 제공하는 충돌 판정 서비스를 사용&lt;br /&gt;&amp;bull; 월드는 크게 세 가지의 충돌 판정 서비스를 제공함.&lt;br /&gt;&amp;bull; 월드 내 배치된 충돌체와 충돌하는지 파악하고, 충돌한 액터 정보를 얻을 수 있음.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock widthContent&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;954&quot; data-origin-height=&quot;323&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cK9JQj/btsNo14ONrx/GaeWQdHwKemvn2qsttbs41/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cK9JQj/btsNo14ONrx/GaeWQdHwKemvn2qsttbs41/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cK9JQj/btsNo14ONrx/GaeWQdHwKemvn2qsttbs41/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcK9JQj%2FbtsNo14ONrx%2FGaeWQdHwKemvn2qsttbs41%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;954&quot; height=&quot;323&quot; data-origin-width=&quot;954&quot; data-origin-height=&quot;323&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;bull; 액션 판정을 위한 트레이스 채널의 생성 : ABAction. 기본 반응은 무시&lt;br /&gt;&amp;bull; 캐릭터 캡슐용 프로필 : ABAction 트레이스 채널에 반응. 오브젝트 타입은 Pawn &lt;br /&gt;&amp;bull; 스켈레탈 메시용 프로필 : 랙돌 구현을 위해 주로 활용됨.&lt;br /&gt;&amp;bull; 기믹 트리거용 프로필 : 폰 캡슐에만 반응하도록 설정. 오브젝트 타입은 WorldStatic&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock widthContent&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;996&quot; data-origin-height=&quot;405&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/dJ4Aj2/btsNqDvy06A/gLNCAqIbtSJwjGAoqlcVb1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/dJ4Aj2/btsNqDvy06A/gLNCAqIbtSJwjGAoqlcVb1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/dJ4Aj2/btsNqDvy06A/gLNCAqIbtSJwjGAoqlcVb1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FdJ4Aj2%2FbtsNqDvy06A%2FgLNCAqIbtSJwjGAoqlcVb1%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;996&quot; height=&quot;405&quot; data-origin-width=&quot;996&quot; data-origin-height=&quot;405&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt; 월드 트레이싱 함수의 선택&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;bull; 세 가지 카테고리로 원하는 함수 이름을 얻을 수 있음.&lt;br /&gt;&amp;nbsp;&amp;bull; 카테고리 1 : 처리 방법 &lt;br /&gt;&amp;nbsp;&amp;bull; 카테고리 2 : 대상 &lt;br /&gt;&amp;nbsp; &amp;bull; Test : 무언가 감지되었는지를 테스트 &lt;br /&gt;&amp;nbsp; &amp;bull; Single 또는 AnyTest : 감지된 단일 물체 정보를 반환 &lt;br /&gt;&amp;nbsp; &amp;bull; Multi : 감지된 모든 물체 정보를 배열로 반환&lt;br /&gt;&amp;nbsp;&amp;bull; 카테고리 3 : 처리 설정 &lt;br /&gt;&amp;nbsp; &amp;bull; ByChannel : 채널 정보를 사용해 감지 &amp;bull; ByObjectType : 물체에 지정된 물리 타입 정보를 사용해 감지 &amp;bull; ByProfile : 프로필 정보를 사용해 감지&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt; {처리방법}{대상}{처리설정}&lt;/b&gt;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt; 캐릭터 공격 판정의 구현&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;bull; 캐릭터의 위치에서 시선 방향으로 물체가 있는지 감지&lt;br /&gt;&amp;bull; 작은 구체를 제작하고 시선 방향으로 특정 거리까지만 투사.&lt;br /&gt;&amp;bull; 하나의 물체만 감지 &lt;br /&gt;&amp;bull; 트레이스 채널을 사용해 감지&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;502&quot; data-origin-height=&quot;211&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/Jx8U4/btsNqA6HWa0/qbHcGknJMKSKuDl8XnH8Y1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/Jx8U4/btsNqA6HWa0/qbHcGknJMKSKuDl8XnH8Y1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/Jx8U4/btsNqA6HWa0/qbHcGknJMKSKuDl8XnH8Y1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FJx8U4%2FbtsNqA6HWa0%2FqbHcGknJMKSKuDl8XnH8Y1%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;211&quot; data-origin-width=&quot;502&quot; data-origin-height=&quot;211&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt; 물리 충돌 테스트&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;bull;&amp;nbsp;디버그&amp;nbsp;드로잉&amp;nbsp;함수를&amp;nbsp;사용해&amp;nbsp;물리&amp;nbsp;충돌을&amp;nbsp;시각적으로&amp;nbsp;테스트 &lt;br /&gt;&amp;bull;&amp;nbsp;90도로&amp;nbsp;회전시킨&amp;nbsp;캡슐을&amp;nbsp;그리기&lt;b&gt;&lt;br /&gt;&lt;/b&gt;&amp;nbsp; &amp;bull; Origin &lt;br /&gt;&amp;nbsp; &amp;bull; HalfHeight &lt;br /&gt;&amp;nbsp; &amp;bull; Radius&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock widthContent&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1129&quot; data-origin-height=&quot;347&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bm8EGN/btsNqIi8Dff/uclYqOycCthxqB5BLTB5dk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bm8EGN/btsNqIi8Dff/uclYqOycCthxqB5BLTB5dk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bm8EGN/btsNqIi8Dff/uclYqOycCthxqB5BLTB5dk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fbm8EGN%2FbtsNqIi8Dff%2FuclYqOycCthxqB5BLTB5dk%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;1129&quot; height=&quot;347&quot; data-origin-width=&quot;1129&quot; data-origin-height=&quot;347&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;&amp;lt;코드 예시&amp;gt;&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;몽타주에서 충돌체크할 노티파이를 클래스로 생성.&lt;/b&gt;&lt;b&gt;&lt;/b&gt;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock widthContent&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;932&quot; data-origin-height=&quot;224&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/dJKoqU/btsNpKJcLfI/VdV4nKtHPt2EqDoSIeFw0K/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/dJKoqU/btsNpKJcLfI/VdV4nKtHPt2EqDoSIeFw0K/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/dJKoqU/btsNpKJcLfI/VdV4nKtHPt2EqDoSIeFw0K/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FdJKoqU%2FbtsNpKJcLfI%2FVdV4nKtHPt2EqDoSIeFw0K%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;224&quot; data-origin-width=&quot;932&quot; data-origin-height=&quot;224&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;lt;.h&amp;gt;&lt;/p&gt;
&lt;pre id=&quot;code_1744947582592&quot; class=&quot;cpp&quot; data-ke-language=&quot;cpp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;UCLASS()
class ARENABATTLEDEMO_API UAnimNotify_AttackHitCheck : public UAnimNotify
{
	GENERATED_BODY()

protected:
	//애니메이션 노티파이가 발생할때 호출되는 이벤트 함수.
	virtual void Notify(USkeletalMeshComponent* MeshComp, UAnimSequenceBase* Animation, const FAnimNotifyEventReference&amp;amp; EventReference) override;
	
	
};&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;lt;CPP&amp;gt;&lt;/p&gt;
&lt;pre id=&quot;code_1744947612032&quot; class=&quot;cpp&quot; data-ke-language=&quot;cpp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;// Fill out your copyright notice in the Description page of Project Settings.


#include &quot;Animation/AnimNotify_AttackHitCheck.h&quot;
#include &quot;Interface/ABAnimationAttackInterface.h&quot;

void UAnimNotify_AttackHitCheck::Notify(USkeletalMeshComponent* MeshComp, UAnimSequenceBase* Animation,
	const FAnimNotifyEventReference&amp;amp; EventReference)
{
	Super::Notify(MeshComp, Animation, EventReference);

	if (MeshComp)
	{
		//애니메이션을 소유하는 액터가 인터페이스를 구현했는지 확인.
		IABAnimationAttackInterface* AttackInterface = Cast&amp;lt;IABAnimationAttackInterface&amp;gt;(MeshComp-&amp;gt;GetOwner());

		//인터페이스를 구현한 경우, 함수 호출.
		if (AttackInterface)
		{
			AttackInterface-&amp;gt;AttackHitCheck();
		}
	}
}&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;831&quot; data-origin-height=&quot;302&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/9N5os/btsNnm2Sn5e/a7z21DSkhxz8zgQTCKKEx0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/9N5os/btsNnm2Sn5e/a7z21DSkhxz8zgQTCKKEx0/img.png&quot; data-alt=&quot;생성한 노티파이를 몽타주에 설정.&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/9N5os/btsNnm2Sn5e/a7z21DSkhxz8zgQTCKKEx0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F9N5os%2FbtsNnm2Sn5e%2Fa7z21DSkhxz8zgQTCKKEx0%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;831&quot; height=&quot;302&quot; data-origin-width=&quot;831&quot; data-origin-height=&quot;302&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;생성한 노티파이를 몽타주에 설정.&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;인터페이스 생성.&lt;/b&gt;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;937&quot; data-origin-height=&quot;580&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/pqRA2/btsNqnmeI82/ygqi1hGKjKoWKufHNWu2UK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/pqRA2/btsNqnmeI82/ygqi1hGKjKoWKufHNWu2UK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/pqRA2/btsNqnmeI82/ygqi1hGKjKoWKufHNWu2UK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FpqRA2%2FbtsNqnmeI82%2Fygqi1hGKjKoWKufHNWu2UK%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;424&quot; height=&quot;580&quot; data-origin-width=&quot;937&quot; data-origin-height=&quot;580&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;공격충돌 체크를 순수 가상함수로 만든다.&amp;lt;CPP&amp;gt;&lt;/b&gt;&lt;b&gt;&lt;/b&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1744947914095&quot; class=&quot;cpp&quot; data-ke-language=&quot;cpp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;// Fill out your copyright notice in the Description page of Project Settings.

#pragma once

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

// This class does not need to be modified.
UINTERFACE(MinimalAPI)
class UABAnimationAttackInterface : public UInterface
{
	GENERATED_BODY()
};

/**
 * 
 */
class ARENABATTLEDEMO_API IABAnimationAttackInterface
{
	GENERATED_BODY()

	// Add interface functions to this class. This is the class that will be inherited to implement this interface.
public:
	virtual void AttackHitCheck() = 0;
};&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;캐릭터클래스에서 인터페이스를 상속.&lt;/b&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1744947952639&quot; class=&quot;cpp&quot; data-ke-language=&quot;cpp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;class ARENABATTLEDEMO_API AABCharacterBase : public ACharacter, public IABAnimationAttackInterface&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;해당 함수를 구현.&amp;lt;CPP&amp;gt;&lt;/b&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1744948034730&quot; class=&quot;cpp&quot; data-ke-language=&quot;cpp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;void AABCharacterBase::AttackHitCheck()
{
	//공격 판정 진행.
	//UE_LOG(LogTemp, Log, TEXT(&quot;AttackHitCheck!!&quot;));

	//충돌 시작 지점 계산.
	//캐릭터 몸통에서 약간 앞으로(캡슐의 반지름 만큼) 설정.
	FVector Start = GetActorLocation() + GetActorForwardVector() * GetCapsuleComponent()-&amp;gt;GetScaledCapsuleRadius();

	//공격 거리.
	const float AttackRange = 50.0f;
	FVector End = Start + GetActorForwardVector() * AttackRange;

	//SCENE_QUERY_STAT: 언리얼에서 지원하는 분석 툴에 태그를 추가.
	//두번째 인자: 복잡한 형태의 충돌체를 감지할지 여부.
	//세번째 인자: 무시할 액터 목록.
	FCollisionQueryParams Params(SCENE_QUERY_STAT(Attack), false,this);

	//트레이스에 사용할 구체의 반지름.
	const float AttakcRadius = 50.0f;

	//트레이스를 활용해 충돌 검사.
	FHitResult OutHitResult;
	bool HitDetected = GetWorld()-&amp;gt;SweepSingleByChannel(
		OutHitResult,
		Start,
		End,
		FQuat::Identity
		, CCHANNEL_ABACTION,
		FCollisionShape::MakeSphere(AttakcRadius),
		Params);

	// 충돌 감지된 경우의 처리.
	if (HitDetected)
	{
		//Damage 양.
		const float AttackDamage = 30.0f;

		//데미지 이벤트.
		FDamageEvent DamageEvent;
		
		//데미지 전달.
		OutHitResult.GetActor()-&amp;gt;TakeDamage(AttackDamage, DamageEvent, GetController(), this);
		
	}

	//충돌 디버그(시각적으로 확인할 수 있도록).
#if ENABLE_DRAW_DEBUG
	//캡슐의 중심 위치.
	FVector CapsuleOrigin = Start + (End - Start) * 0.5f;

	//캡슐의 높이 절반 값.
	float CapsuleHalfHeight = AttackRange * 0.5f;

	//표시할 생상(안맞으면 빨강, 맞으면 초록)
	FColor DrawColor = HitDetected ? FColor::Red : FColor::Green;

	//캡슐 그리기.
	DrawDebugCapsule(GetWorld(),
		CapsuleOrigin,
		CapsuleHalfHeight,
		AttakcRadius,
		FRotationMatrix::MakeFromZ(GetActorForwardVector()).ToQuat(),
		DrawColor,
		false,
		5.0f
		);
	
#endif
	
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>언리얼 엔진 공부/언리얼C++</category>
      <author>gasick</author>
      <guid isPermaLink="true">https://gasick.tistory.com/21</guid>
      <comments>https://gasick.tistory.com/21#entry21comment</comments>
      <pubDate>Fri, 18 Apr 2025 12:47:59 +0900</pubDate>
    </item>
    <item>
      <title>캐릭터 애니메이션 시스템</title>
      <link>https://gasick.tistory.com/20</link>
      <description>&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt; UAnimInstance&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;UAnimInstance는 &lt;b&gt;언리얼 엔진에서 캐릭터의 애니메이션 상태를 관리&lt;/b&gt;하는 핵심 클래스이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li data-end=&quot;331&quot; data-start=&quot;285&quot;&gt;현재 캐릭터 상태(속도, 방향, 점프 여부 등)에 따라 애니메이션을 &lt;b&gt;갱신&lt;/b&gt;&lt;/li&gt;
&lt;li data-end=&quot;384&quot; data-start=&quot;332&quot;&gt;AnimGraph, State Machine 등을 통해 애니메이션 &lt;b&gt;전환 관리&lt;/b&gt;&lt;/li&gt;
&lt;li data-end=&quot;423&quot; data-start=&quot;385&quot;&gt;C++에서 애니메이션 파라미터를 &lt;b&gt;제어하거나 가져올 수 있음&lt;/b&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt; 캐릭터 애니메이션 시스템의 생성 &lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;스켈레탈 메시 컴포넌트의 애니메이션 블루프린트 클래스를 지정한다.&lt;br /&gt;&amp;bull; 캐릭터가 초기화될 때 AnimInstance 클래스의 인스턴스를 생성한다.&lt;br /&gt;&amp;bull; 캐릭터는 GetAnimInstance 함수를 사용해 애니메이션 인스턴스를 얻을 수 있음&lt;br /&gt;&amp;bull; 애니메이션 인스턴스는 GetOwningActor 함수를 사용해 자신을 소유한 액터 정보를 얻을 수 있음&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;676&quot; data-origin-height=&quot;155&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/LPQN8/btsNk3BrYPY/NQDKhoSlx7I76Rlg9D4KEK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/LPQN8/btsNk3BrYPY/NQDKhoSlx7I76Rlg9D4KEK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/LPQN8/btsNk3BrYPY/NQDKhoSlx7I76Rlg9D4KEK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FLPQN8%2FbtsNk3BrYPY%2FNQDKhoSlx7I76Rlg9D4KEK%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;155&quot; data-origin-width=&quot;676&quot; data-origin-height=&quot;155&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 data-ke-size=&quot;size16&quot;&gt;&lt;b&gt; 캐릭터 애니메이션 시스템의 설계&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;애니메이션 블루프린트는 이벤트 그래프와 애님 그래프의 두 영역으로 구성되어 있음.&lt;br /&gt;&amp;bull; 이벤트 그래프에서는 이벤트로부터 상태를 파악할 수 있는 주요 변수를 저장하는데 사용&lt;br /&gt;&amp;bull; 애님 그래프에서는 저장된 변수로부터 지정된 상태의 애니메이션을 재생&lt;br /&gt;&amp;bull; 애님 그래프의 복잡한 상태는 State Alias로 분리해 효과적으로 설계할 수 있음&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;779&quot; data-origin-height=&quot;326&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/c2yhhI/btsNkt1VsAQ/VPypETka8ZC8zELUTAuKfK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/c2yhhI/btsNkt1VsAQ/VPypETka8ZC8zELUTAuKfK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/c2yhhI/btsNkt1VsAQ/VPypETka8ZC8zELUTAuKfK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fc2yhhI%2FbtsNkt1VsAQ%2FVPypETka8ZC8zELUTAuKfK%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;779&quot; height=&quot;326&quot; data-origin-width=&quot;779&quot; data-origin-height=&quot;326&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;&amp;lt;예시&amp;gt;&lt;/b&gt;&lt;b&gt;&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;UAnimInstance를 상속받는 UABAnimInstance 클래스 만들기&lt;/b&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1744611257012&quot; class=&quot;cpp&quot; data-ke-language=&quot;cpp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;// Fill out your copyright notice in the Description page of Project Settings.

#pragma once

#include &quot;CoreMinimal.h&quot;
#include &quot;Animation/AnimInstance.h&quot;
#include &quot;ABAnimInstance.generated.h&quot;

/**
 * 
 */
UCLASS()
class ARENABATTLEDEMO_API UABAnimInstance : public UAnimInstance
{
	GENERATED_BODY()

public:
	UABAnimInstance();

	//애니메이션 초기화될 때 호출.
	virtual void NativeInitializeAnimation() override;
	//애니메이션 업데이트 함수.
	virtual void NativeUpdateAnimation(float DeltaSeconds) override;

	//프로퍼티.
public:
	//애님 인스턴스를 소유하는 캐릭터 참조 변수.
	UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = Character)
	TObjectPtr&amp;lt;class ACharacter&amp;gt; Owner;

	//캐릭터 무브컨트 컴포넌트 참조 변수.
	UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = Character)
	TObjectPtr&amp;lt;class UCharacterMovementComponent&amp;gt; Movement;

	//캐릭터의 현재 이동속도를 저장할 변수.
	UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = Character)
	FVector Velocity;

	//캐릭터의 땅에서의 이동속도를 저장할 변수.
	UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = Character)
	float GroundSpeed;

	//캐릭터가 Idle인지 여부를 확인할 변수.
	UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = Character)
	uint8 bIsIdle : 1;

	//캐릭터가 이동하는지(멈춰있는지) 여부를 판단하는데 사용할 문턱 값.
	UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = Character)
	float MovingThreshold;

	//캐릭터가 공중에서 떨어지는지 여부를 확인하는데 사용할 문턱값.
	UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = Character)
	uint8 bIsFalling :1;

	//캐릭터가 점프하는지 여부를 확인하는데 사용할 문턱값.
	UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = Character)
	uint8 bIsJumping :1;

	//캐릭터가 점프하고있는지 여부를 판단하는데 사용할 문턱 값.
	UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = Character)
	float JumpingThreshold;
};&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;&amp;lt;CPP&amp;gt;&lt;/b&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1744611287198&quot; class=&quot;cpp&quot; data-ke-language=&quot;cpp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;// Fill out your copyright notice in the Description page of Project Settings.


#include &quot;Animation/ABAnimInstance.h&quot;
#include &quot;GameFramework/Character.h&quot;
#include &quot;GameFramework/CharacterMovementComponent.h&quot;

UABAnimInstance::UABAnimInstance()
{
	//문턱값 설정.
	MovingThreshold = 3.0f;
	JumpingThreshold = 100.0f;
}

void UABAnimInstance::NativeInitializeAnimation()
{
	Super::NativeInitializeAnimation();
	//애님 인스턴스를 소유한 폰 또는 액터를 캐릭터로 변환.
	Owner = Cast&amp;lt;ACharacter&amp;gt;(GetOwningActor());

	//캐릭터로 형변환 성공.
	if (Owner)
	{
		//무브먼트 컴포넌트 검색.
		Movement = Owner-&amp;gt;GetCharacterMovement();
	}
}

void UABAnimInstance::NativeUpdateAnimation(float DeltaSeconds)
{
	Super::NativeUpdateAnimation(DeltaSeconds);

	//필요한 값 업데이트.
	if (Movement)
	{
		Velocity = Movement-&amp;gt;Velocity;
		GroundSpeed = Velocity.Size2D();
		bIsIdle = GroundSpeed &amp;lt; MovingThreshold;
		bIsFalling = Movement-&amp;gt;IsFalling();
		bIsJumping = bIsFalling &amp;amp; (Velocity.Z &amp;gt; JumpingThreshold);
	}
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;캐릭터 블루프린트의 Mesh컴포넌트-&amp;gt;Animation-&amp;gt;AnimClass에 생성한 &lt;b&gt;UABAnimInstance&lt;/b&gt;를 설정&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;393&quot; data-origin-height=&quot;117&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/eJoCqS/btsNjF9TcSg/g80NOksjtrniezCA07mNl0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/eJoCqS/btsNjF9TcSg/g80NOksjtrniezCA07mNl0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/eJoCqS/btsNjF9TcSg/g80NOksjtrniezCA07mNl0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FeJoCqS%2FbtsNjF9TcSg%2Fg80NOksjtrniezCA07mNl0%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;393&quot; height=&quot;117&quot; data-origin-width=&quot;393&quot; data-origin-height=&quot;117&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;블루프린트 새로 공부한 기능&lt;/b&gt; &lt;b&gt;&lt;span style=&quot;color: #ee2323;&quot;&gt;Add State Alias&lt;/span&gt; 노드&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt; Add State Alias&lt;/b&gt;는 &lt;b&gt;애니메이션 상태 머신(State Machine)&lt;/b&gt; 내에서 &lt;b&gt;기존 상태에 '별칭(Alias)'을 붙이는 기능&lt;/b&gt;이다.&lt;br /&gt;애님 블루프린트 로직이 많아질수록 보기가 힘들어지는데 별칭을 만들어서 구분지어 사용하는 기능이다.&lt;br /&gt;별칭은 상태 변경을 하지 않고 &lt;b&gt;구조는 유지하면서 의미를 확장&lt;/b&gt;할 때 유용합니다.&lt;br /&gt;!!참고!! 너무 많은 별칭은 오히려 헷갈릴 수 있으니, &lt;b&gt;상황에 맞게 최소화해서 사용&lt;/b&gt;해야 한다.&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/c7LbFU/btsNiX9xk4B/u2PTnB0cZ7hdAKoLCSa83k/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/c7LbFU/btsNiX9xk4B/u2PTnB0cZ7hdAKoLCSa83k/img.png&quot; data-origin-width=&quot;737&quot; data-origin-height=&quot;255&quot; data-is-animation=&quot;false&quot; style=&quot;width: 67.547%; margin-right: 10px;&quot; data-widthpercent=&quot;68.34&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/c7LbFU/btsNiX9xk4B/u2PTnB0cZ7hdAKoLCSa83k/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fc7LbFU%2FbtsNiX9xk4B%2Fu2PTnB0cZ7hdAKoLCSa83k%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;737&quot; height=&quot;255&quot;/&gt;&lt;/span&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cIa0Pp/btsNkVX0pLP/gsSKJr1iAClCG9VZiKp5oK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cIa0Pp/btsNkVX0pLP/gsSKJr1iAClCG9VZiKp5oK/img.png&quot; data-origin-width=&quot;486&quot; data-origin-height=&quot;363&quot; data-is-animation=&quot;false&quot; style=&quot;width: 31.2902%;&quot; data-widthpercent=&quot;31.66&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cIa0Pp/btsNkVX0pLP/gsSKJr1iAClCG9VZiKp5oK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcIa0Pp%2FbtsNkVX0pLP%2FgsSKJr1iAClCG9VZiKp5oK%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;486&quot; height=&quot;363&quot;/&gt;&lt;/span&gt;&lt;/div&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;*ToLand를 &lt;b&gt;Add State Alias&lt;/b&gt;노드로 만들어 Jump, Falling 노드들일때만 탈수 있게 했다.&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;애니메이션 새로 공부한 기능 &lt;span style=&quot;color: #ee2323;&quot;&gt;Additive Anim Type&lt;/span&gt;&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;Additive Animation&lt;/b&gt;은 기존의 기준 포즈(Base Pose)에 대해 &lt;b&gt;상대적으로 덧붙이는 방식&lt;/b&gt;의 애니메이션이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;모드설명&lt;/b&gt;&lt;/p&gt;
&lt;table style=&quot;border-collapse: collapse; width: 100%;&quot; border=&quot;1&quot; data-end=&quot;853&quot; data-start=&quot;680&quot; data-ke-align=&quot;alignLeft&quot;&gt;
&lt;tbody data-end=&quot;853&quot; data-start=&quot;708&quot;&gt;
&lt;tr data-end=&quot;746&quot; data-start=&quot;708&quot;&gt;
&lt;td data-end=&quot;726&quot; data-start=&quot;708&quot;&gt;&lt;b&gt;No Additive&lt;/b&gt;&lt;/td&gt;
&lt;td data-end=&quot;746&quot; data-start=&quot;726&quot;&gt;일반 애니메이션 (덧셈 없음)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr data-end=&quot;797&quot; data-start=&quot;747&quot;&gt;
&lt;td data-end=&quot;774&quot; data-start=&quot;747&quot;&gt;&lt;b&gt;Local Space Additive&lt;/b&gt;&lt;/td&gt;
&lt;td data-end=&quot;797&quot; data-start=&quot;774&quot;&gt;본 로컬 좌표계 기준으로 차이 계산&lt;/td&gt;
&lt;/tr&gt;
&lt;tr data-end=&quot;853&quot; data-start=&quot;798&quot;&gt;
&lt;td data-end=&quot;824&quot; data-start=&quot;798&quot;&gt;&lt;b&gt;Mesh Space Additive&lt;/b&gt;&lt;/td&gt;
&lt;td data-end=&quot;853&quot; data-start=&quot;824&quot;&gt;전체 메시 기준의 글로벌 좌표계에서 차이 계산&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;Local Space&lt;/b&gt;는 각 본이 자기 기준으로 움직이고,&lt;br /&gt;&lt;b&gt;Mesh Space&lt;/b&gt;는 메시 전체 좌표계를 기준으로 계산합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;&amp;lt;예시&amp;gt;&lt;/b&gt;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;752&quot; data-origin-height=&quot;298&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/b75jAU/btsNk2bz3jy/YalBaURqHjSzG9lbx42Jgk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/b75jAU/btsNk2bz3jy/YalBaURqHjSzG9lbx42Jgk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/b75jAU/btsNk2bz3jy/YalBaURqHjSzG9lbx42Jgk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fb75jAU%2FbtsNk2bz3jy%2FYalBaURqHjSzG9lbx42Jgk%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;752&quot; height=&quot;298&quot; data-origin-width=&quot;752&quot; data-origin-height=&quot;298&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;</description>
      <category>언리얼 엔진 공부/언리얼C++</category>
      <author>gasick</author>
      <guid isPermaLink="true">https://gasick.tistory.com/20</guid>
      <comments>https://gasick.tistory.com/20#entry20comment</comments>
      <pubDate>Mon, 14 Apr 2025 16:14:52 +0900</pubDate>
    </item>
    <item>
      <title>데이터 애셋</title>
      <link>https://gasick.tistory.com/19</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;UDataAsset&lt;/b&gt;을 상속받은 언리얼 오브젝트 클래스이다.&lt;br /&gt;에디터에서 &lt;b&gt;애셋 형태로 편리하게 데이터를 관리&lt;/b&gt;할 수 있는 장점이 있다!!.&lt;br /&gt;캐릭터 컨트롤에 관련된 주요 옵션을 모아 애셋으로 관리 한다&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/bZ6WJi/btsNjaWa7uw/pWqIYITKXQq4U1cLlsajuK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bZ6WJi/btsNjaWa7uw/pWqIYITKXQq4U1cLlsajuK/img.png&quot; data-origin-width=&quot;437&quot; data-origin-height=&quot;306&quot; data-is-animation=&quot;false&quot; data-widthpercent=&quot;48.9&quot; style=&quot;width: 48.3362%; margin-right: 10px;&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bZ6WJi/btsNjaWa7uw/pWqIYITKXQq4U1cLlsajuK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbZ6WJi%2FbtsNjaWa7uw%2FpWqIYITKXQq4U1cLlsajuK%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;437&quot; height=&quot;306&quot;/&gt;&lt;/span&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/ccp4sx/btsNk5rRemX/54mxWSAMW09LFGmCfmeN01/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/ccp4sx/btsNk5rRemX/54mxWSAMW09LFGmCfmeN01/img.png&quot; data-origin-width=&quot;470&quot; data-origin-height=&quot;315&quot; data-is-animation=&quot;false&quot; style=&quot;width: 50.501%;&quot; data-widthpercent=&quot;51.1&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/ccp4sx/btsNk5rRemX/54mxWSAMW09LFGmCfmeN01/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fccp4sx%2FbtsNk5rRemX%2F54mxWSAMW09LFGmCfmeN01%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;470&quot; height=&quot;315&quot;/&gt;&lt;/span&gt;&lt;/div&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&amp;nbsp;&lt;/h4&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;데이터 애셋 테스트하면서 정리.&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;두 가지의 컨트롤 모드를 제공&lt;br /&gt;현재 구현된 컨트롤 모드 : 3인칭 솔더뷰&lt;br /&gt;추가로 구현할 컨트롤 모드 : 3인칭 쿼터뷰&lt;br /&gt;입력키 V를 통해 컨트롤 설정을 변경&lt;br /&gt;ENUM을 통해 두 개의 컨트롤 데이터를 관리&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;526&quot; data-origin-height=&quot;237&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/41SHU/btsNivLZVVV/eSeKkjpzaF6JtbZJf2tHC0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/41SHU/btsNivLZVVV/eSeKkjpzaF6JtbZJf2tHC0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/41SHU/btsNivLZVVV/eSeKkjpzaF6JtbZJf2tHC0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F41SHU%2FbtsNivLZVVV%2FeSeKkjpzaF6JtbZJf2tHC0%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;526&quot; height=&quot;237&quot; data-origin-width=&quot;526&quot; data-origin-height=&quot;237&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt; 데이터 애셋의 구성과 적용&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt; 각 섹션별로 데이터를 저장&lt;/b&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Pawn 카테고리&lt;/li&gt;
&lt;li&gt;캐릭터무브먼트 카테고리&lt;/li&gt;
&lt;li&gt;입력 카테고리&lt;/li&gt;
&lt;li&gt;스프링암 카테고리&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Pawn과 캐릭터무브먼트 데이터는 CharacterBase에서 설정&lt;br /&gt;입력과 스프링암 데이터는 CharacterPlayer에서 설정&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;600&quot; data-origin-height=&quot;257&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/pVW2D/btsNiYG7eXx/Nu0soQmXdhCJd78EmSChL0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/pVW2D/btsNiYG7eXx/Nu0soQmXdhCJd78EmSChL0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/pVW2D/btsNiYG7eXx/Nu0soQmXdhCJd78EmSChL0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FpVW2D%2FbtsNiYG7eXx%2FNu0soQmXdhCJd78EmSChL0%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;600&quot; height=&quot;257&quot; data-origin-width=&quot;600&quot; data-origin-height=&quot;257&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;&amp;lt;코드 예시&amp;gt;UABCharacterControlData클래스 만들기&lt;br /&gt;&lt;/b&gt;&lt;b&gt;&lt;/b&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1744602292733&quot; data-ke-type=&quot;codeblock&quot; data-ke-language=&quot;cpp&quot;&gt;&lt;code&gt;// Fill out your copyright notice in the Description page of Project Settings.

#pragma once

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

/**
 * 
 */
UCLASS()
class ARENABATTLEDEMO_API UABCharacterControlData : public UPrimaryDataAsset
{
	GENERATED_BODY()

public:
	//기본값 설정을 위해서.
	UABCharacterControlData();

	UPROPERTY(EditAnywhere, Category = Pawn)
	uint8 bUseControllerRotationYaw : 1;
	
	UPROPERTY(EditAnywhere, Category = CharacterMovement)
	uint8 bOrientRotationToMovement : 1;

	UPROPERTY(EditAnywhere, Category = CharacterMovement)
	uint8 bUseControllerDesiredRotation: 1;

	UPROPERTY(EditAnywhere, Category = CharacterMovement)
	FRotator RotationRate;

	UPROPERTY(EditAnywhere, BlueprintReadOnly,  Category = Input)
	TObjectPtr&amp;lt;class UInputMappingContext&amp;gt; InputMappingContext;

	UPROPERTY(EditAnywhere,  Category = SpringArm)
	float TargetArmLength;

	UPROPERTY(EditAnywhere,  Category = SpringArm)
	FRotator RelativeRotation;
	
	UPROPERTY(EditAnywhere, Category = SpringArm)
	uint8 bUsePawnControlRotation : 1;

	UPROPERTY(EditAnywhere, Category = SpringArm)
	uint8 bInheritPitch : 1;

	UPROPERTY(EditAnywhere, Category = SpringArm)
	uint8 bInheritYaw : 1;

	UPROPERTY(EditAnywhere, Category = SpringArm)
	uint8 bInheritRoll : 1;

	UPROPERTY(EditAnywhere, Category = SpringArm)
	uint8 bDoCollisionTest : 1;
	
};&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;lt;cpp&amp;gt;&lt;/p&gt;
&lt;pre id=&quot;code_1744602382354&quot; class=&quot;cpp&quot; data-ke-language=&quot;cpp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;// Fill out your copyright notice in the Description page of Project Settings.


#include &quot;Character/ABCharacterControlData.h&quot;

UABCharacterControlData::UABCharacterControlData()
{
	TargetArmLength = 500.0f;
	
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;데이터 에셋 생성&lt;/b&gt;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;256&quot; data-origin-height=&quot;178&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cWgSG4/btsNjLIaQ4L/wQjxgFnV8XOWbnRGTUw48k/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cWgSG4/btsNjLIaQ4L/wQjxgFnV8XOWbnRGTUw48k/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cWgSG4/btsNjLIaQ4L/wQjxgFnV8XOWbnRGTUw48k/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcWgSG4%2FbtsNjLIaQ4L%2FwQjxgFnV8XOWbnRGTUw48k%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;256&quot; height=&quot;178&quot; data-origin-width=&quot;256&quot; data-origin-height=&quot;178&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;데이터 에셋 데이터 셋팅&lt;/b&gt;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1163&quot; data-origin-height=&quot;535&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/kGow9/btsNkhz4f1I/cTfz19PuQPnTQqf9OuK02k/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/kGow9/btsNkhz4f1I/cTfz19PuQPnTQqf9OuK02k/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/kGow9/btsNkhz4f1I/cTfz19PuQPnTQqf9OuK02k/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FkGow9%2FbtsNkhz4f1I%2FcTfz19PuQPnTQqf9OuK02k%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;1163&quot; height=&quot;535&quot; data-origin-width=&quot;1163&quot; data-origin-height=&quot;535&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;Shoulder와 Quater때의 Move함수 코드 분리&lt;/b&gt;&lt;b&gt;&lt;/b&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1744602629555&quot; class=&quot;cpp&quot; data-ke-language=&quot;cpp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;void AABCharacterPlayer::QuarterMove(const FInputActionValue&amp;amp; Value)
{
	// 입력 값 읽기.
	FVector2D Movement = Value.Get&amp;lt;FVector2D&amp;gt;();
	
	float MovementVectorSize;
	float MovementVectorSizeSquared = Movement.SizeSquared();

	// 두 방향으로 입력이 들어오면, 이동 방향은 정규화해 크기를 1로 만들고,
	// 입력 스케일을 1로 강제 설정.
	if (MovementVectorSizeSquared &amp;gt; 1.0f)
	{
		Movement.Normalize();
		MovementVectorSize = 1.0f;
	}
	// 입력이 1이하이면, 해당 입력을 스케일로 사용하기 위해 값 계산.
	else
	{
		MovementVectorSize = FMath::Sqrt(MovementVectorSizeSquared);
	}

	FVector MoveDirection = FVector(Movement.X, Movement.Y, 0.0f);

	//캐릭터가 이동하는 방향에 맞게 컨트롤러 회전 설정.
	Controller-&amp;gt;SetControlRotation(FRotationMatrix::MakeFromX(MoveDirection).Rotator());
    
	// 입력에 따른 방향으로 이동하도록 입력 전달.
	AddMovementInput(MoveDirection, MovementVectorSize);
}

void AABCharacterPlayer::ShoulderMove(const FInputActionValue&amp;amp; Value)
{
	//입력값 읽기.
	FVector2D Movement = Value.Get&amp;lt;FVector2D&amp;gt;();

	//컨트롤러의 회전 값.
	FRotator Rotation = GetControlRotation();
	FRotator YawRotation(0.0f, Rotation.Yaw, 0.0f);
	
	//방향 구하기.
	FVector ForwardVector = FRotationMatrix(YawRotation).GetUnitAxis(EAxis::X);
	FVector RightVector = FRotationMatrix(YawRotation).GetUnitAxis(EAxis::Y);
	

	//무브먼트 컴ㅍ포넌트에 값 전달.
	AddMovementInput(ForwardVector, Movement.X);
	AddMovementInput(RightVector, Movement.Y);
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;키입력시 호출 함수 바인딩&lt;/b&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1744602734682&quot; class=&quot;cpp&quot; data-ke-language=&quot;cpp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;void AABCharacterPlayer::SetupPlayerInputComponent(class UInputComponent* PlayerInputComponent)
{
	Super::SetupPlayerInputComponent(PlayerInputComponent);
	auto EnhancedInputComponent = CastChecked&amp;lt;UEnhancedInputComponent&amp;gt;(PlayerInputComponent);

	//Binding
	EnhancedInputComponent-&amp;gt;BindAction(JumpAction, ETriggerEvent::Triggered, this, &amp;amp;ACharacter::Jump);
	EnhancedInputComponent-&amp;gt;BindAction(ChangeControlAction, ETriggerEvent::Triggered, this, &amp;amp;AABCharacterPlayer::ChangeCharacterControl);
	EnhancedInputComponent-&amp;gt;BindAction(ShoulderMoveAction, ETriggerEvent::Triggered, this, &amp;amp;AABCharacterPlayer::ShoulderMove);
	EnhancedInputComponent-&amp;gt;BindAction(ShoulderLookAction, ETriggerEvent::Triggered, this, &amp;amp;AABCharacterPlayer::ShoulderLook);
	EnhancedInputComponent-&amp;gt;BindAction(QuarterMoveAction, ETriggerEvent::Triggered, this, &amp;amp;AABCharacterPlayer::QuarterMove);
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;컨트롤 변경 함수&lt;/b&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1744602769451&quot; class=&quot;cpp&quot; data-ke-language=&quot;cpp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;void AABCharacterPlayer::ChangeCharacterControl()
{
	//사용할 캐릭터 컨트롤을 변경하는 함수.
	if (CurrentCharacterControlType == ECharacterControlType::Quarter)
	{
		SetCharacterControl(ECharacterControlType::Shoulder);
	}
	else if (CurrentCharacterControlType == ECharacterControlType::Shoulder)
	{
		SetCharacterControl(ECharacterControlType::Quarter);
	}
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;해당 컨트롤러 셋팅&lt;/b&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1744602841570&quot; class=&quot;cpp&quot; data-ke-language=&quot;cpp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;void AABCharacterPlayer::SetCharacterControl(ECharacterControlType NewCharacterControlType)
{
	// 변경할 컨트롤 타입에 대응하는 데이터 애셋 로드(TMap으로부터).
	UABCharacterControlData* NewCharacterControl =  CharacterControlManager[NewCharacterControlType];
	check(NewCharacterControl);

	// 데이터 애셋을 사용해 관련 값 설정.
	SetCharacterControlData(NewCharacterControl);

	// Add InputMapping Context to Enhanced Input System.
	APlayerController* PlayerController = CastChecked&amp;lt;APlayerController&amp;gt;(GetController());
	if (auto SubSystem = ULocalPlayer::GetSubsystem&amp;lt;UEnhancedInputLocalPlayerSubsystem&amp;gt;(PlayerController-&amp;gt;GetLocalPlayer()))
	{
		SubSystem-&amp;gt;ClearAllMappings();
		SubSystem-&amp;gt;AddMappingContext(
			NewCharacterControl-&amp;gt;InputMappingContext,
			0
		);
	}

	// 현재 사용 중인 캐릭터 컨트롤 타입 업데이트.
	CurrentCharacterControlType = NewCharacterControlType;
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;결과&lt;/b&gt;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock widthContent&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1035&quot; data-origin-height=&quot;286&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bAfnJE/btsNjkK2jcT/hebBpdxT2cijHktkXVnJOk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bAfnJE/btsNjkK2jcT/hebBpdxT2cijHktkXVnJOk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bAfnJE/btsNjkK2jcT/hebBpdxT2cijHktkXVnJOk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbAfnJE%2FbtsNjkK2jcT%2FhebBpdxT2cijHktkXVnJOk%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;1035&quot; height=&quot;286&quot; data-origin-width=&quot;1035&quot; data-origin-height=&quot;286&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;</description>
      <category>언리얼 엔진 공부/언리얼C++</category>
      <author>gasick</author>
      <guid isPermaLink="true">https://gasick.tistory.com/19</guid>
      <comments>https://gasick.tistory.com/19#entry19comment</comments>
      <pubDate>Mon, 14 Apr 2025 12:54:52 +0900</pubDate>
    </item>
    <item>
      <title>언리얼 엔진 게임 제작 기초</title>
      <link>https://gasick.tistory.com/18</link>
      <description>&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&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;1039&quot; data-origin-height=&quot;348&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cmISMi/btsNft1pR6w/9fuOOkXqWRFJLelwYfBjZK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cmISMi/btsNft1pR6w/9fuOOkXqWRFJLelwYfBjZK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cmISMi/btsNft1pR6w/9fuOOkXqWRFJLelwYfBjZK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcmISMi%2FbtsNft1pR6w%2F9fuOOkXqWRFJLelwYfBjZK%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;1039&quot; height=&quot;348&quot; data-origin-width=&quot;1039&quot; data-origin-height=&quot;348&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;게임 제작을 위해 언리얼 엔진은 자체적으로 설계한 프레임웍을 제공한다.&lt;br /&gt;이를 게임플레이 프레임웍(Gameplay Framework)이라고 부른다.&lt;br /&gt;줄여서 게임 프레임웍 언리얼 게임 프레임웍의 각 구성 요소를 파악하고, 이를 확장하면서 게임을 제작하는 것을 권장&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt; 월드(World)&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;1036&quot; data-origin-height=&quot;338&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/dgPqD3/btsNdWYhL2O/OPYGQKpbDSMWGqkNBR5mkK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/dgPqD3/btsNdWYhL2O/OPYGQKpbDSMWGqkNBR5mkK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/dgPqD3/btsNdWYhL2O/OPYGQKpbDSMWGqkNBR5mkK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FdgPqD3%2FbtsNdWYhL2O%2FOPYGQKpbDSMWGqkNBR5mkK%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;1036&quot; height=&quot;338&quot; data-origin-width=&quot;1036&quot; data-origin-height=&quot;338&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;게임 콘텐츠를 담기 위해 제공되는 가상의 공간 월드는 시간, 트랜스폼 , 틱을 서비스로 제공한다.&lt;br /&gt;월드 세팅이라는 콘텐츠 제작을 위한 기본 환경 설정을 제공한다.&lt;br /&gt;월드의 기본 단위는 액터(Actor)로 정의되며, 액터 클래스는 언제나 접두사 A를 사용한다.&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt; 게임 모드(Game Mode)&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;1028&quot; data-origin-height=&quot;325&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/br2ry8/btsNfhz4ItM/53yJ2K4Q3KoOMStv0pgjTK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/br2ry8/btsNfhz4ItM/53yJ2K4Q3KoOMStv0pgjTK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/br2ry8/btsNfhz4ItM/53yJ2K4Q3KoOMStv0pgjTK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fbr2ry8%2FbtsNfhz4ItM%2F53yJ2K4Q3KoOMStv0pgjTK%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;1028&quot; height=&quot;325&quot; data-origin-width=&quot;1028&quot; data-origin-height=&quot;325&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;게임 규칙을 지정하고 게임을 판정하는 최고 관리자 액터. 형태가 없다.&lt;br /&gt;언리얼 엔진에서 하나의 게임에는 반드시 하나의 게임 모드만 존재한다.&lt;br /&gt;게임 모드에서 입장할 사용자의 규격을 지정할 수 있.&lt;br /&gt;멀티플레이어 게임에서 판정을 처리하는 절대적 권위의 심판&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt; 기믹(Gimmick)&lt;/b&gt;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1025&quot; data-origin-height=&quot;341&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/C493v/btsNeRIH0ch/1Xs7PNcjrmfva0z46STKt1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/C493v/btsNeRIH0ch/1Xs7PNcjrmfva0z46STKt1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/C493v/btsNeRIH0ch/1Xs7PNcjrmfva0z46STKt1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FC493v%2FbtsNeRIH0ch%2F1Xs7PNcjrmfva0z46STKt1%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;1025&quot; height=&quot;341&quot; data-origin-width=&quot;1025&quot; data-origin-height=&quot;341&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;게임 진행을 위한 이벤트를 발생시키는 사물 액터이다.&lt;br /&gt;주로 이벤트 발생을 위한 충돌 영역을 설정하는데, 이를 트리거(Trigger)라고 한다.&lt;br /&gt;트리거를 통해 캐릭터와 상호 작용하고, 월드에 액터를 스폰해 콘텐츠를 전개한다.&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt; 플레이어(Player)&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;1042&quot; data-origin-height=&quot;331&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/nUjxO/btsNfbfRpgG/ZmxCMkqdst5l2k8t4bYgZ0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/nUjxO/btsNfbfRpgG/ZmxCMkqdst5l2k8t4bYgZ0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/nUjxO/btsNfbfRpgG/ZmxCMkqdst5l2k8t4bYgZ0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FnUjxO%2FbtsNfbfRpgG%2FZmxCMkqdst5l2k8t4bYgZ0%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;1042&quot; height=&quot;331&quot; data-origin-width=&quot;1042&quot; data-origin-height=&quot;331&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;게임에 입장한 사용자 액터. 형태가 없다.&lt;br /&gt;게임 모드의 로그인을 통해 사용자가 게임 월드에 입장하면 플레이어가 생성된다.&lt;br /&gt;싱글 플레이 게임에는 0번 플레이어가 설정된다.&lt;br /&gt;사용자와의 최종 커뮤니케이션을 담당한다. ( 예) 입력 장치의 해석, 화면 장치로의 출력 )&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt; 폰(Pawn)&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;1029&quot; data-origin-height=&quot;325&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/d6xsR0/btsNemoIbxy/xsffJv73T7dE5PO8q9q6l0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/d6xsR0/btsNemoIbxy/xsffJv73T7dE5PO8q9q6l0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/d6xsR0/btsNemoIbxy/xsffJv73T7dE5PO8q9q6l0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fd6xsR0%2FbtsNemoIbxy%2FxsffJv73T7dE5PO8q9q6l0%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;1029&quot; height=&quot;325&quot; data-origin-width=&quot;1029&quot; data-origin-height=&quot;325&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;무형의 액터인 플레이어가 빙의해 조종하는 액터이다.&lt;br /&gt;길찾기를 사용할 수 있으며, 기믹 및 다른 폰과 상호작용한다.&lt;br /&gt;폰 중에서 인간형 폰을 별도로 캐릭터라고 지칭한다.&lt;/p&gt;</description>
      <category>언리얼 엔진 공부/언리얼C++</category>
      <author>gasick</author>
      <guid isPermaLink="true">https://gasick.tistory.com/18</guid>
      <comments>https://gasick.tistory.com/18#entry18comment</comments>
      <pubDate>Thu, 10 Apr 2025 14:06:35 +0900</pubDate>
    </item>
    <item>
      <title>어설션(Assertion)</title>
      <link>https://gasick.tistory.com/17</link>
      <description>&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt; 어설션(Assertion)이란?&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;코드에서 반드시 검증을 하고 넘어가야 할 상황이 있을때 사용 하는 비교 함수이다.&lt;br /&gt;에디터 실행시에는 체크가 되며 빌드 실행시 해당 부분들은 빠지기 때문에 안심하고 에디터에서 사용해도 된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt; 언리얼 엔진의 대표 어설션 매크로&lt;/b&gt;&lt;/p&gt;
&lt;table style=&quot;border-collapse: collapse; width: 100%; height: 90px;&quot; border=&quot;1&quot; data-end=&quot;752&quot; data-start=&quot;405&quot; data-ke-align=&quot;alignLeft&quot;&gt;
&lt;tbody data-end=&quot;752&quot; data-start=&quot;479&quot;&gt;
&lt;tr style=&quot;height: 20px;&quot; data-end=&quot;548&quot; data-start=&quot;479&quot;&gt;
&lt;td style=&quot;height: 20px;&quot;&gt;check(조건)&lt;/td&gt;
&lt;td style=&quot;height: 20px;&quot;&gt;조건이 false면 크래시&lt;/td&gt;
&lt;td style=&quot;height: 20px;&quot;&gt;&lt;b&gt;즉시 중단 (Assert failure)&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;height: 20px;&quot;&gt;필수 조건&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 20px;&quot; data-end=&quot;620&quot; data-start=&quot;549&quot;&gt;
&lt;td style=&quot;height: 20px;&quot;&gt;checkf(조건, TEXT(&quot;메시지&quot;))&lt;/td&gt;
&lt;td style=&quot;height: 20px;&quot;&gt;메시지 포함된 check&lt;/td&gt;
&lt;td style=&quot;height: 20px;&quot;&gt;&lt;b&gt;즉시 중단 + 로그&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;height: 20px;&quot;&gt;디버깅 편의&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 20px;&quot; data-end=&quot;677&quot; data-start=&quot;621&quot;&gt;
&lt;td style=&quot;height: 20px;&quot;&gt;ensure(조건)&lt;/td&gt;
&lt;td style=&quot;height: 20px;&quot;&gt;경고 로그 출력만&lt;/td&gt;
&lt;td style=&quot;height: 20px;&quot;&gt;&lt;b&gt;크래시 안 남&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;height: 20px;&quot;&gt;부가적인 안전성 검사&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 20px;&quot; data-end=&quot;752&quot; data-start=&quot;678&quot;&gt;
&lt;td style=&quot;height: 20px;&quot;&gt;ensureMsgf(조건, TEXT(&quot;메시지&quot;))&lt;/td&gt;
&lt;td style=&quot;height: 20px;&quot;&gt;메시지 포함된 ensure&lt;/td&gt;
&lt;td style=&quot;height: 20px;&quot;&gt;&lt;b&gt;로그 출력&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;height: 20px;&quot;&gt;부가적 설명 포함&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #ef5369;&quot;&gt;&lt;b&gt; 어설션&lt;/b&gt;&lt;/span&gt;은 &lt;b&gt;디버깅을 돕는 개발자의 안전망&lt;/b&gt;&lt;br /&gt;check() = &lt;span style=&quot;color: #ef5369;&quot;&gt;&lt;b&gt;강한 보장&lt;/b&gt;&lt;/span&gt;, ensure() = &lt;span style=&quot;color: #8a3db6;&quot;&gt;&lt;b&gt;부드러운 경고 &lt;/b&gt;&lt;/span&gt;&lt;/p&gt;</description>
      <category>언리얼 엔진 공부/언리얼C++</category>
      <category>unrealengine5 c++</category>
      <author>gasick</author>
      <guid isPermaLink="true">https://gasick.tistory.com/17</guid>
      <comments>https://gasick.tistory.com/17#entry17comment</comments>
      <pubDate>Tue, 8 Apr 2025 16:40:09 +0900</pubDate>
    </item>
    <item>
      <title>직렬화</title>
      <link>https://gasick.tistory.com/16</link>
      <description>&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;직렬화(Serialization)란?&lt;/b&gt;&lt;/h4&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;오브젝트나 연결된 오브젝트의 묶음(오브젝트 그래프)을 바이트 스트림으로 변환하는 과정
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;복잡한 데이터를 일렬로 세우기 때문에 직렬화&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;거꾸로 복구시키는 과정도 포함해서 의미
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;시리얼라이제이션(Serialization) : 오브젝트 그래프에서 바이트 스트림으로&lt;/li&gt;
&lt;li&gt;디시리얼라이제이션(Deserialization) : 바이트 스트림에서 오브젝트 그래프로&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;직렬화가 가지는 장점
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;현재 프로그램의 상태를 저장하고 필요한 때 복원할 수 있다. ( 게임의 저장 )&lt;/li&gt;
&lt;li&gt;현재 객체의 정보를 클립보드에 복사해서 다른 프로그램에 전송할 수 있다.&lt;/li&gt;
&lt;li&gt;네트워크를 통해 현재 프로그램의 상태를 다른 컴퓨터에 복원할 수 있다. ( 멀티플레이어 게임 )&lt;/li&gt;
&lt;li&gt;데이터 압축, 암호화를 통해 데이터를 효율적이고 안전하게 보관할 수도 있음.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1024&quot; data-origin-height=&quot;224&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/txutt/btsNaEvT36E/vSrln6rKzgrKJh2KfYC8k1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/txutt/btsNaEvT36E/vSrln6rKzgrKJh2KfYC8k1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/txutt/btsNaEvT36E/vSrln6rKzgrKJh2KfYC8k1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Ftxutt%2FbtsNaEvT36E%2FvSrln6rKzgrKJh2KfYC8k1%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;1024&quot; height=&quot;224&quot; data-origin-width=&quot;1024&quot; data-origin-height=&quot;224&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt; 직렬화 구현 시 고려할 점&lt;/b&gt;&lt;/h4&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;이러한 직렬화를 직접 구현할 경우 다양한 상황을 고려해야 함.
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;데이터 레이아웃 : 오브젝트가 소유한 다양한 데이터를 변환할 것인가?&lt;/li&gt;
&lt;li&gt;이식성 : 서로 다른 시스템에 전송해도 이식될 수 있는가?&lt;/li&gt;
&lt;li&gt;버전 관리 : 새로운 기능이 추가될 때 이를 어떻게 확장하고 처리할 것인가?&lt;/li&gt;
&lt;li&gt;성능 : 네트웍 비용을 줄이기 위해 어떤 데이터 형식을 사용할 것인가?&lt;/li&gt;
&lt;li&gt;보안 : 데이터를 어떻게 안전하게 보호할 것인가?&lt;/li&gt;
&lt;li&gt;에러 처리 : 전송 과정에서 문제가 발생할 경우 이를 어떻게 인식하고 처리할 것인가?&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;이런 상황을 모두 감안해 직렬화 모델을 만드는 것은 쉬운 일이 아님!&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt; 언리얼 엔진의 직렬화 시스템&lt;/b&gt;&lt;/h4&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;언리얼 엔진은 이러한 상황을 모두 고려한 직렬화 시스템을 자체적으로 제공하고 있음&lt;/li&gt;
&lt;li&gt;직렬화 시스템을 위해 제공하는 클래스 FArchive와 연산자
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;아카이브 클래스 ( FArchive )&lt;/li&gt;
&lt;li&gt;Shift(&amp;lt;&amp;lt;) operator&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;다양한 아카이브 클래스의 제공
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;메모리 아카이브 ( FMemoryReader, FMemoryWriter )&lt;/li&gt;
&lt;li&gt;파일 아카이브 ( FArchiveFileReaderGeneric , FArchiveFileWriterGeneric )&lt;/li&gt;
&lt;li&gt;기타 언리얼 오브젝트와 관련된 아카이브 클래스( FArchiveUObject )&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Json 직렬화 기능 : 별도의 라이브러리를 통해 제공하고 있음&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;직렬화 &lt;span style=&quot;color: #333333; text-align: left;&quot;&gt;FArchive&lt;span&gt;&amp;nbsp;코드 예시.&lt;/span&gt;&lt;/span&gt;&lt;/b&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1743996318733&quot; class=&quot;cpp&quot; data-ke-language=&quot;cpp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;// Fill out your copyright notice in the Description page of Project Settings.


#include &quot;MyGameInstance.h&quot;
#include &quot;Student.h&quot;

UMyGameInstance::UMyGameInstance()
{
}

void UMyGameInstance::Init()
{
	Super::Init();

	//데이터 생성.
	FStudentData RawDataSource(23, TEXT(&quot;wnwkdqls&quot;));

	//경로
	const FString SavedPath = FPaths::Combine(FPlatformMisc::ProjectDir(), TEXT(&quot;Saved&quot;));

	//테스트 출력.
	UE_LOG(LogTemp, Log, TEXT(&quot;저장할 파일 경로: %s&quot;), *SavedPath);


	{
		//파일 이름.
		const FString RawDataFileName(TEXT(&quot;RawData.bin&quot;));

		//전체 경로 설정 후 경로로 변경.
		FString RawDataAbsolutePath = FPaths::Combine(SavedPath, RawDataFileName);

		UE_LOG(LogTemp, Log, TEXT(&quot;저장할 파일 전체 경로: %s&quot;), *RawDataAbsolutePath);

		//절대 경로로 변경.
		FPaths::MakeStandardFilename(RawDataAbsolutePath);
		UE_LOG(LogTemp, Log, TEXT(&quot;변경할 파일 전체 경로: %s&quot;), *RawDataAbsolutePath);

		//구조체 데이터 직렬화.
		//CreateFileWriter 함수는 new로 생성 후 반환하기 때문에 delete로 정리 해야 한다.
		FArchive* RawFileWriteAr = IFileManager::Get().CreateFileWriter(*RawDataAbsolutePath);//파일 여는 행위(File.Open이랑 같다)

		if (RawFileWriteAr)
		{
			//데이터 넣기.
			//*RawFileWriteAr &amp;lt;&amp;lt; RawDataSource.Order;
			//*RawFileWriteAr &amp;lt;&amp;lt; RawDataSource.Name;
			*RawFileWriteAr &amp;lt;&amp;lt; RawDataSource;

			//아카이브 닫기.
			RawFileWriteAr-&amp;gt;Close();

			//메모리 해제.
			delete RawFileWriteAr;
			RawFileWriteAr = nullptr;
		}

		//역 직렬화(읽기).
		FStudentData RawDataDeserialized;
		FArchive* RawFileReaderAr = IFileManager::Get().CreateFileReader(*RawDataAbsolutePath);

		if (RawFileReaderAr)
		{
			//데이터 읽기.
			//*RawFileReaderAr &amp;lt;&amp;lt; RawDataDeserialized.Order;
			//*RawFileReaderAr &amp;lt;&amp;lt; RawDataDeserialized.Name;
			*RawFileReaderAr &amp;lt;&amp;lt; RawDataDeserialized;

			RawFileReaderAr-&amp;gt;Close();
			delete RawFileReaderAr;
			RawFileReaderAr = nullptr;

			UE_LOG(LogTemp, Log, TEXT(&quot;[RawData]이름: %s, 순번: %d&quot;), *RawDataDeserialized.Name, RawDataDeserialized.Order);
		}

	}

	//UObject 직렬화.
	StudentSource = NewObject&amp;lt;UStudent&amp;gt;();
	StudentSource-&amp;gt;SetOrder(40);
	StudentSource-&amp;gt;SetName(TEXT(&quot;wnwkdqls&quot;));

	{
		//파일 이름.
		const FString ObjectDataFileName(TEXT(&quot;ObjectData.bin&quot;));

		//파일 경로.
		FString ObjectDataAbsolutePath = FPaths::Combine(*SavedPath, *ObjectDataFileName);

		FPaths::MakeStandardFilename(ObjectDataAbsolutePath);

		//직렬화.
		TArray&amp;lt;uint8&amp;gt; BufferArray;
		FMemoryWriter MemoryWriterAr(BufferArray);
		StudentSource-&amp;gt;Serialize(MemoryWriterAr);

		if (TUniquePtr&amp;lt;FArchive&amp;gt; FileWriterAr = TUniquePtr&amp;lt;FArchive&amp;gt;(
			IFileManager::Get().CreateDebugFileWriter(*ObjectDataAbsolutePath)))
		{
			*FileWriterAr &amp;lt;&amp;lt; BufferArray;
			//StudentSource-&amp;gt;Serialize(*FileWriterAr);
			FileWriterAr-&amp;gt;Close();
		}

		//역 직렬화.
		if (TUniquePtr&amp;lt;FArchive&amp;gt; FileReaderAr = TUniquePtr&amp;lt;FArchive&amp;gt;(
			IFileManager::Get().CreateFileReader(*ObjectDataAbsolutePath)))
		{
			TArray&amp;lt;uint8&amp;gt; BufferArrayFromFile;
			*FileReaderAr &amp;lt;&amp;lt; BufferArrayFromFile;
			FileReaderAr-&amp;gt;Close();

			FMemoryReader MemoryReaderAr(BufferArrayFromFile);

			UStudent* StudentDes = NewObject&amp;lt;UStudent&amp;gt;();
			StudentDes-&amp;gt;Serialize(MemoryReaderAr);

			UE_LOG(LogTemp, Log, TEXT(&quot;이름: %s, 순번: %d&quot;), *StudentDes-&amp;gt;GetName(), StudentDes-&amp;gt;GetOrder());
		}
	}
}&lt;/code&gt;&lt;/pre&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt; Json 직렬화&lt;/b&gt;&lt;/h4&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Json(JavaScript Object Notation)의 약자&lt;/li&gt;
&lt;li&gt;웹 환경에서 서버와 클라이언트 사이에 데이터를 주고받을 때 사용하는 텍스트 기반 데이터 포맷&lt;/li&gt;
&lt;li&gt;Json 장점
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;텍스트임에도 데이터 크기가 가벼움.&lt;/li&gt;
&lt;li&gt;읽기 편해서 데이터를 보고 이해할 수 있음.&lt;/li&gt;
&lt;li&gt;사실 상 웹 통신의 표준으로 널리 사용됨.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Json의 단점
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;지원하는 타입이 몇 가지 안됨. ( 문자, 숫자, 불리언, 널, 배열, 오브젝트만 사용 가능 )&lt;/li&gt;
&lt;li&gt;텍스트 형식으로만 사용할 수 있음.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;언리얼 엔진의 Json, JsonUtilities 라이브러리 활용&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt; Json 데이터 예시&lt;/b&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Json 데이터 유형
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;오브젝트 : {}
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;오브젝트 내 데이터는 키, 밸류 조합으로 구성됨. 예) { &quot;key&quot; : 10 }&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;배열 : []
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;배열 내 데이터는 밸류로만 구성됨. 예) [ &quot;value1&quot;, &quot;value2&quot;, &quot;value3&quot; ]&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;이외 데이터
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;문자열 ( &quot;string&quot; ) , 숫자 ( 10 또는 3.14) , 불리언 ( true 또는 false ) , 널 ( null )로 구성&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;979&quot; data-origin-height=&quot;248&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/EOJ9O/btsNcttQDRa/Wp1xP0DMEJWDRxQ4HKcFKk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/EOJ9O/btsNcttQDRa/Wp1xP0DMEJWDRxQ4HKcFKk/img.png&quot; data-alt=&quot;이미지 예시&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/EOJ9O/btsNcttQDRa/Wp1xP0DMEJWDRxQ4HKcFKk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FEOJ9O%2FbtsNcttQDRa%2FWp1xP0DMEJWDRxQ4HKcFKk%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;979&quot; height=&quot;248&quot; data-origin-width=&quot;979&quot; data-origin-height=&quot;248&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;이미지 예시&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt; &lt;span data-token-index=&quot;0&quot;&gt;언리얼 스마트 포인터 라이브러리 개요&lt;/span&gt; &lt;/b&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;일반 C++ 오브젝트의 포인터 문제를 해결해주는 언리얼 엔진의 라이브러리&lt;/li&gt;
&lt;li&gt;TUniquePtr(유니크포인터) : 지정한 곳에서만 메모리를 관리하는 포인터.
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;특정 오브젝트에게 명확하게 포인터 해지 권한을 주고 싶은 경우.&lt;/li&gt;
&lt;li&gt;delete 구문 없이 함수 실행 후 자동으로 소멸시키고 싶을 때&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;TSharedPtr(공유포인터) : 더 이상 사용되지 않으면 자동으로 메모리를 해지하는 포인터
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;여러 로직에서 할당된 오브젝트가 공유해서 사용되는 경우&lt;/li&gt;
&lt;li&gt;다른 함수로부터 할당된 오브젝트를 Out으로 받는 경우.&lt;/li&gt;
&lt;li&gt;Null 일 수 있음.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;TSharedRef(공유레퍼런스) : 공유포인터와 동일하지만, 유효한 객체를 항상 보장받는 레퍼런스
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;여러 로직에서 할당된 오브젝트가 공유해서 사용되는 경우&lt;/li&gt;
&lt;li&gt;Not Null을 보장받으며 오브젝트를 편리하게 사용하고 싶은 경우&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;Json 직렬화 코드 예시&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;해당 UnrealSerialization.Build.cs 빌트 코드에서 Json 모듈을 추가해 준다.(&lt;span style=&quot;color: #ef5369;&quot;&gt;추가 안하면 빌드에러가 뜸&lt;/span&gt;)&lt;/b&gt;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1032&quot; data-origin-height=&quot;170&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bhgXkq/btsNbOeyhUf/oIIOH9Ri4Tqp1t2EHHk9AK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bhgXkq/btsNbOeyhUf/oIIOH9Ri4Tqp1t2EHHk9AK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bhgXkq/btsNbOeyhUf/oIIOH9Ri4Tqp1t2EHHk9AK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbhgXkq%2FbtsNbOeyhUf%2FoIIOH9Ri4Tqp1t2EHHk9AK%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;1032&quot; height=&quot;170&quot; data-origin-width=&quot;1032&quot; data-origin-height=&quot;170&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;pre id=&quot;code_1744083576977&quot; class=&quot;dts&quot; data-ke-type=&quot;codeblock&quot; data-ke-language=&quot;cpp&quot;&gt;&lt;code&gt;//Json 직렬화.
const FString JsonDataFileName(TEXT(&quot;StudentJsonData.json&quot;));

//경로 설정.
FString JsonDataAbsolutepath = FPaths::Combine(*SavedPath, *JsonDataFileName);

FPaths::MakeStandardFilename(JsonDataAbsolutepath);

//Json 객체 생성.
TSharedRef&amp;lt;FJsonObject&amp;gt; JsonObjectSource = MakeShared&amp;lt;FJsonObject&amp;gt;();

//UObjcect -&amp;gt; Json 객체 변환.
FJsonObjectConverter::UStructToJsonObject(
	StudentSource-&amp;gt;GetClass(),
	StudentSource, 
	JsonObjectSource);


//Json 객체 -&amp;gt; Json문자열.
FString JsonString;
TSharedRef&amp;lt;TJsonWriter&amp;lt;TCHAR&amp;gt;&amp;gt; JsonWriterAr = TJsonWriterFactory&amp;lt;TCHAR&amp;gt;::Create(&amp;amp;JsonString);

if (FJsonSerializer::Serialize(JsonObjectSource, JsonWriterAr))
{
	//파일에 저장.
	FFileHelper::SaveStringToFile(JsonString, *JsonDataAbsolutepath);

}

//Json Read.
FString JsonFromFile;
FFileHelper::LoadFileToString(JsonFromFile, *JsonDataAbsolutepath);

//Json String-&amp;gt; Json Object.
TSharedRef&amp;lt;TJsonReader&amp;lt;TCHAR&amp;gt;&amp;gt; JsonReaderAr = TJsonReaderFactory&amp;lt;TCHAR&amp;gt;::Create(JsonFromFile);

TSharedPtr&amp;lt;FJsonObject&amp;gt; jsonObjectResult;
if (FJsonSerializer::Deserialize(JsonReaderAr, jsonObjectResult))
{
	//Json Object -&amp;gt; UObject.
	UStudent* JsonStudent = NewObject&amp;lt;UStudent&amp;gt;();
	if (FJsonObjectConverter::JsonObjectToUStruct(
		jsonObjectResult.ToSharedRef(),
		JsonStudent-&amp;gt;GetClass(),
		JsonStudent
	))
	{
		// Test Print.
		UE_LOG(LogTemp, Log, TEXT(&quot;[JsonData] 이름: %s, 순번: %d&quot;),
			*JsonStudent-&amp;gt;GetName(), JsonStudent-&amp;gt;GetOrder())
	}
}&lt;/code&gt;&lt;/pre&gt;</description>
      <category>언리얼 엔진 공부/언리얼C++</category>
      <author>gasick</author>
      <guid isPermaLink="true">https://gasick.tistory.com/16</guid>
      <comments>https://gasick.tistory.com/16#entry16comment</comments>
      <pubDate>Tue, 8 Apr 2025 12:40:04 +0900</pubDate>
    </item>
    <item>
      <title>언리얼C++델리게이트(Delegate)</title>
      <link>https://gasick.tistory.com/15</link>
      <description>&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;b&gt; 언리얼 델리게이트(Delegate) &lt;/b&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;/li&gt;
&lt;li&gt;델리게이트의 사전적 의미는 대리자.
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;학사정보의 구독과 알림을 대리해주는 객체&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;시나리오 구현을 위한 설계
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;학사 정보는 구독과 알림을 대행할 델리게이트를 선언.&lt;/li&gt;
&lt;li&gt;학생은 학사 정보의 델리게이트를 통해 알림을 구독.&lt;/li&gt;
&lt;li&gt;학사 정보는 내용 변경시 델리게이트를 사용해 등록한 학생들에게 알림.&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;862&quot; data-origin-height=&quot;194&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/ceOdec/btsM5sJtNjl/ndf66d6Qq6VSgCm1Pw0tr1/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/ceOdec/btsM5sJtNjl/ndf66d6Qq6VSgCm1Pw0tr1/img.jpg&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/ceOdec/btsM5sJtNjl/ndf66d6Qq6VSgCm1Pw0tr1/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FceOdec%2FbtsM5sJtNjl%2Fndf66d6Qq6VSgCm1Pw0tr1%2Fimg.jpg&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/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;194&quot; data-origin-width=&quot;862&quot; data-origin-height=&quot;194&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt; 언리얼 델리게이트의 선언&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;b&gt;&amp;nbsp;언리얼 델리게이트 선언 시 고려사항&lt;/b&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;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;어떤 데이터를 전달하고 받을 것인가? 인자의 수와 각각의 타입을 설계
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;몇 개의 인자를 전달할 것인가?&lt;/li&gt;
&lt;li&gt;어떤 방식으로 전달할 것인가?&lt;/li&gt;
&lt;li&gt;일대일로 전달&lt;/li&gt;
&lt;li&gt;일대다로 전달&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;프로그래밍 환경 설정
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;C++ 프로그래밍에서만 사용할 것인가?&lt;/li&gt;
&lt;li&gt;UFUNCTION으로 지정된 블루프린트 함수와 사용할 것인가?&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;어떤 함수와 연결할 것인가?
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;클래스 외부에 설계된 C++ 함수와 연결&lt;/li&gt;
&lt;li&gt;전역에 설계된 정적 함수와 연결&lt;/li&gt;
&lt;li&gt;언리얼 오브젝트의 멤버 함수와 연결 ( 대부분의 경우에 이 방식을 사용)&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt; 델리게이트의&lt;/b&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-origin-width=&quot;755&quot; data-origin-height=&quot;322&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/HmqlI/btsM7ZMawcO/nlYkBPkQxyxkyIB3Z9rop0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/HmqlI/btsM7ZMawcO/nlYkBPkQxyxkyIB3Z9rop0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/HmqlI/btsM7ZMawcO/nlYkBPkQxyxkyIB3Z9rop0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FHmqlI%2FbtsM7ZMawcO%2FnlYkBPkQxyxkyIB3Z9rop0%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;755&quot; height=&quot;322&quot; data-origin-width=&quot;755&quot; data-origin-height=&quot;322&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;코드 예시&lt;br /&gt;&lt;/b&gt;&lt;b&gt;&lt;/b&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1743668161102&quot; class=&quot;cpp&quot; data-ke-language=&quot;cpp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;// Fill out your copyright notice in the Description page of Project Settings.

#pragma once

#include &quot;CoreMinimal.h&quot;
#include &quot;UObject/NoExportTypes.h&quot;
#include &quot;CourseInfo.generated.h&quot;

// Declare Delegate
DECLARE_MULTICAST_DELEGATE_TwoParams(FCourseInfoOnChanged, const FString&amp;amp;, const FString&amp;amp;);//리턴값이 없는 형. 
//DECLARE_MULTICAST_DELEGATE_RetVal_TwoParams()//리턴값이 있는 형.

/**
 * 
 */
UCLASS()
class UNREALDELEGATE_API UCourseInfo : public UObject
{
	GENERATED_BODY()

public:
	UCourseInfo();

	FCourseInfoOnChanged OnChanged;

	void ChangeCourseInfo(const FString&amp;amp; InSchoolName, const FString&amp;amp; InNewContents);

private:
	UPROPERTY()
	FString Contents;
	
};&lt;/code&gt;&lt;/pre&gt;
&lt;pre id=&quot;code_1743668218022&quot; class=&quot;cpp&quot; data-ke-language=&quot;cpp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;// Fill out your copyright notice in the Description page of Project Settings.


#include &quot;MyGameInstance.h&quot;//본인 클래스의 헤더는 항상 제일 먼저 와야 한다.
#include &quot;Teacher.h&quot;
#include &quot;Student.h&quot;
#include &quot;Staff.h&quot;
#include &quot;Card.h&quot;
#include &quot;CourseInfo.h&quot;

UMyGameInstance::UMyGameInstance()
{
	//기본 값 설정.
	//CDO(Class Default Object).언리얼에 있는 독특한 기능
	//템플릿 클래스 객체로 생성이 된다.
	SchoolName = TEXT(&quot;스쿨&quot;);
}

void UMyGameInstance::Init()
{
	Super::Init();

	UE_LOG(LogTemp, Log, TEXT(&quot;=======================================&quot;));

	//학사 정보 객체 생성.
	CourseInfo = NewObject&amp;lt;UCourseInfo&amp;gt;(this);

	//학생 객체 생성.
	UStudent* Student1 = NewObject&amp;lt;UStudent&amp;gt;();
	Student1-&amp;gt;SetName(TEXT(&quot;학생 1&quot;));

	UStudent* Student2 = NewObject&amp;lt;UStudent&amp;gt;();
	Student2-&amp;gt;SetName(TEXT(&quot;학생 2&quot;));

	UStudent* Student3 = NewObject&amp;lt;UStudent&amp;gt;();
	Student3-&amp;gt;SetName(TEXT(&quot;학생 3&quot;));

	//구독할 함수 등록.
	CourseInfo-&amp;gt;OnChanged.AddUObject(Student1, &amp;amp;UStudent::GetNotification);
	CourseInfo-&amp;gt;OnChanged.AddUObject(Student2, &amp;amp;UStudent::GetNotification);
	CourseInfo-&amp;gt;OnChanged.AddUObject(Student3, &amp;amp;UStudent::GetNotification);

	//발행.
	CourseInfo-&amp;gt;ChangeCourseInfo(SchoolName, TEXT(&quot;새로운 학사 정보&quot;));
	
	
	
	UE_LOG(LogTemp, Log, TEXT(&quot;=======================================&quot;));


}&lt;/code&gt;&lt;/pre&gt;</description>
      <category>언리얼 엔진 공부/언리얼C++</category>
      <author>gasick</author>
      <guid isPermaLink="true">https://gasick.tistory.com/15</guid>
      <comments>https://gasick.tistory.com/15#entry15comment</comments>
      <pubDate>Thu, 3 Apr 2025 17:17:16 +0900</pubDate>
    </item>
    <item>
      <title>컴포지션</title>
      <link>https://gasick.tistory.com/14</link>
      <description>&lt;h3 data-ke-size=&quot;size23&quot;&gt;컴포지션&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;객체 지향 설계에서 상속이 가진 Is-A 관계만 의존해서는 설계와 유지 보수가 어려움이 있다.&lt;/li&gt;
&lt;li&gt;컴포지션은 객체 지향 설계에서 Has-A 관계를 구현하는 설계 방법&lt;/li&gt;
&lt;li&gt;컴포지션 활용
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;복합적인 기능을 가진 거대한 클래스를 효과적으로 설계하는 데 유용하게 사용할 수 있습니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt; &lt;span data-token-index=&quot;0&quot;&gt;언리얼 엔진에서의 컴포지션 구현 방법&lt;/span&gt; &lt;/b&gt;&lt;/h4&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;하나의 언리얼 오브젝트에는 항상 클래스 기본 오브젝트 CDO 가 있다.&lt;/li&gt;
&lt;li&gt;언리얼 오브젝트에 다른 언리얼 오브젝트를 조합할 때 두 가지의 선택지가 존재한다.
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;방법 1 : CDO 에 미리 언리얼 오브젝트를 생성해 조합한다(필수로 포함해야 하는 경우).&lt;/li&gt;
&lt;li&gt;방법 2 : CDO 에 빈 포인터만 넣고 런타임에서 언리얼 오브젝트를 생성해 조합한다(선택적으로 포함해야 하는 경우).&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;언리얼 오브젝트를 생성할 때 컴포지션 정보를 구축할 수 있다.
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;내가 소유한 언리얼 오브젝트를 Subobject라고 한다.&lt;/li&gt;
&lt;li&gt;나를 소유한 언리얼 오브젝트를 Outer 라고 한다.&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1266&quot; data-origin-height=&quot;252&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cFC5WT/btsM65FPSpN/SR4tuWl9lqQNlZJIUiu871/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cFC5WT/btsM65FPSpN/SR4tuWl9lqQNlZJIUiu871/img.jpg&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cFC5WT/btsM65FPSpN/SR4tuWl9lqQNlZJIUiu871/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcFC5WT%2FbtsM65FPSpN%2FSR4tuWl9lqQNlZJIUiu871%2Fimg.jpg&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1266&quot; height=&quot;252&quot; data-origin-width=&quot;1266&quot; data-origin-height=&quot;252&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;CreateDefaultSubobject&amp;lt;T&amp;gt;(FName SubobjectName)를 사용하여 CDO가 생성 될때 생성.(생성자에서만 호출해야 함&lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;)&lt;br /&gt;런타임에 필요시에 NewObject()를 사용 하여 생성.&lt;/p&gt;</description>
      <category>언리얼 엔진 공부/언리얼C++</category>
      <author>gasick</author>
      <guid isPermaLink="true">https://gasick.tistory.com/14</guid>
      <comments>https://gasick.tistory.com/14#entry14comment</comments>
      <pubDate>Thu, 3 Apr 2025 12:19:12 +0900</pubDate>
    </item>
    <item>
      <title>인터페이스</title>
      <link>https://gasick.tistory.com/13</link>
      <description>&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;b&gt;인터페이스란?&lt;/b&gt;&lt;br /&gt;&lt;b&gt;객체가 &lt;span style=&quot;color: #ef5369;&quot;&gt;반드시&lt;/span&gt; 구현해야 할 행동을 지정하는데 활용하는 타입입니다. 다형성의 구현 의존성이 분리된 설계 유용하게 활용한다.&lt;/b&gt;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;792&quot; data-origin-height=&quot;465&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/8NCUF/btsM5xQVwbZ/bzv8jUigWvmUkONglWF7uK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/8NCUF/btsM5xQVwbZ/bzv8jUigWvmUkONglWF7uK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/8NCUF/btsM5xQVwbZ/bzv8jUigWvmUkONglWF7uK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F8NCUF%2FbtsM5xQVwbZ%2Fbzv8jUigWvmUkONglWF7uK%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;454&quot; data-origin-width=&quot;792&quot; data-origin-height=&quot;465&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt; 언리얼 엔진 C++ 인터페이스 특징&lt;/b&gt;&lt;/h4&gt;
&lt;p data-end=&quot;454&quot; data-start=&quot;228&quot; data-ke-size=&quot;size16&quot;&gt;1️⃣ &lt;b&gt;UInterface 클래스를 사용하여 선언해야 함&lt;/b&gt;&lt;br /&gt;2️⃣ &lt;b&gt;UObject 기반이므로, 언리얼의 리플렉션 시스템(Reflection)을 지원&lt;/b&gt;&lt;br /&gt;3️⃣ &lt;b&gt;다른 UObject 기반 클래스에만 적용 가능&lt;/b&gt; (AActor 등)&lt;br /&gt;4️⃣ &lt;b&gt;인터페이스의 함수를 반드시 가상 함수(virtual)로 선언해야 함&lt;/b&gt;&lt;br /&gt;5️⃣ &lt;b&gt;C++과 블루프린트 모두에서 사용할 수 있음&lt;/b&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1743582555715&quot; style=&quot;background-color: #f8f8f8; color: #383a42; text-align: start;&quot; data-ke-type=&quot;codeblock&quot; data-ke-language=&quot;cpp&quot;&gt;&lt;code&gt;// IMyInterface.h
#pragma once

#include &quot;UObject/Interface.h&quot;
#include &quot;IMyInterface.generated.h&quot;

// UINTERFACE() 매크로를 사용하여 인터페이스 선언
UINTERFACE(MinimalAPI)
class UMyInterface : public UInterface
{
    GENERATED_BODY()
};

// 인터페이스를 구현할 클래스가 상속받을 실제 인터페이스
class MYPROJECT_API IMyInterface
{
    GENERATED_BODY()

public:
    // 인터페이스의 순수 가상 함수 선언
    virtual void DoSomething() = 0;
};&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;&lt;b&gt;UINTERFACE(MinimalAPI)&lt;/b&gt; &amp;rarr; 언리얼의 리플렉션 시스템을 사용하도록 설정&lt;br /&gt;IMyInterface 클래스에서 DoSomething()을 순수 가상 함수로 선언&lt;br /&gt;UMyInterface는 UObject의 기능을 포함하는 &lt;b&gt;언리얼 고유의 인터페이스 시스템&lt;/b&gt;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;620&quot; data-origin-height=&quot;172&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/b7CJF4/btsM6IYddiQ/WKb0ePe74g7jkeZqOcG2W1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/b7CJF4/btsM6IYddiQ/WKb0ePe74g7jkeZqOcG2W1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/b7CJF4/btsM6IYddiQ/WKb0ePe74g7jkeZqOcG2W1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fb7CJF4%2FbtsM6IYddiQ%2FWKb0ePe74g7jkeZqOcG2W1%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;172&quot; data-origin-width=&quot;620&quot; data-origin-height=&quot;172&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;</description>
      <category>언리얼 엔진 공부/언리얼C++</category>
      <author>gasick</author>
      <guid isPermaLink="true">https://gasick.tistory.com/13</guid>
      <comments>https://gasick.tistory.com/13#entry13comment</comments>
      <pubDate>Wed, 2 Apr 2025 17:35:29 +0900</pubDate>
    </item>
  </channel>
</rss>