<?xml version="1.0" encoding="utf-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
    <channel>
        <title>cassis_soda.log</title>
        <link>https://velog.io/</link>
        <description></description>
        <lastBuildDate>Wed, 28 May 2025 12:55:22 GMT</lastBuildDate>
        <docs>https://validator.w3.org/feed/docs/rss2.html</docs>
        <generator>https://github.com/jpmonette/feed</generator>
        <image>
            <title>cassis_soda.log</title>
            <url>https://velog.velcdn.com/images/cassis_soda/profile/0f2a57ea-e69b-4e4d-88fa-cb0b3b04d163/social_profile.png</url>
            <link>https://velog.io/</link>
        </image>
        <copyright>Copyright (C) 2019. cassis_soda.log. All rights reserved.</copyright>
        <atom:link href="https://v2.velog.io/rss/cassis_soda" rel="self" type="application/rss+xml"/>
        <item>
            <title><![CDATA[[Unreal] 최종 프로젝트 - 샷건]]></title>
            <link>https://velog.io/@cassis_soda/Unreal-%EC%B5%9C%EC%A2%85-%ED%94%84%EB%A1%9C%EC%A0%9D%ED%8A%B8-%EC%83%B7%EA%B1%B4</link>
            <guid>https://velog.io/@cassis_soda/Unreal-%EC%B5%9C%EC%A2%85-%ED%94%84%EB%A1%9C%EC%A0%9D%ED%8A%B8-%EC%83%B7%EA%B1%B4</guid>
            <pubDate>Wed, 28 May 2025 12:55:22 GMT</pubDate>
            <description><![CDATA[<p>※ 해당 기록은 Unreal 5.5.4 버전을 기준으로 작성되었습니다.</p>
<hr>
<h3 id="0-서론">0. 서론</h3>
<p>&nbsp;&nbsp;&nbsp;&nbsp; 이전에 만들었던 <code>WeaponBase</code>와 <code>WeaponDataAsset</code>을 이용해 무기를 구현한다. 그 시작은 샷건부터이다.</p>
<h3 id="1-base">1. Base</h3>
<p><img src="https://velog.velcdn.com/images/cassis_soda/post/8b654701-f35a-447b-9065-7db0e0ea8b1c/image.png" alt=""></p>
<p>&nbsp;&nbsp;&nbsp;&nbsp; 샷건으로 근거리 공격이나 휘두르기 등을 가능케할 생각이 없기 때문에 박스 컴포넌트를 <code>RootComponent</code>로 하여, 메쉬와 사격시 총알이 나가는 기준 위치를 지정할 <code>ArrowComponent</code>를 붙였다.</p>
<h3 id="2-기본-공격">2. 기본 공격</h3>
<p>&nbsp;&nbsp;&nbsp;&nbsp; 샷건은 따로 스킬을 구현하지 않는 우리 게임의 유이한 원거리 무기이다. 기본 공격은 다섯 갈래로 발사된다. <code>ArrowComoponent</code>인 <code>MuzzlePosComp</code>의 Yaw값을 중앙값으로 각각 <code>{ -20, -10, 0, 10, 20 }</code> 의 값의 방향으로 라인트레이스를 쏜다. </p>
<pre><code class="language-cpp">    for (int i = -2; i &lt; 3; ++i)
    {
        float angle = BaseAngle + (i * AngleOffset);
        FRotator rot (0.f, angle, 0.f);
        FVector FireDirection = rot.Vector().GetSafeNormal ();

        FVector MuzzlePos = MuzzlePosComp-&gt;GetComponentLocation();
        FVector TargetPos = MuzzlePos + FireDirection.GetSafeNormal() * Data-&gt;NormalAtt.AttRange;

        FHitResult outHit;
        FCollisionQueryParams params;
        params.AddIgnoredActor (this);
        params.AddIgnoredActor (this-&gt;GetOwner());

        bool bHit = GetWorld ()-&gt;LineTraceSingleByChannel (outHit, MuzzlePos, TargetPos, ECC_Visibility, params);

        DrawDebugLine(GetWorld (), MuzzlePos,TargetPos, FColor::Yellow);

        if (bHit)
        {
            AActor* HitActor = outHit.GetActor();
            if (HitActor)
            {
                AMW_Character* player = Cast&lt;AMW_Character&gt;(HitActor);
                if (player)
                {
                    int32&amp; HitCount = HitActorCount.FindOrAdd(player);

                }
            }
        }

    }</code></pre>
<p>&nbsp;&nbsp;&nbsp;&nbsp; 다음은 피격 처리 로직이다. 다섯 개의 펠릿이 전부 같은 데미지로 들어간다면 근거리에서 이를 대항할 수 없을 것이다. 롤에서 이와 비슷한 스킬로 하이머딩거 W 스킬, 마법공학 초소형 로켓이 있다(앞으로 편의상 w로 한다). 
<img src="https://velog.velcdn.com/images/cassis_soda/post/d4bf3b40-8139-4987-8790-511a7553c8a8/image.png" alt="">
&nbsp;&nbsp;&nbsp;&nbsp; W 특징으로 첫 번째 로켓에 피격당한 적은 추가 로켓에 피격당할 시 첫 로켓보다 더 감소한 피해를 입는다. 그래서 다음과 같은 로직을 사용했다. 라인트레이스 선 한개와 충돌할 때 <code>HitActorCount</code>의 값을 증가시킨다. 첫 데미지는 데이터 에셋에 저장된 데미지를 넣고, <code>충돌 횟수 - 1</code> 만큼의 추가 데미지를 가한다.</p>
<pre><code class="language-cpp">for (const TPair&lt;AMW_Character*, int32&gt;&amp; HitPair : HitActorCount)
    {
        AMW_Character* HitPlayer = HitPair.Key;
        int32 HitCount = HitPair.Value;

        float TotalDamage = Data-&gt;NormalAtt.AttDamage;
        if (HitCount &gt; 1)
        {
            TotalDamage += (HitCount - 1) * 25.f;
        }
        UGameplayStatics::ApplyDamage (HitPlayer, TotalDamage, GetOwner ()-&gt;GetInstigatorController (), GetOwner (), UDamageType::StaticClass ());
    }</code></pre>
<h3 id="3-애니메이션">3. 애니메이션</h3>
<p><img src="https://velog.velcdn.com/images/cassis_soda/post/96fb5ad5-0813-4585-b682-aa60a6f1d538/image.png" alt="">
&nbsp;&nbsp;&nbsp;&nbsp; 애니메이션 블루프린트에 Default Slot을 추가하고 몽타주에서 설정한 Slot으로 변경한다. </p>
<h3 id="4-앞으로-할-일">4. 앞으로 할 일</h3>
<p>&nbsp;&nbsp;&nbsp;&nbsp; 맨손을 포함하는 무기 State의 갯수가 9개이다. 애니메이션 블루프린트를 구조화하지 않으면 크게 꼬여 나중에 손대기 힘들어질 것이다. ABP를 정리하고, 나머지 무기를 구현할 계획이다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[Unreal] 최종 프로젝트 - WeaponBase, DataAsset]]></title>
            <link>https://velog.io/@cassis_soda/Unreal-%EC%B5%9C%EC%A2%85-%ED%94%84%EB%A1%9C%EC%A0%9D%ED%8A%B8-WeaponBase-DataAsset</link>
            <guid>https://velog.io/@cassis_soda/Unreal-%EC%B5%9C%EC%A2%85-%ED%94%84%EB%A1%9C%EC%A0%9D%ED%8A%B8-WeaponBase-DataAsset</guid>
            <pubDate>Thu, 22 May 2025 01:39:49 GMT</pubDate>
            <description><![CDATA[<p>※ 해당 기록은 Unreal 5.5.4 버전을 기준으로 작성되었습니다.</p>
<hr>
<h3 id="0-서론">0. 서론</h3>
<p>&nbsp;&nbsp;&nbsp;&nbsp; 여러 무기의 기반이 되는 WeaponBase 클래스를 구성하고, 각 무기들의 공격에 관련한 정보를 담은 DataAsset을 만들었다.</p>
<h3 id="1-weapon-dataasset">1. Weapon DataAsset</h3>
<p><img src="https://velog.velcdn.com/images/cassis_soda/post/0145748f-e718-4e4c-8720-9510704fd69d/image.png" alt="">
&nbsp;&nbsp;&nbsp;&nbsp; 데이터 에셋을 다음과 같이 구현했다. 사실 csv에 모든 정보를 담아두고 불러올까도 생각했지만 생각보다 양이 그렇게 많지 않고 데이터 에셋을 처음 사용하는 거라 적절하게 분배할 방법을 알지 못했다. <code>FSkillInfo</code>라는 구조체를 선언하여, 그 내부에 공격의 데미지, 사거리, 몽타주, 몽타주 재생 배율, CC기와 그 시간의 정보를 담고 이 구조체로 공격을 구현했다. <code>magazine</code>은 샷건과 나뭇가지를 위한 변수다. 샷건은 강력하고, 원거리라는 이점 대신에 공격 가능 횟수를 제한할 예정이다. 나뭇가지는 일정 횟수 이상 공격하면 나뭇가지가 파괴되도록 구현할 계획이다. 그래서 이런식으로 소모값이 없는 무기는 <code>magazine = -1</code>로 두어, 감소처리 등을 적용하지 않을 예정이다.</p>
<h3 id="2-weaponbase">2. WeaponBase</h3>
<p><img src="https://velog.velcdn.com/images/cassis_soda/post/13062bd1-3a3d-4b90-9126-2f1f173b1c20/image.png" alt="">
&nbsp;&nbsp;&nbsp;&nbsp; 모든 무기의 부모 클래스인 <code>WeaponBase</code>클래스이다. 어짜피 인스턴스를 만들 일이 없을 것 같아 순수 가상 클래스로 구현할까도 생각했지만, 무기 장착에서 탐지하는 액터를 <code>WeaponBase</code>로 캐스팅해서 판단하고 있어서 이는 반려되었다. 각 무기에서 공격을 구현하고, <code>Data</code>에 담긴 정보로 공격을 직접적으로 수행할 것이다.</p>
<h3 id="3-앞으로-할-일">3. 앞으로 할 일</h3>
<p>&nbsp;&nbsp;&nbsp;&nbsp; <code>WeaponBase</code>를 뿌리로 하여 여러 무기를 구현할 차례다. 시작은 기본 공격 하나만 있는 샷건부터다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[Unreal] 최종 프로젝트 - Player 무기 줍기, 버리기
]]></title>
            <link>https://velog.io/@cassis_soda/Unreal-%EC%B5%9C%EC%A2%85-%ED%94%84%EB%A1%9C%EC%A0%9D%ED%8A%B8-Player-%EB%AC%B4%EA%B8%B0-%EC%A4%8D%EA%B8%B0-%EB%B2%84%EB%A6%AC%EA%B8%B0</link>
            <guid>https://velog.io/@cassis_soda/Unreal-%EC%B5%9C%EC%A2%85-%ED%94%84%EB%A1%9C%EC%A0%9D%ED%8A%B8-Player-%EB%AC%B4%EA%B8%B0-%EC%A4%8D%EA%B8%B0-%EB%B2%84%EB%A6%AC%EA%B8%B0</guid>
            <pubDate>Tue, 20 May 2025 02:01:48 GMT</pubDate>
            <description><![CDATA[<p>※ 해당 기록은 Unreal 5.5.4 버전을 기준으로 작성되었습니다.</p>
<hr>
<h3 id="0-서론">0. 서론</h3>
<p>&nbsp;&nbsp;&nbsp;&nbsp; 무기 장착을 관리하는 <code>EquipComponent</code>를 만들어 무기 줍고 버리는 기능을 구현했다.</p>
<h3 id="1-equipcomponent">1. EquipComponent</h3>
<p>&nbsp;&nbsp;&nbsp;&nbsp; 사용자의 입력을 받아 장비중이 아니면 바닥의 무기를 주워 장착하고, 장비 중이라면 무기를 다시 바닥에 내려두는 역할을 하는 컴포넌트다.</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp; 처음에 장착/해제를 구현할 때, 두 기능을 각각의 함수로 분리하여 구현하고, 두 함수를 같은 키에 바인딩했다. 물론 <code>bool</code>변수를 두어 <code>true</code>일 땐 <code>UnEquip()</code>에서는 <code>return</code>하는, 반대의 경우에도 마찬가지로 동작하게 구현했다.</p>
<pre><code class="language-cpp">// 이전의 구현방식
void UEquipComponent::SetupInputBinding(class UEnhancedInputComponent* Input)
{
    Super::SetupInputBinding(Input);

    Input-&gt;BindAction(IA_Equip, ETriggerEvent::Started, this, &amp;UEquipComponent::Equip);
    Input-&gt;BindAction(IA_Equip, ETriggerEvent::Started, this, &amp;UEquipComponent::UnEquip);
}</code></pre>
<p>&nbsp;&nbsp;&nbsp;&nbsp; 위와 같이 만들었을 때 문제가 발생했다. 예상 가능한 문제는 우선 하나의 키에 두 액션을 바인딩한 점이었다. 의도로는 무기를 갖고있으면 버리는 함수가, 무기가 없다면 무기를 줍는 액션이 수행되기를 바랐다. 하지만 무기를 버리는 액션은 실행이 마치 <code>continue</code>되는 느낌이었다.
&nbsp;&nbsp;&nbsp;&nbsp; 검색으로 찾아보니 우선순위가 낮은 액션에 대해 Input을 소모할 가능성이 있었다. 즉 우선순위가 높다면 그것이 처리되고 다른 액션은 처리하지 않는 것이다.
<img src="https://velog.velcdn.com/images/cassis_soda/post/929a3964-4f36-4167-855f-1a00e3ae51ce/image.png" alt="">
위 이미지는 InputAction에서의 설정이다. Consume Lower Priority Enhanced Input Mapping이 기본으로 체크 되어 있어서 평소에 크게 관심갖지 않았던 부분이라 무심코 넘겼다. 아마 이 부분이 두 가지의 <code>ETriggerEvent::Started</code>로 바인딩 된 함수 중에 먼저 바인딩된 <code>Equip</code>만 실행되게 한 듯 하다. <code>InputAction</code>을 두 개로 분리하고, 각각의 다른 키에 바인딩 해서 실행했을 때는 정상적으로 실행되었다. 로직은 문제가 없었으나 내부의 처리의 이슈였던게 맞았다.
&nbsp;&nbsp;&nbsp;&nbsp; 그래서 두 함수를 하나의 함수로 합쳐서 역시 <code>bool</code>값으로 분기하여 장착과 해제를 실행하도록 했다. 다시 <code>InputAction</code>과 키를 하나로 합쳐서 실행시켰다.
<img src="https://velog.velcdn.com/images/cassis_soda/post/ef49fce8-ac23-4602-967b-afd20dce6298/image.gif" alt="">
무기를 잡고, 버리는 기능이 제대로 실행되는 것을 볼 수 있다.</p>
<h3 id="2-앞으로-할-일">2. 앞으로 할 일</h3>
<p>&nbsp;&nbsp;&nbsp;&nbsp; 무기의 근본이 되는 <code>WeaponBase</code>를 만들고, 무기의 데이터 정보를 갖는 <code>WeaponDataAsset</code>을 만들 예정이다. <code>WeaponBase</code>를 상속해 여러 종류의 무기를 구현해야 한다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[Unreal] 최종 프로젝트 - Player 이동, 대쉬]]></title>
            <link>https://velog.io/@cassis_soda/Unreal-%EC%B5%9C%EC%A2%85-%ED%94%84%EB%A1%9C%EC%A0%9D%ED%8A%B8-Player-%EC%9D%B4%EB%8F%99-%EB%8C%80%EC%89%AC</link>
            <guid>https://velog.io/@cassis_soda/Unreal-%EC%B5%9C%EC%A2%85-%ED%94%84%EB%A1%9C%EC%A0%9D%ED%8A%B8-Player-%EC%9D%B4%EB%8F%99-%EB%8C%80%EC%89%AC</guid>
            <pubDate>Wed, 14 May 2025 12:03:38 GMT</pubDate>
            <description><![CDATA[<p>※ 해당 기록은 Unreal 5.5.4 버전을 기준으로 작성되었습니다.</p>
<hr>
<h3 id="0-서론">0. 서론</h3>
<p>&nbsp;&nbsp;&nbsp;&nbsp; 플레이어를 이동시키고 대쉬하는 기본적인 기능을 구현했다.</p>
<h3 id="1-player">1. Player</h3>
<p>&nbsp;&nbsp;&nbsp;&nbsp; 우선 플레이어에는 Mesh와 Delegate를 추가했다. 이 Delegate를 이용해서 플레이어가 갖고있는 컴포넌트들에서 입력을 받아 처리할 수 있을 것이다.</p>
<pre><code class="language-cpp">// AMW_Player.h에서
DECLARE_MULTICAST_DELEGATE_OneParam(FInputBindingDelegate, class UEnhancedInputComponent*);

// UInputBaseComponent.cpp에서
void UInputBaseComponent::InitializeComponent()
{
    OwnerPlayer = Cast&lt;AMW_Character&gt;(GetOwner());
    OwnerPlayer-&gt;InputBindingDelegate.AddUObject(this, &amp;UInputBaseComponent::SetupInputBinding);
}</code></pre>
<p><code>UInputBascComponent</code>는 키 입력을 받는 액터 컴포넌트로, 이를 부모로 하여 <code>UMoveComponent</code>와 <code>UAttackComponent</code> 등으로 확장할 계획으로 만들었다. <code>SetupInputBinding()</code>을 <code>virtual</code>로 선언하여 자식 컴포넌트에서 구현하도록 했으므로, 자식 컴포넌트에서 구현한 함수들이 델리게이트에 모두 등록되게 된다. 이 때 델리게이트의 <code>MULTICAST</code>는 네트워크의 RPC와는 관계 없는 부분이므로 헷갈리지 말도록 하자. 저 부분을 <code>SINGLE</code>로 구현한다면 한 가지의 함수만 등록되어 나머지가 동작하지 않을 수 있다.
&nbsp;&nbsp;&nbsp;&nbsp; <code>StateComponent</code>를 두어 플레이어의 상태를 관찰하도록 했다. 플레이어가 특정 행동을 하면 그에 맞게 상태가 변화하고, 이를 AnimInstance에서 받아와 플레이어의 애니메이션을 바꾸는 데 사용하도록 했다. </p>
<pre><code class="language-cpp">// AMW_Player.cpp에서
    bUseControllerRotationPitch = false;
    bUseControllerRotationRoll = false;
    bUseControllerRotationYaw = false;

    GetCharacterMovement ()-&gt;bOrientRotationToMovement = true;
    // 중략
    SpringArmComp-&gt;SetUsingAbsoluteRotation (true); // 캐릭터가 회전해도 스프링암은 회전하지 않게</code></pre>
<p>&nbsp;&nbsp;&nbsp;&nbsp; 이번에 탑다운 뷰로 만들면서 다른 게임에서는 하지 않았던 설정이다. 카메라가 플레이어를 따라가는 것은 같지만, 카메라가 플레이어의 회전을 반영하지 않도록하고, 플레이어가 움직이고자 하는 방향으로 입력이 들어오면 그 방향으로 캐릭터를 회전시키는 것이다. 이런 관련된 처리를 하지 않으면 단순히 스프링암이 플레이어의 머리 위에 달려있는 TPS처럼 돌아갈 것이다.</p>
<h3 id="2-대쉬">2. 대쉬</h3>
<p>&nbsp;&nbsp;&nbsp;&nbsp; 애니메이션을 추가하기 전 우선 플레이어를 <code>LaunchCharacter()</code>를 이용해서 날렸다. 
<img src="https://velog.velcdn.com/images/cassis_soda/post/970f7d3f-7387-4ac1-83d3-d77fd6af2d0c/image.gif" alt="">
FSM을 이용해서 대쉬할 때 애니메이션을 재생하려고 했다. 그러나 공중에서 이동할 떄 문제가 있었다. <code>LaunchPlayer()</code>는 기본적으로 바닥과의 마찰에 영향을 받는다. 그래서 바닥에서 적절한 거리만큼 이동하도록 힘을 설정했어도 언덕 지형에서 바닥으로 내려올 떄, 위의 gif처럼 공중에서 보다 멀리 날아가는 현상이 있었다. 
<img src="https://velog.velcdn.com/images/cassis_soda/post/1edb0e86-bea5-43fd-80b8-d89538929a50/image.gif" alt="">
그래서 Force Root Lock 옵션을 해제하고 몽타주를 사용해서 구현하였다. 애니메이션이 자체적으로 갖는 이동 거리만큼 이동하는 방식이다.
&nbsp;&nbsp;&nbsp;&nbsp; 상태 변경을 행동을 담당하는 함수에서 하고 있었다. WASD 키 입력이 들어올 때 동작하는 <code>Move()</code>에서 Move 상태로 전이시키는 식이다. 그러나 문제가 있었는데, 이 대쉬 몽타주가 실행되는 동안에 키 입력을 하면 해당 방향으로 이동하진 않지만 상태가 Move로 전이되는 것이다. 그래서 <code>IsAnyMontagePlaying()</code> 함수를 사용해서 몽타주 재생을 판단한다. 상태 전이는 액션 함수의 마지막에서 처리하게 작성해서 맨 앞줄에 체크해서 리턴하도록 구현했다.</p>
<pre><code class="language-cpp">void UMoveComponent::Move(const struct FInputActionValue&amp; values)
{
    if (animInstance-&gt;IsAnyMontagePlaying()) return;

    FVector2D input = values.Get&lt;FVector2D&gt;();

    Direction.X = input.X;
    Direction.Y = input.Y;

    OwnerPlayer-&gt;StateComp-&gt;SetMoveState ();
}</code></pre>
<h3 id="3-앞으로-할-것">3. 앞으로 할 것</h3>
<p>&nbsp;&nbsp;&nbsp;&nbsp; 무기를 줍고 버리는 기능이 다음이다. 무기의 Base가 되는 클래스를 구현하고, 무기를 줍고 버리는 <code>WeaponComp</code>를 구현해야한다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[Unreal] 최종 프로젝트 - Player 구조]]></title>
            <link>https://velog.io/@cassis_soda/Unreal-%EC%B5%9C%EC%A2%85-%ED%94%84%EB%A1%9C%EC%A0%9D%ED%8A%B8-Player-%EA%B5%AC%EC%A1%B0</link>
            <guid>https://velog.io/@cassis_soda/Unreal-%EC%B5%9C%EC%A2%85-%ED%94%84%EB%A1%9C%EC%A0%9D%ED%8A%B8-Player-%EA%B5%AC%EC%A1%B0</guid>
            <pubDate>Tue, 13 May 2025 02:21:31 GMT</pubDate>
            <description><![CDATA[<p>※ 해당 기록은 Unreal 5.5.4 버전을 기준으로 작성되었습니다.</p>
<hr>
<h3 id="0-서론">0. 서론</h3>
<p>&nbsp;&nbsp;&nbsp;&nbsp; 이전에 VR 프로젝트를 진행하면서 플레이어를 담당했을 때, <code>player.cpp</code>에 기능을 전부 담아두는 바람에 소스파일 하나만 500줄이 넘어갔었다. 조금 복잡하고, 알아보기 까다롭긴 하지만 그래도 C++의 OOP를 살리고자 이번 프로젝트는 플레이어를 구조화하여 작성하기로 한다.</p>
<h3 id="1-player">1. Player</h3>
<p><img src="https://velog.velcdn.com/images/cassis_soda/post/ae7a8db6-c86b-4a1d-b6e4-f83c04607b8d/image.png" alt=""> &nbsp;&nbsp;&nbsp;&nbsp; 플레이어가 가지는 여러 기능들을 공통된 기능으로 묶어서 컴포넌트화하려고 한다. 앞으로 추가될 컴포넌트를 러프하게 작성했다. <code>StatusComp</code>에는 플레이어의 체력과 관련된 부분을 넣을 예정이다. <code>StateComp</code>는 플레이어 행동에 따른 Move라던지 Attack 같은 상태 변화를 Set하는 부분이 될 것이다. 나머지 컴포넌트는 이름에서 추정할 수 있을 것이다.</p>
<h3 id="2-앞으로-할-일">2. 앞으로 할 일</h3>
<p>&nbsp;&nbsp;&nbsp;&nbsp; 우선 플레이어를 만들고 이동시키는 것이다. 기존에 만들었던 게임과는 시점이 달라 새롭게 만드는 재미가 있을 것 같다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[Unreal] 최종 프로젝트 - 기획]]></title>
            <link>https://velog.io/@cassis_soda/Unreal-%EC%B5%9C%EC%A2%85-%ED%94%84%EB%A1%9C%EC%A0%9D%ED%8A%B8-%EA%B8%B0%ED%9A%8D</link>
            <guid>https://velog.io/@cassis_soda/Unreal-%EC%B5%9C%EC%A2%85-%ED%94%84%EB%A1%9C%EC%A0%9D%ED%8A%B8-%EA%B8%B0%ED%9A%8D</guid>
            <pubDate>Mon, 12 May 2025 06:21:27 GMT</pubDate>
            <description><![CDATA[<p>※ 해당 기록은 Unreal 5.5.4 버전을 기준으로 작성되었습니다.</p>
<hr>
<h2 id="bangarang">Bangarang</h2>
<h3 id="0-개요">0. 개요</h3>
<p>&nbsp;&nbsp;&nbsp;&nbsp; 네트워크 PVP 대전 게임</p>
<h3 id="1-레퍼런스">1. 레퍼런스</h3>
<p>&nbsp;&nbsp;&nbsp;&nbsp;1) 주요 공통 키워드 : PvP ; 한정된 전투 구역(낙사), 캐주얼 그래픽, 쿼터뷰
&nbsp;&nbsp;&nbsp;&nbsp; 2) 핵심 재미 요소
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; (1) 무기별 다양한 액션 요소 : 무기 각자의 고유 스킬이 존재한다.
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; (2) 어느 곳도 안전하지 않다 : 사방에서 접근하는 적과 싸워야 한다. 맵의 경계를 등지는 것은 낙사의 위험이 있다.</p>
<table>
<thead>
<tr>
<th>&lt;아수라장&gt;</th>
<th>&lt;로스트아크&gt; 태초의 섬</th>
</tr>
</thead>
<tbody><tr>
<td><img src="https://velog.velcdn.com/images/cassis_soda/post/32756d7f-b8ae-424c-b80c-ecd13cab5756/image.png" alt=""></td>
<td><img src="https://velog.velcdn.com/images/cassis_soda/post/0217ad2e-9a1b-43ea-85b6-fffc6147638c/image.png" alt=""></td>
</tr>
<tr>
<td>특징</td>
<td>특징</td>
</tr>
<tr>
<td>화려한 타격감과 과장된 연출</td>
<td>- 무기마다 고유한 스킬 및 전투 스타일</td>
</tr>
<tr>
<td></td>
<td>- 크립(중립 몬스터)과 이를 사냥하여 얻는 무기 강화 요소</td>
</tr>
<tr>
<td><a href="https://youtu.be/iX5YUE1rQi4?si=3up7HjMncSwsWtXJ">참고 영상</a></td>
<td><a href="https://youtu.be/YVfn8oZvGx4?si=O_DV58ktkf6xllt1">참고 영상</a></td>
</tr>
</tbody></table>
<table>
<thead>
<tr>
<th>&lt;파티애니멀즈&gt;</th>
<th>&lt;R.E.P.O&gt; 패배자들의 왕</th>
</tr>
</thead>
<tbody><tr>
<td><img src="https://velog.velcdn.com/images/cassis_soda/post/e7b1009f-a12e-45b1-a8b7-4a4599e09295/image.png" alt=""></td>
<td><img src="https://velog.velcdn.com/images/cassis_soda/post/88a29b2c-7617-48e2-9f8f-e40addb1389a/image.png" alt=""></td>
</tr>
<tr>
<td>특징</td>
<td>특징</td>
</tr>
<tr>
<td>최대 8인, 개인전 또는 다양한 팀전 가능</td>
<td>- 특정 무기의 독특한 기믹</td>
</tr>
<tr>
<td>ex) 2 vs 2 vs 2 vs 2, 4 vs 4 등</td>
<td>ex) 야구배트는 다른 무기보다 넉백 거리가 더 김</td>
</tr>
<tr>
<td><a href="https://youtu.be/0bb9NJn9PFs?si=PCgUfBj2gzVHdLgN&amp;t=986">참고 영상</a></td>
<td><a href="https://youtu.be/IBbAC-lgv_w?si=hgrcWMxTF7OwAKLP&amp;t=1130">참고 영상</a></td>
</tr>
</tbody></table>
<h3 id="2-레벨-구성">2. 레벨 구성</h3>
<p><img src="https://velog.velcdn.com/images/cassis_soda/post/a9e6b630-e326-42dc-8b2a-71afb9aefd9a/image.png" alt=""></p>
<ul>
<li>최대 8인, 개인전 혹은 2인 팀전</li>
<li>인게임 플로우
<img src="https://velog.velcdn.com/images/cassis_soda/post/1b5e2ea3-5540-4b71-9ebf-ab5804d71dbd/image.png" alt=""></li>
</ul>
<h3 id="3-승리-조건--배틀로얄">3. 승리 조건 ; 배틀로얄</h3>
<ul>
<li>일정 시간마다 줄어드는 전장 ; 전장 밖은 낙사 지역</li>
<li>만약 인원이 8명에 미치지 못한다면, 플레이어의 선택에 따라 AI봇 추가 기능</li>
<li>봇은 우선 고급으로 만들고 나중에 너프하는 느낌</li>
</ul>
<h3 id="4-카메라-및-맵">4. 카메라 및 맵</h3>
<p>&nbsp;&nbsp;&nbsp;&nbsp; 1) 카메라 : 플레이어 기준 고정, 탑다운 
&nbsp;&nbsp;&nbsp;&nbsp; 2) 맵
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; - 낙사 가능
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; - 여러 종류의 근접 무기 배치. ex) 해머, 배트, 나뭇가지
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; - 강력한 원거리 무기 ; 기믹 해제로 획득 또는 일정 시간 이후에 드랍되는 방식</p>
<h3 id="5-플레이어">5. 플레이어</h3>
<p>&nbsp;&nbsp;&nbsp;&nbsp;1) 이동 ; WASD
&nbsp;&nbsp;&nbsp;&nbsp; 2) 전투
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; - 기본 상태 : 맨손 공격 (데미지, 사거리 최소)
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; - 기본 공격 : 좌클릭
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; - 스킬 ( 무기 장착 ) : Q, 우클릭
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; - 대쉬 : LShift ; 무적은 아님, CC기에 끊길 수 있음
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; - 포션
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; - 무기 줍기 / 버리기 : C
&nbsp;&nbsp;&nbsp;&nbsp; 3) 체력
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; - 전부 소모시 일정시간 그로기 상태에 빠지고, 비교적 적은 양의 임시 체력을 갖는다. 이 시간 내 임시 체력을 다 잃어 죽지 않는다면 적은 양의 체력을 회복하여 부활
&nbsp;&nbsp;&nbsp;&nbsp; 4) 사망
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; - 갖고 있는 무기와 포션을 떨어뜨림. 다른 플레이어들이 주워서 사용 가능. 버프를 갖고있었다면 죽인 상대에게 버프가 옮겨 감</p>
<h3 id="6-크립-중립-몬스터">6. 크립 (중립 몬스터)</h3>
<p>&nbsp;&nbsp;&nbsp;&nbsp;- 맵에 소수 배치, 사냥 시 보상으로 버프나 회복 아이템 루팅
&nbsp;&nbsp;&nbsp;&nbsp; - 2종
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &gt; 버프 : 버프를 갖고 있는 플레이어를 처치하면 버프를 빼앗아 올 수 있음. 몬스터로부터 처음 획득 시 30초간 유지. 플레이어로부터 빼앗아 올 시 잔여시간 +10초. 불 속성(추가 도트 데미지), 얼음 속성 (피격 대상에게 슬로우)
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &gt; 회복 아이템 : 플레이어가 소지하다가 사용 가능</p>
<h3 id="7-아이템-리스트">7. 아이템 리스트</h3>
<table>
<thead>
<tr>
<th>무기 이름</th>
<th>무기 특징 (속도/데미지)</th>
<th>특수 스킬 (군중 제어)</th>
<th>공격 스킬 (딜 관련)</th>
</tr>
</thead>
<tbody><tr>
<td>해머</td>
<td>느리고 강함</td>
<td>땅 찍기 (기절)</td>
<td>휠윈드</td>
</tr>
<tr>
<td>배트</td>
<td>느리고 강함</td>
<td>풀 스윙 (넉백)</td>
<td>평캔</td>
</tr>
<tr>
<td>방패</td>
<td>느리고 보통</td>
<td>짧은 기절</td>
<td>패링 (성공 시 상대 넉백)</td>
</tr>
<tr>
<td>너클</td>
<td>빠르고 중간</td>
<td>발차기</td>
<td>순간 붙기</td>
</tr>
<tr>
<td>후라이팬</td>
<td>빠르고 약함</td>
<td>실드</td>
<td>평캔</td>
</tr>
<tr>
<td>샷건</td>
<td>느리고 강함</td>
<td></td>
<td></td>
</tr>
<tr>
<td>나뭇가지 (전)</td>
<td>빠르고 약함</td>
<td>M회 소모로 강한 공격</td>
<td>M회 소모로 강한 공격</td>
</tr>
<tr>
<td>나뭇가지 (후)</td>
<td>느리고 강함</td>
<td>마법 구체 발사</td>
<td>속성 휘두르기</td>
</tr>
<tr>
<td>나뭇가지는 기본적으로 공격 횟수의 제한; n회 공격 시 파괴</td>
<td></td>
<td></td>
<td></td>
</tr>
<tr>
<td>플레이어가 버프를 획득하면 스태프로 강화. 버프 효과가 종료될 시 스태프는 다시 나뭇가지로 퇴화.</td>
<td></td>
<td></td>
<td></td>
</tr>
<tr>
<td>버프를 갖고 있는 플레이어가 나뭇가지를 획득하면 바로 강화</td>
<td></td>
<td></td>
<td></td>
</tr>
<tr>
<td>샷건의 탄약 제한 있음</td>
<td></td>
<td></td>
<td></td>
</tr>
</tbody></table>
<hr>
]]></description>
        </item>
        <item>
            <title><![CDATA[[Unreal] 최종 프로젝트 개관]]></title>
            <link>https://velog.io/@cassis_soda/Unreal-%EC%B5%9C%EC%A2%85-%ED%94%84%EB%A1%9C%EC%A0%9D%ED%8A%B8-%EA%B0%9C%EA%B4%80</link>
            <guid>https://velog.io/@cassis_soda/Unreal-%EC%B5%9C%EC%A2%85-%ED%94%84%EB%A1%9C%EC%A0%9D%ED%8A%B8-%EA%B0%9C%EA%B4%80</guid>
            <pubDate>Mon, 12 May 2025 02:09:17 GMT</pubDate>
            <description><![CDATA[<hr>
<h3>프로젝트 요약</h3>

<ol>
<li>작업 기간 : 2025.05.07 ~ 2025.06.30</li>
<li>인력 구성 : 3인</li>
<li>프로젝트 내용 : Bangarang ; 창작 프로젝트</li>
<li>주요 업무 : 플레이어, 무기</li>
<li>개발 환경 : Unreal C++, Blueprint</li>
<li>링크<ul>
<li><a href="https://github.com/Team-Vincimus/Bangarang">깃허브</a></li>
</ul>
</li>
</ol>
<hr>
<p>&nbsp;&nbsp;&nbsp;&nbsp; 그간 프로젝트를 진행하면서 개발일지를 꾸준히 쓰지 못했다. 이번 프로젝트는 마지막이니만큼 꾸준히 쓰려고 노력할 것이다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[Unreal] 4차 프로젝트 - 아이템 구조화]]></title>
            <link>https://velog.io/@cassis_soda/Unreal-4%EC%B0%A8-%ED%94%84%EB%A1%9C%EC%A0%9D%ED%8A%B8-%EC%95%84%EC%9D%B4%ED%85%9C-%EA%B5%AC%EC%A1%B0%ED%99%94</link>
            <guid>https://velog.io/@cassis_soda/Unreal-4%EC%B0%A8-%ED%94%84%EB%A1%9C%EC%A0%9D%ED%8A%B8-%EC%95%84%EC%9D%B4%ED%85%9C-%EA%B5%AC%EC%A1%B0%ED%99%94</guid>
            <pubDate>Thu, 10 Apr 2025 13:13:28 GMT</pubDate>
            <description><![CDATA[<p>※ 해당 기록은 Unreal 5.5.4 버전을 기준으로 작성되었습니다.</p>
<hr>
<h3 id="0-서론">0. 서론</h3>
<p>&nbsp;&nbsp;&nbsp;&nbsp; &lt; LOCKDOWN Protocol &gt;에는 다양한 아이템이 존재한다. 그 중에서 우리가 구현하고자 하는 미션에 사용되는 아이템과, 칼을 만들고 추가적으로 아이템의 확장 가능성을 고려해서 상속으로 아이템 클래스를 구성하였다.</p>
<h3 id="1-item">1. Item</h3>
<p><img src="https://velog.velcdn.com/images/cassis_soda/post/9bf9b79d-9cd4-45ca-a3f1-7bfa0f70db6f/image.png" alt="클래스 다이어그램">
&nbsp;&nbsp;&nbsp;&nbsp; Item 클래스는 기본적으로 다섯 가지의 속성을 갖는다. <code>protected</code>로 선언하여 자식 클래스에서만 접근이 가능하고, <code>Getter</code>와<code>Setter</code> 함수를 넣어 외부에서 접근할 수 있도록 했다.
&nbsp;&nbsp;&nbsp;&nbsp; 각 아이템도 그들이 미션에서 사용될 쓰임새를 고려해 속성을 주었다. 전력 공급 미션에서 전류가 흐르는 방향을 색이 이어지는 것으로 판단한다. 다음 두 사진은 전력 공급 미션에서 퓨즈를 배치한 모습이다. 색이 연결되지 않으면 전류가 흐르지 않는다. 색상 판단을 위해 <code>Enum</code>클래스로 세 가지 색을 선언하고, <code>pair</code>에 두 색을 담아 앞의 숫놈이 <code>front</code>, 뒤의 암놈이 <code>second</code>의 정보를 담도록 했다. 칼은 단순히 공격을 위한 아이템이므로 다른 속성을 가질 필요가 없어서 메쉬만 부여했다.
<img src="https://velog.velcdn.com/images/cassis_soda/post/a4641eff-4f53-4f40-bb3e-4afeebff6e9e/image.png" alt="">
<img src="https://velog.velcdn.com/images/cassis_soda/post/3b2aff40-0a78-47dd-b433-588f53ee9cd8/image.png" alt=""></p>
<h3 id="2-마치며">2. 마치며</h3>
<p>&nbsp;&nbsp;&nbsp;&nbsp; 다음은 타블렛이다. 인게임에서 플레이어의 현재 위치를 간략히 보여주는 미니맵 기능과, 현재 잔여 미션 횟수와 미션의 이름에 마우스를 올리면 미션을 수행하는 장소를 미니맵에 띄우는 기능을 갖고있다. 위젯을 만들고 액터에 붙이고 플레이어의 입력에 따라 타블렛을 꺼내고 집어넣을 수 있도록 우선적으로 구현할 것이다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[Unreal] 4차 프로젝트 개관]]></title>
            <link>https://velog.io/@cassis_soda/Unreal-4%EC%B0%A8-%ED%94%84%EB%A1%9C%EC%A0%9D%ED%8A%B8-%EA%B0%9C%EA%B4%80</link>
            <guid>https://velog.io/@cassis_soda/Unreal-4%EC%B0%A8-%ED%94%84%EB%A1%9C%EC%A0%9D%ED%8A%B8-%EA%B0%9C%EA%B4%80</guid>
            <pubDate>Thu, 10 Apr 2025 12:52:45 GMT</pubDate>
            <description><![CDATA[<hr>
<h3>프로젝트 요약</h3>

<ol>
<li>작업 기간 : 2025.04.02 ~ 2025.04.30</li>
<li>인력 구성 : 2인</li>
<li>프로젝트 내용 : &lt; LOCKDOWN Protocol &gt; 모작</li>
<li>주요 업무 : 시스템, 아이템 및 인벤토리, 미션; 스캐너, 전력공급 </li>
<li>개발 환경 : Unreal C++, Blueprint</li>
<li>링크<ul>
<li><a href="https://ez23re.atlassian.net/jira/software/projects/O200K/boards/37/timeline?assignee=unassigned%2C712020%3A3fa89b88-6dc7-4f89-b626-d393eb17e055%2C712020%3A18215e82-aa2b-4f56-93a2-6cf828d92567&amp;timeline=WEEKS">간트 차트</a></li>
<li><a href="https://github.com/LOCKDOWNProtocol/200OK">깃허브</a></li>
</ul>
</li>
</ol>
<hr>
]]></description>
        </item>
        <item>
            <title><![CDATA[[코딩 테스트] Backtracking > N과 M (9)]]></title>
            <link>https://velog.io/@cassis_soda/%EC%BD%94%EB%94%A9-%ED%85%8C%EC%8A%A4%ED%8A%B8-Backtracking-N%EA%B3%BC-M-9</link>
            <guid>https://velog.io/@cassis_soda/%EC%BD%94%EB%94%A9-%ED%85%8C%EC%8A%A4%ED%8A%B8-Backtracking-N%EA%B3%BC-M-9</guid>
            <pubDate>Tue, 25 Mar 2025 00:52:28 GMT</pubDate>
            <description><![CDATA[<h3 id="0-링크">0. 링크</h3>
<p>&nbsp;&nbsp;&nbsp;&nbsp; <a href="https://www.acmicpc.net/problem/15663">백준 문제 링크</a></p>
<h3 id="1-설명">1. 설명</h3>
<blockquote>
</blockquote>
<p>N개의 자연수와 자연수 M이 주어졌을 때, 아래 조건을 만족하는 길이가 M인 수열을 모두 구하는 프로그램을 작성하시오.</p>
<blockquote>
<blockquote>
</blockquote>
</blockquote>
<ul>
<li>N개의 자연수 중에서 M개를 고른 수열<blockquote>
</blockquote>
</li>
<li>입력<blockquote>
<blockquote>
<ul>
<li>첫째 줄에 자연수 N과 M이 주어진다. (1 ≤ M ≤ N ≤ 8)</li>
<li>둘째 줄에 N개의 수가 주어진다. 입력으로 주어지는 수는 10,000보다 작거나 같은 자연수이다.</li>
</ul>
</blockquote>
</blockquote>
</li>
<li>출력 <blockquote>
<blockquote>
<ul>
<li>한 줄에 하나씩 문제의 조건을 만족하는 수열을 출력한다. 중복되는 수열을 여러 번 출력하면 안되며, 각 수열은 공백으로 구분해서 출력해야 한다.</li>
<li>수열은 사전 순으로 증가하는 순서로 출력해야 한다.</li>
</ul>
</blockquote>
</blockquote>
</li>
</ul>
<h4 id="입출력-예시">입출력 예시</h4>
<table>
<thead>
<tr>
<th>input</th>
<th>output</th>
</tr>
</thead>
<tbody><tr>
<td>3 1<br>4 4 2</td>
<td>2<br>4</td>
</tr>
<tr>
<td>4 2<br>9 7 9 1</td>
<td>1 7<br>1 9<br>7 1<br>7 9<br>9 1<br>9 7<br>9 9</td>
</tr>
</tbody></table>
<h3 id="2-해설">2. 해설</h3>
<ol>
<li><code>M</code>개의 수를 갖는 순열을 만들기 위한 벡터 <code>printList</code>  선언</li>
<li>입력을 받아 처리할 수를 저장하는 벡터 <code>inputList</code> 선언</li>
<li>이전의 입력값이 중복인지 아닌지 검출하는 <code>prev</code> 선언<ul>
<li>중복값이 연속으로 등장하므로 (정렬하고 시작) 이전값을 검사</li>
</ul>
</li>
<li><code>Backtracking()</code>함수 내에서 아래와 같이 반복한다.<blockquote>
<p>1) <code>list.size() == m</code>이면  <code>return</code>후 출력
2) <code>i = 1; i &lt;= N</code>인 동안 다음을 반복한다.</p>
<blockquote>
<p> <code>if (!visited[i] &amp;&amp; prev != inputList[i])</code>
 (1)  <code>visited[i] = true</code>
 (2) <code>list.push_back()</code>
 (3) <code>Backtracking(inputList[i])</code>
 (4) <code>list.pop_back()</code>
 (5) <code>visited[i] = false</code>
 (6) <code>prev = inputList[i]</code> <code>// 이 레벨에서 사용된 값 기록</code></p>
