<?xml version="1.0" encoding="utf-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
    <channel>
        <title>power_overwhelming</title>
        <link>https://velog.io/</link>
        <description>이것 저것 다해보는 삶</description>
        <lastBuildDate>Fri, 08 May 2026 12:12:45 GMT</lastBuildDate>
        <docs>https://validator.w3.org/feed/docs/rss2.html</docs>
        <generator>https://github.com/jpmonette/feed</generator>
        <image>
            <title>power_overwhelming</title>
            <url>https://images.velog.io/images/bak_chun8/profile/fb237a59-9e1d-4b15-8aec-77cf4716f727/8300.png</url>
            <link>https://velog.io/</link>
        </image>
        <copyright>Copyright (C) 2019. power_overwhelming. All rights reserved.</copyright>
        <atom:link href="https://v2.velog.io/rss/bak_chun8" rel="self" type="application/rss+xml"/>
        <item>
            <title><![CDATA[TIL: Unreal C++ 27일차]]></title>
            <link>https://velog.io/@bak_chun8/TIL-Unreal-C-27%EC%9D%BC%EC%B0%A8</link>
            <guid>https://velog.io/@bak_chun8/TIL-Unreal-C-27%EC%9D%BC%EC%B0%A8</guid>
            <pubDate>Fri, 08 May 2026 12:12:45 GMT</pubDate>
            <description><![CDATA[<h2 id="누적-학습-시간--260시간-34분-">*<em>누적 학습 시간 : 260시간 34분 *</em></h2>
<h4 id="📅-2026-05-08">📅 2026-05-08</h4>
<p>무한성이라고 불리던 스테이지를 일찍 탈출한 6명이서 사이드 프로젝트를 진행하기로 했다.
순전히 기능 공부를 위함이고 출시계획같은건 당연하게도 없다.</p>
<h3 id="프로젝트-진행기간--260511--260521">프로젝트 진행기간 : 26.05.11 ~ 26.05.21</h3>
<p>주제는 3D 뱀파이어 서바이벌이다.
개발 진행상황은 <a href="https://github.com/NBC-SideProject/Vampire-Survival">레포지토리 참조</a></p>
<p>MVP 스팩을 크게 6가지로 나눴다.
스킬 / 캐릭터 / 적 / 시스템(웨이브, 스포너) / 아이템 / UI
나는 그 중 시스템(웨이브, 스포너)를 담당하게 됐다.</p>
<h1 id="enemyspawner">EnemySpawner</h1>
<p>마침 해당 사이드 프로젝트를 진행하기 전 EnemySpawner를 만들어봤었다.
해당 코드 첨부</p>
<h2 id="enemyspawnerh">EnemySpawner.h</h2>
<pre><code class="language-cpp">// 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;EnemySpawner.generated.h&quot;

class ACharacter;

UCLASS()
class METAL3D_API AEnemySpawner : public AActor
{
    GENERATED_BODY()

public:
    // Sets default values for this actor&#39;s properties
    AEnemySpawner();

protected:
    // Called when the game starts or when spawned
    virtual void BeginPlay() override;

public:
    UFUNCTION(BlueprintCallable, Category=&quot;Spawner&quot;)
    void StartSpawning();

    UFUNCTION(BlueprintCallable, Category=&quot;Spawner&quot;)
    void StopSpawning();

private:
    UPROPERTY(EditAnywhere, Category=&quot;Spawner&quot;)
    TSubclassOf&lt;ACharacter&gt; EnemyClass;

    UPROPERTY(EditAnywhere, Category=&quot;Spawner&quot;)
    float SpawnRadius = 1200.f;

    UPROPERTY(EditAnywhere, Category=&quot;Spawner&quot;)
    float SpawnInterval = 3.f;

    UPROPERTY(EditAnywhere, Category=&quot;Spawner&quot;)
    int32 MaxEnemyCount = 100;

    UPROPERTY(EditAnywhere, Category=&quot;Spawner&quot;)
    bool bAutoStart = true;

    UPROPERTY()
    TArray&lt;TObjectPtr&lt;AActor&gt;&gt; SpawnedEnemies;

    FTimerHandle SpawnTimerHandler;

    void SpawnEnemy();

    bool FindSpawnLocation(FVector&amp; OutLocation) const;

    void CleanupInvalidEnemies();
};
</code></pre>
<h2 id="enemyspawnercpp">EnemySpawner.cpp</h2>
<pre><code class="language-cpp">// Fill out your copyright notice in the Description page of Project Settings.


#include &quot;Spawner/EnemySpawner.h&quot;

#include &quot;NavigationSystem.h&quot;
#include &quot;GameFramework/Character.h&quot;
#include &quot;TimerManager.h&quot;
#include &quot;Components/CapsuleComponent.h&quot;
#include &quot;Engine/World.h&quot;

// Sets default values
AEnemySpawner::AEnemySpawner()
{
    // Set this actor to call Tick() every frame.  You can turn this off to improve performance if you don&#39;t need it.
    PrimaryActorTick.bCanEverTick = true;
}

// Called when the game starts or when spawned
void AEnemySpawner::BeginPlay()
{
    Super::BeginPlay();

    if (bAutoStart)
    {
        StartSpawning();
    }
}

void AEnemySpawner::StartSpawning()
{
    if (!GetWorld())
    {
        return;
    }

    GetWorld()-&gt;GetTimerManager().SetTimer(
        SpawnTimerHandler,
        this,
        &amp;AEnemySpawner::SpawnEnemy,
        SpawnInterval,
        true,
        0.f
    );
}

void AEnemySpawner::StopSpawning()
{
    if (!GetWorld())
    {
        return;
    }

    GetWorld()-&gt;GetTimerManager().ClearTimer(SpawnTimerHandler);
}

void AEnemySpawner::SpawnEnemy()
{
    if (!EnemyClass)
    {
        UE_LOG(LogTemp, Warning, TEXT(&quot;Enemy Spawner: EnemyClass not set&quot;));
        return;
    }

    CleanupInvalidEnemies();
    if (SpawnedEnemies.Num() &gt;= MaxEnemyCount)
    {
        return;
    }

    FVector SpawnLocation;
    if (!FindSpawnLocation(SpawnLocation))
    {
        UE_LOG(LogTemp, Warning, TEXT(&quot;Enemy Spawner: Failed to find spawn location&quot;));
        return;
    }

    ACharacter* DefaultEnemy = EnemyClass-&gt;GetDefaultObject&lt;ACharacter&gt;();
    if (!DefaultEnemy || !DefaultEnemy-&gt;GetCapsuleComponent())
    {
        return;
    }

    const float CapsuleHalfHeight = DefaultEnemy-&gt;GetCapsuleComponent()-&gt;GetScaledCapsuleHalfHeight();
    SpawnLocation.Z += CapsuleHalfHeight;

    const FRotator SpawnRotation = FRotator::ZeroRotator;

    FActorSpawnParameters SpawnParams;

    SpawnParams.Owner = this;
    SpawnParams.SpawnCollisionHandlingOverride =
        ESpawnActorCollisionHandlingMethod::AdjustIfPossibleButDontSpawnIfColliding;

    ACharacter* SpawnedEnemy = GetWorld()-&gt;SpawnActor&lt;ACharacter&gt;(
        EnemyClass,
        SpawnLocation,
        SpawnRotation,
        SpawnParams
    );

    if (SpawnedEnemy)
    {
        SpawnedEnemies.Add(SpawnedEnemy);
    }
}

bool AEnemySpawner::FindSpawnLocation(FVector&amp; OutLocation) const
{
    UNavigationSystemV1* NavSystem = UNavigationSystemV1::GetCurrent(GetWorld());

    if (!NavSystem)
    {
        return false;
    }

    FNavLocation NavLocation;

    const bool bFound = NavSystem-&gt;GetRandomReachablePointInRadius(
        GetActorLocation(),
        SpawnRadius,
        NavLocation
    );

    if (!bFound)
    {
        return false;
    }

    OutLocation = NavLocation.Location;
    return true;
}

void AEnemySpawner::CleanupInvalidEnemies()
{
    SpawnedEnemies.RemoveAll([](const TObjectPtr&lt;AActor&gt;&amp; Enemy)
    {
        return !IsValid(Enemy);
    });
}
</code></pre>
<p>해당 코드를 복붙 후 수정.. 하고싶지만 우선은 Unreal c++에 익숙해지는게 먼저기 때문에 다시 손으로 따라쳐볼 예정이다. 거기에 달라지는 점들이 몇가지 있어서 추가 수정을 붙일 예정이다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[TIL: Unreal C++ 26일차]]></title>
            <link>https://velog.io/@bak_chun8/TIL-Unreal-C-26%EC%9D%BC%EC%B0%A8</link>
            <guid>https://velog.io/@bak_chun8/TIL-Unreal-C-26%EC%9D%BC%EC%B0%A8</guid>
            <pubDate>Thu, 07 May 2026 12:01:28 GMT</pubDate>
            <description><![CDATA[<h2 id="누적-학습-시간--248시간-34분-">*<em>누적 학습 시간 : 248시간 34분 *</em></h2>
<h4 id="📅-2026-05-07">📅 2026-05-07</h4>
<h1 id="c-캐릭터-생성">C++ 캐릭터 생성</h1>
<h2 id="playercharacterh">PlayerCharacter.h</h2>
<pre><code class="language-cpp">#pragma once

#include &quot;CoreMinimal.h&quot;
#include &quot;GameFramework/Character.h&quot;
#include &quot;InputActionValue.h&quot;

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

class UInputMappingContext;
class UInputAction;
class USceneComponent;

UCLASS()
class METAL3D_API APlayerCharacter : public ACharacter
{
    GENERATED_BODY()

public:
    // Sets default values for this character&#39;s properties
    APlayerCharacter();

protected:
    // Called when the game starts or when spawned
    virtual void BeginPlay() override;

    // 인풋 컴포넌트 오버라이드
    virtual void SetupPlayerInputComponent(UInputComponent* PlayerInputComponent) override;
    UPROPERTY(EditDefaultsOnly, BlueprintReadOnly, Category=&quot;Input&quot;)
    TObjectPtr&lt;UInputMappingContext&gt; DefaultMappingContext;

    UPROPERTY(EditDefaultsOnly, BlueprintReadOnly, Category=&quot;Input&quot;)
    TObjectPtr&lt;UInputAction&gt; MoveAction;

    UPROPERTY(EditDefaultsOnly, BlueprintReadOnly, Category=&quot;Input&quot;)
    TObjectPtr&lt;UInputAction&gt; LookAction;

    UPROPERTY(EditDefaultsOnly, BlueprintReadOnly, Category=&quot;Input&quot;)
    TObjectPtr&lt;UInputAction&gt; FireAction;

    virtual void NotifyControllerChanged() override;


    // Projectile
    UPROPERTY(EditDefaultsOnly, Category=&quot;Combat&quot;)
    TSubclassOf&lt;class AProjectile&gt; ProjectileClass;

    USceneComponent* FindMuzzlePoint() const;
    UPROPERTY(EditDefaultsOnly, Category=&quot;Combat&quot;)
    FName MuzzlePointComponentName = TEXT(&quot;MuzzlePoint&quot;);

    UPROPERTY(EditDefaultsOnly, Category=&quot;Combat&quot;)
    float AimTraceDistance = 10000.f;

    void Fire();

public:
    // Called every frame
    virtual void Tick(float DeltaTime) override;

private:
    void Move(const FInputActionValue&amp; Value);
    void Look(const FInputActionValue&amp; Value);
};
</code></pre>
<h2 id="playercharactercpp">PlayerCharacter.cpp</h2>
<pre><code class="language-cpp">// Fill out your copyright notice in the Description page of Project Settings.


#include &quot;Player/PlayerCharacter.h&quot;

#include &quot;EnhancedInputComponent.h&quot;
#include &quot;EnhancedInputSubsystems.h&quot;
#include &quot;Projectile/Projectile.h&quot;
#include &quot;GameFramework/PlayerController.h&quot;
#include &quot;Camera/CameraComponent.h&quot;
#include &quot;Kismet/GameplayStatics.h&quot;
#include &quot;GameFramework/PlayerController.h&quot;
#include &quot;DrawDebugHelpers.h&quot;

// Sets default values
APlayerCharacter::APlayerCharacter()
{
    // Set this character to call Tick() every frame.  You can turn this off to improve performance if you don&#39;t need it.
    PrimaryActorTick.bCanEverTick = true;
}

// Called when the game starts or when spawned
void APlayerCharacter::BeginPlay()
{
    Super::BeginPlay();
}

// Called every frame
void APlayerCharacter::Tick(float DeltaTime)
{
    Super::Tick(DeltaTime);
}

// Called to bind functionality to input
void APlayerCharacter::SetupPlayerInputComponent(UInputComponent* PlayerInputComponent)
{
    Super::SetupPlayerInputComponent(PlayerInputComponent);
    UEnhancedInputComponent* EnhancedInputComponent = Cast&lt;UEnhancedInputComponent&gt;(PlayerInputComponent);

    if (!EnhancedInputComponent)
    {
        return;
    }

    if (MoveAction)
    {
        EnhancedInputComponent-&gt;BindAction(
            MoveAction,
            ETriggerEvent::Triggered,
            this,
            &amp;APlayerCharacter::Move
        );
    }

    if (LookAction)
    {
        EnhancedInputComponent-&gt;BindAction(
            LookAction,
            ETriggerEvent::Triggered,
            this,
            &amp;APlayerCharacter::Look
        );
    }

    if (FireAction)
    {
        EnhancedInputComponent-&gt;BindAction(
            FireAction,
            ETriggerEvent::Triggered,
            this,
            &amp;APlayerCharacter::Fire
        );
    }
}

void APlayerCharacter::NotifyControllerChanged()
{
    Super::NotifyControllerChanged();

    APlayerController* PlayerController = Cast&lt;APlayerController&gt;(GetController());
    if (!PlayerController)
    {
        return;
    }

    ULocalPlayer* LocalPlayer = PlayerController-&gt;GetLocalPlayer();
    if (!LocalPlayer)
    {
        return;
    }

    UEnhancedInputLocalPlayerSubsystem* InputSubsystem = LocalPlayer-&gt;GetSubsystem&lt;
        UEnhancedInputLocalPlayerSubsystem&gt;();
    if (InputSubsystem &amp;&amp; DefaultMappingContext)
    {
        InputSubsystem-&gt;AddMappingContext(DefaultMappingContext, 0);
    }
}


void APlayerCharacter::Move(const FInputActionValue&amp; Value)
{
    const FVector2D MovementVector = Value.Get&lt;FVector2D&gt;();

    if (Controller == nullptr)
    {
        return;
    }

    const FRotator ControlRotation = Controller-&gt;GetControlRotation();
    const FRotator YawRotation(0.0f, ControlRotation.Yaw, 0.0f);

    const FVector ForwardDirection = FRotationMatrix(YawRotation).GetUnitAxis(EAxis::X);
    const FVector RightDirection = FRotationMatrix(YawRotation).GetUnitAxis(EAxis::Y);

    AddMovementInput(ForwardDirection, MovementVector.Y);
    AddMovementInput(RightDirection, MovementVector.X);
}

void APlayerCharacter::Look(const FInputActionValue&amp; Value)
{
    const FVector2D LookAxis = Value.Get&lt;FVector2D&gt;();

    AddControllerYawInput(LookAxis.X);
    AddControllerPitchInput(-LookAxis.Y);
}

USceneComponent* APlayerCharacter::FindMuzzlePoint() const
{
    TArray&lt;USceneComponent*&gt; SceneComponents;

    GetComponents&lt;USceneComponent&gt;(SceneComponents);

    for (USceneComponent* Component : SceneComponents)
    {
        if (IsValid(Component) &amp;&amp; Component-&gt;GetName() == MuzzlePointComponentName.ToString())
        {
            return Component;
        }
    }

    return nullptr;
}


void APlayerCharacter::Fire()
{
    if (!ProjectileClass)
    {
        return;
    }

    APlayerController* PC = Cast&lt;APlayerController&gt;(GetController());
    if (!PC)
    {
        return;
    }

    int32 ViewportX = 0;
    int32 ViewportY = 0;

    PC-&gt;GetViewportSize(ViewportX, ViewportY);

    const float ScreenCenterX = ViewportX * 0.5f;
    const float ScreenCenterY = ViewportY * 0.5f;

    FVector WorldLocation;
    FVector WorldDirection;

    const bool bDeprojected = PC-&gt;DeprojectScreenPositionToWorld(
        ScreenCenterX,
        ScreenCenterY,
        WorldLocation,
        WorldDirection
    );

    if (!bDeprojected)
    {
        return;
    }

    const FVector TraceStart = WorldLocation;
    const FVector TraceEnd = TraceStart + WorldDirection * AimTraceDistance;

    FHitResult CameraHit;

    FCollisionQueryParams QueryParams;
    QueryParams.AddIgnoredActor(this);

    GetWorld()-&gt;LineTraceSingleByChannel(
        CameraHit,
        TraceStart,
        TraceEnd,
        ECC_Visibility,
        QueryParams
    );

    const FVector AimTarget = CameraHit.bBlockingHit ? CameraHit.ImpactPoint : TraceEnd;

    const USceneComponent* MuzzlePoint = FindMuzzlePoint();
    const FVector MuzzleLocation = MuzzlePoint-&gt;GetComponentLocation();

    const FVector FireDirection = (AimTarget - MuzzleLocation).GetSafeNormal();

    const FRotator SpawnRotation = FireDirection.Rotation();

    DrawDebugLine(
        GetWorld(),
        TraceStart,
        TraceEnd,
        FColor::Green,
        false,
        2.f,
        0,
        1.f
    );

    DrawDebugLine(
        GetWorld(),
        MuzzleLocation,
        MuzzleLocation + FireDirection * 3000.f,
        FColor::Red,
        false,
        2.f,
        0,
        2.f
    );

    FActorSpawnParameters SpawnParams;
    SpawnParams.Owner = this;
    SpawnParams.Instigator = this;
    SpawnParams.SpawnCollisionHandlingOverride =
        ESpawnActorCollisionHandlingMethod::AlwaysSpawn;

    GetWorld()-&gt;SpawnActor&lt;AProjectile&gt;(
        ProjectileClass,
        MuzzleLocation,
        SpawnRotation,
        SpawnParams
    );
}
</code></pre>
]]></description>
        </item>
        <item>
            <title><![CDATA[TIL: Unreal C++ 25일차]]></title>
            <link>https://velog.io/@bak_chun8/TIL-Unreal-C-25%EC%9D%BC%EC%B0%A8</link>
            <guid>https://velog.io/@bak_chun8/TIL-Unreal-C-25%EC%9D%BC%EC%B0%A8</guid>
            <pubDate>Wed, 06 May 2026 11:20:14 GMT</pubDate>
            <description><![CDATA[<h2 id="누적-학습-시간--236시간-34분-">*<em>누적 학습 시간 : 236시간 34분 *</em></h2>
<h4 id="📅-2026-05-06">📅 2026-05-06</h4>
<h1 id="ue-handbook"><a href="https://ue-docs-korea.github.io/UE-handbook/">UE-Handbook</a></h1>
<p>기존 에픽게임즈 언리얼 공식문서가 사실 초보가 보기에는 너무 어렵게 되어있다.
그래서 Docusaurus이용해서 handbook을 만들어봤다.
아이디어는 예전에 TS handbook이 도움이 많이됐던게 기억나서 만들어봤다.
유지보수를 계속 할지는 모르겠지만 혹시 필요한사람들이 많이들 봤으면 좋겠다.</p>
<h1 id="c-사용-imc-ia-등록">C++ 사용 IMC, IA 등록</h1>
<p>언리얼에서 pawn, character가 사용자의 입력대로 이동하려면 <code>Input Mapping Context</code>와 <code>Input Action</code>을 등록해주어야한다.</p>
<p>이전에는 블루프린트로 가볍게 했지만 C++에서는 어떻게 하는지 알아보자.</p>
<h2 id="imc-ia-생성">IMC, IA 생성</h2>
<p><img src="https://velog.velcdn.com/images/bak_chun8/post/1ce039c6-5013-45dc-bb2d-d740a4a25a89/image.png" alt=""></p>
<p>일단 IMC, IA생성은 블루프린트와 동일하다.</p>
<h3 id="imc">IMC</h3>
<p><img src="https://velog.velcdn.com/images/bak_chun8/post/a62d40d8-f824-4916-9e5a-4854ea790fe7/image.png" alt=""></p>
<h3 id="ia">IA</h3>
<p><img src="https://velog.velcdn.com/images/bak_chun8/post/b310024f-d2ee-4d62-bf6c-5c6cf01c9bf6/image.png" alt=""></p>
<h2 id="c-playercharacter-클래스-생성">C++ PlayerCharacter 클래스 생성</h2>
<p>Character 클래스를 부모로 만든 PlayerCharacter 클래스를 생성해준다.</p>
<h3 id="playercharacterh">PlayerCharacter.h</h3>
<pre><code class="language-cpp">#pragma once

#include &quot;CoreMinimal.h&quot;
#include &quot;GameFramework/Character.h&quot;
#include &quot;APlayerCharacter.generated.h&quot;

UCLASS()
class METAL3D_API APlayerCharacter : public ACharacter
{
    GENERATED_BODY()

public:
    // Sets default values for this character&#39;s properties
    APlayerCharacter();

protected:
    // Called when the game starts or when spawned
    virtual void BeginPlay() override;

public:    
    // Called every frame
    virtual void Tick(float DeltaTime) override;

    // Called to bind functionality to input
    virtual void SetupPlayerInputComponent(class UInputComponent* PlayerInputComponent) override;

};</code></pre>
<h3 id="playercharactercpp">PlayerCharacter.cpp</h3>
<pre><code class="language-cpp">#include &quot;Player/PlayerCharacter.h&quot;

// Sets default values
APlayerCharacter::APlayerCharacter()
{
     // Set this character to call Tick() every frame.  You can turn this off to improve performance if you don&#39;t need it.
    PrimaryActorTick.bCanEverTick = true;

}

// Called when the game starts or when spawned
void APlayerCharacter::BeginPlay()
{
    Super::BeginPlay();

}

// Called every frame
void APlayerCharacter::Tick(float DeltaTime)
{
    Super::Tick(DeltaTime);

}

// Called to bind functionality to input
void APlayerCharacter::SetupPlayerInputComponent(UInputComponent* PlayerInputComponent)
{
    Super::SetupPlayerInputComponent(PlayerInputComponent);

}</code></pre>
<p>생성 해준 뒤에는 미리 만들어놓은 IMC와 IA를 연결하는 작업을 진행해준다.</p>
<h2 id="playercharacterh-수정">PlayerCharacter.h 수정</h2>
<pre><code class="language-cpp">#pragma once

#include &quot;CoreMinimal.h&quot;
#include &quot;GameFramework/Character.h&quot;
#include &quot;InputActionValue.h&quot;

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

class UInputMappingContext;
class UInputAction;

UCLASS()
class METAL3D_API APlayerCharacter : public ACharacter
{
    GENERATED_BODY()

public:
    // Sets default values for this character&#39;s properties
    APlayerCharacter();

protected:
    // Called when the game starts or when spawned
    virtual void BeginPlay() override;

    // 인풋 컴포넌트 오버라이드
    virtual void SetupPlayerInputComponent(UInputComponent* PlayerInputComponent) override;
    UPROPERTY(EditDefaultsOnly, BlueprintReadOnly, Category=&quot;Input&quot;)
    TObjectPtr&lt;UInputMappingContext&gt; DefaultMappingContext;

    UPROPERTY(EditDefaultsOnly, BlueprintReadOnly, Category=&quot;Input&quot;)
    TObjectPtr&lt;UInputAction&gt; MoveAction;

    virtual void NotifyControllerChanged() override;

public:
    // Called every frame
    virtual void Tick(float DeltaTime) override;

private:
    void Move(const FInputActionValue&amp; Value);
};</code></pre>
<h2 id="해설-및-수정사항">해설 및 수정사항</h2>
<p>뭐가 많이 추가됐는데 한줄씩 보겠다.</p>
<h3 id="헤더-include">헤더 include</h3>
<pre><code class="language-cpp">include &quot;InputActionValue.h&quot;</code></pre>
<p>이 헤더는 아래서 정의한 <code>void Move(const FInputActionValue&amp; Value)</code>에서
<code>FInputActionValue</code>타입을 헤더의 상수 시그니처에서 사용 중이기 때문에 필요하다.</p>
<pre><code class="language-cpp">include &quot;PlayerCharacter.generated.h&quot;</code></pre>
<p>해당 헤더는는 언리얼 리플렉션 코드 생성용 헤더이다.
<code>*.generated.h</code>는 반드시 해당 헤더의 include중 마지막에 와야한다.</p>
<h3 id="전방선언">전방선언</h3>
<pre><code class="language-cpp">class UInputMappingContext;
class UInputAction;</code></pre>
<p><code>protected:</code>에서 선언된</p>
<pre><code class="language-cpp">UPROPERTY(EditDefaultsOnly, BlueprintReadOnly, Category=&quot;Input&quot;)
TObjectPtr&lt;UInputMappingContext&gt; DefaultMappingContext;

UPROPERTY(EditDefaultsOnly, BlueprintReadOnly, Category=&quot;Input&quot;)
TObjectPtr&lt;UInputAction&gt; MoveAction;</code></pre>
<p>에서 포인터로써만 들고있기 때문에 헤더에서는 전체 include가 아닌 전방선언만 해도 오케이다.</p>
<h3 id="uclass">UCLASS()</h3>
<p>언리얼 리플렉션 시스템에 해당 클래스를 등록한다.</p>
<p>이게 있어야 BP생성시 해당 클래스를 부모로 선택 가능하고 <code>UPROPERTY</code>,<code>UFUNCTION</code> 같은 시스템도 작동한다.</p>
<h3 id="metal3d_api">METAL3D_API</h3>
<p>프로젝트 모듈 export/import 매크로
프로젝트명이 Metal3D라서 자동으로 붙은 것이다.</p>
<h3 id="aplayercharacter--public-acharacter">APlayerCharacter : public ACharacter</h3>
<p>ACharacter를 상속받은 PlayerCharacter 클래스</p>
<pre><code>AActor
자식 &gt; APawn
의 자식 &gt; ACharacter
의 자식 &gt; APlayerCharacter</code></pre><p>class 이름이 PlayerCharacter인데 <a href="https://ue-docs-korea.github.io/UE-handbook/prefixes-and-naming">왜 APlayerCharacter일까?</a></p>
<h3 id="generated_body">GENERATED_BODY()</h3>
<p>언리얼이 생성한 리플렉션 코드를 클래스 안에 삽입하는 매크로
<code>UCLASS()</code> 에는 필수로 들어간다.</p>
<h3 id="aplayercharacter">APlayerCharacter()</h3>
<pre><code class="language-cpp">APlayerCharacter();</code></pre>
<p>생성자</p>
<h3 id="beginplay">Beginplay</h3>
<pre><code class="language-cpp">virtual void BeginPlay() override;</code></pre>
<p>블프에서 많이 보던 BeginPlay 노드가 이 부분이다.</p>
<h3 id="setupplayerinputcomponent">SetupPlayerInputComponent</h3>
<pre><code class="language-cpp">virtual void SetupPlayerInputComponent(UInputComponent* PlayerInputComponent) override;</code></pre>
<p>Pawn/Character가 Possess되어 입력 컴포넌트가 준비될 때 호출</p>
<p>블프와는 다르게 여기서 IMC, IA를 연결한다.</p>
<h3 id="uproperty">UPROPERTY(...)</h3>
<pre><code class="language-cpp">UPROPERTY(EditDefaultsOnly, BlueprintReadOnly, Category=&quot;Input&quot;)</code></pre>
<p><code>UPROPERTY</code>
일반 C++변수를 리플렉션 시스템에 등록하는 매크로
등록된 변수는 언리얼 에디터에서 노출되며 블루프린트 접근, 직렬화, GC 추적같은 언언리얼 기능의 대상이 된다.</p>
<p><code>EditDefaultsOnly</code>
인스턴스마다 수정하는 값이 아닌
클래스 기본값, BP defaults에서만 수정 가능하게 한다.</p>
<p><code>BlueprintReadOnly</code>
블루프린트에서 해당 값을 읽을 수는 있지만 수정할 수는 없다.</p>
<p><code>Category=&quot;Input&quot;</code>
언리얼 에디터 &gt; Detail 패널 내에서 Input 카테고리 아래에서 보여준다.</p>
<h3 id="tobjectptruinputmappingcontext"><code>TObjectPtr&lt;UInputMappingContext&gt;</code></h3>
<pre><code class="language-cpp">TObjectPtr&lt;UInputMappingContext&gt; DefaultMappingContext;</code></pre>
<p>UinputMappingContext 타입의 UObject를 가리키는 포인터.
IMC_PlayerCharacter같은 IMC 에셋을 참조한다.</p>
<h3 id="notifycontrollerchanged">NotifyControllerChanged()</h3>
<pre><code class="language-cpp">virtual void NotifyControllerChanged() override;</code></pre>
<p>pawn, character를 소유하거나 조종하는 Controller가 변경되었음을 알리는 함수</p>
<p>플레이어가 조종하면 <code>APlayerController</code> AI가 조종하면 <code>AAIController</code>가 붙는다.</p>
<p>해당 함수는 Controller가 변경되는 시점에 호출되는데 대표적으로</p>
<ul>
<li>PlayerController가 Character에 Possess됐을 때</li>
<li>Controller가 바뀌었을 때</li>
<li>Possess, UnPossess 흐름에서 Controller 상태가 달라졌을 때</li>
</ul>
<p>블프와 달리 IMC를 해당 함수에서 붙이게 되는데 이유가
<code>BeginPlay()</code>시점에 아직 Controller나 LocalPlayer가 준비되지 않았을 수 있기 때문이다.</p>
<h3 id="move">Move()</h3>
<p>이동 입력을 처리하는 <strong>사용자 정의 함수</strong></p>
<h2 id="playercharactercpp-수정">PlayerCharacter.cpp 수정</h2>
<pre><code class="language-cpp">#include &quot;Player/PlayerCharacter.h&quot;

#include &quot;EnhancedInputComponent.h&quot;
#include &quot;EnhancedInputSubsystems.h&quot;

// Sets default values
APlayerCharacter::APlayerCharacter()
{
    // Set this character to call Tick() every frame.  You can turn this off to improve performance if you don&#39;t need it.
    PrimaryActorTick.bCanEverTick = true;
}

// Called when the game starts or when spawned
void APlayerCharacter::BeginPlay()
{
    Super::BeginPlay();


}

// Called every frame
void APlayerCharacter::Tick(float DeltaTime)
{
    Super::Tick(DeltaTime);
}

// Called to bind functionality to input
void APlayerCharacter::SetupPlayerInputComponent(UInputComponent* PlayerInputComponent)
{
    Super::SetupPlayerInputComponent(PlayerInputComponent);
    UEnhancedInputComponent* EnhancedInputComponent = Cast&lt;UEnhancedInputComponent&gt;(PlayerInputComponent);

    if (!EnhancedInputComponent)
    {
        return;
    }

    if (MoveAction)
    {
        EnhancedInputComponent-&gt;BindAction(
            MoveAction,
            ETriggerEvent::Triggered,
            this,
            &amp;APlayerCharacter::Move
        );
    }
}

void APlayerCharacter::NotifyControllerChanged()
{
    Super::NotifyControllerChanged();

    APlayerController* PlayerController = Cast&lt;APlayerController&gt;(GetController());
    if (!PlayerController)
    {
        return;
    }

    ULocalPlayer* LocalPlayer = PlayerController-&gt;GetLocalPlayer();
    if (!LocalPlayer)
    {
        return;
    }

    UEnhancedInputLocalPlayerSubsystem* InputSubsystem = LocalPlayer-&gt;GetSubsystem&lt;
        UEnhancedInputLocalPlayerSubsystem&gt;();
    if (InputSubsystem &amp;&amp; DefaultMappingContext)
    {
        InputSubsystem-&gt;AddMappingContext(DefaultMappingContext, 0);
    }
}


void APlayerCharacter::Move(const FInputActionValue&amp; Value)
{
    const FVector2D MovementVector = Value.Get&lt;FVector2D&gt;();

    if (Controller == nullptr)
    {
        return;
    }

    const FRotator ControlRotation = Controller-&gt;GetControlRotation();
    const FRotator YawRotation(0.0f, ControlRotation.Yaw, 0.0f);

    const FVector ForwardDirection = FRotationMatrix(YawRotation).GetUnitAxis(EAxis::X);
    const FVector RightDirection = FRotationMatrix(YawRotation).GetUnitAxis(EAxis::Y);

    AddMovementInput(ForwardDirection, MovementVector.Y);
    AddMovementInput(RightDirection, MovementVector.X);
}
</code></pre>
<h2 id="해설-및-수정사항-1">해설 및 수정사항</h2>
<h3 id="헤더">헤더</h3>
<pre><code class="language-cpp">#include &quot;EnhancedInputComponent.h&quot;</code></pre>
<p>헤더와는 다르게 EnhancedInputComponent를 포인터로 들고만 있는게 아니라
실제로 멤버 함수를 호출하거나 사용하기 때문에 필요하다.</p>
<h3 id="setupplayerinputcomponent-1">SetupPlayerInputComponent</h3>
<pre><code class="language-cpp">void APlayerCharacter::SetupPlayerInputComponent(UInputComponent* PlayerInputComponent)
{
    Super::SetupPlayerInputComponent(PlayerInputComponent);
    UEnhancedInputComponent* EnhancedInputComponent = Cast&lt;UEnhancedInputComponent&gt;(PlayerInputComponent);

    if (!EnhancedInputComponent)
    {
        return;
    }

    if (MoveAction)
    {
        EnhancedInputComponent-&gt;BindAction(
            MoveAction,
            ETriggerEvent::Triggered,
            this,
            &amp;APlayerCharacter::Move
        );
    }
}</code></pre>
<p>pawn, character의 입력 바인딩 구성 함수</p>
<h3 id="supersetupplayerinputcomponentplayerinputcomponent">Super::SetupPlayerInputComponent(PlayerInputComponent)</h3>
<p><code>Super::</code>는 언리얼 C++에서 부모클래스의 별칭이다.
해당 클래스의 부모클래스에서 해야할 처리를 먼저 수행하겠다는 의미다.</p>
<p>언리얼 오버라이드 함수에서 특별한 이유가 없으면 <code>Super::...()</code>를 호출하는게 기본</p>
<h3 id="castuenhancedinputcomponent"><code>Cast&lt;UEnhancedInputComponent&gt;</code></h3>
<p>언리얼의 Cast&lt;&gt;는 c++의 dynamic_cast와 비슷한 목적이다.
다른 점은 Cast&lt;&gt;는 리플렉션 시스템을 기반으로 UObject 타입 변환을 수행한다.
블프에서 많이 보던 Cast to 다</p>
<p>캐스팅을 하는 이유는 함수 인자가 기본 타입이기 때문이다.</p>
<pre><code class="language-cpp">// 함수인자 UInputComponent &gt; UEnhancedInputComponent로 캐스팅
UEnhancedInputComponent* EnhancedInputComponent = 
            Cast&lt;UEnhancedInputComponent&gt;(PlayerInputComponent);</code></pre>
<p>실패할 수도 있기 때문에 바로 체크도 해준다.</p>
<pre><code class="language-cpp">    if (!EnhancedInputComponent)
    {
        return;
    }</code></pre>
<h3 id="moveaction-바인딩">MoveAction 바인딩</h3>
<pre><code class="language-cpp">if (MoveAction)
    {
        EnhancedInputComponent-&gt;BindAction(
            MoveAction,
            ETriggerEvent::Triggered,
            this,
            &amp;APlayerCharacter::Move
        );
    }</code></pre>
<p><img src="https://velog.velcdn.com/images/bak_chun8/post/6a591d67-2dcf-4746-8c37-70e8a833fdc3/image.png" alt=""></p>
<p>헤더에서 <code>UPROPERTY</code>로 등록한 MoveAction이 있다.
언리얼 에디터에서 실제로 IA_Move를 등록해준 IA를 MoveAction에 바인딩해준다.</p>
<p><code>MoveAction</code>
어떤 Input Action을 감시할지 지정</p>
<p><code>ETriggerEvent::Triggered</code>
Input Action이 어떤 상태일 때 호출할지 정함</p>
<p><code>this</code>
호출 대상 객체, 현재 PlayerCharacter를 말함</p>
<p><code>&amp;APlayerCharacter::Move</code>
호출할 멤버함수 포인터</p>
<h3 id="notifycontrollerchanged-1">NotifyControllerChanged</h3>
<pre><code class="language-cpp">void APlayerCharacter::NotifyControllerChanged()
{
    Super::NotifyControllerChanged();

    APlayerController* PlayerController = Cast&lt;APlayerController&gt;(GetController());
    if (!PlayerController)
    {
        return;
    }

    ULocalPlayer* LocalPlayer = PlayerController-&gt;GetLocalPlayer();
    if (!LocalPlayer)
    {
        return;
    }

    UEnhancedInputLocalPlayerSubsystem* InputSubsystem = LocalPlayer-&gt;GetSubsystem&lt;
        UEnhancedInputLocalPlayerSubsystem&gt;();
    if (InputSubsystem &amp;&amp; DefaultMappingContext)
    {
        InputSubsystem-&gt;AddMappingContext(DefaultMappingContext, 0);
    }
}</code></pre>
<p>어차피 여러번 할텐데 너무 길어져서 나중에 더 작성해야지</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[TIL: C++ TextRPG 개발 24일차]]></title>
            <link>https://velog.io/@bak_chun8/TIL-C-TextRPG-%EA%B0%9C%EB%B0%9C-24%EC%9D%BC%EC%B0%A8</link>
            <guid>https://velog.io/@bak_chun8/TIL-C-TextRPG-%EA%B0%9C%EB%B0%9C-24%EC%9D%BC%EC%B0%A8</guid>
            <pubDate>Mon, 04 May 2026 09:57:04 GMT</pubDate>
            <description><![CDATA[<h2 id="누적-학습-시간--224시간-34분-">*<em>누적 학습 시간 : 224시간 34분 *</em></h2>
<h4 id="📅-2026-05-04">📅 2026-05-04</h4>
<h1 id="textrpg-완성본">TextRPG 완성본</h1>
<p><a href="https://github.com/Carrymachine/CH2-quest/tree/main/TextRPG">깃허브 링크</a></p>
<h1 id="c-학습">C++ 학습</h1>
<h2 id="solid-원칙"><a href="https://velog.io/@bak_chun8/TIL-SOLID-%EC%9B%90%EC%B9%99">SOLID 원칙</a></h2>
<h2 id="inline-static과-constexpr"><a href="https://velog.io/@bak_chun8/TIL-inline-static">inline static과 constexpr</a></h2>
<h2 id="rider-ide-c-표준버전-변경법"><a href="https://velog.io/@bak_chun8/Rider-IDE-C%ED%91%9C%EC%A4%80-%EB%B2%84%EC%A0%84-%EB%B3%80%EA%B2%BD%EB%B2%95">Rider IDE C++ 표준버전 변경법</a></h2>
]]></description>
        </item>
        <item>
            <title><![CDATA[Rider IDE C++표준 버전 변경법]]></title>
            <link>https://velog.io/@bak_chun8/Rider-IDE-C%ED%91%9C%EC%A4%80-%EB%B2%84%EC%A0%84-%EB%B3%80%EA%B2%BD%EB%B2%95</link>
            <guid>https://velog.io/@bak_chun8/Rider-IDE-C%ED%91%9C%EC%A4%80-%EB%B2%84%EC%A0%84-%EB%B3%80%EA%B2%BD%EB%B2%95</guid>
            <pubDate>Mon, 04 May 2026 06:16:52 GMT</pubDate>
            <description><![CDATA[<h4 id="📅-2026-05-04">📅 2026-05-04</h4>
<h1 id="rider-ide-c-표준-버전-변경">Rider IDE C++ 표준 버전 변경</h1>
<p>inline static 멤버변수 만들려고 하니까 C++ 17 이상에서만 가능하다고 에러가 표시됐다.</p>
<p>Unreal C++ 프로젝트는 자동으로 C++20이상으로 세팅되는데 일단 콘솔 프로젝트는 C++14로 세팅되는 것 같았다.</p>
<p>그래서 프로젝트 자체의 C++ 버전 업 하는 방법을 알아봤다.</p>
<p><img src="https://velog.velcdn.com/images/bak_chun8/post/29ed9cc7-9916-4d73-b849-f47764074bbb/image.png" alt=""></p>
<p>프로젝트 &gt; 우클릭 &gt; properties</p>
<p><img src="https://velog.velcdn.com/images/bak_chun8/post/9d47b79f-7090-4219-9acf-4ca42707e249/image.png" alt=""></p>
<p>C++20으로 변경</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[TIL: inline static과 constexpr]]></title>
            <link>https://velog.io/@bak_chun8/TIL-inline-static</link>
            <guid>https://velog.io/@bak_chun8/TIL-inline-static</guid>
            <pubDate>Mon, 04 May 2026 06:14:38 GMT</pubDate>
            <description><![CDATA[<h4 id="📅-2026-05-04">📅 2026-05-04</h4>
<h1 id="inline-static">inline static</h1>
<p>class 내무 멤버변수를 static으로 선언할 경우에 초기화가 불가능하다.
cpp에서 따로 초기화를 해줘야하는데</p>
<p>C++17부터 inline을 붙임으로써 선언과 동시에 초기화가 가능하다.</p>
<pre><code class="language-cpp">class Test
{
    static int a; // 오류X cpp에서 반드시 초기화
    static int a = 10; // 오류 발생

    inline static int a = 10; // 오류X cpp 초기화X

}</code></pre>
<h1 id="constexpr">constexpr</h1>
<pre><code class="language-cpp">class Test
{
    static int a;
    constexpr int a = 10;

    static constexpr int a = 10;

}</code></pre>
<h2 id="static">static</h2>
<pre><code class="language-cpp">class Test
{
    static int a;
}

int Test::a = 10;

Test::a = 20; // 가능</code></pre>
<ul>
<li>모든 객체가 하나의 x를 공유</li>
<li>런타임에 값 변경 가능</li>
<li>별도 정의 필요 (C++17 이전 기준)</li>
</ul>
<h2 id="constexpr-1">constexpr</h2>
<pre><code class="language-cpp">class Test
{
    constexpr int a = 10;
}


Test::a = 20; // 불가능</code></pre>
<ul>
<li>컴파일 타임에 값 확정</li>
<li>암묵적으로 const</li>
<li>변경 불가</li>
<li>함수에도 적용 가능 (constexpr function)</li>
</ul>
<h2 id="static-constexpr">static constexpr</h2>
<p>클래스에 속한 컴파일 타임 상수</p>
<ul>
<li>static &gt; 클래스 전체에서 공유</li>
<li>constexpr &gt; 컴파일 타임 상수</li>
</ul>
<h3 id="1-컴파일-타임-값">1. 컴파일 타임 값</h3>
<pre><code class="language-cpp">class Test
{
    static constexpr int a = 10;
}

int array[Test::a];</code></pre>
<p>컴파일 타임에 배열의 크기 요구시 static만으로는 불가능(런타임 초기화 해야하기 때문)</p>
<h3 id="2-헤더에서-안전하게-정의">2. 헤더에서 안전하게 정의</h3>
<pre><code class="language-cpp">class Test
{
    static constexpr int a = 10;
}</code></pre>
<p>ODR문제 X
별도의 cpp 필요X</p>
<h4 id="odr">ODR?</h4>
<p>One Definition Rule</p>
<h2 id="inline-static-vs-static-constexpr">inline static vs static constexpr</h2>
<table>
<thead>
<tr>
<th>항목</th>
<th>inline static</th>
<th>static constexpr</th>
</tr>
</thead>
<tbody><tr>
<td>변경 가능</td>
<td>O</td>
<td>X</td>
</tr>
<tr>
<td>컴파일 타임 상수</td>
<td>X</td>
<td>O</td>
</tr>
<tr>
<td>메모리</td>
<td>있음</td>
<td>거의 없음</td>
</tr>
<tr>
<td>용도</td>
<td>상태값</td>
<td>상수</td>
</tr>
</tbody></table>
]]></description>
        </item>
        <item>
            <title><![CDATA[TIL: SOLID 원칙]]></title>
            <link>https://velog.io/@bak_chun8/TIL-SOLID-%EC%9B%90%EC%B9%99</link>
            <guid>https://velog.io/@bak_chun8/TIL-SOLID-%EC%9B%90%EC%B9%99</guid>
            <pubDate>Mon, 04 May 2026 06:13:58 GMT</pubDate>
            <description><![CDATA[<h4 id="📅-2026-05-04">📅 2026-05-04</h4>
<h1 id="solid-원칙">SOLID 원칙</h1>
<p>TextRPG를 제작하면서 특정 물체(캐릭터, 적 등)를 Actor라는 최상위 클래스에서 기본적인 요소를 관리했다.
Unreal처럼 만들어보고 싶다는 이유에서 였는데 여러 문제가 발생했는데
해당 Actor가</p>
<ul>
<li>공격이 가능한가?</li>
<li>피해를 입을 수 있는가?</li>
</ul>
<p>위 두가지 때문에 캐릭터(폰)라는 한정된 역할만 할 수 있었다.
그때 같은 기수의 Dr. Dragon &#39;D&#39; Water 님께서 SOLID 원칙을 알려주셨다.</p>
<p>이전에도 FE엔지니어를 하면서 Repository 패턴을 사용할 일이 있었는데 그때 들어본 원칙이기도 하지만 제대로 알아볼 생각은 하지 않았었다.
실제로 3년넘게 실무하면서 class 사용해본 일이 손에 꼽을 정도로 FE에서는 class를 잘 사용하지 않는다.</p>
<h2 id="ssrp---single-responsibility-principle">S(SRP) - Single Responsibility Principle</h2>
<p><strong>단일 책임 원칙</strong>
클래스는 1개의 이유로만 변경되어야 한다.</p>
<pre><code class="language-cpp">class Player {
public:
    void Update();
    void Render();
    void SaveToFile();   // 저장 책임
    void SendNetwork();  // 네트워크 책임
};

// 분리
struct PlayerState {
    int hp;
    Vector3 position;
};

class PlayerSystem {
public:
    void Update(PlayerState&amp;);
};

class SaveSystem {
public:
    void Save(const PlayerState&amp;);
};</code></pre>
<h2 id="oocp---openclosed-principle">O(OCP) - Open/Closed Principle</h2>
<p><strong>개방/폐쇄 원칙</strong>
확장에는 열려있고 수정에는 닫혀있어야한다.</p>
<pre><code class="language-cpp">// 무기 추가할 때마다 값 추가해야함 
int CalcDamage(std::string type) {
    if (type == &quot;sword&quot;) return 10;
    if (type == &quot;gun&quot;) return 20;
}


// 개선
struct WeaponData
{
    int damage;
}

class Weapon
{
    WeaponData weaponData;

public:
    int ApplyDamage()
    {
        return weaponData.damage;
    }

}</code></pre>
<h2 id="llsp---liskov-substitution-principle">L(LSP) - Liskov Substitution Principle</h2>
<p><strong>리스코프 치환 원칙</strong>
자식 클래스는 부모 클래스를 완전히 대체 가능해야한다.</p>
<pre><code class="language-cpp">class Character
{
public:
    virtual void Attack() = 0;
};

class Pacifist : public Character
{
public:
    // 부모 Character는 Attack 호출 시 함수가 실행되는 것을 기대함
    // Pacifist에서 예외처리로 프로그램을 비정상 종료시키기 때문에 LSP 원칙에 어긋남
    void Attack() override { throw; } 
}



// 개선
class IAttackable
{
public:
    virtual void Attack() = 0;
}

class NPC { }; // 공격 없음
class Box { }; // 공격 없음

class Warrior : public IAttackable
{
    void Attack() override { ... }
}
</code></pre>
<h2 id="iisp---interface-segregation-principle">I(ISP) - Interface Segregation Principle</h2>
<p><strong>인터페이스 분리 원칙</strong>
클라이언트는 자신이 사용하지 않는 인터페이스에 의존해서는 안된다.</p>
<pre><code class="language-cpp">class IUnit
{
public:
    virtual void Move() = 0;
    virtual void Attack() = 0;
    virtual void Fly() = 0; // 거북이 Actor에 Fly???
}


// 개선
class MoveComponent { void Move(); }
class AttackComponent { void Attack(); }
class FlyComponenty { void Fly(); }

class Unit
{
    MoveComponent* move;
    AttackComponent* attack;
    // 해당 Unit은 Fly 기능이 없으므로 객체 생성X
}</code></pre>
<h2 id="ddip---dependency-inversion-principle">D(DIP) - Dependency Inversion Principle</h2>
<p><strong>의존성 역전 원칙</strong>
고수준 모듈은 저수준 모듈에 의존해서는 안되고 둘 다 추상화에 의존해야한다.</p>
<pre><code class="language-cpp">// Game이 더 고수준 모듈
class Game
{
    // 저수준 모듈 Sword에 의존하면 원칙 위배
    Sword sword;
}

// 개선

class Weapon
{
public:
    virtual void Attack() = 0;
}

class Game
{
    std::unique_ptr&lt;Weapon&gt; weapon;

    Game(satd::unique_ptr&lt;Weapon&gt; w) : weapon(std::move(w)) { };

}</code></pre>
<h2 id="총-정리">총 정리</h2>
<p>SRP → “변경 이유 1개” 시스템 단위 분리
OCP → “코드 말고 데이터로 확장” 데이터 기반 확장
LSP → “상속 대신 능력 분리” 상속 최소화
ISP → “인터페이스 → 컴포넌트로” 컴포넌트
DIP → “new 하지 말고 주입해라” DI + smart pointer</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[TIL: C++ TextRPG 개발 23일차]]></title>
            <link>https://velog.io/@bak_chun8/TIL-C-TextRPG-%EA%B0%9C%EB%B0%9C-23%EC%9D%BC%EC%B0%A8</link>
            <guid>https://velog.io/@bak_chun8/TIL-C-TextRPG-%EA%B0%9C%EB%B0%9C-23%EC%9D%BC%EC%B0%A8</guid>
            <pubDate>Thu, 30 Apr 2026 11:35:34 GMT</pubDate>
            <description><![CDATA[<h2 id="누적-학습-시간--212시간-34분-">*<em>누적 학습 시간 : 212시간 34분 *</em></h2>
<h4 id="📅-2026-04-30">📅 2026-04-30</h4>
<h1 id="가상함수와-추상클래스">가상함수와 추상클래스</h1>
<h5 id="참고--httpsbaggu4402tistorycom15">참고:  <a href="https://baggu4402.tistory.com/15">https://baggu4402.tistory.com/15</a></h5>
<p>TRPG 개발 중 캐릭터의 직업클래스와 부모 클래스를 만들면서 사용할 일이 있어서 알아봤다.</p>
<p>C++에서는 java처럼 abstact같은 키워드가 없다.</p>
<p>순수가상함수가 포함되어 있는 클래스는 추상클래스로 취급되고 순수가상함수는 상속받는 자식클래스에서 반드시 구현해야한다.</p>
<p><strong>순수 가상 함수는 부모의 강제 규칙, 오버라이딩은 자식의 실제 구현.</strong></p>
<pre><code class="language-cpp">class Job
{
public:
    virtual Stat SetBaseState() const = 0;
    virtual std::string GetJobName() const = 0;
}

class Warrior : public Job
{
    Stat SetBaseState() const override
    {
        ...구현...
    }
    std::string GetJobName() const override
    {
        ...구현...
    }
}
</code></pre>
<h2 id="ts-interface-생각하면-편함">TS interface 생각하면 편함!</h2>
<p>사실 class는 뭐 HttpClient 구현체 만들때나 쓰고 일부 백엔드 작업하면서나 써봤지 알고는 있지만 쓸일이 그닥 많지는 않았다.</p>
<p>어렵게 생각하지말고 내 입장에서는
추상클래스 = interface 라고 생각하면 기초수준에서의 이해는 되는 것 같다.</p>
<p>다만 다른점은 C++ 추상클래스는 클래스가 구현을 포함할 수 있고 TS interface는 구현을 위한 명세만을 다룬다는 점이다.
또한 TS interface는 ? 를 사용해 해당 함수가 상속받는 클래스 내부에 존재할수도 존재하지 않을 수도 있는 상태를 만들 수 있는데</p>
<p>C++에는 그런 방법이 없고 SOLID 원칙에서 안티패턴으로 간주하고 리팩토링을 하는 것을 권장하고있다.</p>
<h2 id="에러케이스">에러케이스</h2>
<pre><code class="language-cpp">#pragma once
#include &lt;functional&gt;
#include &lt;map&gt;
#include &lt;string&gt;

#include &quot;Actor/Stat.h&quot;

enum class JobState { Warrior, Mage };

class Job
{
    std::map&lt;char, std::function&lt;void()&gt;&gt; jobFuncMap;

public:
    virtual std::string Test();
    virtual Stat SetBaseStat() const = 0;
    virtual std::string GetJobName() const = 0;
};</code></pre>
<h4 id="순수가상함수-아니면-되는거-아니었나요">순수가상함수 아니면 되는거 아니었나요?</h4>
<blockquote>
<p>순수가상함수는 구현할 필요가 없습니다. 왜냐?
자식클래스에서 <strong>반드시</strong> 구현해야 하기 때문에 추상클래스에서 구현할 필요가 없습니다.
하지만 일반 가상함수는 컴파일러가 어딘가에 구현이 있다고 생각하고 링크를 하다가 구현이 없으면 LNK에러가 발생하게 됩니다.</p>
</blockquote>
<pre><code class="language-cpp">#pragma once
#include &lt;functional&gt;
#include &lt;map&gt;
#include &lt;string&gt;

#include &quot;Actor/Stat.h&quot;

enum class JobState { Warrior, Mage };

class Job
{
    std::map&lt;char, std::function&lt;void()&gt;&gt; jobFuncMap;

public:
    virtual std::string Test() { return &quot;&quot; } ;
    virtual Stat SetBaseStat() const = 0;
    virtual std::string GetJobName() const = 0;
};</code></pre>
<p>그래서 위와같이 구현을 추가해주면 에러가 사라집니다.</p>
<h1 id="랜덤">랜덤</h1>
<p>던전 이벤트 구현을 위해 C++에서는 랜덤을 어떻게 돌리나 검색을 해봤다.
그랬더니 나는 단순하게 Rand() 함수라던가.. 를 생각했는데
무슨 메르센 트위스터 알고리즘을 이용한 난수 생성기를 이용하는게 좋다더라.....
당최 무슨 소린지 모르겠지만 일단 써보고 랜덤을 사용해야할 경우에 계속 사용하다보면 익숙해지지 않을까 싶다.............</p>
<h2 id="사용법">사용법</h2>
<pre><code class="language-cpp">// 하드웨어 기반 난수 생성 시드
static std::random_device rd;

// tm19937 = 난수엔진
// 메르센 트위스터 알고리즘을 사용함
// gen(seed)를 통해 생성 &gt; gen(rd())
static std::mt19937 gen(rd());

// 균등 분포 정수 생성기(?????)
// dist() 를 통해 범위 지정
static std::uniform_real_distribution&lt;&gt; dist(0, 2);

dungeonEventState = static_cast&lt;DungeonEvent&gt;(dist(gen))

0 &gt; Nothing
1 &gt; EncounterEnemy
2 &gt; FindTreasure</code></pre>
<h1 id="static-키워드">static 키워드</h1>
<p>static은 lifetime과 scope를 고정한다.</p>
<h2 id="함수-내부-static">함수 내부 static</h2>
<pre><code class="language-cpp">void A()
{
    static int a = 10; // 함수 끝나도 살아있음
}</code></pre>
<p>최초 1회만 초기화 가능하며 함수 호출이 끝나면 값이 살아있다.</p>
<h2 id="class-내부-static">class 내부 static</h2>
<pre><code class="language-cpp">class A 
{
    static int count; // 해당 class로 생성된 객체와 모두 공유됨
}

A a;
A b;

a.count // 10
b.count // 10

//하지만 static 멤버변수 호출은 아래가 정석 다 공유되니까!
A::count  // 10</code></pre>
]]></description>
        </item>
        <item>
            <title><![CDATA[TIL: C++ TextRPG 개발 22일차]]></title>
            <link>https://velog.io/@bak_chun8/TIL-C-TextRPG-%EA%B0%9C%EB%B0%9C-22%EC%9D%BC%EC%B0%A8</link>
            <guid>https://velog.io/@bak_chun8/TIL-C-TextRPG-%EA%B0%9C%EB%B0%9C-22%EC%9D%BC%EC%B0%A8</guid>
            <pubDate>Wed, 29 Apr 2026 12:10:12 GMT</pubDate>
            <description><![CDATA[<h2 id="누적-학습-시간--200시간-34분-">*<em>누적 학습 시간 : 200시간 34분 *</em></h2>
<h4 id="📅-2026-04-29">📅 2026-04-29</h4>
<h1 id="directory-구조">Directory 구조</h1>
<p><img src="https://velog.velcdn.com/images/bak_chun8/post/9e77907d-9c56-4004-ab8d-d6dd381ff1ee/image.png" alt=""></p>
<p>C++에서 어떤 구조가 좋을지는 아직 학습을 못해서 일단 생각대로 만들어보려고 한다.
main() 함수는 TextRPG.cpp 안에 있다.</p>
<p><img src="https://velog.velcdn.com/images/bak_chun8/post/5d22a566-2443-4c02-b953-04023a48f694/image.png" alt=""></p>
<p>main() 함수는 오직 GameStart() 함수만 담고있다.</p>
<h1 id="gamestate-및-선택지-분기처리">GameState 및 선택지 분기처리</h1>
<p>여러 방법이 떠올랐는데 가장 기본적으로는 cin으로 입력받고 switch/case로 분기하는 것이었다.</p>
<p>근데 뭔가 최선은 아닌 듯 해서 방법을 찾아보니 Function Map이라는 자료구조가 있다는 걸 알게됐다.</p>
<h2 id="function-map">Function Map</h2>
<blockquote>
<p>입력 값(key)에 따라 실행할 함수를 연결 해놓은 자료구조</p>
</blockquote>
<pre><code class="language-cpp">switch (input)
{
case &#39;1&#39;: Attack(); break;
case &#39;2&#39;: Defend(); break;
}</code></pre>
<p>위와같은 switch case를 데이터 구조로 변경한 것이다.</p>
<h2 id="사용법">사용법</h2>
<pre><code class="language-cpp">#include &lt;map&gt;
#include &lt;functional&gt;

// 선언
std::map&lt;char, std::function&lt;void()&gt;&gt; funcMap;</code></pre>
<h2 id="실사용-예제">실사용 예제</h2>
<pre><code class="language-cpp">#include &lt;iostream&gt;
#include &lt;functional&gt;
#include &lt;map&gt;

void Attack() { std::cout &lt;&lt; &quot;Attack&quot;; } 
void Defend() { std::coud &lt;&lt; &quot;Defend&quot;; }

void main()
{
    std::map&lt;char, std::function&lt;void()&gt;&gt; funcMap;

    funcMap[&#39;q&#39;] = Attack;
    funcMap[&#39;w&#39;] = Defend;

    char input;

    std::cin &gt;&gt; input;

    funcMap[input]();
}</code></pre>
<p>결과적으로 함수를 값처럼 사용할 수 있다는 점에서 Delegate와 비슷하지만 이벤트 시스템은 직접 구현해야된다거나 하는 점에서 Delegate는 상위 개념으로 볼 수 있다.</p>
<p>그리고 코드를 딱 보면 알겠지만 switch/case 쓰는 것 보다 코드가 클린해진다. </p>
<p>그리고 이후 입력&gt;출력 데이터 추가에도 매우 용이하다.</p>
<h2 id="가상함수">가상함수</h2>
<p>Stat SetBaseStat() const override</p>
<p>override
부모클래스의 가상함수를 재정의 하겠다는 뜻</p>
<p>virtual Stat SetBaseStat() const = 0;</p>
<p>= 0 하는 이유 &gt; 이유없음 그냥 C++ 문법 얘는 순수가상함수다 라는걸 나타냄</p>
<p>내일  GameState &gt; GetPlayerCharacter부터 시작</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[TIL: Unreal C++ 21일차]]></title>
            <link>https://velog.io/@bak_chun8/TIL-Unreal-C-21%EC%9D%BC%EC%B0%A8</link>
            <guid>https://velog.io/@bak_chun8/TIL-Unreal-C-21%EC%9D%BC%EC%B0%A8</guid>
            <pubDate>Tue, 28 Apr 2026 12:32:06 GMT</pubDate>
            <description><![CDATA[<h2 id="누적-학습-시간--188시간-34분-">*<em>누적 학습 시간 : 188시간 34분 *</em></h2>
<h4 id="📅-2026-04-28">📅 2026-04-28</h4>
<h1 id="언리얼에서-c로-액터클래스-생성">언리얼에서 C++로 액터클래스 생성</h1>
<h2 id="클래스-생성">클래스 생성</h2>
<p><img src="https://velog.velcdn.com/images/bak_chun8/post/be0bbe5b-88e8-48e0-818d-e26a9e9bcedb/image.png" alt=""></p>
<p><img src="https://velog.velcdn.com/images/bak_chun8/post/5ab029e4-5d62-4bc9-ad0b-be66f050e903/image.png" alt=""></p>
<p>에디터 &gt; 상단 Tools &gt; New C++ Class &gt; Actor &gt; MyActor로 생성</p>
<h2 id="콘텐츠-브라우저에서-bp-생성">콘텐츠 브라우저에서 BP 생성</h2>
<p><img src="https://velog.velcdn.com/images/bak_chun8/post/a62675ed-3117-411a-a155-f1b948899703/image.png" alt=""></p>
<p>콘텐츠 브라우저 &gt; 우클릭 &gt; ALL CLASSES &gt; MyActor 검색</p>
<h1 id="location이동-및-rotation-구현">Location이동 및 Rotation 구현</h1>
<h2 id="myactorh">MyActor.h</h2>
<pre><code class="language-cpp">#pragma once

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

UCLASS()
class PROJECT1TOGETHER_API AMyActor : public AActor
{
    GENERATED_BODY()

public:    
    // Sets default values for this actor&#39;s properties
    AMyActor();

    FVector PrevLocation;
    FVector CurrentLocation;
    float TotalDistance;
    int MoveCount;

protected:
    // Called when the game starts or when spawned
    virtual void BeginPlay() override;

public:    
    // Called every frame
    virtual void Tick(float DeltaTime) override;



    void Move();
    void Turn();
    void TriggerEvent();
};
</code></pre>
<h2 id="myactorcpp">MyActor.cpp</h2>
<pre><code class="language-cpp">#include &quot;MyActor.h&quot;

// Sets default values
AMyActor::AMyActor()
{
    // Set this actor to call Tick() every frame.  You can turn this off to improve performance if you don&#39;t need it.
    PrimaryActorTick.bCanEverTick = true;
}

// Called when the game starts or when spawned
void AMyActor::BeginPlay()
{
    Super::BeginPlay();

}

// Called every frame
void AMyActor::Tick(float DeltaTime)
{
    Super::Tick(DeltaTime);

}

void AMyActor::Move()
{
    FVector Target;

    // -50 ~ 50 randomeFloat
    Target.X = FMath::FRandRange(-50.0, 50.0);
    Target.Y = FMath::FRandRange(-50.0, 50.0);
    Target.Z = 0;

    AddActorWorldOffset(Target);

}

void AMyActor::Turn()
{
    FRotator DeltaRotation;

    DeltaRotation.Yaw = FMath::FRandRange(-180.0, 180.0);
    DeltaRotation.Pitch = 0;
    DeltaRotation.Roll = 0;

    AddActorWorldRotation(DeltaRotation);
}
</code></pre>
<h1 id="과제-1">과제 1</h1>
<ul>
<li><ol>
<li>이동할 때마다 좌표, MoveCount 출력</li>
</ol>
</li>
<li><ol start="2">
<li>50% 확률 이동</li>
</ol>
</li>
<li><ol start="3">
<li>10회 이동 후 최종결과 리포트</li>
</ol>
</li>
</ul>
<h2 id="myactorh-1">MyActor.h</h2>
<pre><code class="language-cpp">// 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;MyActor.generated.h&quot;

UCLASS()
class PROJECT1TOGETHER_API AMyActor : public AActor
{
    GENERATED_BODY()

public:    
    // Sets default values for this actor&#39;s properties
    AMyActor();

    FVector PrevLocation;
    FVector CurrentLocation;
    float TotalDistance;
    int MoveCount;

protected:
    // Called when the game starts or when spawned
    virtual void BeginPlay() override;

public:    
    // Called every frame
    virtual void Tick(float DeltaTime) override;



    void Move();
    void Turn();
    void TriggerEvent(int key);
};</code></pre>
<h2 id="myactorcpp-1">MyActor.cpp</h2>
<pre><code class="language-cpp">// Fill out your copyright notice in the Description page of Project Settings.


#include &quot;MyActor.h&quot;

// Sets default values
AMyActor::AMyActor()
{
    // Set this actor to call Tick() every frame.  You can turn this off to improve performance if you don&#39;t need it.
    PrimaryActorTick.bCanEverTick = true;
    TotalDistance = 0.0f;
    MoveCount = 0;
}

// Called when the game starts or when spawned
void AMyActor::BeginPlay()
{
    Super::BeginPlay();
    PrevLocation = GetActorLocation();

    for (int32 i = 0; i &lt; 10; i++)
    {
        bool CanMove = FMath::RandBool();
        if (CanMove)
        {
            TriggerEvent(i);
        }
    }
}

// Called every frame
void AMyActor::Tick(float DeltaTime)
{
    Super::Tick(DeltaTime);

}

void AMyActor::Move()
{
    FVector Target;

    // -50 ~ 50 randomeFloat
    Target.X = FMath::FRandRange(-50.0, 50.0);
    Target.Y = FMath::FRandRange(-50.0, 50.0);
    Target.Z = 0;

    AddActorWorldOffset(Target);

}

void AMyActor::Turn()
{
    FRotator DeltaRotation;

    DeltaRotation.Yaw = FMath::FRandRange(-180.0, 180.0);
    DeltaRotation.Pitch = 0;
    DeltaRotation.Roll = 0;

    AddActorWorldRotation(DeltaRotation);
}

void AMyActor::TriggerEvent(int key)
{
    int32 BaseCountKey = MoveCount * 10 + key ;
    MoveCount++;
    Move();
    Turn();
    CurrentLocation = GetActorLocation();
    TotalDistance += FVector::Dist(PrevLocation,CurrentLocation);


    if (GEngine)
    {
        GEngine -&gt; AddOnScreenDebugMessage(BaseCountKey + 1, 5, FColor::Red, FString::Printf(TEXT(&quot;Prev: %ls&quot;),*PrevLocation.ToString()));
        GEngine -&gt; AddOnScreenDebugMessage(BaseCountKey + 2, 5, FColor::Red, FString::Printf(TEXT(&quot;Current: %ls&quot;),*CurrentLocation.ToString()));
        GEngine -&gt; AddOnScreenDebugMessage(BaseCountKey + 3, 5, FColor::Red, FString::Printf(TEXT(&quot;TotalDist: %f&quot;),TotalDistance));
        GEngine -&gt; AddOnScreenDebugMessage(BaseCountKey + 4, 5, FColor::Red, FString::Printf(TEXT(&quot;MoveCount: %d&quot;),MoveCount));
    }


    UE_LOG(LogTemp, Warning, TEXT(&quot;Init: %s&quot;), *PrevLocation.ToString());
    UE_LOG(LogTemp, Warning, TEXT(&quot;Current: %s&quot;), *CurrentLocation.ToString());
    UE_LOG(LogTemp, Warning, TEXT(&quot;Acc: %f&quot;), TotalDistance);
    UE_LOG(LogTemp, Warning, TEXT(&quot;MoveCount: %d&quot;), MoveCount);

    PrevLocation = CurrentLocation;
}
</code></pre>
<h2 id="결과">결과</h2>
<p><img src="https://velog.velcdn.com/images/bak_chun8/post/f0fd7110-eacb-4079-8ce2-f181fa89aadf/image.png" alt=""></p>
<h1 id="에디터-종료-후-액터-재생성-오류">에디터 종료 후 액터 재생성 오류</h1>
<p>언리얼 에디터 + Rider로 신나게 작업하다가 에디터, Rider 닫고 재시작을 했더니
레벨에서 액터가 사라지고 BP_MyActor를 드래그앤드랍으로 배치시 실패 오류 로그가 발생한다.</p>
<p>튜터님께 질문드린결과 실무에서도 C++ 프로젝트 열때 Rider에서 여는게 일반적이라고 알려주셨다.</p>
<p>에디터에서 열 경우 에디터 하단 Contents Browser에 C++ Classes 폴더가 없는데 이는 C++ 컴파일이 되지 않아서 인식해지 못해서 발생한다고 생각했다.</p>
<p><img src="https://velog.velcdn.com/images/bak_chun8/post/e6f9eabe-4a27-43d7-aa79-3e9ab195100d/image.png" alt=""></p>
<p>그래서 튜터님 말씀대로 언리얼과 Rider를 종료하고
Rider로 프로젝트를 연 뒤 우측 상단 프로젝트명 옆 실행 버튼이 활성화될 때 까지 기다렸다가 누르면 빌드가 시작되면서 언리얼에디터가 열리는데</p>
<p>이때 문제가 해결된 것을 볼 수 있다. 이때부터는 에디터부터 열어도 오류가 발생하지 않는다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[Git사용법]]></title>
            <link>https://velog.io/@bak_chun8/Git%EC%82%AC%EC%9A%A9%EB%B2%95</link>
            <guid>https://velog.io/@bak_chun8/Git%EC%82%AC%EC%9A%A9%EB%B2%95</guid>
            <pubDate>Tue, 28 Apr 2026 05:03:02 GMT</pubDate>
            <description><![CDATA[<p><img src="https://velog.velcdn.com/images/bak_chun8/post/2ab1bd7e-96d9-47aa-a2d8-ea4241b01cad/image.png" alt=""></p>
<p><code>git init</code>
해당 폴더를 레포지토리에 업로드 하고싶어요.</p>
<blockquote>
<p>git repository에 push하기 위해 initializing</p>
</blockquote>
<p><code>git add README.md</code>
이 저장소는 <del>~</del>를 위한 저장소입니다.
사용법은 아래와 같습니다.</p>
<blockquote>
<p>git repository 사용 설명서 추가</p>
</blockquote>
<p><code>git add &lt;file-name&gt;</code>
해당 commit에는 이런 파일들이 변경(생성, 추가, 삭제 등)되었습니다.</p>
<blockquote>
<p>commit할 파일들 선택</p>
</blockquote>
<p><code>git commit -m &quot;first commit&quot;</code>
해당 커밋은 &quot;first commit&quot; 이라는 내용이 변경되었습니다.</p>
<blockquote>
<p>repository에 push하기 전 업로드할 파일 확정</p>
</blockquote>
<p><code>git branch -M main</code>
main이라는 브랜치를 만들어서 해당 브랜치를 master branch로 설정할께요.</p>
<blockquote>
<p>해당 레포지토리의 master branch를 생성 이름은 main</p>
</blockquote>
<p><code>git remote add origin https://github.com/Carrymachine/Test.git</code>
앞으로 수정되는 내용들은 모두 여기에 업로드할께요.</p>
<blockquote>
<p>업로드할 주소를 origin이라는 이름으로 추가할께요.</p>
</blockquote>
<p><code>git push -u origin main</code>
origin의 main 브랜치에 업로드해주세요.</p>
<blockquote>
<p>origin의 main 브랜치에 push -u는 업스트림인데 신경안써도 무방</p>
</blockquote>
<p><code>git switch -c &quot;브랜치 명&quot;</code>
현재 기준의 코드베이스에서 &quot;브랜치 명&quot;으로 브랜치 생성 후 이동할께요.</p>
<blockquote>
<p>(main에서 명령어 입력 시) main을 기준으로 &quot;브랜치 명&quot;의 브랜치를 생성후 이동</p>
</blockquote>
<pre><code>git pull
또는
git pull &lt;브랜치 명&gt;</code></pre><p>현재 선택된 브랜치의 내용을 현재 코드베이스로 가져올께요.</p>
<blockquote>
<p>(git pull만 할 경우) 현재 브랜치의 코드베이스 pull
(&lt;브랜치 명&gt;함께 입력 시) 현재 브랜치에 &lt;브랜치 명&gt; 코드베이스 pull</p>
</blockquote>
]]></description>
        </item>
        <item>
            <title><![CDATA[TIL: 발로란트 클론코딩 17~20일차]]></title>
            <link>https://velog.io/@bak_chun8/TIL-Unreal5-%EA%B0%9C%EB%B0%9C-%EA%B8%B0%EB%B3%B8-1617%EC%9D%BC%EC%B0%A8</link>
            <guid>https://velog.io/@bak_chun8/TIL-Unreal5-%EA%B0%9C%EB%B0%9C-%EA%B8%B0%EB%B3%B8-1617%EC%9D%BC%EC%B0%A8</guid>
            <pubDate>Mon, 27 Apr 2026 10:10:37 GMT</pubDate>
            <description><![CDATA[<h2 id="누적-학습-시간--176시간-34분-">*<em>누적 학습 시간 : 176시간 34분 *</em></h2>
<h4 id="📅-2026-04-2427">📅 2026-04-24~27</h4>
<h1 id="플레이영상"><a href="https://www.youtube.com/watch?v=XGHrBtWhAFo">플레이영상</a></h1>
<pre><code class="language-cpp"></code></pre>
<h1 id="c-문법-공부">C++ 문법 공부</h1>
<p>지금 그거할 시간이 없어!!!!!</p>
<h1 id="플레이-화면-맛보기">플레이 화면 맛보기</h1>
<p><img src="https://velog.velcdn.com/images/bak_chun8/post/4f6f54f3-b3c6-45d5-83cb-5d3227b09f00/image.png" alt=""></p>
<h1 id="ui">UI</h1>
<p><img src="https://velog.velcdn.com/images/bak_chun8/post/e561688f-6de7-47de-b8f9-129bb50e5708/image.png" alt=""></p>
<p><img src="https://velog.velcdn.com/images/bak_chun8/post/6fcdc4ee-78f9-496f-a154-7b5de6ce3b62/image.png" alt=""></p>
<p><img src="https://velog.velcdn.com/images/bak_chun8/post/6f8f4bbe-fbcc-4e47-90ae-63d4a449d5bb/image.png" alt=""></p>
<p><img src="https://velog.velcdn.com/images/bak_chun8/post/72dd81d9-5c6a-456f-abbc-6275fc34ec91/image.png" alt=""></p>
<h1 id="맵">맵</h1>
<p><img src="https://velog.velcdn.com/images/bak_chun8/post/fdb6e9ce-9434-4dff-8dc8-348d994310c7/image.png" alt=""></p>
<h1 id="플레이어">플레이어</h1>
<h2 id="캐릭터">캐릭터</h2>
<p><img src="https://velog.velcdn.com/images/bak_chun8/post/f12c4d08-6ffb-4e1e-ba3f-df94d3609b58/image.png" alt=""></p>
<h2 id="기본조작">기본조작</h2>
<p><img src="https://velog.velcdn.com/images/bak_chun8/post/3507e4db-7432-4e53-8599-a5e217e385d8/image.png" alt=""></p>
<h2 id="앉기">앉기</h2>
<p><img src="https://velog.velcdn.com/images/bak_chun8/post/abdcc548-20e2-4635-a78d-13374dfe40bb/image.png" alt=""></p>
<h2 id="격발-재장전">격발, 재장전</h2>
<p><img src="https://velog.velcdn.com/images/bak_chun8/post/8da144c9-fce6-4043-b43f-761937f14c0b/image.png" alt=""></p>
<h3 id="fireonce">FireOnce</h3>
<p><img src="https://velog.velcdn.com/images/bak_chun8/post/7a0ea076-e2b2-4560-9483-41cfb4ed341c/image.png" alt=""></p>
<p><img src="https://velog.velcdn.com/images/bak_chun8/post/75ae1e99-d3b2-47f6-97db-e1089a42ce5d/image.png" alt=""></p>
<p><img src="https://velog.velcdn.com/images/bak_chun8/post/28b6c445-6d22-4419-bb86-73bd17823542/image.png" alt=""></p>
<p><img src="https://velog.velcdn.com/images/bak_chun8/post/bc2de687-02b9-4335-ab2c-a944f8f136eb/image.png" alt=""></p>
<h3 id="reload">Reload</h3>
<p><img src="https://velog.velcdn.com/images/bak_chun8/post/2a3a65fd-2177-4bbe-8870-89f84dd88717/image.png" alt=""></p>
<h2 id="정조준">정조준</h2>
<p><img src="https://velog.velcdn.com/images/bak_chun8/post/784cdd77-13cb-44bc-9910-c37707075930/image.png" alt=""></p>
<h2 id="피해-입었을-경우">피해 입었을 경우</h2>
<p><img src="https://velog.velcdn.com/images/bak_chun8/post/111a110b-a08a-48e9-aa4e-a3198e0cba2e/image.png" alt=""></p>
<h2 id="사망">사망</h2>
<p><img src="https://velog.velcdn.com/images/bak_chun8/post/460c7397-0ea9-4cd6-ad89-b0211c8fb4b0/image.png" alt=""></p>
<h1 id="적-ai">적 AI</h1>
<h2 id="캐릭터-1">캐릭터</h2>
<p><img src="https://velog.velcdn.com/images/bak_chun8/post/c5d22a53-beda-45e3-9258-4f12ea94e6c4/image.png" alt=""></p>
<h2 id="랜덤-이동">랜덤 이동</h2>
<p><img src="https://velog.velcdn.com/images/bak_chun8/post/9a72b710-41bd-441f-a9d0-a2e9e7d30efd/image.png" alt=""></p>
<h2 id="추적">추적</h2>
<p><img src="https://velog.velcdn.com/images/bak_chun8/post/f1944520-ad4d-4cda-9336-dc03ee84c6d8/image.png" alt=""></p>
<h2 id="격발">격발</h2>
<p><img src="https://velog.velcdn.com/images/bak_chun8/post/3f5ab40d-991b-4898-82db-1e1e1c5ec219/image.png" alt=""></p>
<p><img src="https://velog.velcdn.com/images/bak_chun8/post/a1d34492-a12c-4281-98e7-5dfa6ce59e34/image.png" alt=""></p>
<p><img src="https://velog.velcdn.com/images/bak_chun8/post/3bf9955d-124a-4ae2-ba6d-adb6c92324d6/image.png" alt=""></p>
<h2 id="피해-입었을-경우-1">피해 입었을 경우</h2>
<p><img src="https://velog.velcdn.com/images/bak_chun8/post/c197f10b-e1f4-465a-82c6-a67b174cf78a/image.png" alt=""></p>
<h2 id="사망-1">사망</h2>
<p><img src="https://velog.velcdn.com/images/bak_chun8/post/0bd13981-c668-4572-951e-f06b8c560678/image.png" alt=""></p>
<p><img src="https://velog.velcdn.com/images/bak_chun8/post/1bdbdc30-9526-4ff0-9e3b-30e65797a1c7/image.png" alt=""></p>
<h1 id="총기밴달">총기(밴달)</h1>
<p><img src="https://velog.velcdn.com/images/bak_chun8/post/61a2335b-7de3-45c4-90fd-7ba969258236/image.png" alt=""></p>
<h2 id="에셋">에셋</h2>
<p><img src="https://velog.velcdn.com/images/bak_chun8/post/7d40bb42-49f2-4d3e-b9a2-d48125e9bf39/image.png" alt=""></p>
<h2 id="에미터-나이아가라-시스템">에미터, 나이아가라 시스템</h2>
<p><img src="https://velog.velcdn.com/images/bak_chun8/post/2809e9f3-b0ad-4ce1-9b7a-173e0d728fbd/image.png" alt=""></p>
]]></description>
        </item>
        <item>
            <title><![CDATA[TIL: Unreal5 Blueprint 16일차]]></title>
            <link>https://velog.io/@bak_chun8/TIL-Unreal5-%EA%B0%9C%EB%B0%9C-%EA%B8%B0%EB%B3%B8-15%EC%9D%BC%EC%B0%A8-80q0tvvd</link>
            <guid>https://velog.io/@bak_chun8/TIL-Unreal5-%EA%B0%9C%EB%B0%9C-%EA%B8%B0%EB%B3%B8-15%EC%9D%BC%EC%B0%A8-80q0tvvd</guid>
            <pubDate>Thu, 23 Apr 2026 13:19:07 GMT</pubDate>
            <description><![CDATA[<h2 id="누적-학습-시간--128시간-34분-">*<em>누적 학습 시간 : 128시간 34분 *</em></h2>
<h4 id="📅-2026-04-23">📅 2026-04-23</h4>
<pre><code class="language-cpp"></code></pre>
<h1 id="c-문법-공부">C++ 문법 공부</h1>
<p>오늘 안했다 FPS 제작 개꿀잼</p>
<h1 id="fps-제작">FPS 제작</h1>
<h2 id="animation-montage">Animation Montage</h2>
<p>그동안은 ABP 안에서 Statemachine으로 각 애니메이션들의 상태들을 관리햇었다.</p>
<p>그러다보니 노드가 필요이상으로 커지기도 하고
FPS에서 필요한 여러 상태들(대기, 걷기, 뛰기, 앉기, 격발, 재장전 등)이 얽혀서 재장전이 씹힌다거나 총을 쏘면서 재장전을 한다던가 재장전중인데 모션이 안나온다던가 온갖 버그가 나왔다.</p>
<p>그래서 공식문서를 보던 중 Animation Montage라는 걸 발견했는데</p>
<p>쉽게 말하면 <strong>강제재생</strong>이다.</p>
<p>그래서 단발성 애니메이션인 격발모션, 재장전 등에 사용해봤다.</p>
<h3 id="montage-생성">Montage 생성</h3>
<p>우클릭 &gt; animation &gt; animation montage에서 생성 가능하다.</p>
<p><img src="https://velog.velcdn.com/images/bak_chun8/post/55029788-2227-49ae-b163-9b690d8e5649/image.png" alt=""></p>
<p>스켈레톤매시를 설정 후 들어가서
Montage Manager &gt; Add Montage &gt; slot 추가</p>
<p>일반 애니메이션 시퀀스처럼 notify도 전달할 수 있다.</p>
<h3 id="animgrahp">AnimGrahp</h3>
<p><img src="https://velog.velcdn.com/images/bak_chun8/post/732d5b3d-a896-4414-82b3-67aa129347a9/image.png" alt=""></p>
<p>그후 애님그래프에서 우클릭 &gt; slot &#39;defaultGroup&#39; &gt; 디테일에서 설정해준 슬롯으로 변경하면 끝이다. 이제 몽타주를 사용해보자.</p>
<h3 id="montage-사용">Montage 사용</h3>
<p><img src="https://velog.velcdn.com/images/bak_chun8/post/8ea515f5-e2d9-4fa8-9a69-be4e974e1a84/image.png" alt=""></p>
<p>우클릭 &gt; Montage Play 노드 &gt; target 및 몽타주 지정 하면 끝이다.</p>
<p>이러면 특정 로직을 통해 애니메이션 실행이 가능하고 강제로 실행된다.
중간에 변경되는 state나 이런 부분들은 잘 예외처리 해주면 된다.</p>
<p>그리고 강제로 실행하는 만큼 강제로 멈출 수도 있는데 Montage Stop 노드를 사용하면 된다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[TIL: Unreal5 Blueprint 15일차]]></title>
            <link>https://velog.io/@bak_chun8/TIL-Unreal5-%EA%B0%9C%EB%B0%9C-%EA%B8%B0%EB%B3%B8-15%EC%9D%BC%EC%B0%A8</link>
            <guid>https://velog.io/@bak_chun8/TIL-Unreal5-%EA%B0%9C%EB%B0%9C-%EA%B8%B0%EB%B3%B8-15%EC%9D%BC%EC%B0%A8</guid>
            <pubDate>Wed, 22 Apr 2026 13:20:03 GMT</pubDate>
            <description><![CDATA[<h2 id="누적-학습-시간--116시간-34분-">*<em>누적 학습 시간 : 116시간 34분 *</em></h2>
<h4 id="📅-2026-04-22">📅 2026-04-22</h4>
<pre><code class="language-cpp"></code></pre>
<h1 id="c-문법-공부">C++ 문법 공부</h1>
<p><a href="https://velog.io/@bak_chun8/TIL-C-%ED%85%9C%ED%94%8C%EB%A6%BF">C++템플릿</a></p>
<h1 id="fps-제작">FPS 제작</h1>
<p>발로란트 좋아해서 발로란트 스타일로 만들어보고 있다.</p>
<p><img src="https://velog.velcdn.com/images/bak_chun8/post/87f8fbbc-6345-4ba0-8d6b-c25a72cbd996/image.png" alt=""></p>
]]></description>
        </item>
        <item>
            <title><![CDATA[TIL: C++ 템플릿]]></title>
            <link>https://velog.io/@bak_chun8/TIL-C-%ED%85%9C%ED%94%8C%EB%A6%BF</link>
            <guid>https://velog.io/@bak_chun8/TIL-C-%ED%85%9C%ED%94%8C%EB%A6%BF</guid>
            <pubDate>Wed, 22 Apr 2026 00:37:50 GMT</pubDate>
            <description><![CDATA[<h4 id="📅-2026-04-22">📅 2026-04-22</h4>
<p>오전 1시간동안 코드카타를 하는데 아직 그럴 실력이 안돼서 일단 문법 공부를 더 해보려고한다.</p>
<h1 id="템플릿">템플릿</h1>
<p>타입을 매개변수로 받는 코드 생성 매커니즘이다.
컴파일 타임에 타입별 코드를 생성하는 시스템이라고 생각하면 좋다.
TS를 하던 내 입장에서는 제네릭이라고 생각하니까 이해가 편했다.</p>
<ul>
<li>컴파일 타임에 동작</li>
<li>타입 안정성 보장</li>
<li>런타임 오버헤드 없음</li>
</ul>
<pre><code class="language-cpp">template&lt;typename T&gt;
T Add(T a, T b)
{
    return a + b;
}

&gt;&gt; 컴파일 시

int Add (int a, int b)
{ ... }

float Add ( float a, float b)
{ ... }
</code></pre>
<h2 id="왜-필요한가">왜 필요한가?</h2>
<h3 id="타입-안전--성능-확보">타입 안전 + 성능 확보</h3>
<ul>
<li>void*, 캐스팅 필요없음</li>
<li>컴파일 타임 체크</li>
<li>inline 최적화 가능<blockquote>
<p>가상함수보다 빠름</p>
</blockquote>
</li>
</ul>
<h3 id="추상화-수준-상승">추상화 수준 상승</h3>
<ul>
<li>제네릭 프로그래밍</li>
<li>자료구조/알고리즘을 타입과 분리</li>
</ul>
<h3 id="코드-재사용">코드 재사용</h3>
<p>여러 타입에서 같은 로직 사용</p>
<h2 id="사용-예시">사용 예시</h2>
<h3 id="함수-템플릿">함수 템플릿</h3>
<pre><code class="language-cpp">template&lt;typename T&gt;
T Max(T a, T b)
{ ... }</code></pre>
<h3 id="클래스-템플릿">클래스 템플릿</h3>
<pre><code class="language-cpp">template&lt;typename T&gt;
class Box
{
public:
    T value;
}


Box&lt;int&gt; a;
Box&lt;float&gt; b;</code></pre>
<h3 id="stl">STL</h3>
<pre><code class="language-cpp">std::vector&lt;int&gt; v;
std::sort(v.begin(), v.end());</code></pre>
<ul>
<li><code>vector&lt;T&gt;</code> &gt; 타입별 컨테이너 생성</li>
<li><code>sort&lt;T&gt;</code> &gt; 타입에 맞는 정렬코드 생성</li>
</ul>
<h3 id="템플릿-특수화">템플릿 특수화</h3>
<pre><code class="language-cpp">template&lt;&gt;
class Box&lt;bool&gt;
{ ... }</code></pre>
<blockquote>
<p>특정 타입에 대해 다르게 동작하도록 커스터마이징 특정 타입에 대해 다르게 동작하도록 커스터마이징</p>
</blockquote>
<h3 id="2개-이상의-타입">2개 이상의 타입</h3>
<pre><code class="language-cpp">template&lt;typename T, typename K, typename F ... &gt;
class Test
{
public:
    T First;
    K Second;
    F Third;
    ...
}

Test&lt;int, float, double&gt; a;</code></pre>
<h3 id="가변템플릿">가변템플릿</h3>
<pre><code class="language-cpp">template&lt;typename... Args&gt;</code></pre>
<ul>
<li>템플릿 코드 폭발 방지
없으면 아래와 같이 모두 만들어줘야함<blockquote>
<pre><code class="language-cpp">Print(T);
Print(T, K);
Print(T, K, F);
...</code></pre>
</blockquote>
</li>
</ul>
<p>가변 템플릿은 쓸 내용이 많아서 나중에 다시 한번 공부할 예정</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[TIL: Unreal5 Blueprint 14일차]]></title>
            <link>https://velog.io/@bak_chun8/TIL-Unreal5-%EA%B0%9C%EB%B0%9C-%EA%B8%B0%EB%B3%B8-14%EC%9D%BC%EC%B0%A8</link>
            <guid>https://velog.io/@bak_chun8/TIL-Unreal5-%EA%B0%9C%EB%B0%9C-%EA%B8%B0%EB%B3%B8-14%EC%9D%BC%EC%B0%A8</guid>
            <pubDate>Tue, 21 Apr 2026 12:13:19 GMT</pubDate>
            <description><![CDATA[<h2 id="누적-학습-시간--104시간-34분-">*<em>누적 학습 시간 : 104시간 34분 *</em></h2>
<h4 id="📅-2026-04-21">📅 2026-04-21</h4>
<pre><code class="language-cpp"></code></pre>
<h1 id="c-문법-공부">C++ 문법 공부</h1>
<p><a href="https://velog.io/@bak_chun8/TIL-%EC%8A%A4%EB%A7%88%ED%8A%B8-%ED%8F%AC%EC%9D%B8%ED%84%B0">스마트 포인터</a></p>
<h1 id="fps-제작">FPS 제작</h1>
<h2 id="발싸-">발싸-!</h2>
<p><img src="https://velog.velcdn.com/images/bak_chun8/post/d5433552-b741-406c-a3a6-daec9f6d637b/image.png" alt=""></p>
<p>수업시간에 들은 것 따라가면서 시간이 남아서 발로란트의 밴달 연사를 구현해봤다.
이펙트나 나머지는 내일 진행해볼 예정</p>
<h2 id="함수화">함수화</h2>
<p><img src="https://velog.velcdn.com/images/bak_chun8/post/f13e490b-1d36-45e4-b55f-bcaba9fe1dde/image.png" alt=""></p>
<p>블프에서 노드가 많아지니까 아무래도 함수를 자주 사용하게된다.</p>
<h2 id="라인트레이스">라인트레이스</h2>
<p><img src="https://velog.velcdn.com/images/bak_chun8/post/efc395dc-09d8-451f-8d64-f9ae1bdec254/image.png" alt=""></p>
<p>FPS에서 빠지면 안되는 그것...</p>
<p>일반적으로 실질적인 라인트레이스는 카메라기준
이펙트나 다른 부가적인 것들은 머즐을 기준으로 하면 될 것 같다.</p>
<h2 id="줌인아웃">줌인아웃</h2>
<p><img src="https://velog.velcdn.com/images/bak_chun8/post/fd200c00-92b7-4e21-b148-b9dc9eb3afec/image.png" alt=""></p>
<p>많은 FPS에서 지원하는 정조준 사격 모드 온오프 기능
애니메이션까지 넣으면 매우 그럴듯해 보일 것 같다.</p>
<h2 id="결과물">결과물</h2>
<p><img src="https://velog.velcdn.com/images/bak_chun8/post/ed1c74a2-8972-4f42-8afa-54d7fe6ceecb/image.png" alt=""></p>
<h3 id="튜터님이-검색해보라고-한-것-주말에-꼭-보기">튜터님이 검색해보라고 한 것 주말에 꼭 보기</h3>
<p>Cascadeur</p>
<p>Procedurally Animating</p>
<p>game animation sample(unreal)</p>
<p>meta human </p>
]]></description>
        </item>
        <item>
            <title><![CDATA[TIL: 스마트 포인터]]></title>
            <link>https://velog.io/@bak_chun8/TIL-%EC%8A%A4%EB%A7%88%ED%8A%B8-%ED%8F%AC%EC%9D%B8%ED%84%B0</link>
            <guid>https://velog.io/@bak_chun8/TIL-%EC%8A%A4%EB%A7%88%ED%8A%B8-%ED%8F%AC%EC%9D%B8%ED%84%B0</guid>
            <pubDate>Tue, 21 Apr 2026 00:59:45 GMT</pubDate>
            <description><![CDATA[<h4 id="📅-2026-04-21">📅 2026-04-21</h4>
<p>오전 1시간동안 코드카타를 하는데 아직 그럴 실력이 안돼서 일단 문법 공부를 더 해보려고한다.</p>
<h1 id="스마트-포인터">스마트 포인터</h1>
<p>19일 작성한 RAII 패턴 기반의 포인터 래퍼이다.
사용하는 목적은 수동으로 해주던 new/delete의 할당/해제를 자동으로 해준다. 또한 소유권을 명확하게 하는데 의의가 있다.</p>
<h2 id="왜-필요한가">왜 필요한가?</h2>
<p>일반 포인터는 주소를 담고 있다는 사실을 알고있다.
하지만</p>
<ul>
<li>누가 해제해야하는지</li>
<li>언제 소멸하는지</li>
<li>소유권이 누구한테 있는지</li>
</ul>
<p>이게 코드만 보고 명확하지 않다.
그래서 이중 해제, 누수, dangling pointer가 생긴다.
C++ 가이드라인에서 소유권이 필요하면 스마트 포인터, 소유권 자체가 필요없다면 일반 값이나 참조, 일반 포인터가 더 적절하다고 한다.</p>
<h3 id="dangling-pointer">dangling pointer?</h3>
<p>이미 해제된 메모리 영역을 가르키는 포인터를 말함</p>
<ul>
<li>포인터가 주소를 가지고있음</li>
<li>근데 그 주소는 이미 해제됨</li>
<li>정의되지 않은 동작 발생할 확률 증가</li>
</ul>
<h4 id="메모리-해제-후-포인터-유지">메모리 해제 후 포인터 유지</h4>
<pre><code class="language-cpp">int* p = new int(10);

delete p; // p는 아직 주소 가지고 있음</code></pre>
<h4 id="지역-변수-주소-반환">지역 변수 주소 반환</h4>
<pre><code class="language-cpp">function RtrnPointer()
{
    int x = 10;

    return &amp;x; // 함수가 끝나면 지역변수 사라짐
}</code></pre>
<h4 id="객체-소멸-후-참조-유지">객체 소멸 후 참조 유지</h4>
<pre><code class="language-cpp">int* p;


function RtrnPointer()
{
    int x = 10;
    p = &amp;x;w
} // x소멸</code></pre>
<h2 id="단일소유권-stdunique_ptr">단일소유권 std::unique_ptr</h2>
<pre><code class="language-cpp">std::unique_ptr&lt;int&gt; p = std::make_unique&lt;int&gt;(10);</code></pre>
<p>하나의 객체만 특정 자원을 소유하는 구조.</p>
<ul>
<li>스코프를 벗어남.</li>
<li>reset()</li>
<li>다른 포인터로 재대입
위 3가지 경우에 관리 대상이 삭제된다.
또한 커스텀 deleter를 둘 수 있고 move가능 copy 불가능하다.</li>
</ul>
<h3 id="move와-copy">move와 copy?</h3>
<pre><code class="language-cpp">// copy 
std::unique_ptr&lt;int&gt; p1 = std::make_unique&lt;int&gt;(10);
std::unique_ptr&lt;int&gt; p2 = p1; // 컴파일 에러

// move
std::unique_ptr&lt;int&gt; p1 = std::make_unique&lt;int&gt;(10);
std::unique_ptr&lt;int&gt; p2 = std::move(p1);</code></pre>
<h2 id="공유소유권-stdshared_ptr">공유소유권 std::shared_ptr</h2>
<pre><code class="language-cpp">#include &lt;memory&gt;

std::shared_ptr&lt;int&gt; p1 = std::make_shared&lt;int&gt;(10);
std::shared_ptr&lt;int&gt; p2 = p1; // 공동소유</code></pre>
<p>shared_ptr는 여러 포인터가 같은 객체를 공동 소유한다.
같은 객체를 소유한 여러 포인터 중 마지막 포인터가 소멸되거나 다른 대상으로 지정되면 객체가 삭제된다.</p>
<h3 id="왜-필요한가-1">왜 필요한가?</h3>
<ul>
<li>여러 객체/시스템이 실제로 같은 대상을 함께 소유해야할 경우</li>
<li>소유자가 동적으로 늘어나거나 줄어들어서 하나로 정하기 애매할 때</li>
<li>콜백, 비동기, 옵저버 패턴에서 수명 연장이 명시적으로 필요할 때</li>
</ul>
<h3 id="주의">주의</h3>
<p>자칫 소유권이 불명확한 설계를 덮어버리는 도구가 되기 쉽다.
공유 포인터는 런타임 코스트를 수반하므로 자제하는게 좋다.</p>
<h2 id="감시-stdweak_ptr">감시 std::weak_ptr</h2>
<pre><code class="language-cpp">#include &lt;memory&gt;


std::shared_ptr&lt;int&gt; sp = std::make_shared&lt;int&gt;(10);
std::weak_ptr&lt;int&gt; wp = sp;

if (auto locked = wp.lock())
{ ... }</code></pre>
<p>weak_ptr은 shared_ptr이 관리하는 객체를 소유하지 않고 가리키기만 한다.</p>
<p>역참조가 불가능하며 lock()으로 임시로 shared_ptr을 얻어서 사용해야한다.
또한 expired()로 삭제 됐는지 확인이 가능하다.</p>
<h3 id="왜-필요한가-2">왜 필요한가?</h3>
<p>가장 대표적인 이유는 순환참조 방지이다.</p>
<pre><code class="language-cpp">struct B;

struct A
{
    std::shared_ptr&lt;B&gt; b;
}

struct B
{
    std::shared_ptr&lt;A&gt; a; // 서로 shared_ptr이면 해제 안됨
}</code></pre>
<p>위의 구조는 서로가 서로를 소유해서 참조 카운트가 0이 안된다.
때문에 메모리 릭이 발생하는데 이렇게 설계해야할 경우 한쪽을 weak으로 끊으면 된다.</p>
<ul>
<li>부모-자식, 옵저버, 이벤트리스너처럼 연결을 필요하지만 소유는 아닐경우</li>
<li>캐시나 백 레퍼런스</li>
<li>shared_ptr 구조에서 순환참조 방지</li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[TIL: Unreal5 Blueprint 13일차]]></title>
            <link>https://velog.io/@bak_chun8/TIL-Unreal5-%EA%B0%9C%EB%B0%9C-%EA%B8%B0%EB%B3%B8-13%EC%9D%BC%EC%B0%A8-fjsfxfbt</link>
            <guid>https://velog.io/@bak_chun8/TIL-Unreal5-%EA%B0%9C%EB%B0%9C-%EA%B8%B0%EB%B3%B8-13%EC%9D%BC%EC%B0%A8-fjsfxfbt</guid>
            <pubDate>Mon, 20 Apr 2026 12:02:00 GMT</pubDate>
            <description><![CDATA[<h2 id="누적-학습-시간--92시간-34분-">*<em>누적 학습 시간 : 92시간 34분 *</em></h2>
<h4 id="📅-2026-04-20">📅 2026-04-20</h4>
<p>원래 썸네일로 쓰던 학습시간이 오늘부터는 12시간으로 고정이라 이제 시간만 더하고 썸네일은 달지 않을 예정입니다. (아쉽네요)</p>
<pre><code class="language-cpp"></code></pre>
<h2 id="c-문법-공부">C++ 문법 공부</h2>
<p><a href="https://velog.io/@bak_chun8/TIL-C%EC%99%80-C%EC%9D%98-%EB%8F%99%EC%A0%81%ED%95%A0%EB%8B%B9-%EC%B0%A8%EC%9D%B4">C와 C++의 동적할당 차이점</a></p>
<h2 id="라인트레이스">라인트레이스</h2>
<p>쉽게 라인을 발사해서 부딪히는 것을 검출
x-ray처럼 관통 검출도 가능(멀티-라인트레이스)</p>
<p><img src="https://velog.velcdn.com/images/bak_chun8/post/de562d88-104c-465c-a550-7898143e4171/image.png" alt=""></p>
<h2 id="레벨">레벨</h2>
<p>우선 블루프린트 &gt; 게임모드로 하나 생성해준다 이름은 BP_GameMode
<img src="https://velog.velcdn.com/images/bak_chun8/post/68b154d2-1011-44d1-898f-004dbb0d8763/image.png" alt=""></p>
<p>이후 2개의 함수 생성</p>
<h3 id="load-ailevel">Load AILevel</h3>
<p><img src="https://velog.velcdn.com/images/bak_chun8/post/4be85f1e-3976-4bc3-9c74-aa03a384384c/image.png" alt=""></p>
<h3 id="unload-ailevel">UnLoad AILevel</h3>
<p><img src="https://velog.velcdn.com/images/bak_chun8/post/41c71d33-3ec6-414b-a5d8-cca4108383a3/image.png" alt=""></p>
<p>불러올 레벨을 만들고 AI 액터를 넣어준다.</p>
<p><img src="https://velog.velcdn.com/images/bak_chun8/post/d7d2fa92-67a6-4a8c-91af-2171feb3b9f4/image.png" alt=""></p>
<p>큐브 액터를 만들고 레벨을 로드 해보자.
난 캐릭터가 상자에 충돌했을 경우 레벨을 로드하려고한다.</p>
<p><img src="https://velog.velcdn.com/images/bak_chun8/post/4811fddc-80b4-44d3-8bd1-37d73692f5ec/image.png" alt=""></p>
<p>레벨이 로드되면 Hello 스트링을 프린트하도록 했는데 그림자분신술을 써버린다.</p>
<p><img src="https://velog.velcdn.com/images/bak_chun8/post/0e54572c-2cfc-476a-8a4c-890052885200/image.png" alt=""></p>
]]></description>
        </item>
        <item>
            <title><![CDATA[TIL: C와 C++의 동적할당 차이]]></title>
            <link>https://velog.io/@bak_chun8/TIL-C%EC%99%80-C%EC%9D%98-%EB%8F%99%EC%A0%81%ED%95%A0%EB%8B%B9-%EC%B0%A8%EC%9D%B4</link>
            <guid>https://velog.io/@bak_chun8/TIL-C%EC%99%80-C%EC%9D%98-%EB%8F%99%EC%A0%81%ED%95%A0%EB%8B%B9-%EC%B0%A8%EC%9D%B4</guid>
            <pubDate>Mon, 20 Apr 2026 04:49:37 GMT</pubDate>
            <description><![CDATA[<h4 id="📅-2026-04-20">📅 2026-04-20</h4>
<p>캠프에서 C언어 완전 기초.. 부터 수업을 하길래 생각난 김에 차이에 대해서 공부해보려고 하다가
동적할당이라는 주제를 선택했다.</p>
<h2 id="핵심적인-차이점">핵심적인 차이점</h2>
<h3 id="c">C</h3>
<p>단순 메모리 확보/해제</p>
<h3 id="c-1">C++</h3>
<p>객체의 생성/소멸까지 포함된 메모리 관리</p>
<h2 id="문법">문법</h2>
<h3 id="c-2">C</h3>
<pre><code class="language-c">int* arr = (int*)malloc(sizeof(int) * 10);
free(arr);</code></pre>
<h3 id="c-3">C++</h3>
<pre><code class="language-cpp">int* arr = new int[10];
delete[] arr;</code></pre>
<h2 id="생성자--소멸자-호출-여부">생성자 / 소멸자 호출 여부</h2>
<h3 id="c-4">C</h3>
<p>생성자/소멸자 없이 메모리 덩어리</p>
<h3 id="c-5">C++</h3>
<pre><code class="language-cpp">class A
{
    A() { printf(&quot;생성&quot;); }
    ~A() { printf(&quot;소멸&quot;); }
}

A* a = new A();
delete a;</code></pre>
<h2 id="타입-안정성">타입 안정성</h2>
<h3 id="c-6">C</h3>
<pre><code class="language-c">void* ptr = malloc(sizeof(int));
int* iptr = (int*)ptr; // 캐스팅 필요</code></pre>
<ul>
<li>타입정보 X</li>
<li>캐스팅 실수해도 컴파일에러X</li>
</ul>
<h3 id="c-7">C++</h3>
<pre><code class="language-cpp">int* ptr = new int;</code></pre>
<ul>
<li>타입 자동 추론</li>
<li>캐스팅 필요없음</li>
<li>컴파일 시 타입체크</li>
</ul>
<h2 id="초기화">초기화</h2>
<h3 id="c-8">C</h3>
<pre><code class="language-c">int* a = (int*)malloc(sizeof(int)); // 더미값</code></pre>
<h3 id="c-9">C++</h3>
<pre><code class="language-cpp">int* a = new int(); // 0 초기화
int* b = new int(10); // 값 지정 가능</code></pre>
<h2 id="배열처리">배열처리</h2>
<h3 id="c-10">C</h3>
<pre><code class="language-c">int* arr = (int*)malloc(sizeof(int) * 10);
free(arr);</code></pre>
<h3 id="c-11">C++</h3>
<pre><code class="language-cpp">int* arr = new int[10];
delete[] arr;</code></pre>
<h2 id="실패처리">실패처리</h2>
<h3 id="c-12">C</h3>
<pre><code class="language-c">if (ptr == NULL) { ... }</code></pre>
<h3 id="c-13">C++</h3>
<pre><code class="language-cpp">int* ptr = new int; // 실패시 예외 발생</code></pre>
<h2 id="설계관점">설계관점</h2>
<h3 id="c-14">C</h3>
<ul>
<li>단순 구조체 + 메모리</li>
<li>사람이 직접 관리</li>
<li>휴먼에러 확률 높음</li>
</ul>
<h3 id="c-15">C++</h3>
<ul>
<li>객체중심</li>
<li>생성/소멸 자동 관리 (stl)</li>
<li>RAII 패턴 사용</li>
</ul>
<h4 id="raii란">RAII란?</h4>
<p>메모리 자동 해제까지 염두하는 설계법</p>
<pre><code class="language-cpp">class string
{
public:
    char* c = nullptr;

    string(size_t len)
    {
        c = new char[len];
    }

    ~string()
    {
        delete[] c;
    }
};

string s(100);
strcpy(s.c, &quot;Hello, world!&quot;);
throw 0;

// 출처 : https://m.blog.naver.com/kmc7468/220927032462</code></pre>
<p>std::shared_ptr, std::unique_ptr 등 모두 RAII 패턴으로 작성됨</p>
<h3 id="따라서">따라서</h3>
<p><strong>모던C++에서는 STL 사용하는게 정석</strong></p>
<h2 id="그럼에도-c에서-직접-메모리-관리를-하는-경우">그럼에도 C++에서 직접 메모리 관리를 하는 경우</h2>
<h3 id="c-라이브러리와-연동-시">C 라이브러리와 연동 시</h3>
<p>C API는 malloc/free로 작성되어 있어서</p>
<h3 id="매우매우-로우레벨-메모리-제어-시게임엔진시스템">매우매우 로우레벨 메모리 제어 시(게임/엔진/시스템)</h3>
<ul>
<li>메모리 풀</li>
<li>커스텀 allocator</li>
<li>성능 튜닝</li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[TIL: Unreal5 Blueprint 12일차]]></title>
            <link>https://velog.io/@bak_chun8/TIL-Unreal5-%EA%B0%9C%EB%B0%9C-%EA%B8%B0%EB%B3%B8-12%EC%9D%BC%EC%B0%A8</link>
            <guid>https://velog.io/@bak_chun8/TIL-Unreal5-%EA%B0%9C%EB%B0%9C-%EA%B8%B0%EB%B3%B8-12%EC%9D%BC%EC%B0%A8</guid>
            <pubDate>Thu, 16 Apr 2026 09:09:48 GMT</pubDate>
            <description><![CDATA[<h2 id="누적-학습-시간--80시간-34분-">*<em>누적 학습 시간 : 80시간 34분 *</em></h2>
<h4 id="📅-2026-04-16">📅 2026-04-16</h4>
<pre><code class="language-cpp"></code></pre>
<h2 id="복습-및-퀘스트">복습 및 퀘스트</h2>
<p><img src="https://velog.velcdn.com/images/bak_chun8/post/4f86ccfd-739c-4b5e-a91d-5d076c59af09/image.png" alt=""></p>
<p>장애물 피하기를 만들었다.</p>
<p>골렘에 AI를 적용해 랜덤한 X값으로 이동하게 만들었다.</p>
<p><img src="https://velog.velcdn.com/images/bak_chun8/post/d525e267-e95c-4390-8dbe-be0fc7b2c0d1/image.png" alt=""></p>
<p><img src="https://velog.velcdn.com/images/bak_chun8/post/e5106749-450a-481b-ad3d-03006df65824/image.png" alt=""></p>
<p>골렙과 충돌 시 터지는 이펙트와 함께 사망 UI가 발생한다.
(UI 수정 도중 찍어서 얼라인이 안맞는다.)</p>
]]></description>
        </item>
    </channel>
</rss>