</blockquote>
</blockquote>
</li>
</ol>
<h3 id="3-코드">3. 코드</h3>
<pre><code class="language-cpp">#include &lt;iostream&gt;
#include &lt;vector&gt;
#include &lt;algorithm&gt;
#include &lt;unordered_map&gt;

using namespace std;

int n, m;
int k;
vector &lt;int&gt; inputList;
vector &lt;int&gt; printList;
vector &lt;bool&gt; visited;


void print()
{
    for (size_t i = 0; i &lt; printList.size(); i++)
        cout &lt;&lt; printList[i]  &lt;&lt; &quot; &quot;;
    cout &lt;&lt; &#39;\n&#39;;

}

void Backtracking(int key)
{
    if (printList.size() == m)
    {
        print();
        return;
    }
    int prev = -1;
    for (size_t i = 0; i &lt; inputList.size(); i++)
    {
        if (!visited[i] &amp;&amp; inputList[i] != prev)
        {
            visited[i] = true;
            printList.push_back(inputList[i]);
            Backtracking(inputList[i]);
            printList.pop_back();
            visited[i] = false;
            prev = inputList[i]; 

        }
    }
}

int main()
{
    cin &gt;&gt; n &gt;&gt; m;

    for (int i = 0; i &lt; n; i++)
    {
        int num;
        cin &gt;&gt; num;
        inputList.push_back(num);
    }
    sort(inputList.begin(), inputList.end());
    visited = vector &lt;bool&gt;(n, false);

    Backtracking(-1);
}</code></pre>
<h3 id="4-감상">4. 감상</h3>
<p>&nbsp;&nbsp;&nbsp;&nbsp; 백트래킹을 이해했다고 생각한 날 부숴버린 문제. 이해하고 있는게 아니라 외우고 있던 거였다. 이렇게 저렇게 문제를 풀어보며 IDE상에서는 문제없이 풀었지만 백준에 제출하니 시간초과를 뱉어내더라. 결국 AI의 해설의 도움을 받아서 해결했다. 다시 풀어봐야한다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[Unreal] VR 프로젝트 - 3버튼 공격]]></title>
            <link>https://velog.io/@cassis_soda/Unreal-VR-%ED%94%84%EB%A1%9C%EC%A0%9D%ED%8A%B8-3%EB%B2%84%ED%8A%BC-%EA%B3%B5%EA%B2%A9</link>
            <guid>https://velog.io/@cassis_soda/Unreal-VR-%ED%94%84%EB%A1%9C%EC%A0%9D%ED%8A%B8-3%EB%B2%84%ED%8A%BC-%EA%B3%B5%EA%B2%A9</guid>
            <pubDate>Mon, 24 Mar 2025 07:26:48 GMT</pubDate>
            <description><![CDATA[<p>※ 해당 기록은 Unreal 5.5.4 버전을 기준으로 작성되었습니다.</p>
<hr>
<h3 id="0-서론">0. 서론</h3>
<p>&nbsp;&nbsp;&nbsp;&nbsp; 버튼 3개의 입력으로 공격이 나가도록 구현하고자 했다. 해당 기능은 1차 프로젝트 때 구현하고자 했지만, 언리얼을 처음으로 배우던 때인지라 적당한 방법을 생각해내지 못하고 포기했었다. 그 당시에는 2버튼이었지만 이번엔 더 나아간 3버튼이다.</p>
<h3 id="1-트러블-슈팅">1. 트러블 슈팅</h3>
<p>&nbsp;&nbsp;&nbsp;&nbsp; 첫 번째로 사용한 방법은 하나의 InputAction에 키를 세 개를 할당하고, <code>WasInputKeyJustPressed()</code>과 <code>WasInputKeyJustReleased()</code>를 사용해서 키가 눌리고 떼질 때 <code>bool</code>변수의 값을 바꿔서 세 변수의 <code>and</code>연산이 <code>true</code>일 때 공격이 나가도록 구현했다. 이 방식으로 했을 때의 문제는, 입력이 칼타이밍으로 받아져야 한다는 것이었다. 버튼을 세 개를 동시에 눌러도 두 개까지 입력이 들어가는 경우가 많았고, 여러 번 눌러도 10 ~ 15번 중 한 번만이 세 개가 입력되었다.
&nbsp;&nbsp;&nbsp;&nbsp; 두 번째로 사용한 방법은 조합 허용 시간을 지정해서 각각 입력이 들어오는 시간의 차가 허용 시간보다 작을 경우 공격이 나가도록 구현했다.</p>
<pre><code class="language-cpp">if (PlayerController-&gt;WasInputKeyJustPressed(EKeys::Q))
    {
        bIsQPressed = true;
        LastQTime = CurrentTime;
    }
 ...
if (bIsQPressed &amp;&amp; bIsEPressed &amp;&amp; bIsXPressed &amp;&amp;
        FMath::Abs(LastQTime - LastETime) &lt;= ComboWindow &amp;&amp;
        FMath::Abs(LastETime - LastXTime) &lt;= ComboWindow &amp;&amp;
        FMath::Abs(LastQTime - LastXTime) &lt;= ComboWindow)
        ...</code></pre>
<p>&nbsp;&nbsp;&nbsp;&nbsp;위 같은 방식으로 각각의 키간의 입력시간의 차가 허용시간보다 작다면 실행하는 것인데, 첫 번째 방법보다는 개선되었지만 이것도 원하는 것처럼 누를 때마다 원하는 대로 입력이 들어가지 않았다.
&nbsp;&nbsp;&nbsp;&nbsp;다음으로 시도한 것은 2번의 방법에 <code>Tarray</code>를 사용하는 것이었다. 플레이어가 키를 입력하면 이를 <code>Tarray</code>에 넣고, 그 안에 값이 3개 있고 시간차 안이라면 실행하기 였다. 이 방법도 앞의 방법보단 개선이 있었지만 해결하진 못했다.</p>
<h3 id="2-해결">2. 해결</h3>
<p>&nbsp;&nbsp;&nbsp;&nbsp; 그래서 마지막으로 시도한 것이 각 키를 개별의 InputAction으로 분리한 것이다. B버튼, RightTrigger, RightGrip으로 분리하고, 분리한 키에 대해 함수를 각각 할당하여 처리했다. 버튼을 누를 때 <code>Tarray</code>안에 없으면 넣어주도록하고, 버튼을 뗄 때는 안에 있는 버튼이 있으면 제거하면서 버튼이 눌렸는지 판단하는 <code>bool</code>변수의 값도 그에 맞춰 바뀌도록 했다.</p>
<pre><code class="language-cpp">void ASHPlayer::OnBPressed(const FInputActionValue&amp; InputValue)
{
    if (!RightPressedKeys.Contains(EKeys::Q))
    {
        isBPressed = true;
        RightPressedKeys.Add(EKeys::Q);
    }

}

void ASHPlayer::OnBReleased(const FInputActionValue&amp; InputValue)
{
    if (RightPressedKeys.Contains(EKeys::Q))
    {
        RightPressedKeys.RemoveSingle(EKeys::Q);
        isBPressed = false;
        isDelay = false;
    }
    ShiftDilation ();    
}
...
// Tick()에서
bRightPunch = isBPressed &amp;&amp; isRGripPressed &amp;&amp; isRTriggerPressed;
if (!isGrapping &amp;&amp; bRightPunch &amp;&amp; RightPressedKeys.Contains (EKeys::Q) &amp;&amp;
    RightPressedKeys.Contains (EKeys::E) &amp;&amp; RightPressedKeys.Contains (EKeys::C))
{
    RightPunch ();
}</code></pre>
<p>&nbsp;&nbsp;&nbsp;&nbsp; Tick에서 세 <code>bool</code>변수가 true인지 검사하고, <code>Tarray</code>안에 세 키가 모두 있다면 실행하도록 했다. 이 방법은 꽤나 유효해서 원하는 결과를 내주었다. 아래의 gif를 보면 키를 순차적으로 누를 때도 문제 없이 주먹을 쥐는 것을 볼 수 있다.
<img src="https://velog.velcdn.com/images/cassis_soda/post/70e1e7a6-ea55-4e6c-818d-56b89e2c940f/image.gif" alt="손"></p>
<h3 id="3-앞으로의-구현">3. 앞으로의 구현</h3>
<p>&nbsp;&nbsp;&nbsp;&nbsp; 이것저것 시도하고 찾아보면서 문제를 해결하는데 3일정도 걸렸다. 금방 끝날 줄 알았는데 생각보다 오래 걸렸다. 주먹을 쥐고, 콜리전까지 추가해둔 상태라 휘두르고 충돌 처리에서 이어지는 체력 감소 처리까지 구현해야한다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[Unreal] VR 프로젝트 - Player 이동]]></title>
            <link>https://velog.io/@cassis_soda/Unreal-VR-%ED%94%84%EB%A1%9C%EC%A0%9D%ED%8A%B8-Player-%EC%9D%B4%EB%8F%99</link>
            <guid>https://velog.io/@cassis_soda/Unreal-VR-%ED%94%84%EB%A1%9C%EC%A0%9D%ED%8A%B8-Player-%EC%9D%B4%EB%8F%99</guid>
            <pubDate>Thu, 20 Mar 2025 15:49:59 GMT</pubDate>
            <description><![CDATA[<p>※ 해당 기록은 Unreal 5.5.4 버전을 기준으로 작성되었습니다.</p>
<hr>
<h3 id="0-서론">0. 서론</h3>
<p>&nbsp;&nbsp;&nbsp;&nbsp; &lt; SuperHot &gt;은 VR 버전도 따로 존재한다. 원본은 플레이어가 WASD로 움직이고 마우스로 화면이 돌아가면 시간이 흐른다. VR 버전은 VR 장비를 착용하고 플레이하면서 화면을 도는 것을 제어하기 힘드므로 제자리에서 액션을 하는, 원본에 비해 좀 더 퍼즐적인 느낌이 강해진 작품으로 알고 있다. 그러나 플레이어가 좀 더 다양한 기능을 갖는 것이 내 실력을 높이는 데 도움이 되므로 이동 기능을 구현한다.
&nbsp;&nbsp;&nbsp;&nbsp; VR 기기를 실제로 보는 것도, VR 컨텐츠를 플레이하는 것도 이번이 처음이다. 짧은 기간의 프로젝트지만 기대가 크다. </p>
<h3 id="1-wasd-input">1. WASD Input</h3>
<p>&nbsp;&nbsp;&nbsp;&nbsp; 우선 아직 VR 플러그인을 추가하진 않아서 우선 WASD와 마우스를 기본으로 매핑해두었다. Enhanced Input을 사용해서 엔진에서 매핑하는 건 항상 금방하는데 뭐랄까 C++로 넘어와서 처리할 때가 되면 그렇게 헷갈리지 않을 수가 없다.</p>
<pre><code class="language-cpp">void ASHPlayer::SetupPlayerInputComponent(UInputComponent* PlayerInputComponent)
{
    Super::SetupPlayerInputComponent(PlayerInputComponent);

    auto pc = Cast&lt;APlayerController&gt;(GetController());
    if (pc)
    {
        auto subSys = ULocalPlayer::GetSubsystem&lt;UEnhancedInputLocalPlayerSubsystem&gt;(pc-&gt;GetLocalPlayer());
        if (subSys)
        {
            subSys-&gt;AddMappingContext(IMC_Player, 1);
        }
    }

    auto playerInput = Cast&lt;UEnhancedInputComponent&gt;(PlayerInputComponent);
    if (playerInput)
    {
        playerInput-&gt;BindAction(IA_PlayerMove, ETriggerEvent::Triggered, this, &amp;ASHPlayer::Move);
        playerInput-&gt;BindAction(IA_PlayerTurn, ETriggerEvent::Triggered, this, &amp;ASHPlayer::Turn);

        playerInput-&gt;BindAction(IA_PlayerMove, ETriggerEvent::Started, this, &amp;ASHPlayer::ShiftDilation);
        playerInput-&gt;BindAction(IA_PlayerMove, ETriggerEvent::Completed, this, &amp;ASHPlayer::ShiftDilation);
    }

}</code></pre>
<p>&nbsp;&nbsp;&nbsp;&nbsp; 프로젝트를 새로 펼 때마다 한 번씩은 무조건 사용하게 되는 친구라지만, 맨 처음에 한 번 만들고는 아래의 InputAction과 함수를 연결하는 부분 추가하는 거 말고는 잘 건드리지 않는 부분이라 그런가 잘 외워지진 않는 거 같다.
&nbsp;&nbsp;&nbsp;&nbsp; 이건 아마 정확한 정보는 아닐지도 모르지만, <code>AddMapingContext(IMC_Player, 1)</code>에서 <code>1</code>을 <code>0</code>으로 했었을 때 입력이 씹히는 현상이 있었다. 아무래도 0보단 우선순위가 높은 1이 낫다는 편.</p>
<h3 id="2-마치며">2. 마치며</h3>
<p>&nbsp;&nbsp;&nbsp;&nbsp; 당일 수업에 나갔던 파트라 만드는 것 자체는 금방 구현했었다. 그러나 왜인지 몰라도 코드 문제없고, 인풋 매핑 컨텍스트에 제대로 넣어놨는데 W 누르면 왼쪽 앞으로 가고, S 누르면 앞으로 가고 이런 식으로 입력이 이상하게 들어갔다. 도저히 뭐가 문젠지 감도 못잡았던거 그냥 매핑 컨텍스트 안에서 할당된거 다 지우고 다시 넣어주니까 제대로 돌아갔다. 정말 이해할 수 없다.
&nbsp;&nbsp;&nbsp;&nbsp; 앞으로 구현해야할 것들이 잔뜩이다. 24일인 프로토 발표까지 계획 중인 사항은 잡기와 근접 공격, 총탄의 움직임까지다. 단순히 키보드 마우스로 구현하는 거라면, 처음하는 1인칭이래도 일단 에셋 넣고 애니메이션 넣고 하면 그만이지만, VR은 뭔가 느낌이 다르다. 배우는 내용이 매번 새롭다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[Unreal] VR 프로젝트 개관]]></title>
            <link>https://velog.io/@cassis_soda/Unreal-VR-%ED%94%84%EB%A1%9C%EC%A0%9D%ED%8A%B8-%EA%B0%9C%EA%B4%80</link>
            <guid>https://velog.io/@cassis_soda/Unreal-VR-%ED%94%84%EB%A1%9C%EC%A0%9D%ED%8A%B8-%EA%B0%9C%EA%B4%80</guid>
            <pubDate>Wed, 19 Mar 2025 14:08:15 GMT</pubDate>
            <description><![CDATA[<hr>
<h3>프로젝트 요약</h3>

<ol>
<li>작업 기간 : 2025.03.18 ~ 2025.04.01</li>
<li>인력 구성 : 2인</li>
<li>프로젝트 내용 : &lt; SuperHot &gt; 일부 스테이지 VR 구현</li>
<li>주요 업무 : Player, VR Controll, 시스템</li>
<li>개발 환경 : Unreal C++, Blueprint</li>
<li>링크<ul>
<li><a href="https://docs.google.com/spreadsheets/d/1MfZHnHre5zbB1WCfgHVoWWoz3U9FR_wW-hxjgzD-eS8/edit?gid=0#gid=0">간트 차트</a></li>
<li><a href="https://github.com/CassisSoda/SuperHot_Copy">깃허브</a></li>
</ul>
</li>
</ol>
<hr>
]]></description>
        </item>
        <item>
            <title><![CDATA[[코딩 테스트] Backtracking > N과 M (7)]]></title>
            <link>https://velog.io/@cassis_soda/%EC%BD%94%EB%94%A9-%ED%85%8C%EC%8A%A4%ED%8A%B8-Backtracking-N%EA%B3%BC-M-7</link>
            <guid>https://velog.io/@cassis_soda/%EC%BD%94%EB%94%A9-%ED%85%8C%EC%8A%A4%ED%8A%B8-Backtracking-N%EA%B3%BC-M-7</guid>
            <pubDate>Tue, 18 Mar 2025 01:27:33 GMT</pubDate>
            <description><![CDATA[<h3 id="0-링크">0. 링크</h3>
<p>&nbsp;&nbsp;&nbsp;&nbsp; <a href="https://www.acmicpc.net/problem/15656">백준 문제 링크</a></p>
<h3 id="1-문제">1. 문제</h3>
<blockquote>
</blockquote>
<p>N개의 자연수와 자연수 M이 주어졌을 때, 아래 조건을 만족하는 길이가 M인 수열을 모두 구하는 프로그램을 작성하시오. N개의 자연수는 모두 다른 수이다.</p>
<blockquote>
<blockquote>
<ul>
<li>N개의 자연수 중에서 M개를 고른 수열같은 수를 여러 번 골라도 된다.</li>
<li>고른 수열은 오름차순이어야 한다.</li>
</ul>
</blockquote>
</blockquote>
<ul>
<li>입력<blockquote>
<blockquote>
<ul>
<li>첫째 줄에 N과 M이 주어진다. (1 ≤ M ≤ N ≤ 8)</li>
<li>둘째 줄에 N개의 수가 주어진다. 입력으로 주어지는 수는 10,000보다 작거나 같은 자연수이다.</li>
</ul>
</blockquote>
</blockquote>
</li>
<li>출력<blockquote>
<blockquote>
<ul>
<li>한 줄에 하나씩 문제의 조건을 만족하는 수열을 출력한다. 중복되는 수열을 여러 번 출력하면 안되며, 각 수열은 공백으로 구분해서 출력해야 한다.</li>
<li>수열은 사전 순으로 증가하는 순서로 출력해야 한다.</li>
</ul>
</blockquote>
</blockquote>
</li>
</ul>
<h4 id="입출력-예시">입출력 예시</h4>
<table>
<thead>
<tr>
<th>input</th>
<th>output</th>
</tr>
</thead>
<tbody><tr>
<td>3 1<br>4 5 2</td>
<td>2<br>4<br>5</td>
</tr>
<tr>
<td>4 2<br>9 8 7 1</td>
<td>1 1<br>1 7<br>1 8<br>1 9<br>7 1<br>7 7<br>7 8<br>7 9<br>8 1<br>8 7<br>8 8<br>8 9<br>9 1<br>9 7<br>9 8<br>9 9</td>
</tr>
</tbody></table>
<h3 id="2-해설">2. 해설</h3>
<ol>
<li><code>M</code>개의 수를 갖는 순열을 만들기 위한 벡터 <code>printList</code>  선언</li>
<li>입력을 받아 처리할 수를 저장하는 벡터 <code>inputList</code> 선언</li>
<li><code>Backtracking()</code>함수 내에서 아래와 같이 반복한다.<blockquote>
<p>1) <code>list.size() == m</code>이면  <code>return</code>후 출력
2) <code>i = 1; i &lt;= N</code>인 동안 다음을 반복한다.</p>
<blockquote>
</blockquote>
<p> (1) <code>list.push_back()</code>
 (2) <code>Backtracking()</code>
 (3) <code>list.pop_back()</code></p>
</blockquote>
</li>
</ol>
<h3 id="3-코드">3. 코드</h3>
<pre><code class="language-cpp">#include &lt;iostream&gt;
#include &lt;vector&gt;
#include &lt;algorithm&gt;

using namespace std;

int n,m;
vector &lt;int&gt; inputList;
vector &lt;int&gt; printList;

void print()
{
    for(size_t i = 0; i &lt; printList.size(); i++)
    {
        cout &lt;&lt; printList[i] &lt;&lt; &quot; &quot;;
    }
    cout &lt;&lt; &#39;\n&#39;;
}

void Backtracking()
{
    if(printList.size() == m)
    {
        print();
        return;
    }

    for(size_t i =0; i &lt; inputList.size(); i++)
    {
        printList.emplace_back(inputList[i]);
        Backtracking();
        printList.pop_back();
    }

}

int main()
{
    cin &gt;&gt; n &gt;&gt; m;

    for(size_t i = 0; i &lt; n; i++)
    {
        int number; 
        cin &gt;&gt; number;
        inputList.emplace_back(number);
    }
    sort(inputList.begin(), inputList.end());
    Backtracking();
}</code></pre>
<h3 id="4-감상">4. 감상</h3>
<p>&nbsp;&nbsp;&nbsp;&nbsp; <a href="https://velog.io/@cassis_soda/%EC%BD%94%EB%94%A9-%ED%85%8C%EC%8A%A4%ED%8A%B8-Backtracking-N%EA%B3%BC-M-3">3번 문제</a>와의 친구 문제.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[코딩 테스트] Backtracking N과 M (6)]]></title>
            <link>https://velog.io/@cassis_soda/%EC%BD%94%EB%94%A9-%ED%85%8C%EC%8A%A4%ED%8A%B8-Backtracking-N%EA%B3%BC-M-6</link>
            <guid>https://velog.io/@cassis_soda/%EC%BD%94%EB%94%A9-%ED%85%8C%EC%8A%A4%ED%8A%B8-Backtracking-N%EA%B3%BC-M-6</guid>
            <pubDate>Tue, 18 Mar 2025 01:26:59 GMT</pubDate>
            <description><![CDATA[<h3 id="0-링크">0. 링크</h3>
<p>&nbsp;&nbsp;&nbsp;&nbsp; <a href="https://www.acmicpc.net/problem/15655">백준 문제 링크</a></p>
<h3 id="1-문제">1. 문제</h3>
<blockquote>
</blockquote>
<p>N개의 자연수와 자연수 M이 주어졌을 때, 아래 조건을 만족하는 길이가 M인 수열을 모두 구하는 프로그램을 작성하시오. N개의 자연수는 모두 다른 수이다.</p>
<blockquote>
<blockquote>
<ul>
<li>N개의 자연수 중에서 M개를 고른 수열</li>
<li>고른 수열은 오름차순이어야 한다.</li>
</ul>
</blockquote>
</blockquote>
<ul>
<li>입력<blockquote>
<blockquote>
<ul>
<li>첫째 줄에 N과 M이 주어진다. (1 ≤ M ≤ N ≤ 8)</li>
<li>둘째 줄에 N개의 수가 주어진다. 입력으로 주어지는 수는 10,000보다 작거나 같은 자연수이다.</li>
</ul>
</blockquote>
</blockquote>
</li>
<li>출력<blockquote>
<blockquote>
<ul>
<li>한 줄에 하나씩 문제의 조건을 만족하는 수열을 출력한다. 중복되는 수열을 여러 번 출력하면 안되며, 각 수열은 공백으로 구분해서 출력해야 한다.</li>
<li>수열은 사전 순으로 증가하는 순서로 출력해야 한다.</li>
</ul>
</blockquote>
</blockquote>
</li>
</ul>
<h4 id="입출력-예시">입출력 예시</h4>
<table>
<thead>
<tr>
<th>input</th>
<th>output</th>
</tr>
</thead>
<tbody><tr>
<td>3 1<br>4 5 2</td>
<td>2<br>4<br>5</td>
</tr>
<tr>
<td>4 2<br>9 8 7 1</td>
<td>1 1<br>1 7<br>1 8<br>1 9<br>7 1<br>7 7<br>7 8<br>7 9<br>8 1<br>8 7<br>8 8<br>8 9<br>9 1<br>9 7<br>9 8<br>9 9</td>
</tr>
</tbody></table>
<h3 id="2-해설">2. 해설</h3>
<ol>
<li><code>M</code>개의 수를 갖는 순열을 만들기 위한 벡터 <code>printList</code>  선언</li>
<li>입력을 받아 처리할 수를 저장하는 벡터 <code>inputList</code> 선언</li>
<li><code>Backtracking()</code>함수 내에서 아래와 같이 반복한다.<blockquote>
<p>1) <code>list.size() == m</code>이면  <code>return</code>후 출력
2) <code>i = count; i &lt;= N</code>인 동안 다음을 반복한다.</p>
<blockquote>
</blockquote>
<p> (1) <code>list.push_back(i)</code>
 (2) <code>Backtracking( i + 1 )</code>
 // 2와 <code>i = count</code>로 <code>list[0]</code> 보다 작거나 같은 것은 <code>list</code>에 올 수 없음을 보장한다.
 (3) <code>list.pop_back()</code></p>
</blockquote>
</li>
</ol>
<h3 id="3-코드">3. 코드</h3>
<pre><code class="language-cpp">#include &lt;iostream&gt;
#include &lt;vector&gt;
#include &lt;algorithm&gt;

using namespace std;

int n, m;
vector &lt;int&gt; printList;
vector &lt;int&gt; inputList;

void print()
{
    for(size_t i = 0; i &lt; printList.size(); i++)
    {
        cout &lt;&lt; printList[i] &lt;&lt; &quot; &quot;;
    }
    cout &lt;&lt; &#39;\n&#39;;
}

// 수열이 주어지고, 중복을 허용하지 않는 조합 선택
void Backtracking(int count)
{
    if (printList.size() == m)
    {
        print();
        return;
    }

    for(size_t i = count; i &lt; n; i++)
    {
        printList.push_back(inputList[i]);
        Backtracking(i+1);
        printList.pop_back();
    }

}

int main()
{
    cin &gt;&gt; n &gt;&gt; m;
    for( size_t i = 0; i &lt; n ; i++)
    {
        int number;
        cin &gt;&gt; number;
        inputList.push_back(number);
    }
    sort(inputList.begin(), inputList.end());
    Backtracking(0);
}</code></pre>
<h3 id="4-감상">4. 감상</h3>
<p>&nbsp;&nbsp;&nbsp;&nbsp; <a href="https://velog.io/@cassis_soda/%EC%BD%94%EB%94%A9-%ED%85%8C%EC%8A%A4%ED%8A%B8-Backtracking-N%EA%B3%BC-M-2">2번 문제</a>와의 친구 문제.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[코딩 테스트] Backtracking > N과 M (5)]]></title>
            <link>https://velog.io/@cassis_soda/%EC%BD%94%EB%94%A9-%ED%85%8C%EC%8A%A4%ED%8A%B8-Backtracking-N%EA%B3%BC-M-5</link>
            <guid>https://velog.io/@cassis_soda/%EC%BD%94%EB%94%A9-%ED%85%8C%EC%8A%A4%ED%8A%B8-Backtracking-N%EA%B3%BC-M-5</guid>
            <pubDate>Tue, 18 Mar 2025 01:26:39 GMT</pubDate>
            <description><![CDATA[<h3 id="0-링크">0. 링크</h3>
<p>&nbsp;&nbsp;&nbsp;&nbsp; <a href="https://www.acmicpc.net/problem/15654">백준 문제 링크</a></p>
<h3 id="1-문제">1. 문제</h3>
<blockquote>
</blockquote>
<p>N개의 자연수와 자연수 M이 주어졌을 때, 아래 조건을 만족하는 길이가 M인 수열을 모두 구하는 프로그램을 작성하시오. N개의 자연수는 모두 다른 수이다.</p>
<blockquote>
<blockquote>
</blockquote>
<p>N개의 자연수 중에서 M개를 고른 수열</p>
</blockquote>
<ul>
<li>입력<blockquote>
<blockquote>
<ul>
<li>첫째 줄에 N과 M이 주어진다. (1 ≤ M ≤ N ≤ 8)</li>
<li>둘째 줄에 N개의 수가 주어진다. 입력으로 주어지는 수는 10,000보다 작거나 같은 자연수이다.</li>
</ul>
</blockquote>
</blockquote>
</li>
<li>출력<blockquote>
<blockquote>
<ul>
<li>한 줄에 하나씩 문제의 조건을 만족하는 수열을 출력한다. 중복되는 수열을 여러 번 출력하면 안되며, 각 수열은 공백으로 구분해서 출력해야 한다.</li>
<li>수열은 사전 순으로 증가하는 순서로 출력해야 한다.</li>
</ul>
</blockquote>
</blockquote>
</li>
</ul>
<h4 id="입출력-예시">입출력 예시</h4>
<table>
<thead>
<tr>
<th>input</th>
<th>output</th>
</tr>
</thead>
<tbody><tr>
<td>3 1<br>4 5 2</td>
<td>2<br>4<br>5</td>
</tr>
<tr>
<td>4 2<br>9 8 7 1</td>
<td>1 7<br>1 8<br>1 9<br>7 1<br>7 8<br>7 9<br>8 1<br>8 7<br>8 9<br>9 1<br>9 7<br>9 8</td>
</tr>
</tbody></table>
<h3 id="2-해설">2. 해설</h3>
<ol>
<li>중복을 허용하지 않는 순열을 고르기 위한 벡터 <code>visited</code> 선언</li>
<li><code>M</code>개의 수를 갖는 순열을 만들기 위한 벡터 <code>printList</code>  선언</li>
<li>입력을 받아 처리할 수를 저장하는 벡터 <code>inputList</code> 선언</li>
<li><code>Backtracking()</code>함수 내에서 아래와 같이 반복한다.<blockquote>
<p>1) <code>list.size() == m</code>이면  <code>return</code>후 출력
2) <code>i = 1; i &lt;= N</code>인 동안 다음을 반복한다.</p>
<blockquote>
<p>(1)    <code>!visited[i]</code>일 경우</p>
<blockquote>
<p>(2) <code>visited[i] = true</code> 
 (3) <code>list.push_back(i)</code>
 (4) <code>Backtracking()</code>
 (5) <code>list.pop_back()</code>
 (6) <code>visited[i]  = false</code> // 2와 6을 통해, (1, 1)은 불가능하도록, (2, 1)과 (3, 1)은 가능하게 한다.</p>
</blockquote>
</blockquote>
</blockquote>
</li>
</ol>
<h3 id="3-코드">3. 코드</h3>
<pre><code class="language-cpp">#include &lt;iostream&gt;
#include &lt;vector&gt;
#include &lt;algorithm&gt;

using namespace std;

int n, m;
vector &lt;int&gt; printList;
vector &lt;int&gt; inputList;
vector &lt;bool&gt; visited;

void print()
{
    for(size_t i = 0; i &lt; printList.size(); i++)
    {
        cout &lt;&lt; printList[i] &lt;&lt; &quot; &quot;;
    }
    cout &lt;&lt; &#39;\n&#39;;
}

void Backtracking()
{
    if(printList.size() == m)
    {
        print();
        return;
    }

    for(size_t i = 0; i &lt; inputList.size(); i++)
    {
       if(!visited[i])
       {
           visited[i] = true;
           printList.emplace_back(inputList[i]);
           Backtracking();
           printList.pop_back();
           visited[i] = false;
       }


    }
}

int main()
{
    cin &gt;&gt; n &gt;&gt; m;

    for(int i = 0; i &lt; n; i++)
    {
        int number; 
        cin &gt;&gt; number;
        inputList.emplace_back(number);
    }
    visited = vector&lt;bool&gt;(n, false);
    sort(inputList.begin(), inputList.end());
    Backtracking();
}
</code></pre>
<h3 id="4-감상">4. 감상</h3>
<p>&nbsp;&nbsp;&nbsp;&nbsp; 앞의 <a href="https://www.acmicpc.net/problem/15649">1번 문제</a>와 입력을 받는다는 점을 제외하면 같은 문제나 다름없다. 때문에 문제를 푸는데도 얼마 걸리지 않았다. 오름차순을 위해 <code>main()</code>에서 <code>Backtracking()</code>을 시작하기 전에 <code>inputList</code>를 <code>sort()</code>하고 시작함을 주의하자.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[코딩 테스트] Backtracking > N과 M (3)]]></title>
            <link>https://velog.io/@cassis_soda/%EC%BD%94%EB%94%A9-%ED%85%8C%EC%8A%A4%ED%8A%B8-Backtracking-N%EA%B3%BC-M-3</link>
            <guid>https://velog.io/@cassis_soda/%EC%BD%94%EB%94%A9-%ED%85%8C%EC%8A%A4%ED%8A%B8-Backtracking-N%EA%B3%BC-M-3</guid>
            <pubDate>Mon, 17 Mar 2025 06:04:16 GMT</pubDate>
            <description><![CDATA[<h3 id="0-링크">0. 링크</h3>
<p>&nbsp;&nbsp;&nbsp;&nbsp; <a href="https://www.acmicpc.net/problem/15651">백준 문제 링크</a></p>
<h3 id="1--설명">1.  설명</h3>
<blockquote>
</blockquote>
<p>자연수 N과 M이 주어졌을 때, 아래 조건을 만족하는 길이가 M인 수열을 모두 구하는 프로그램을 작성하시오.</p>
<blockquote>
<blockquote>
</blockquote>
</blockquote>
<ul>
<li>1부터 N까지 자연수 중에서 M개를 고른 수열</li>
<li>같은 수를 여러 번 골라도 된다.<blockquote>
</blockquote>
</li>
<li>입력<blockquote>
<blockquote>
</blockquote>
<p>첫째 줄에 자연수 N과 M이 주어진다. (1 ≤ M ≤ N ≤ 7)</p>
</blockquote>
</li>
<li>출력<blockquote>
<blockquote>
<ul>
<li>한 줄에 하나씩 문제의 조건을 만족하는 수열을 출력한다. 중복되는 수열을 여러 번 출력하면 안되며, 각 수열은 공백으로 구분해서 출력해야 한다.</li>
<li>수열은 사전 순으로 증가하는 순서로 출력해야 한다.</li>
</ul>
</blockquote>
</blockquote>
</li>
</ul>
<h4 id="입출력-예시">입출력 예시</h4>
<table>
<thead>
<tr>
<th>input</th>
<th>output</th>
</tr>
</thead>
<tbody><tr>
<td><code>3 1</code></td>
<td>1<br>2<br>3</td>
</tr>
<tr>
<td><code>4 2</code></td>
<td>1 1<br>1 2<br>1 3<br>1 4<br>2 1<br>2 2<br>2 3<br>2 4<br>3 1<br>3 2<br>3 3<br>3 4<br>4 1<br>4 2<br>4 3<br>4 4</td>
</tr>
</tbody></table>
<h3 id="2-해설">2. 해설</h3>
<ol>
<li><code>M</code>개의 수를 갖는 순열을 만들기 위한 <code>list</code> 벡터 선언</li>
<li><code>Backtracking()</code>함수 내에서 아래와 같이 반복한다.<blockquote>
<p>1) <code>list.size() == m</code>이면  <code>return</code>후 출력
2) <code>i = 1; i &lt;= N</code>인 동안 다음을 반복한다.</p>
<blockquote>
</blockquote>
<p> (1) <code>list.push_back()</code>
 (2) <code>Backtracking()</code>
 (3) <code>list.pop_back()</code></p>
</blockquote>
</li>
</ol>
<h3 id="3-코드">3. 코드</h3>
<pre><code class="language-cpp">#include &lt;iostream&gt;
#include &lt;vector&gt;
using namespace std;

int n,m;

vector &lt;int&gt; list;

void print()
{
    for(int i = 0; i &lt; list.size(); i++)
    {
        cout &lt;&lt; list[i] &lt;&lt; &quot; &quot;;
    }
    cout &lt;&lt; &quot;\n&quot;;
}

// m개로 이루어진 중복조합
void Backtracking()
{
    if (list.size() == m)
    {
        print();
        return;
    }

    for(int i = 1; i &lt;= n; i++)
    {
        list.push_back(i);
        Backtracking();
        list.pop_back();
    }
}

int main()
{
    cin &gt;&gt; n &gt;&gt; m;
    Backtracking();

}
</code></pre>
<h3 id="4-감상">4. 감상</h3>
<p>&nbsp;&nbsp;&nbsp;&nbsp; 스터디에서 푼 마지막 문제. 사실 제일 처음 푼 문제지만, 문제 번호가 3번이라 나중에 적는다. 모든 조합을 검토하는 백트래킹의 가장 기본이 되는 문제되겠다. 참고가 될진 모르겠지만 <a href="https://velog.io/@cassis_soda/%EC%BD%94%EB%94%A9-%ED%85%8C%EC%8A%A4%ED%8A%B8-Backtracking-N%EA%B3%BC-M-1">1번</a>과 <a href="https://velog.io/@cassis_soda/%EC%BD%94%EB%94%A9-%ED%85%8C%EC%8A%A4%ED%8A%B8-Backtracking-N%EA%B3%BC-M-2">2번</a>을 다시 보며 차이를 이해하자.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[코딩 테스트] Backtracking > N과 M (2)]]></title>
            <link>https://velog.io/@cassis_soda/%EC%BD%94%EB%94%A9-%ED%85%8C%EC%8A%A4%ED%8A%B8-Backtracking-N%EA%B3%BC-M-2</link>
            <guid>https://velog.io/@cassis_soda/%EC%BD%94%EB%94%A9-%ED%85%8C%EC%8A%A4%ED%8A%B8-Backtracking-N%EA%B3%BC-M-2</guid>
            <pubDate>Mon, 17 Mar 2025 05:45:06 GMT</pubDate>
            <description><![CDATA[<h3 id="0-링크">0. 링크</h3>
<p>&nbsp;&nbsp;&nbsp;&nbsp; <a href="https://www.acmicpc.net/problem/15650">백준 문제 링크</a></p>
<h3 id="1-설명">1. 설명</h3>
<blockquote>
</blockquote>
<p>자연수 N과 M이 주어졌을 때, 아래 조건을 만족하는 길이가 M인 수열을 모두 구하는 프로그램을 작성하시오.</p>
<blockquote>
<blockquote>
</blockquote>
</blockquote>
<ul>
<li>1부터 N까지 자연수 중에서 중복 없이 M개를 고른 수열</li>
<li>고른 수열은 오름차순이어야 한다.<blockquote>
</blockquote>
</li>
<li>입력<blockquote>
<blockquote>
</blockquote>
<p>첫째 줄에 자연수 N과 M이 주어진다. (1 ≤ M ≤ N ≤ 8)</p>
</blockquote>
</li>
<li>출력 <blockquote>
<blockquote>
<ul>
<li>한 줄에 하나씩 문제의 조건을 만족하는 수열을 출력한다. 중복되는 수열을 여러 번 출력하면 안되며, 각 수열은 공백으로 구분해서 출력해야 한다.</li>
<li>수열은 사전 순으로 증가하는 순서로 출력해야 한다.</li>
</ul>
</blockquote>
</blockquote>
</li>
</ul>
<h4 id="입출력-예시">입출력 예시</h4>
<table>
<thead>
<tr>
<th>input</th>
<th>output</th>
</tr>
</thead>
<tbody><tr>
<td><code>3 1</code></td>
<td>1<br>2<br>3</td>
</tr>
<tr>
<td><code>4 2</code></td>
<td>1 2<br>1 3<br>1 4<br>2 3<br>2 4<br>3 4</td>
</tr>
</tbody></table>
<h3 id="2-해설">2. 해설</h3>
<ol>
<li><code>M</code>개의 수를 갖는 순열을 만들기 위한 <code>list</code> 벡터 선언</li>
<li><code>Backtracking()</code>함수 내에서 아래와 같이 반복한다.<blockquote>
<p>1) <code>list.size() == m</code>이면  <code>return</code>후 출력
2) <code>i = count; i &lt;= N</code>인 동안 다음을 반복한다.</p>
<blockquote>
</blockquote>
<p> (1) <code>list.push_back(i)</code>
 (2) <code>Backtracking( i + 1 )</code>
 // 2와 <code>i = count</code>로 <code>list[0]</code> 보다 작거나 같은 것은 <code>list</code>에 올 수 없음을 보장한다.
 (3) <code>list.pop_back()</code></p>
</blockquote>
</li>
</ol>
<h3 id="3-코드">3. 코드</h3>
<pre><code class="language-cpp">#include &lt;iostream&gt;
#include &lt;vector&gt;

using namespace std;

int n, m;

vector &lt;int&gt; list;

void print()
{
    for (size_t i = 0; i &lt; list.size(); i++)
    {
        cout &lt;&lt; list[i] &lt;&lt; &quot; &quot;;
    }
    cout &lt;&lt; &#39;\n&#39;;
}

void Backtracking(int count)
{
   if (list.size() == m) {
       print();
       return;
   }

    for(size_t i = (size_t)count; i &lt;= n ; i++)
    {
        list.push_back(i);
        Backtracking(i + 1);
        list.pop_back();
    }

}
int main()
{
    cin &gt;&gt; n &gt;&gt; m;
    Backtracking(1);

}
</code></pre>
<h3 id="4-감상">4. 감상</h3>
<p>&nbsp;&nbsp;&nbsp;&nbsp; 앞의 <a href="https://velog.io/@cassis_soda/%EC%BD%94%EB%94%A9-%ED%85%8C%EC%8A%A4%ED%8A%B8-Backtracking-N%EA%B3%BC-M-1">N과 M (1)</a>과 같은 유형의 문제다. 다른 점은 1번은 순열이고, 2번은 조합의 문제였다. 순열애서는 <code>visited</code> 벡터가 쓰였다. 조합에서는 자신과 자신보다 작은 수는 검사하지 않기 위해 <code>Backtracking()</code>에서 인자 <code>count</code>를 받아서 사용했다. 다음은 중복을 허용하는 순열이다!</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[코딩 테스트] Backtracking > N과 M (1)]]></title>
            <link>https://velog.io/@cassis_soda/%EC%BD%94%EB%94%A9-%ED%85%8C%EC%8A%A4%ED%8A%B8-Backtracking-N%EA%B3%BC-M-1</link>
            <guid>https://velog.io/@cassis_soda/%EC%BD%94%EB%94%A9-%ED%85%8C%EC%8A%A4%ED%8A%B8-Backtracking-N%EA%B3%BC-M-1</guid>
            <pubDate>Mon, 17 Mar 2025 04:18:22 GMT</pubDate>
            <description><![CDATA[<h3 id="0-링크">0. 링크</h3>
<p>&nbsp;&nbsp;&nbsp;&nbsp; <a href="https://www.acmicpc.net/problem/15649">백준 문제 링크</a></p>
<h3 id="1-설명">1. 설명</h3>
<blockquote>
</blockquote>
<p>자연수 N과 M이 주어졌을 때, 아래 조건을 만족하는 길이가 M인 수열을 모두 구하는 프로그램을 작성하시오.</p>
<blockquote>
<blockquote>
</blockquote>
<p>1부터 N까지 자연수 중에서 중복 없이 M개를 고른 수열 </p>
</blockquote>
<ul>
<li>입력<blockquote>
<blockquote>
</blockquote>
<p>첫째 줄에 자연수 N과 M이 주어진다. (1 ≤ M ≤ N ≤ 8)</p>
</blockquote>
</li>
<li>출력<blockquote>
<blockquote>
<ul>
<li>한 줄에 하나씩 문제의 조건을 만족하는 수열을 출력한다. 중복되는 수열을 여러 번 출력하면 안되며, 각 수열은 공백으로 구분해서 출력해야 한다.</li>
<li>수열은 사전 순으로 증가하는 순서로 출력해야 한다.</li>
</ul>
</blockquote>
</blockquote>
</li>
</ul>
<h4 id="입출력-예시">입출력 예시</h4>
<table>
<thead>
<tr>
<th>input</th>
<th>output</th>
</tr>
</thead>
<tbody><tr>
<td><code>3 1</code></td>
<td>1<br>2<br>3</td>
</tr>
<tr>
<td><code>4 2</code></td>
<td>1 2<br>1 3<br>1 4<br>2 1<br>2 3<br>2 4<br>3 1<br>3 2<br>3 4<br>4 1<br>4 2<br>4 3</td>
</tr>
</tbody></table>
<h3 id="2-해설">2. 해설</h3>
<ol>
<li>중복을 허용하지 않는 순열을 고르기 위한 <code>visited</code> 벡터 선언</li>
<li><code>M</code>개의 수를 갖는 순열을 만들기 위한 <code>list</code> 벡터 선언</li>
<li><code>Backtracking()</code>함수 내에서 아래와 같이 반복한다.<blockquote>
<p>1) <code>list.size() == m</code>이면  <code>return</code>후 출력
2) <code>i = 1; i &lt;= N</code>인 동안 다음을 반복한다.</p>
<blockquote>
<p>(1)    <code>!visited[i]</code>일 경우</p>
<blockquote>
<p>(2) <code>visited[i] = true</code> 
 (3) <code>list.push_back(i)</code>
 (4) <code>Backtracking()</code>
 (5) <code>list.pop_back()</code>
 (6) <code>visited[i]  = false</code> // 2와 6을 통해, (1, 1)은 불가능하도록, (2, 1)과 (3, 1)은 가능하게 한다.</p>
</blockquote>
</blockquote>
</blockquote>
</li>
</ol>
<h3 id="3-코드">3. 코드</h3>
<pre><code class="language-cpp">#include &lt;iostream&gt;
#include &lt;vector&gt;

using namespace std;

int n,m;
vector &lt;int&gt; list;
vector &lt;bool&gt; visited;

void print()
{
    for(size_t i = 0; i &lt; list.size(); i++)
    {
        cout &lt;&lt; list[i] &lt;&lt; &quot; &quot;;
    }
    cout &lt;&lt; &#39;\n&#39;;
}

// 중복을 허용하지 않는 순열을 만들기
void Backtracking()
{
    if(list.size() == m)
    {
        print();
        return;
    }

    for(size_t i = 1; i &lt;= n ; i++)
    {
        if(!visited[i])
        {
            visited[i] = true;
            list.push_back(i);
            Backtracking();
            list.pop_back();
            visited[i] = false;
        }
    }
}

int main()
{
    cin &gt;&gt; n &gt;&gt; m;
    visited = vector&lt;bool&gt;(n+1, false);
    Backtracking();
}</code></pre>
<h3 id="4-감상">4. 감상</h3>
<p>&nbsp;&nbsp;&nbsp;&nbsp; 코테 스터디 시간에 풀어본 문제다. 백트래킹의 개념을 이해하는 문제였다. 앞으로 이어지는 두 개의 시리즈도 비슷하지만 다른 유형을 다룬다. 나를 위해 혹은 이 글을 보는 당신에게 도움이 될 <a href="https://www.acmicpc.net/workbook/view/7135">백트래킹 시리즈 모음</a>이다. </p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[코딩 테스트] Hashmap > 완주하지 못한 선수]]></title>
            <link>https://velog.io/@cassis_soda/%EC%BD%94%EB%94%A9-%ED%85%8C%EC%8A%A4%ED%8A%B8-Hashmap-%EC%99%84%EC%A3%BC%ED%95%98%EC%A7%80-%EB%AA%BB%ED%95%9C-%EC%84%A0%EC%88%98</link>
            <guid>https://velog.io/@cassis_soda/%EC%BD%94%EB%94%A9-%ED%85%8C%EC%8A%A4%ED%8A%B8-Hashmap-%EC%99%84%EC%A3%BC%ED%95%98%EC%A7%80-%EB%AA%BB%ED%95%9C-%EC%84%A0%EC%88%98</guid>
            <pubDate>Thu, 13 Mar 2025 01:12:47 GMT</pubDate>
            <description><![CDATA[<h3 id="0-링크">0. 링크</h3>
<p>&nbsp;&nbsp;&nbsp;&nbsp;<a href="https://school.programmers.co.kr/learn/courses/30/lessons/42576">프로그래머스 문제 링크</a></p>
<h3 id="1-설명">1. 설명</h3>
<blockquote>
<p>수많은 마라톤 선수들이 마라톤에 참여하였습니다. 단 한 명의 선수를 제외하고는 모든 선수가 마라톤을 완주하였습니다.
마라톤에 참여한 선수들의 이름이 담긴 배열 <code>participant</code>와 완주한 선수들의 이름이 담긴 배열 <code>completion</code>이 주어질 때, 완주하지 못한 선수의 이름을 <code>return</code> 하도록 <code>solution</code> 함수를 작성해주세요.</p>
<blockquote>
<ul>
<li>제한사항<ul>
<li>마라톤 경기에 참여한 선수의 수는 1명 이상 100,000명 이하입니다.</li>
<li><code>completion</code>의 길이는 <code>participant</code>의 길이보다 1 작습니다.</li>
<li>참가자의 이름은 1개 이상 20개 이하의 알파벳 소문자로 이루어져 있습니다.</li>
<li>가자 중에는 동명이인이 있을 수 있습니다.</li>
</ul>
</li>
</ul>
</blockquote>
</blockquote>
<h4 id="입출력-예시">입출력 예시</h4>
<table>
<thead>
<tr>
<th>participant</th>
<th>completion</th>
<th>return</th>
</tr>
</thead>
<tbody><tr>
<td><code>[&quot;leo&quot;, &quot;kiki&quot;, &quot;eden&quot;]</code></td>
<td><code>[&quot;eden&quot;, &quot;kiki&quot;]</code></td>
<td><code>&quot;leo&quot;</code></td>
</tr>
<tr>
<td><code>[&quot;marina&quot;, &quot;josipa&quot;, &quot;nikola&quot;, &quot;vinko&quot;, &quot;filipa&quot;]</code></td>
<td><code>[&quot;josipa&quot;, &quot;filipa&quot;, &quot;marina&quot;, &quot;nikola&quot;]</code></td>
<td><code>&quot;vinko&quot;</code></td>
</tr>
<tr>
<td><code>[&quot;mislav&quot;, &quot;stanko&quot;, &quot;mislav&quot;, &quot;ana&quot;]</code></td>
<td><code>[&quot;stanko&quot;, &quot;ana&quot;, &quot;mislav&quot;]</code></td>
<td><code>&quot;mislav&quot;</code></td>
</tr>
</tbody></table>
<h4 id="입출력-예시-설명">입출력 예시 설명</h4>
<p>입출력 예 설명
예제 #1
<code>&quot;leo&quot;</code>는 참여자 명단에는 있지만, 완주자 명단에는 없기 때문에 완주하지 못했습니다.</p>
<p>예제 #2
<code>&quot;vinko&quot;</code>는 참여자 명단에는 있지만, 완주자 명단에는 없기 때문에 완주하지 못했습니다.</p>
<p>예제 #3
<code>&quot;mislav&quot;</code>는 참여자 명단에는 두 명이 있지만, 완주자 명단에는 한 명밖에 없기 때문에 한명은 완주하지 못했습니다.</p>
<h3 id="2-해설">2. 해설</h3>
<ol>
<li>참가자의 이름과 등장 횟수를 기록할 2차원 Hashmap(unordered_map) 선언</li>
<li>반복문으로 <code>participant</code>의 크기만큼 순회하며 Hashmap에 있는 이름에 1씩 더해준다.</li>
<li>반복문으로 <code>completion</code>의 크기만큼 순회하며 Hashmap에 있는 이름에 1씩 빼준다.
 3-1. 이 과정에서 <code>participant</code>에는 있지만 <code>completion</code>에는 없는 이름을 거를 수 있다.</li>
<li>map을 순회하면서 1이 남아있다면 그 요소를 리턴</li>
</ol>
<h3 id="3-코드">3. 코드</h3>
<pre><code class="language-cpp">#include &lt;string&gt;
#include &lt;vector&gt;
#include &lt;unordered_map&gt;

using namespace std;

unordered_map&lt;string, int&gt; map;

string solution(vector&lt;string&gt; participant, vector&lt;string&gt; completion) {

    //반복문으로 participant 만큼 돌면서 map에 있는 이름에 1씩 더해준다.
    for(size_t i = 0; i &lt; participant.size(); i++)
    {
        map[participant[i]]++;
    }
    // completion에 이름이 있다면, 숫자를 하나 줄여준다.
    for (auto&amp; c : completion)
    {
        map[c]--;
    }
    //map을 순회하면서 1이 남아있다면 그 요소를 리턴
    for(auto&amp; m : map)
    {
        if (m.second == 1)
            return m.first;
    }

}</code></pre>
<h3 id="4-감상">4. 감상</h3>
<p>&nbsp;&nbsp;&nbsp;&nbsp; 이전에 python으로 풀어봤었던 문제였다. 접근법은 같았으나, 내 방법이 조금 더 복잡했다. 해결법을 알고 있어서 해설을 이해하는 것이 어렵지 않았다.</p>
]]></description>
        </item>
    </channel>
</rss>